From 6d8f1667228d74e8c9afca52bafb50c0730a0d83 Mon Sep 17 00:00:00 2001 From: Erich Moraga <33645296+ErichMoraga@users.noreply.github.com> Date: Sun, 18 Dec 2022 22:09:40 -0600 Subject: [PATCH 001/484] Add several missing HIGHLANDER_TSS2 firmwares (#26801) * Add several missing HIGHLANDER_TSS2 firmwares `@randywf#0891` 2023 Toyota Highlander (ICE) DongleID/route d9049fcd50225c9d|2022-12-14--12-16-16 * docs.py gen'd CARS.md w/ 2023 Highlander update --- docs/CARS.md | 2 +- selfdrive/car/toyota/values.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index 4f1a7cb652..ce067609aa 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -180,7 +180,7 @@ A supported vehicle is one that just works when you install a comma three. All s |Toyota|Corolla Hatchback 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| |Toyota|Corolla Hybrid 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| |Toyota|Highlander 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Toyota|Highlander 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Highlander 2020-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| |Toyota|Highlander Hybrid 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| |Toyota|Highlander Hybrid 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| |Toyota|Mirai 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index 8ff06a591a..80c0ccbb83 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -129,7 +129,7 @@ CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = { ToyotaCarInfo("Lexus UX Hybrid 2019-22"), ], CAR.HIGHLANDER: ToyotaCarInfo("Toyota Highlander 2017-19", video_link="https://www.youtube.com/watch?v=0wS0wXSLzoo"), - CAR.HIGHLANDER_TSS2: ToyotaCarInfo("Toyota Highlander 2020-22"), + CAR.HIGHLANDER_TSS2: ToyotaCarInfo("Toyota Highlander 2020-23"), CAR.HIGHLANDERH: ToyotaCarInfo("Toyota Highlander Hybrid 2017-19"), CAR.HIGHLANDERH_TSS2: ToyotaCarInfo("Toyota Highlander Hybrid 2020-22"), CAR.PRIUS: [ @@ -980,12 +980,14 @@ FW_VERSIONS = { b'8965B48241\x00\x00\x00\x00\x00\x00', b'8965B48310\x00\x00\x00\x00\x00\x00', b'8965B48320\x00\x00\x00\x00\x00\x00', + b'8965B48400\x00\x00\x00\x00\x00\x00', ], (Ecu.abs, 0x7b0, None): [ b'\x01F15260E051\x00\x00\x00\x00\x00\x00', b'\x01F15260E061\x00\x00\x00\x00\x00\x00', b'\x01F15260E110\x00\x00\x00\x00\x00\x00', b'\x01F15260E170\x00\x00\x00\x00\x00\x00', + b'\x01F15260E05300\x00\x00\x00\x00', ], (Ecu.engine, 0x700, None): [ b'\x01896630E62100\x00\x00\x00\x00', @@ -1003,6 +1005,7 @@ FW_VERSIONS = { b'\x01896630ED9100\x00\x00\x00\x00', b'\x01896630EE1000\x00\x00\x00\x00', b'\x01896630EE1100\x00\x00\x00\x00', + b'\x01896630EG5000\x00\x00\x00\x00', ], (Ecu.fwdRadar, 0x750, 0xf): [ b'\x018821F3301400\x00\x00\x00\x00', @@ -1013,6 +1016,7 @@ FW_VERSIONS = { b'\x028646F0E02100\x00\x00\x00\x008646G2601200\x00\x00\x00\x00', b'\x028646F4803000\x00\x00\x00\x008646G5301200\x00\x00\x00\x00', b'\x028646F4803000\x00\x00\x00\x008646G3304000\x00\x00\x00\x00', + b'\x028646F4803200\x00\x00\x00\x008646G3304000\x00\x00\x00\x00', ], }, CAR.HIGHLANDERH_TSS2: { From 777dc9dcd2ef3c1011d487f7584f3da9c396e513 Mon Sep 17 00:00:00 2001 From: Samer Khatib <56684133+skhatib07@users.noreply.github.com> Date: Tue, 20 Dec 2022 16:44:33 -0500 Subject: [PATCH 002/484] Added unofficial support for Ubuntu 22.10 in ubuntu_setup.sh (#26828) * Added unofficial support for Ubuntu 22.10 in ubuntu_setup.sh Added a conditional to check if $UBUNTU_CODENAME is equal to "kinetic" in ubuntu_setup.sh. Installs the same packages as Ubuntu 22.04 LTS Jammy Jellyfish. * Attempt #2 at adding unofficial support for Ubuntu 22.10 in ubuntu_setup.sh Still checks to see if $UBUNTU_CODENAME is equal to "kinetic" I just fixed some errors that were appearing beforehand * Update tools/ubuntu_setup.sh * Update tools/ubuntu_setup.sh * Update tools/ubuntu_setup.sh * Update tools/ubuntu_setup.sh Co-authored-by: Adeeb Shihadeh --- tools/ubuntu_setup.sh | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tools/ubuntu_setup.sh b/tools/ubuntu_setup.sh index 89d33a6127..09296ef94d 100755 --- a/tools/ubuntu_setup.sh +++ b/tools/ubuntu_setup.sh @@ -82,7 +82,7 @@ function install_ubuntu_common_requirements() { } # Install Ubuntu 22.04 LTS packages -function install_ubuntu_jammy_requirements() { +function install_ubuntu_lts_latest_requirements() { install_ubuntu_common_requirements $SUDO apt-get install -y --no-install-recommends \ @@ -108,7 +108,10 @@ if [ -f "/etc/os-release" ]; then source /etc/os-release case "$VERSION_CODENAME" in "jammy") - install_ubuntu_jammy_requirements + install_ubuntu_lts_latest_requirements + ;; + "kinetic") + install_ubuntu_lts_latest_requirements ;; "focal") install_ubuntu_focal_requirements @@ -120,8 +123,8 @@ if [ -f "/etc/os-release" ]; then if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1 fi - if [ "$UBUNTU_CODENAME" = "jammy" ]; then - install_ubuntu_jammy_requirements + if [ "$UBUNTU_CODENAME" = "jammy" ] || [ "$UBUNTU_CODENAME" = "kinetic" ]; then + install_ubuntu_lts_latest_requirements else install_ubuntu_focal_requirements fi From 49ae806534a32858a327745698ca2f36e5210803 Mon Sep 17 00:00:00 2001 From: Hamid Ebadi Date: Tue, 20 Dec 2022 22:45:06 +0100 Subject: [PATCH 003/484] carla sim: fix accelerometer sensor data (#26794) --- tools/sim/bridge.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/sim/bridge.py b/tools/sim/bridge.py index f72927ba9a..f0694078e2 100755 --- a/tools/sim/bridge.py +++ b/tools/sim/bridge.py @@ -128,7 +128,7 @@ def imu_callback(imu, vehicle_state): vehicle_state.bearing_deg = math.degrees(imu.compass) dat = messaging.new_message('accelerometer') dat.accelerometer.sensor = 4 - dat.accelerometer.type = 0x1 + dat.accelerometer.type = 0x10 dat.accelerometer.timestamp = dat.logMonoTime # TODO: use the IMU timestamp dat.accelerometer.init('acceleration') dat.accelerometer.acceleration.v = [imu.accelerometer.x, imu.accelerometer.y, imu.accelerometer.z] From 5ab2fc16ab5b883f3fa151d0e7362bc73545ca32 Mon Sep 17 00:00:00 2001 From: Mitchell Goff Date: Tue, 20 Dec 2022 14:19:21 -0800 Subject: [PATCH 004/484] model_replay: use last N frames of segment so navRoute is valid (#26790) * model_replay: use last N frames of segment so navRoute is valid * Use llk logMonoTime for MapRenderState.locationMonoTime * Record mapRenderState * update refs Co-authored-by: Comma Device --- selfdrive/navd/map_renderer.cc | 4 ++-- selfdrive/test/process_replay/model_replay.py | 16 +++++++++++----- .../test/process_replay/model_replay_ref_commit | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/selfdrive/navd/map_renderer.cc b/selfdrive/navd/map_renderer.cc index 91f31d06dc..51676bb3a3 100644 --- a/selfdrive/navd/map_renderer.cc +++ b/selfdrive/navd/map_renderer.cc @@ -169,7 +169,7 @@ void MapRenderer::publish(const double render_time) { VisionBuf* buf = vipc_server->get_buffer(VisionStreamType::VISION_STREAM_MAP); VisionIpcBufExtra extra = { .frame_id = frame_id, - .timestamp_sof = sm->rcv_time("liveLocationKalman"), + .timestamp_sof = (*sm)["liveLocationKalman"].getLogMonoTime(), .timestamp_eof = ts, }; @@ -206,7 +206,7 @@ void MapRenderer::publish(const double render_time) { // Send state msg MessageBuilder msg; auto state = msg.initEvent().initMapRenderState(); - state.setLocationMonoTime(sm->rcv_time("liveLocationKalman")); + state.setLocationMonoTime((*sm)["liveLocationKalman"].getLogMonoTime()); state.setRenderTime(render_time); state.setFrameId(frame_id); pm->send("mapRenderState", msg); diff --git a/selfdrive/test/process_replay/model_replay.py b/selfdrive/test/process_replay/model_replay.py index ce861b37e6..324801fead 100755 --- a/selfdrive/test/process_replay/model_replay.py +++ b/selfdrive/test/process_replay/model_replay.py @@ -43,12 +43,12 @@ def replace_calib(msg, calib): def nav_model_replay(lr): - sm = messaging.SubMaster(['navModel', 'navThumbnail']) + sm = messaging.SubMaster(['navModel', 'navThumbnail', 'mapRenderState']) pm = messaging.PubMaster(['liveLocationKalman', 'navRoute']) nav = [m for m in lr if m.which() == 'navRoute'] llk = [m for m in lr if m.which() == 'liveLocationKalman'] - assert len(nav) > 0 and len(llk) >= NAV_FRAMES + assert len(nav) > 0 and len(llk) >= NAV_FRAMES and nav[0].logMonoTime < llk[-NAV_FRAMES].logMonoTime log_msgs = [] try: @@ -59,8 +59,8 @@ def nav_model_replay(lr): # setup position and route for _ in range(10): - for s in (llk, nav): - pm.send(s[0].which(), s[0].as_builder().to_bytes()) + for s in (llk[-NAV_FRAMES], nav[0]): + pm.send(s.which(), s.as_builder().to_bytes()) sm.update(1000) if sm.updated['navModel']: break @@ -74,12 +74,16 @@ def nav_model_replay(lr): sm.update(0) # run replay - for n in range(NAV_FRAMES): + for n in range(len(llk) - NAV_FRAMES, len(llk)): pm.send(llk[n].which(), llk[n].as_builder().to_bytes()) m = messaging.recv_one(sm.sock['navThumbnail']) assert m is not None, f"no navThumbnail, frame={n}" log_msgs.append(m) + m = messaging.recv_one(sm.sock['mapRenderState']) + assert m is not None, f"no mapRenderState, frame={n}" + log_msgs.append(m) + m = messaging.recv_one(sm.sock['navModel']) assert m is not None, f"no navModel response, frame={n}" log_msgs.append(m) @@ -231,6 +235,8 @@ if __name__ == "__main__": 'navModel.dspExecutionTime', 'navModel.modelExecutionTime', 'navThumbnail.timestampEof', + 'mapRenderState.locationMonoTime', + 'mapRenderState.renderTime', ] if PC: ignore += [ diff --git a/selfdrive/test/process_replay/model_replay_ref_commit b/selfdrive/test/process_replay/model_replay_ref_commit index aa204e0f8a..22e021dcaf 100644 --- a/selfdrive/test/process_replay/model_replay_ref_commit +++ b/selfdrive/test/process_replay/model_replay_ref_commit @@ -1 +1 @@ -4ff972367fdb9546be68ee0ba0d45cf4f839dae7 +db587bfef2317c5a3471632ac47381457e1be853 \ No newline at end of file From f75b640cf57d8f1a54d2d421194178d3ea9d38f1 Mon Sep 17 00:00:00 2001 From: lucentheart <97204067+lucentheart@users.noreply.github.com> Date: Tue, 20 Dec 2022 20:57:07 -0500 Subject: [PATCH 005/484] Add missing FW versions for 2019 Honda Passport (#26815) Update values.py --- selfdrive/car/honda/values.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/selfdrive/car/honda/values.py b/selfdrive/car/honda/values.py index 905c9f4b4f..2906b23bda 100644 --- a/selfdrive/car/honda/values.py +++ b/selfdrive/car/honda/values.py @@ -1167,6 +1167,7 @@ FW_VERSIONS = { (Ecu.programmedFuelInjection, 0x18da10f1, None): [ b'37805-RLV-B220\x00\x00', b'37805-RLV-B210\x00\x00', + b'37805-RLV-L160\x00\x00', ], (Ecu.eps, 0x18da30f1, None): [ b'39990-TGS-A230\x00\x00', @@ -1177,6 +1178,7 @@ FW_VERSIONS = { ], (Ecu.gateway, 0x18daeff1, None): [ b'38897-TG7-A040\x00\x00', + b'38897-TG7-A030\x00\x00', ], (Ecu.srs, 0x18da53f1, None): [ b'77959-TGS-A010\x00\x00', @@ -1186,10 +1188,12 @@ FW_VERSIONS = { ], (Ecu.transmission, 0x18da1ef1, None): [ b'28101-5EZ-A600\x00\x00', + b'28101-5EZ-A430\x00\x00', ], (Ecu.combinationMeter, 0x18da60f1, None): [ b'78109-TGS-AT20\x00\x00', b'78109-TGS-AX20\x00\x00', + b'78109-TGS-AJ20\x00\x00', ], (Ecu.vsa, 0x18da28f1, None): [ b'57114-TGS-A530\x00\x00', From c4d2e8bcf4ce7187c92a4321e0ab2d99fa0791a6 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 20 Dec 2022 20:03:24 -0800 Subject: [PATCH 006/484] bump panda --- panda | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda b/panda index 9e5c28e568..345147fe2b 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 9e5c28e568a1efe4bac340b48b3c16aafc82e84c +Subproject commit 345147fe2bc3a06c44709426f9fcd298588b9fe4 From af561393fe0d535315a52e35556697667886089d Mon Sep 17 00:00:00 2001 From: Robbe Derks Date: Wed, 28 Dec 2022 15:38:10 +0100 Subject: [PATCH 007/484] Tesla non-zero stopAccel (#26840) remove stopAccel override --- selfdrive/car/tesla/interface.py | 1 - 1 file changed, 1 deletion(-) diff --git a/selfdrive/car/tesla/interface.py b/selfdrive/car/tesla/interface.py index 49e06d8923..6a73472108 100755 --- a/selfdrive/car/tesla/interface.py +++ b/selfdrive/car/tesla/interface.py @@ -23,7 +23,6 @@ class CarInterface(CarInterfaceBase): ret.longitudinalTuning.kpV = [0] ret.longitudinalTuning.kiBP = [0] ret.longitudinalTuning.kiV = [0] - ret.stopAccel = 0.0 ret.longitudinalActuatorDelayUpperBound = 0.5 # s ret.radarTimeStep = (1.0 / 8) # 8Hz From f15a5c9ad697e41eb273875577855ec5edede7f4 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 29 Dec 2022 08:47:49 +0800 Subject: [PATCH 008/484] cabana: align y axis correctly (#26837) --- tools/cabana/chartswidget.cc | 39 ++++++++++++++++++++++++------------ tools/cabana/chartswidget.h | 5 ++++- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index bc28666a34..da39fb1a66 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -156,6 +156,7 @@ void ChartsWidget::showChart(const QString &id, const Signal *sig, bool show, bo QObject::connect(chart, &ChartView::zoomReset, this, &ChartsWidget::zoomReset); QObject::connect(chart, &ChartView::seriesRemoved, this, &ChartsWidget::seriesChanged); QObject::connect(chart, &ChartView::seriesAdded, this, &ChartsWidget::seriesChanged); + QObject::connect(chart, &ChartView::axisYUpdated, this, &ChartsWidget::alignCharts); charts_layout->insertWidget(0, chart); charts.push_back(chart); } @@ -171,6 +172,7 @@ void ChartsWidget::removeChart(ChartView *chart) { charts.removeOne(chart); chart->deleteLater(); updateToolBar(); + alignCharts(); emit seriesChanged(); } @@ -183,6 +185,16 @@ void ChartsWidget::removeAll() { emit seriesChanged(); } +void ChartsWidget::alignCharts() { + int plot_left = 0; + for (auto c : charts) { + plot_left = qMax((qreal)plot_left, c->getYAsixLabelWidth()); + } + for (auto c : charts) { + c->setPlotAreaLeftPosition(plot_left); + } +} + bool ChartsWidget::eventFilter(QObject *obj, QEvent *event) { if (obj != this && event->type() == QEvent::Close) { emit dock_btn->triggered(); @@ -202,7 +214,6 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { chart->addAxis(axis_y, Qt::AlignLeft); chart->legend()->setShowToolTips(true); chart->layout()->setContentsMargins(0, 0, 0, 0); - chart->setMargins(QMargins(20, 11, 11, 11)); QToolButton *remove_btn = new QToolButton(); remove_btn->setText("X"); @@ -234,6 +245,19 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { QObject::connect(manage_btn, &QToolButton::clicked, this, &ChartView::manageSeries); } +qreal ChartView::getYAsixLabelWidth() const { + QFontMetrics fm(axis_y->labelsFont()); + int n = qMax(int(-qFloor(std::log10((axis_y->max() - axis_y->min()) / (axis_y->tickCount() - 1)))), 0) + 1; + return qMax(fm.width(QString::number(axis_y->min(), 'f', n)), fm.width(QString::number(axis_y->max(), 'f', n))) + 20; +} + +void ChartView::setPlotAreaLeftPosition(int pos) { + if (std::ceil(chart()->plotArea().left()) != pos) { + const float left_margin = chart()->margins().left() + pos - chart()->plotArea().left(); + chart()->setMargins(QMargins(left_margin, 11, 11, 11)); + } +} + void ChartView::addSeries(const QString &msg_id, const Signal *sig) { QLineSeries *series = new QLineSeries(this); series->setUseOpenGL(true); @@ -361,16 +385,6 @@ void ChartView::setDisplayRange(double min, double max) { } } -void ChartView::adjustChartMargins() { - // TODO: Remove hardcoded aligned_pos - const int aligned_pos = 60; - if ((int)chart()->plotArea().left() != aligned_pos) { - const float left_margin = chart()->margins().left() + aligned_pos - chart()->plotArea().left(); - chart()->setMargins(QMargins(left_margin, 11, 11, 11)); - scene()->invalidate({}, QGraphicsScene::ForegroundLayer); - } -} - void ChartView::updateSeries(const Signal *sig) { auto events = can->events(); if (!events || sigs.isEmpty()) return; @@ -436,8 +450,7 @@ void ChartView::updateAxisY() { double range = max_y - min_y; applyNiceNumbers(min_y - range * 0.05, max_y + range * 0.05); } - - adjustChartMargins(); + QTimer::singleShot(0, this, &ChartView::axisYUpdated); } void ChartView::applyNiceNumbers(qreal min, qreal max) { diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index 1799a25488..7a8325382d 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -26,6 +26,8 @@ public: void updateSeries(const Signal *sig = nullptr); void setEventsRange(const std::pair &range); void setDisplayRange(double min, double max); + void setPlotAreaLeftPosition(int pos); + qreal getYAsixLabelWidth() const; struct SigItem { QString msg_id; @@ -44,6 +46,7 @@ signals: void zoomIn(double min, double max); void zoomReset(); void remove(); + void axisYUpdated(); private slots: void msgRemoved(uint32_t address); @@ -58,7 +61,6 @@ private: void mouseMoveEvent(QMouseEvent *ev) override; void leaveEvent(QEvent *event) override; void resizeEvent(QResizeEvent *event) override; - void adjustChartMargins(); void updateAxisY(); void updateTitle(); void updateFromSettings(); @@ -89,6 +91,7 @@ signals: void seriesChanged(); private: + void alignCharts(); void removeChart(ChartView *chart); void eventsMerged(); void updateState(); From 65509669b676bad41d57443454dbea8ffbc1c8f7 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 29 Dec 2022 08:51:02 +0800 Subject: [PATCH 009/484] Cabana: add tool to find similar bits (#26834) * find similar bits * set window title --- tools/cabana/SConscript | 2 +- tools/cabana/mainwin.cc | 8 ++ tools/cabana/mainwin.h | 2 + tools/cabana/tools/findsimilarbits.cc | 115 ++++++++++++++++++++++++++ tools/cabana/tools/findsimilarbits.h | 23 ++++++ 5 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 tools/cabana/tools/findsimilarbits.cc create mode 100644 tools/cabana/tools/findsimilarbits.h diff --git a/tools/cabana/SConscript b/tools/cabana/SConscript index 52fe9e346d..718e2e50af 100644 --- a/tools/cabana/SConscript +++ b/tools/cabana/SConscript @@ -18,7 +18,7 @@ cabana_env = qt_env.Clone() prev_moc_path = cabana_env['QT_MOCHPREFIX'] cabana_env['QT_MOCHPREFIX'] = os.path.dirname(prev_moc_path) + '/cabana/moc_' cabana_lib = cabana_env.Library("cabana_lib", ['mainwin.cc', 'binaryview.cc', 'chartswidget.cc', 'historylog.cc', 'videowidget.cc', 'signaledit.cc', 'dbcmanager.cc', - 'canmessages.cc', 'commands.cc', 'messageswidget.cc', 'settings.cc', 'detailwidget.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) + 'canmessages.cc', 'commands.cc', 'messageswidget.cc', 'settings.cc', 'detailwidget.cc', 'tools/findsimilarbits.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) cabana_env.Program('_cabana', ['cabana.cc', cabana_lib], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) if GetOption('test'): diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index f14d522c20..4ae53223a8 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -133,6 +133,9 @@ void MainWindow::createActions() { commands_act->setDefaultWidget(undo_view); commands_menu->addAction(commands_act); + QMenu *tools_menu = menuBar()->addMenu(tr("&Tools")); + tools_menu->addAction(tr("Find &Similar Bits"), this, &MainWindow::findSimilarBits); + QMenu *help_menu = menuBar()->addMenu(tr("&Help")); help_menu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt); } @@ -283,3 +286,8 @@ void MainWindow::setOption() { SettingsDlg dlg(this); dlg.exec(); } + +void MainWindow::findSimilarBits() { + FindSimilarBitsDlg dlg(this); + dlg.exec(); +} diff --git a/tools/cabana/mainwin.h b/tools/cabana/mainwin.h index 17c513d809..5d3ba470a1 100644 --- a/tools/cabana/mainwin.h +++ b/tools/cabana/mainwin.h @@ -11,6 +11,7 @@ #include "tools/cabana/detailwidget.h" #include "tools/cabana/messageswidget.h" #include "tools/cabana/videowidget.h" +#include "tools/cabana/tools/findsimilarbits.h" class MainWindow : public QMainWindow { Q_OBJECT @@ -41,6 +42,7 @@ protected: void DBCFileChanged(); void updateDownloadProgress(uint64_t cur, uint64_t total, bool success); void setOption(); + void findSimilarBits(); VideoWidget *video_widget; MessagesWidget *messages_widget; diff --git a/tools/cabana/tools/findsimilarbits.cc b/tools/cabana/tools/findsimilarbits.cc new file mode 100644 index 0000000000..5fa5bcf7b8 --- /dev/null +++ b/tools/cabana/tools/findsimilarbits.cc @@ -0,0 +1,115 @@ +#include "tools/cabana/tools/findsimilarbits.h" + +#include +#include +#include +#include +#include +#include + +#include "tools/cabana/canmessages.h" +#include "tools/cabana/dbcmanager.h" + +FindSimilarBitsDlg::FindSimilarBitsDlg(QWidget *parent) : QDialog(parent) { + setWindowTitle(tr("Find similar bits")); + QVBoxLayout *main_layout = new QVBoxLayout(this); + + QHBoxLayout *form_layout = new QHBoxLayout(); + bus_combo = new QComboBox(this); + QSet bus_set; + for (auto it = can->can_msgs.begin(); it != can->can_msgs.end(); ++it) { + bus_set << DBCManager::parseId(it.key()).first; + } + for (uint8_t bus : bus_set) { + bus_combo->addItem(QString::number(bus)); + } + bus_combo->model()->sort(0); + bus_combo->setCurrentIndex(0); + form_layout->addWidget(new QLabel("Bus")); + form_layout->addWidget(bus_combo); + + bit_combo = new QComboBox(this); + bit_combo->addItems({"0", "1"}); + bit_combo->setCurrentIndex(1); + form_layout->addWidget(new QLabel("Bit")); + form_layout->addWidget(bit_combo); + + min_msgs = new QLineEdit(this); + min_msgs->setValidator(new QIntValidator(this)); + min_msgs->setText("100"); + form_layout->addWidget(new QLabel("Min msg count")); + form_layout->addWidget(min_msgs); + search_btn = new QPushButton(tr("&Find"), this); + form_layout->addWidget(search_btn); + form_layout->addStretch(1); + main_layout->addLayout(form_layout); + + table = new QTableWidget(this); + table->setEditTriggers(QAbstractItemView::NoEditTriggers); + table->horizontalHeader()->setStretchLastSection(true); + main_layout->addWidget(table); + + setMinimumSize({700, 500}); + QObject::connect(search_btn, &QPushButton::clicked, this, &FindSimilarBitsDlg::find); +} + +void FindSimilarBitsDlg::find() { + search_btn->setEnabled(false); + table->clear(); + auto msg_mismatched = calcBits(bus_combo->currentText().toUInt(), bit_combo->currentIndex(), min_msgs->text().toInt()); + table->setRowCount(msg_mismatched.size()); + table->setColumnCount(6); + table->setHorizontalHeaderLabels({"address", "byte idx", "bit idx", "mismatches", "total", "perc%"}); + for (int i = 0; i < msg_mismatched.size(); ++i) { + auto &m = msg_mismatched[i]; + table->setItem(i, 0, new QTableWidgetItem(QString("%1").arg(m.address, 1, 16))); + table->setItem(i, 1, new QTableWidgetItem(QString::number(m.byte_idx))); + table->setItem(i, 2, new QTableWidgetItem(QString::number(m.bit_idx))); + table->setItem(i, 3, new QTableWidgetItem(QString::number(m.mismatches))); + table->setItem(i, 4, new QTableWidgetItem(QString::number(m.total))); + table->setItem(i, 5, new QTableWidgetItem(QString::number(m.perc))); + } + search_btn->setEnabled(true); +} + +QList FindSimilarBitsDlg::calcBits(uint8_t bus, int bit_to_find, int min_msgs_cnt) { + QHash> mismatches; + QHash msg_count; + auto events = can->events(); + for (auto e : *events) { + if (e->which == cereal::Event::Which::CAN) { + for (const auto &c : e->event.getCan()) { + if (c.getSrc() == bus) { + uint32_t address = c.getAddress(); + ++msg_count[address]; + auto &mismatched = mismatches[address]; + const auto dat = c.getDat(); + if (mismatched.size() < dat.size() * 8) { + mismatched.resize(dat.size() * 8); + } + for (int i = 0; i < dat.size(); ++i) { + for (int j = 0; j < 8; ++j) { + int bit = ((dat[i] >> (7 - j)) & 1) != 0; + mismatched[i * 8 + j] += (bit != bit_to_find); + } + } + } + } + } + } + + QList result; + result.reserve(mismatches.size()); + for (auto it = mismatches.begin(); it != mismatches.end(); ++it) { + if (auto cnt = msg_count[it.key()]; cnt > min_msgs_cnt) { + auto &mismatched = it.value(); + for (int i = 0; i < mismatched.size(); ++i) { + if (uint32_t perc = (mismatched[i] / (double)cnt) * 100; perc < 50) { + result.push_back({it.key(), (uint32_t)i / 8, (uint32_t)i % 8, mismatched[i], cnt, perc}); + } + } + } + } + std::sort(result.begin(), result.end(), [](auto &l, auto &r) { return l.perc > r.perc; }); + return result; +} diff --git a/tools/cabana/tools/findsimilarbits.h b/tools/cabana/tools/findsimilarbits.h new file mode 100644 index 0000000000..79db4a1c69 --- /dev/null +++ b/tools/cabana/tools/findsimilarbits.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include +#include + +class FindSimilarBitsDlg : public QDialog { +public: + FindSimilarBitsDlg(QWidget *parent); + +private: + struct mismatched_struct { + uint32_t address, byte_idx, bit_idx, mismatches, total, perc; + }; + QList calcBits(uint8_t bus, int bit_to_find, int min_msgs_cnt); + void find(); + + QTableWidget *table; + QComboBox *bus_combo, *bit_combo; + QPushButton *search_btn; + QLineEdit *min_msgs; +}; From 7ae87923864af4cd1378eeccc23c002fc66447e5 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 29 Dec 2022 10:21:06 +0800 Subject: [PATCH 010/484] Cabana: make all panels dockable into each other (#26838) --- tools/cabana/cabana.cc | 4 +- tools/cabana/chartswidget.cc | 4 +- tools/cabana/mainwin.cc | 100 ++++++++++++++++------------------- tools/cabana/mainwin.h | 6 +-- tools/cabana/settings.cc | 6 ++- tools/cabana/settings.h | 2 +- tools/cabana/videowidget.cc | 13 +++-- tools/cabana/videowidget.h | 2 +- 8 files changed, 70 insertions(+), 67 deletions(-) diff --git a/tools/cabana/cabana.cc b/tools/cabana/cabana.cc index 51418e293f..4f037ba595 100644 --- a/tools/cabana/cabana.cc +++ b/tools/cabana/cabana.cc @@ -6,6 +6,8 @@ #include "tools/cabana/mainwin.h" int main(int argc, char *argv[]) { + QCoreApplication::setApplicationName("Cabana"); + QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); initApp(argc, argv); QApplication app(argc, argv); @@ -36,7 +38,7 @@ int main(int argc, char *argv[]) { int ret = 0; if (p.loadRoute(route, cmd_parser.value("data_dir"), replay_flags)) { MainWindow w; - w.showMaximized(); + w.show(); ret = app.exec(); } return ret; diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index da39fb1a66..71886a113f 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -1,5 +1,6 @@ #include "tools/cabana/chartswidget.h" +#include #include #include #include @@ -42,7 +43,8 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { main_layout->addWidget(charts_scroll); max_chart_range = settings.max_chart_x_range; - use_dark_theme = palette().color(QPalette::WindowText).value() > palette().color(QPalette::Background).value(); + use_dark_theme = QApplication::style()->standardPalette().color(QPalette::WindowText).value() > + QApplication::style()->standardPalette().color(QPalette::Background).value(); updateToolBar(); QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &ChartsWidget::removeAll); diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index 4ae53223a8..43aa330029 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -1,19 +1,16 @@ #include "tools/cabana/mainwin.h" #include -#include #include #include +#include #include #include #include -#include #include #include #include #include -#include -#include #include #include #include @@ -25,58 +22,20 @@ void qLogMessageHandler(QtMsgType type, const QMessageLogContext &context, const } MainWindow::MainWindow() : QMainWindow() { - setWindowTitle("Cabana"); - QWidget *central_widget = new QWidget(this); - QHBoxLayout *main_layout = new QHBoxLayout(central_widget); - main_layout->setContentsMargins(11, 11, 11, 0); - main_layout->setSpacing(0); - - splitter = new QSplitter(Qt::Horizontal, this); - splitter->setHandleWidth(11); - - QWidget *messages_container = new QWidget(this); - QVBoxLayout *messages_layout = new QVBoxLayout(messages_container); - messages_layout->setContentsMargins(0, 0, 0, 0); - - // left panel - dbc_combo = createDBCSelector(); - messages_layout->addWidget(dbc_combo); - messages_widget = new MessagesWidget(this); - messages_layout->addWidget(messages_widget); - splitter->addWidget(messages_container); - - charts_widget = new ChartsWidget(this); + createDockWindows(); detail_widget = new DetailWidget(charts_widget, this); - splitter->addWidget(detail_widget); - if (!settings.splitter_state.isEmpty()) { - splitter->restoreState(settings.splitter_state); - } - main_layout->addWidget(splitter); - - // right widgets - QWidget *right_container = new QWidget(this); - right_container->setFixedWidth(640); - r_layout = new QVBoxLayout(right_container); - r_layout->setContentsMargins(11, 0, 0, 0); - QHBoxLayout *right_hlayout = new QHBoxLayout(); - fingerprint_label = new QLabel(this); - right_hlayout->addWidget(fingerprint_label, 0, Qt::AlignLeft); - - // TODO: click to select another route. - right_hlayout->addWidget(new QLabel(can->routeName()), 0, Qt::AlignRight); - r_layout->addLayout(right_hlayout); - - video_widget = new VideoWidget(this); - r_layout->addWidget(video_widget, 0, Qt::AlignTop); - r_layout->addWidget(charts_widget, 1); - r_layout->addStretch(0); - main_layout->addWidget(right_container); - - setCentralWidget(central_widget); + detail_widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); + setCentralWidget(detail_widget); createActions(); createStatusBar(); createShortcuts(); + restoreGeometry(settings.geometry); + if (isMaximized()) { + setGeometry(QApplication::desktop()->availableGeometry(this)); + } + restoreState(settings.window_state); + qRegisterMetaType("uint64_t"); qRegisterMetaType("ReplyMsgType"); installMessageHandler([this](ReplyMsgType type, const std::string msg) { @@ -140,6 +99,39 @@ void MainWindow::createActions() { help_menu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt); } +void MainWindow::createDockWindows() { + // left panel + QWidget *messages_container = new QWidget(this); + QVBoxLayout *messages_layout = new QVBoxLayout(messages_container); + dbc_combo = createDBCSelector(); + messages_layout->addWidget(dbc_combo); + messages_widget = new MessagesWidget(this); + messages_layout->addWidget(messages_widget); + + QDockWidget *dock = new QDockWidget(tr("MESSAGES"), this); + dock->setObjectName("MessagesPanel"); + dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea | Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea); + dock->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); + dock->setWidget(messages_container); + addDockWidget(Qt::LeftDockWidgetArea, dock); + + // right panel + QWidget *right_container = new QWidget(this); + r_layout = new QVBoxLayout(right_container); + charts_widget = new ChartsWidget(this); + video_widget = new VideoWidget(this); + r_layout->addWidget(video_widget, 0, Qt::AlignTop); + r_layout->addWidget(charts_widget, 1); + r_layout->addStretch(0); + + video_dock = new QDockWidget(can->routeName(), this); + video_dock->setObjectName(tr("VideoPanel")); + video_dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); + video_dock->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); + video_dock->setWidget(right_container); + addDockWidget(Qt::RightDockWidgetArea, video_dock); +} + QComboBox *MainWindow::createDBCSelector() { QComboBox *c = new QComboBox(this); c->setEditable(true); @@ -205,7 +197,7 @@ void MainWindow::loadDBCFromClipboard() { void MainWindow::loadDBCFromFingerprint() { auto fingerprint = can->carFingerprint(); - fingerprint_label->setText(fingerprint.isEmpty() ? tr("Unknown Car") : fingerprint); + video_dock->setWindowTitle(tr("ROUTE: %1 FINGERPINT: %2").arg(can->routeName()).arg(fingerprint.isEmpty() ? tr("Unknown Car") : fingerprint)); if (!fingerprint.isEmpty()) { auto dbc_name = fingerprint_to_dbc[fingerprint]; if (dbc_name != QJsonValue::Undefined) { @@ -257,7 +249,6 @@ void MainWindow::dockCharts(bool dock) { floating_window->setLayout(new QVBoxLayout()); floating_window->layout()->addWidget(charts_widget); floating_window->installEventFilter(charts_widget); - floating_window->setMinimumSize(QGuiApplication::primaryScreen()->size() / 2); floating_window->showMaximized(); } } @@ -277,7 +268,8 @@ void MainWindow::closeEvent(QCloseEvent *event) { if (floating_window) floating_window->deleteLater(); - settings.splitter_state = splitter->saveState(); + settings.geometry = saveGeometry(); + settings.window_state = saveState(); settings.save(); QWidget::closeEvent(event); } diff --git a/tools/cabana/mainwin.h b/tools/cabana/mainwin.h index 5d3ba470a1..f8b5f92349 100644 --- a/tools/cabana/mainwin.h +++ b/tools/cabana/mainwin.h @@ -1,10 +1,10 @@ #pragma once #include +#include #include #include #include -#include #include #include "tools/cabana/chartswidget.h" @@ -35,6 +35,7 @@ signals: protected: void createActions(); + void createDockWindows(); QComboBox *createDBCSelector(); void createStatusBar(); void createShortcuts(); @@ -45,14 +46,13 @@ protected: void findSimilarBits(); VideoWidget *video_widget; + QDockWidget *video_dock; MessagesWidget *messages_widget; DetailWidget *detail_widget; ChartsWidget *charts_widget; - QSplitter *splitter; QWidget *floating_window = nullptr; QVBoxLayout *r_layout; QProgressBar *progress_bar; - QLabel *fingerprint_label; QJsonDocument fingerprint_to_dbc; QComboBox *dbc_combo; }; diff --git a/tools/cabana/settings.cc b/tools/cabana/settings.cc index 63e26f3808..5e7f833317 100644 --- a/tools/cabana/settings.cc +++ b/tools/cabana/settings.cc @@ -19,7 +19,8 @@ void Settings::save() { s.setValue("chart_height", chart_height); s.setValue("max_chart_x_range", max_chart_x_range); s.setValue("last_dir", last_dir); - s.setValue("splitter_state", splitter_state); + s.setValue("window_state", window_state); + s.setValue("geometry", geometry); } void Settings::load() { @@ -29,7 +30,8 @@ void Settings::load() { chart_height = s.value("chart_height", 200).toInt(); max_chart_x_range = s.value("max_chart_x_range", 3 * 60).toInt(); last_dir = s.value("last_dir", QDir::homePath()).toString(); - splitter_state = s.value("splitter_state").toByteArray(); + window_state = s.value("window_state").toByteArray(); + geometry = s.value("geometry").toByteArray(); } // SettingsDlg diff --git a/tools/cabana/settings.h b/tools/cabana/settings.h index 1db92fe231..d231a3a53a 100644 --- a/tools/cabana/settings.h +++ b/tools/cabana/settings.h @@ -18,7 +18,7 @@ public: int chart_height = 200; int max_chart_x_range = 3 * 60; // 3 minutes QString last_dir; - QByteArray splitter_state; + QByteArray window_state, geometry; signals: void changed(); diff --git a/tools/cabana/videowidget.cc b/tools/cabana/videowidget.cc index 6cd173b514..7e40ba2adb 100644 --- a/tools/cabana/videowidget.cc +++ b/tools/cabana/videowidget.cc @@ -17,10 +17,17 @@ inline QString formatTime(int seconds) { return QDateTime::fromTime_t(seconds).toString(seconds > 60 * 60 ? "hh:mm:ss" : "mm:ss"); } -VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent) { - QVBoxLayout *main_layout = new QVBoxLayout(this); +VideoWidget::VideoWidget(QWidget *parent) : QFrame(parent) { + setFrameShape(QFrame::StyledPanel); + setFrameShadow(QFrame::Sunken); + QHBoxLayout *containter_layout = new QHBoxLayout(this); + QVBoxLayout *main_layout = new QVBoxLayout(); main_layout->setContentsMargins(0, 0, 0, 0); + containter_layout->addStretch(1); + containter_layout->addLayout(main_layout); + containter_layout->addStretch(1); + cam_widget = new CameraWidget("camerad", can->visionStreamType(), false, this); cam_widget->setFixedSize(parent->width(), parent->width() / 1.596); main_layout->addWidget(cam_widget); @@ -56,8 +63,6 @@ VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent) { } main_layout->addLayout(control_layout); - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - QObject::connect(can, &CANMessages::updated, this, &VideoWidget::updateState); QObject::connect(slider, &QSlider::sliderReleased, [this]() { can->seekTo(slider->value() / 1000.0); }); QObject::connect(slider, &QSlider::valueChanged, [=](int value) { time_label->setText(formatTime(value / 1000)); }); diff --git a/tools/cabana/videowidget.h b/tools/cabana/videowidget.h index ea62081a91..86cdc6f114 100644 --- a/tools/cabana/videowidget.h +++ b/tools/cabana/videowidget.h @@ -35,7 +35,7 @@ private: QSize thumbnail_size = {}; }; -class VideoWidget : public QWidget { +class VideoWidget : public QFrame { Q_OBJECT public: From ab797588f800c02c6ea181a86b3b1c7d760e8c72 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 29 Dec 2022 10:22:04 +0800 Subject: [PATCH 011/484] Cabana: double click on logs cell to open the chart (#26833) --- tools/cabana/chartswidget.cc | 7 ++++--- tools/cabana/detailwidget.cc | 3 +++ tools/cabana/historylog.cc | 12 ++++++++++++ tools/cabana/historylog.h | 4 ++++ 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 71886a113f..cf01533aaa 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -145,8 +145,9 @@ ChartView *ChartsWidget::findChart(const QString &id, const Signal *sig) { void ChartsWidget::showChart(const QString &id, const Signal *sig, bool show, bool merge) { setUpdatesEnabled(false); - if (show) { - ChartView *chart = merge && charts.size() > 0 ? charts.back() : nullptr; + ChartView *chart = findChart(id, sig); + if (show && !chart) { + chart = merge && charts.size() > 0 ? charts.back() : nullptr; if (!chart) { chart = new ChartView(this); chart->chart()->setTheme(use_dark_theme ? QChart::QChart::ChartThemeDark : QChart::ChartThemeLight); @@ -163,7 +164,7 @@ void ChartsWidget::showChart(const QString &id, const Signal *sig, bool show, bo charts.push_back(chart); } chart->addSeries(id, sig); - } else if (ChartView *chart = findChart(id, sig)) { + } else if (!show && chart) { chart->removeSeries(id, sig); } updateToolBar(); diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index 108aa8776e..06377616da 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -108,6 +108,9 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart }); QObject::connect(tabbar, &QTabBar::tabCloseRequested, tabbar, &QTabBar::removeTab); QObject::connect(charts, &ChartsWidget::seriesChanged, this, &DetailWidget::updateChartState); + QObject::connect(history_log, &LogsWidget::openChart, [this](const QString &id, const Signal *sig) { + this->charts->showChart(id, sig, true, false); + }); QObject::connect(undo_stack, &QUndoStack::indexChanged, [this]() { if (undo_stack->count() > 0) dbcMsgChanged(); diff --git a/tools/cabana/historylog.cc b/tools/cabana/historylog.cc index 3e456e44e9..0fd939c6f7 100644 --- a/tools/cabana/historylog.cc +++ b/tools/cabana/historylog.cc @@ -22,6 +22,8 @@ QVariant HistoryLogModel::data(const QModelIndex &index, int role) const { return !sigs.empty() ? QString::number(m.sig_values[index.column() - 1]) : m.data; } else if (role == Qt::FontRole && index.column() == 1 && sigs.empty()) { return QFontDatabase::systemFont(QFontDatabase::FixedFont); + } else if (role == Qt::ToolTipRole && index.column() > 0 && !sigs.empty()) { + return tr("double click to open the chart"); } return {}; } @@ -182,6 +184,7 @@ LogsWidget::LogsWidget(QWidget *parent) : QWidget(parent) { logs->setModel(model); main_layout->addWidget(logs); + QObject::connect(logs, &QTableView::doubleClicked, this, &LogsWidget::doubleClicked); QObject::connect(signals_cb, SIGNAL(currentIndexChanged(int)), this, SLOT(setFilter())); QObject::connect(comp_box, SIGNAL(currentIndexChanged(int)), this, SLOT(setFilter())); QObject::connect(value_edit, &QLineEdit::textChanged, this, &LogsWidget::setFilter); @@ -218,3 +221,12 @@ void LogsWidget::setFilter() { } model->setFilter(signals_cb->currentIndex(), value_edit->text(), cmp); } + +void LogsWidget::doubleClicked(const QModelIndex &index) { + if (index.isValid()) { + if (model->sigs.size() > 0 && index.column() > 0) { + emit openChart(model->msg_id, model->sigs[index.column()-1]); + } + can->seekTo(model->messages[index.row()].mono_time / (double)1e9 - can->routeStartTime()); + } +} diff --git a/tools/cabana/historylog.h b/tools/cabana/historylog.h index d5ae47192e..c636f9b48f 100644 --- a/tools/cabana/historylog.h +++ b/tools/cabana/historylog.h @@ -60,10 +60,14 @@ public: void setMessage(const QString &message_id); void updateState() { model->updateState(); } +signals: + void openChart(const QString &msg_id, const Signal *sig); + private slots: void setFilter(); private: + void doubleClicked(const QModelIndex &index); void showEvent(QShowEvent *event) override { model->setMessage(model->msg_id); }; HistoryLog *logs; From 981532f0c32f58e0ba4675fe3b066d7c6d0d9a8e Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Thu, 29 Dec 2022 17:56:35 -0700 Subject: [PATCH 012/484] Laikad preperation (#26800) * laikad update, renaming * update locationd * address PR comments * draft to fix replay * fix process relay to allow no response for messages * bump cereal * update process replay ref commit * move laikad helpers to laika * fix ublox test * update refs * add proper qcom replay support * fix gnss support if both is available * update refs * move laika back to master * move cereal back to master Co-authored-by: Kurt Nistelberger --- cereal | 2 +- laika_repo | 2 +- release/files_common | 1 - selfdrive/locationd/laikad.py | 154 +++++++++++------- selfdrive/locationd/laikad_helpers.py | 89 ---------- selfdrive/locationd/locationd.cc | 92 ++++++++++- selfdrive/locationd/locationd.h | 2 + .../locationd/test/test_ublox_processing.py | 7 +- .../test/process_replay/process_replay.py | 25 ++- selfdrive/test/process_replay/ref_commit | 2 +- 10 files changed, 197 insertions(+), 179 deletions(-) delete mode 100644 selfdrive/locationd/laikad_helpers.py diff --git a/cereal b/cereal index 22b1431132..f200875ca3 160000 --- a/cereal +++ b/cereal @@ -1 +1 @@ -Subproject commit 22b1431132b038253a24ab3fbbe3af36ef93b95b +Subproject commit f200875ca300d3a7b9293c4effcc9456e359e505 diff --git a/laika_repo b/laika_repo index e1049cde0a..5eb0c3c259 160000 --- a/laika_repo +++ b/laika_repo @@ -1 +1 @@ -Subproject commit e1049cde0a68f7d4a70b1ebd76befdc0e163ad55 +Subproject commit 5eb0c3c2596dd12a232b83bdb057a716810e89cf diff --git a/release/files_common b/release/files_common index f438904118..a06543abbf 100644 --- a/release/files_common +++ b/release/files_common @@ -233,7 +233,6 @@ selfdrive/locationd/generated/gps.cpp selfdrive/locationd/generated/gps.h selfdrive/locationd/laikad.py -selfdrive/locationd/laikad_helpers.py selfdrive/locationd/locationd.h selfdrive/locationd/locationd.cc selfdrive/locationd/paramsd.py diff --git a/selfdrive/locationd/laikad.py b/selfdrive/locationd/laikad.py index 6936d88acc..1f13c2b69a 100755 --- a/selfdrive/locationd/laikad.py +++ b/selfdrive/locationd/laikad.py @@ -20,7 +20,7 @@ from laika.ephemeris import Ephemeris, EphemerisType, convert_ublox_ephem, parse from laika.gps_time import GPSTime from laika.helpers import ConstellationId from laika.raw_gnss import GNSSMeasurement, correct_measurements, process_measurements, read_raw_ublox, read_raw_qcom -from selfdrive.locationd.laikad_helpers import calc_pos_fix_gauss_newton, get_posfix_sympy_fun +from laika.opt import calc_pos_fix, get_posfix_sympy_fun, calc_vel_fix, get_velfix_sympy_func from selfdrive.locationd.models.constants import GENERATED_DIR, ObservationKind from selfdrive.locationd.models.gnss_kf import GNSSKalman from selfdrive.locationd.models.gnss_kf import States as GStates @@ -58,9 +58,9 @@ class Laikad: self.load_cache() self.posfix_functions = {constellation: get_posfix_sympy_fun(constellation) for constellation in (ConstellationId.GPS, ConstellationId.GLONASS)} - self.last_pos_fix = [] - self.last_pos_residual = [] - self.last_pos_fix_t = None + self.velfix_function = get_velfix_sympy_func() + self.last_fix_pos = None + self.last_fix_t = None self.gps_week = None self.use_qcom = use_qcom @@ -92,20 +92,23 @@ class Laikad: cloudlog.debug("Cache saved") self.last_cached_t = t - def get_est_pos(self, t, processed_measurements): - if self.last_pos_fix_t is None or abs(self.last_pos_fix_t - t) >= 2: - min_measurements = 6 if any(p.constellation_id == ConstellationId.GLONASS for p in processed_measurements) else 5 - pos_fix, pos_fix_residual = calc_pos_fix_gauss_newton(processed_measurements, self.posfix_functions, min_measurements=min_measurements) - if len(pos_fix) > 0: - self.last_pos_fix_t = t - residual_median = np.median(np.abs(pos_fix_residual)) - if np.median(np.abs(pos_fix_residual)) < POS_FIX_RESIDUAL_THRESHOLD: - cloudlog.debug(f"Pos fix is within threshold with median: {residual_median.round()}") - self.last_pos_fix = pos_fix[:3] - self.last_pos_residual = pos_fix_residual - else: - cloudlog.debug(f"Pos fix failed with median: {residual_median.round()}. All residuals: {np.round(pos_fix_residual)}") - return self.last_pos_fix + def get_lsq_fix(self, t, measurements): + if self.last_fix_t is None or abs(self.last_fix_t - t) > 0: + min_measurements = 6 if any(p.constellation_id == ConstellationId.GLONASS for p in measurements) else 5 + position_solution, pr_residuals = calc_pos_fix(measurements, self.posfix_functions, min_measurements=min_measurements) + + if len(position_solution) < 3: + return None + position_estimate = position_solution[:3] + #TODO median abs residual is decent estimate of std, can be improved with measurements stds and/or DOP + position_std = np.median(np.abs(pr_residuals)) * np.ones(3) + velocity_solution, prr_residuals = calc_vel_fix(measurements, position_estimate, self.velfix_function, min_measurements=min_measurements) + if len(velocity_solution) < 3: + return None + + velocity_estimate = velocity_solution[:3] + velocity_std = np.median(np.abs(prr_residuals)) * np.ones(3) + return position_estimate, position_std, velocity_estimate, velocity_std def is_good_report(self, gnss_msg): if gnss_msg.which() == 'drMeasurementReport' and self.use_qcom: @@ -148,11 +151,36 @@ class Laikad: self.astro_dog.add_navs({ephem.prn: [ephem]}) self.cache_ephemeris(t=ephem.epoch) + def process_report(self, new_meas, t): + # Filter measurements with unexpected pseudoranges for GPS and GLONASS satellites + new_meas = [m for m in new_meas if 1e7 < m.observables['C1C'] < 3e7] + processed_measurements = process_measurements(new_meas, self.astro_dog) + + instant_fix = self.get_lsq_fix(t, processed_measurements) + if instant_fix is None: + return None + else: + position_estimate, position_std, velocity_estimate, velocity_std = instant_fix + self.last_fix_t = t + self.last_fix_pos = position_estimate + self.lat_fix_pos_std = position_std + + corrected_measurements = correct_measurements(processed_measurements, position_estimate, self.astro_dog) + if (t*1e9) % 10 == 0: + cloudlog.debug(f"Measurements Incoming/Processed/Corrected: {len(new_meas), len(processed_measurements), len(corrected_measurements)}") + return position_estimate, position_std, velocity_estimate, velocity_std, corrected_measurements, processed_measurements + def process_gnss_msg(self, gnss_msg, gnss_mono_time: int, block=False): - if self.is_good_report(gnss_msg): + if self.is_ephemeris(gnss_msg): + self.read_ephemeris(gnss_msg) + return None + elif self.is_good_report(gnss_msg): + week, tow, new_meas = self.read_report(gnss_msg) - self.gps_week = week + if len(new_meas) == 0: + return None + self.gps_week = week t = gnss_mono_time * 1e-9 if week > 0: self.got_first_gnss_msg = True @@ -160,44 +188,38 @@ class Laikad: if self.auto_fetch_orbits: self.fetch_orbits(latest_msg_t, block) - # Filter measurements with unexpected pseudoranges for GPS and GLONASS satellites - new_meas = [m for m in new_meas if 1e7 < m.observables['C1C'] < 3e7] - - processed_measurements = process_measurements(new_meas, self.astro_dog) - est_pos = self.get_est_pos(t, processed_measurements) - - corrected_measurements = correct_measurements(processed_measurements, est_pos, self.astro_dog) if len(est_pos) > 0 else [] - if gnss_mono_time % 10 == 0: - cloudlog.debug(f"Measurements Incoming/Processed/Corrected: {len(new_meas), len(processed_measurements), len(corrected_measurements)}") - - self.update_localizer(est_pos, t, corrected_measurements) - kf_valid = all(self.kf_valid(t)) - ecef_pos = self.gnss_kf.x[GStates.ECEF_POS] - ecef_vel = self.gnss_kf.x[GStates.ECEF_VELOCITY] - - p = self.gnss_kf.P.diagonal() - pos_std = np.sqrt(p[GStates.ECEF_POS]) - vel_std = np.sqrt(p[GStates.ECEF_VELOCITY]) + output = self.process_report(new_meas, t) + if output is None: + return None + position_estimate, position_std, velocity_estimate, velocity_std, corrected_measurements, _ = output + self.update_localizer(position_estimate, t, corrected_measurements) meas_msgs = [create_measurement_msg(m) for m in corrected_measurements] - dat = messaging.new_message("gnssMeasurements") + msg = messaging.new_message("gnssMeasurements") measurement_msg = log.LiveLocationKalman.Measurement.new_message - dat.gnssMeasurements = { + + P_diag = self.gnss_kf.P.diagonal() + kf_valid = all(self.kf_valid(t)) + msg.gnssMeasurements = { "gpsWeek": week, "gpsTimeOfWeek": tow, - "positionECEF": measurement_msg(value=ecef_pos.tolist(), std=pos_std.tolist(), valid=kf_valid), - "velocityECEF": measurement_msg(value=ecef_vel.tolist(), std=vel_std.tolist(), valid=kf_valid), - # TODO std is incorrectly the dimension of the measurements and not 3D - "positionFixECEF": measurement_msg(value=self.last_pos_fix, std=self.last_pos_residual, valid=self.last_pos_fix_t == t), - "ubloxMonoTime": gnss_mono_time, + "kalmanPositionECEF": measurement_msg(value=self.gnss_kf.x[GStates.ECEF_POS].tolist(), + std=np.sqrt(P_diag[GStates.ECEF_POS]).tolist(), + valid=kf_valid), + "kalmanVelocityECEF": measurement_msg(value=self.gnss_kf.x[GStates.ECEF_VELOCITY].tolist(), + std=np.sqrt(P_diag[GStates.ECEF_VELOCITY]).tolist(), + valid=kf_valid), + "positionECEF": measurement_msg(value=position_estimate, std=position_std.tolist(), valid=bool(self.last_fix_t == t)), + "velocityECEF": measurement_msg(value=velocity_estimate, std=velocity_std.tolist(), valid=bool(self.last_fix_t == t)), + + "measTime": gnss_mono_time, "correctedMeasurements": meas_msgs } - return dat - elif self.is_ephemeris(gnss_msg): - self.read_ephemeris(gnss_msg) + return msg #elif gnss_msg.which() == 'ionoData': - # todo add this. Needed to better correct messages offline. First fix ublox_msg.cc to sent them. + # TODO: add this, Needed to better correct messages offline. First fix ublox_msg.cc to sent them. + def update_localizer(self, est_pos, t: float, measurements: List[GNSSMeasurement]): # Check time and outputs are valid @@ -349,9 +371,23 @@ class EphemerisSourceType(IntEnum): qcom = 3 -def main(sm=None, pm=None): +def process_msg(laikad, gnss_msg, mono_time, block=False): + # TODO: Understand and use remaining unknown constellations + if gnss_msg.which() == "drMeasurementReport": + if getattr(gnss_msg, gnss_msg.which()).source not in ['glonass', 'gps', 'beidou', 'sbas']: + return None + + if getattr(gnss_msg, gnss_msg.which()).gpsWeek > np.iinfo(np.int16).max: + # gpsWeek 65535 is received rarely from quectel, this cannot be + # passed to GnssMeasurements's gpsWeek (Int16) + return None + + return laikad.process_gnss_msg(gnss_msg, mono_time, block=block) + + +def main(sm=None, pm=None, qc=None): use_qcom = not Params().get_bool("UbloxAvailable", block=True) - if use_qcom: + if use_qcom or (qc is not None and qc): raw_gnss_socket = "qcomGnss" else: raw_gnss_socket = "ubloxGnss" @@ -371,19 +407,13 @@ def main(sm=None, pm=None): if sm.updated[raw_gnss_socket]: gnss_msg = sm[raw_gnss_socket] - # TODO: Understand and use remaining unknown constellations - if gnss_msg.which() == "drMeasurementReport": - if getattr(gnss_msg, gnss_msg.which()).source not in ['glonass', 'gps', 'beidou', 'sbas']: - continue + msg = process_msg(laikad, gnss_msg, sm.logMonoTime[raw_gnss_socket], replay) + if msg is None: + msg = messaging.new_message("gnssMeasurements") + msg.valid = False - if getattr(gnss_msg, gnss_msg.which()).gpsWeek > np.iinfo(np.int16).max: - # gpsWeek 65535 is received rarely from quectel, this cannot be - # passed to GnssMeasurements's gpsWeek (Int16) - continue + pm.send('gnssMeasurements', msg) - msg = laikad.process_gnss_msg(gnss_msg, sm.logMonoTime[raw_gnss_socket], block=replay) - if msg is not None: - pm.send('gnssMeasurements', msg) if not laikad.got_first_gnss_msg and sm.updated['clocks']: clocks_msg = sm['clocks'] t = GPSTime.from_datetime(datetime.utcfromtimestamp(clocks_msg.wallTimeNanos * 1E-9)) diff --git a/selfdrive/locationd/laikad_helpers.py b/selfdrive/locationd/laikad_helpers.py deleted file mode 100644 index f13e8e73bb..0000000000 --- a/selfdrive/locationd/laikad_helpers.py +++ /dev/null @@ -1,89 +0,0 @@ -import numpy as np -import sympy - -from laika.constants import EARTH_ROTATION_RATE, SPEED_OF_LIGHT -from laika.helpers import ConstellationId - - -def calc_pos_fix_gauss_newton(measurements, posfix_functions, x0=None, signal='C1C', min_measurements=6): - ''' - Calculates gps fix using gauss newton method - To solve the problem a minimal of 4 measurements are required. - If Glonass is included 5 are required to solve for the additional free variable. - returns: - 0 -> list with positions - ''' - if x0 is None: - x0 = [0, 0, 0, 0, 0] - n = len(measurements) - if n < min_measurements: - return [], [] - - Fx_pos = pr_residual(measurements, posfix_functions, signal=signal) - x = gauss_newton(Fx_pos, x0) - residual, _ = Fx_pos(x, weight=1.0) - return x.tolist(), residual.tolist() - - -def pr_residual(measurements, posfix_functions, signal='C1C'): - def Fx_pos(inp, weight=None): - vals, gradients = [], [] - - for meas in measurements: - pr = meas.observables[signal] - pr += meas.sat_clock_err * SPEED_OF_LIGHT - - w = (1 / meas.observables_std[signal]) if weight is None else weight - - val, *gradient = posfix_functions[meas.constellation_id](*inp, pr, *meas.sat_pos, w) - vals.append(val) - gradients.append(gradient) - return np.asarray(vals), np.asarray(gradients) - - return Fx_pos - - -def gauss_newton(fun, b, xtol=1e-8, max_n=25): - for _ in range(max_n): - # Compute function and jacobian on current estimate - r, J = fun(b) - - # Update estimate - delta = np.linalg.pinv(J) @ r - b -= delta - - # Check step size for stopping condition - if np.linalg.norm(delta) < xtol: - break - return b - - -def get_posfix_sympy_fun(constellation): - # Unknowns - x, y, z = sympy.Symbol('x'), sympy.Symbol('y'), sympy.Symbol('z') - bc = sympy.Symbol('bc') - bg = sympy.Symbol('bg') - var = [x, y, z, bc, bg] - - # Knowns - pr = sympy.Symbol('pr') - sat_x, sat_y, sat_z = sympy.Symbol('sat_x'), sympy.Symbol('sat_y'), sympy.Symbol('sat_z') - weight = sympy.Symbol('weight') - - theta = EARTH_ROTATION_RATE * (pr - bc) / SPEED_OF_LIGHT - val = sympy.sqrt( - (sat_x * sympy.cos(theta) + sat_y * sympy.sin(theta) - x) ** 2 + - (sat_y * sympy.cos(theta) - sat_x * sympy.sin(theta) - y) ** 2 + - (sat_z - z) ** 2 - ) - - if constellation == ConstellationId.GLONASS: - res = weight * (val - (pr - bc - bg)) - elif constellation == ConstellationId.GPS: - res = weight * (val - (pr - bc)) - else: - raise NotImplementedError(f"Constellation {constellation} not supported") - - res = [res] + [sympy.diff(res, v) for v in var] - - return sympy.lambdify([x, y, z, bc, bg, pr, sat_x, sat_y, sat_z, weight], res, modules=["numpy"]) diff --git a/selfdrive/locationd/locationd.cc b/selfdrive/locationd/locationd.cc index 4325900c0e..442adcd347 100755 --- a/selfdrive/locationd/locationd.cc +++ b/selfdrive/locationd/locationd.cc @@ -25,8 +25,15 @@ const double MAX_FILTER_REWIND_TIME = 0.8; // s // TODO: GPS sensor time offsets are empirically calculated // They should be replaced with synced time from a real clock -const double GPS_LOCATION_SENSOR_TIME_OFFSET = 0.630; // s -const double GPS_LOCATION_EXTERNAL_SENSOR_TIME_OFFSET = 0.095; // s +const double GPS_QUECTEL_SENSOR_TIME_OFFSET = 0.630; // s +const double GPS_UBLOX_SENSOR_TIME_OFFSET = 0.095; // s +const float GPS_MUL_FACTOR = 10.0; +const float GPS_POS_STD_THRESHOLD = 50.0; +const float GPS_VEL_STD_THRESHOLD = 5.0; +const float GPS_POS_ERROR_RESET_THRESHOLD = 200.0; +const float GPS_POS_STD_RESET_THRESHOLD = 5.0; +const float GPS_VEL_STD_RESET_THRESHOLD = 0.5; +const float GPS_ORIENTATION_ERROR_RESET_THRESHOLD = 5.0; static VectorXd floatlist2vector(const capnp::List::Reader& floatlist) { VectorXd res(floatlist.size()); @@ -316,8 +323,8 @@ void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::R VectorXd ecef_pos = this->converter->ned2ecef({ 0.0, 0.0, 0.0 }).to_vector(); VectorXd ecef_vel = this->converter->ned2ecef({ log.getVNED()[0], log.getVNED()[1], log.getVNED()[2] }).to_vector() - ecef_pos; - MatrixXdr ecef_pos_R = Vector3d::Constant(std::pow(10.0 * log.getAccuracy(),2) + std::pow(10.0 * log.getVerticalAccuracy(),2)).asDiagonal(); - MatrixXdr ecef_vel_R = Vector3d::Constant(std::pow(log.getSpeedAccuracy() * 10.0, 2)).asDiagonal(); + MatrixXdr ecef_pos_R = Vector3d::Constant(std::pow(GPS_MUL_FACTOR * log.getAccuracy(),2) + std::pow(GPS_MUL_FACTOR * log.getVerticalAccuracy(),2)).asDiagonal(); + MatrixXdr ecef_vel_R = Vector3d::Constant(std::pow(GPS_MUL_FACTOR * log.getSpeedAccuracy(), 2)).asDiagonal(); this->unix_timestamp_millis = log.getUnixTimestampMillis(); double gps_est_error = (this->kf->get_x().segment(STATE_ECEF_POS_START) - ecef_pos).norm(); @@ -348,6 +355,77 @@ void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::R this->kf->predict_and_observe(sensor_time, OBSERVATION_ECEF_VEL, { ecef_vel }, { ecef_vel_R }); } +void Localizer::handle_gnss(double current_time, const cereal::GnssMeasurements::Reader& log) { + + this->gps_valid = log.getPositionECEF().getValid() && log.getVelocityECEF().getValid(); + if (!this->gps_valid) { + this->determine_gps_mode(current_time); + return; + } + this->gps_mode = true; + + double sensor_time = log.getMeasTime() * 1e-9; + if (ublox_available) + sensor_time -= GPS_UBLOX_SENSOR_TIME_OFFSET; + else + sensor_time -= GPS_QUECTEL_SENSOR_TIME_OFFSET; + + auto ecef_pos_v = log.getPositionECEF().getValue(); + VectorXd ecef_pos = Vector3d(ecef_pos_v[0], ecef_pos_v[1], ecef_pos_v[2]); + + // indexed at 0 cause all std values are the same MAE + auto ecef_pos_std = log.getPositionECEF().getStd()[0]; + MatrixXdr ecef_pos_R = Vector3d::Constant(pow(GPS_MUL_FACTOR*ecef_pos_std, 2)).asDiagonal(); + + auto ecef_vel_v = log.getVelocityECEF().getValue(); + VectorXd ecef_vel = Vector3d(ecef_vel_v[0], ecef_vel_v[1], ecef_vel_v[2]); + + // indexed at 0 cause all std values are the same MAE + auto ecef_vel_std = log.getVelocityECEF().getStd()[0]; + MatrixXdr ecef_vel_R = Vector3d::Constant(pow(GPS_MUL_FACTOR*ecef_vel_std, 2)).asDiagonal(); + + double gps_est_error = (this->kf->get_x().segment(STATE_ECEF_POS_START) - ecef_pos).norm(); + + VectorXd orientation_ecef = quat2euler(vector2quat(this->kf->get_x().segment(STATE_ECEF_ORIENTATION_START))); + VectorXd orientation_ned = ned_euler_from_ecef({ ecef_pos[0], ecef_pos[1], ecef_pos[2] }, orientation_ecef); + + LocalCoord convs((ECEF){ .x = ecef_pos[0], .y = ecef_pos[1], .z = ecef_pos[2] }); + ECEF next_ecef = {.x = ecef_pos[0] + ecef_vel[0], .y = ecef_pos[1] + ecef_vel[1], .z = ecef_pos[2] + ecef_vel[2]}; + VectorXd ned_vel = convs.ecef2ned(next_ecef).to_vector(); + double bearing_rad = atan2(ned_vel[1], ned_vel[0]); + + VectorXd orientation_ned_gps = Vector3d(0.0, 0.0, bearing_rad); + VectorXd orientation_error = (orientation_ned - orientation_ned_gps).array() - M_PI; + for (int i = 0; i < orientation_error.size(); i++) { + orientation_error(i) = std::fmod(orientation_error(i), 2.0 * M_PI); + if (orientation_error(i) < 0.0) { + orientation_error(i) += 2.0 * M_PI; + } + orientation_error(i) -= M_PI; + } + VectorXd initial_pose_ecef_quat = quat2vector(euler2quat(ecef_euler_from_ned({ ecef_pos(0), ecef_pos(1), ecef_pos(2) }, orientation_ned_gps))); + + if (ecef_pos_std > GPS_POS_STD_THRESHOLD || ecef_vel_std > GPS_VEL_STD_THRESHOLD) { + this->gps_valid = false; + this->determine_gps_mode(current_time); + return; + } + + if ((gps_est_error > GPS_POS_ERROR_RESET_THRESHOLD && ecef_pos_std < GPS_POS_STD_RESET_THRESHOLD) || this->last_gps_msg == 0) { + // always reset on first gps message and if the location is off but the accuracy is high + LOGE("Locationd vs gnssMeasurement position difference too large, kalman reset"); + this->reset_kalman(NAN, initial_pose_ecef_quat, ecef_pos, ecef_vel, ecef_pos_R, ecef_vel_R); + } else if (ecef_vel_std < GPS_VEL_STD_RESET_THRESHOLD && orientation_error.norm() > GPS_ORIENTATION_ERROR_RESET_THRESHOLD) { + LOGE("Locationd vs gnssMeasurement orientation difference too large, kalman reset"); + this->reset_kalman(NAN, initial_pose_ecef_quat, ecef_pos, ecef_vel, ecef_pos_R, ecef_vel_R); + this->kf->predict_and_observe(sensor_time, OBSERVATION_ECEF_ORIENTATION_FROM_GPS, { initial_pose_ecef_quat }); + } + + this->last_gps_msg = current_time; + this->kf->predict_and_observe(sensor_time, OBSERVATION_ECEF_POS, { ecef_pos }, { ecef_pos_R }); + this->kf->predict_and_observe(sensor_time, OBSERVATION_ECEF_VEL, { ecef_vel }, { ecef_vel_R }); +} + void Localizer::handle_car_state(double current_time, const cereal::CarState::Reader& log) { this->car_speed = std::abs(log.getVEgo()); if (log.getStandstill()) { @@ -497,9 +575,11 @@ void Localizer::handle_msg(const cereal::Event::Reader& log) { } else if (log.isGyroscope()) { this->handle_sensor(t, log.getGyroscope()); } else if (log.isGpsLocation()) { - this->handle_gps(t, log.getGpsLocation(), GPS_LOCATION_SENSOR_TIME_OFFSET); + this->handle_gps(t, log.getGpsLocation(), GPS_QUECTEL_SENSOR_TIME_OFFSET); } else if (log.isGpsLocationExternal()) { - this->handle_gps(t, log.getGpsLocationExternal(), GPS_LOCATION_EXTERNAL_SENSOR_TIME_OFFSET); + this->handle_gps(t, log.getGpsLocationExternal(), GPS_UBLOX_SENSOR_TIME_OFFSET); + //} else if (log.isGnssMeasurements()) { + // this->handle_gnss(t, log.getGnssMeasurements()); } else if (log.isCarState()) { this->handle_car_state(t, log.getCarState()); } else if (log.isCameraOdometry()) { diff --git a/selfdrive/locationd/locationd.h b/selfdrive/locationd/locationd.h index f0872d9f56..7a282be9d7 100755 --- a/selfdrive/locationd/locationd.h +++ b/selfdrive/locationd/locationd.h @@ -52,6 +52,7 @@ public: void handle_msg(const cereal::Event::Reader& log); void handle_sensor(double current_time, const cereal::SensorEventData::Reader& log); void handle_gps(double current_time, const cereal::GpsLocationData::Reader& log, const double sensor_time_offset); + void handle_gnss(double current_time, const cereal::GnssMeasurements::Reader& log); void handle_car_state(double current_time, const cereal::CarState::Reader& log); void handle_cam_odo(double current_time, const cereal::CameraOdometry::Reader& log); void handle_live_calib(double current_time, const cereal::LiveCalibrationData::Reader& log); @@ -77,6 +78,7 @@ private: bool device_fell = false; bool gps_mode = false; bool gps_valid = false; + double last_gps_msg = 0; bool ublox_available = true; bool observation_timings_invalid = false; std::map observation_values_invalid; diff --git a/selfdrive/locationd/test/test_ublox_processing.py b/selfdrive/locationd/test/test_ublox_processing.py index 427003b24c..7aa588d43e 100755 --- a/selfdrive/locationd/test/test_ublox_processing.py +++ b/selfdrive/locationd/test/test_ublox_processing.py @@ -4,7 +4,8 @@ import numpy as np from laika import AstroDog from laika.helpers import ConstellationId -from laika.raw_gnss import calc_pos_fix, correct_measurements, process_measurements, read_raw_ublox +from laika.raw_gnss import correct_measurements, process_measurements, read_raw_ublox +from laika.opt import calc_pos_fix from selfdrive.test.openpilotci import get_url from tools.lib.logreader import LogReader @@ -54,14 +55,14 @@ class TestUbloxProcessing(unittest.TestCase): processed_meas = process_measurements(measurements, dog) count_processed_measurements += len(processed_meas) pos_fix = calc_pos_fix(processed_meas) - if len(pos_fix) > 0 and all(pos_fix[0] != 0): + if len(pos_fix) > 0 and all(p != 0 for p in pos_fix[0]): position_fix_found += 1 corrected_meas = correct_measurements(processed_meas, pos_fix[0][:3], dog) count_corrected_measurements += len(corrected_meas) pos_fix = calc_pos_fix(corrected_meas) - if len(pos_fix) > 0 and all(pos_fix[0] != 0): + if len(pos_fix) > 0 and all(p != 0 for p in pos_fix[0]): pos_ests.append(pos_fix[0]) position_fix_found_after_correcting += 1 diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index bc55e33cbf..c5465fc025 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -251,8 +251,10 @@ def ublox_rcv_callback(msg): def laika_rcv_callback(msg, CP, cfg, fsm): if msg.which() == 'ubloxGnss' and msg.ubloxGnss.which() == "measurementReport": return ["gnssMeasurements"], True + elif msg.which() == 'qcomGnss' and msg.qcomGnss.which() == "drMeasurementReport": + return ["gnssMeasurements"], True else: - return [], True + return [], False CONFIGS = [ @@ -360,24 +362,11 @@ CONFIGS = [ tolerance=None, fake_pubsubmaster=False, ), - ProcessConfig( - proc_name="laikad", - subtest_name="Offline", - pub_sub={ - "ubloxGnss": ["gnssMeasurements"], - "clocks": [] - }, - ignore=["logMonoTime"], - init_callback=get_car_params, - should_recv_callback=laika_rcv_callback, - tolerance=NUMPY_TOLERANCE, - fake_pubsubmaster=True, - environ={"LAIKAD_NO_INTERNET": "1"}, - ), ProcessConfig( proc_name="laikad", pub_sub={ "ubloxGnss": ["gnssMeasurements"], + "qcomGnss": ["gnssMeasurements"], "clocks": [] }, ignore=["logMonoTime"], @@ -474,6 +463,12 @@ def python_replay_process(cfg, lr, fingerprint=None): all_msgs = sorted(lr, key=lambda msg: msg.logMonoTime) pub_msgs = [msg for msg in all_msgs if msg.which() in list(cfg.pub_sub.keys())] + # laikad needs decision between submaster ubloxGnss and qcomGnss, prio given to ubloxGnss + if cfg.proc_name == "laikad": + args = (*args, not any(m.which() == "ubloxGnss" for m in pub_msgs)) + service = "qcomGnss" if args[2] else "ubloxGnss" + pub_msgs = [m for m in pub_msgs if m.which() == service or m.which() == 'clocks'] + controlsState = None initialized = False for msg in lr: diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 1d586b0334..603d2b2d96 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -05ba201bcf97b6c1067dbcde2a60f71f43469c56 +557ffbf5a1c9cafba1ff8d6f3e2642df98b2d6e6 \ No newline at end of file From e01809857174533e61a25d4bcb85b708031aa8cb Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 31 Dec 2022 05:57:29 +0800 Subject: [PATCH 013/484] Cabana: add dynamic/static mode to logs view (#26832) --- tools/cabana/historylog.cc | 141 +++++++++++++++++++++++-------------- tools/cabana/historylog.h | 23 ++++-- 2 files changed, 105 insertions(+), 59 deletions(-) diff --git a/tools/cabana/historylog.cc b/tools/cabana/historylog.cc index 0fd939c6f7..485a21cc1b 100644 --- a/tools/cabana/historylog.cc +++ b/tools/cabana/historylog.cc @@ -7,12 +7,6 @@ // HistoryLogModel -HistoryLogModel::HistoryLogModel(QObject *parent) : QAbstractTableModel(parent) { - QObject::connect(can, &CANMessages::seekedTo, [this]() { - if (!msg_id.isEmpty()) setMessage(msg_id); - }); -} - QVariant HistoryLogModel::data(const QModelIndex &index, int role) const { if (role == Qt::DisplayRole) { const auto &m = messages[index.row()]; @@ -29,17 +23,19 @@ QVariant HistoryLogModel::data(const QModelIndex &index, int role) const { } void HistoryLogModel::setMessage(const QString &message_id) { - beginResetModel(); + msg_id = message_id; sigs.clear(); - messages.clear(); - has_more_data = true; - if (auto dbc_msg = dbc()->msg(message_id)) { + if (auto dbc_msg = dbc()->msg(msg_id)) { sigs = dbc_msg->getSignals(); } - if (msg_id != message_id || sigs.empty()) { - filter_cmp = nullptr; - } - msg_id = message_id; + filter_cmp = nullptr; + refresh(); +} + +void HistoryLogModel::refresh() { + beginResetModel(); + last_fetch_time = 0; + messages.clear(); updateState(); endResetModel(); } @@ -60,33 +56,40 @@ QVariant HistoryLogModel::headerData(int section, Qt::Orientation orientation, i return {}; } -void HistoryLogModel::setFilter(int sig_idx, const QString &value, std::function cmp) { - if (sig_idx < sigs.size()) { - filter_sig_idx = sig_idx; - filter_value = value.toDouble(); - filter_cmp = value.isEmpty() ? nullptr : cmp; - beginResetModel(); - messages.clear(); - updateState(); - endResetModel(); +void HistoryLogModel::setDynamicMode(int state) { + dynamic_mode = state != 0; + refresh(); +} + +void HistoryLogModel::segmentsMerged() { + if (!dynamic_mode) { + has_more_data = true; } } +void HistoryLogModel::setFilter(int sig_idx, const QString &value, std::function cmp) { + filter_sig_idx = sig_idx; + filter_value = value.toDouble(); + filter_cmp = value.isEmpty() ? nullptr : cmp; + refresh(); +} + void HistoryLogModel::updateState() { if (!msg_id.isEmpty()) { - uint64_t last_mono_time = messages.empty() ? 0 : messages.front().mono_time; - auto new_msgs = fetchData(last_mono_time, (can->currentSec() + can->routeStartTime()) * 1e9); + uint64_t current_time = (can->currentSec() + can->routeStartTime()) * 1e9; + auto new_msgs = dynamic_mode ? fetchData(current_time, last_fetch_time) : fetchData(0); if ((has_more_data = !new_msgs.empty())) { beginInsertRows({}, 0, new_msgs.size() - 1); messages.insert(messages.begin(), std::move_iterator(new_msgs.begin()), std::move_iterator(new_msgs.end())); endInsertRows(); } + last_fetch_time = current_time; } } void HistoryLogModel::fetchMore(const QModelIndex &parent) { if (!messages.empty()) { - auto new_msgs = fetchData(0, messages.back().mono_time); + auto new_msgs = fetchData(messages.back().mono_time); if ((has_more_data = !new_msgs.empty())) { beginInsertRows({}, messages.size(), messages.size() + new_msgs.size() - 1); messages.insert(messages.end(), std::move_iterator(new_msgs.begin()), std::move_iterator(new_msgs.end())); @@ -95,19 +98,12 @@ void HistoryLogModel::fetchMore(const QModelIndex &parent) { } } -std::deque HistoryLogModel::fetchData(uint64_t min_mono_time, uint64_t max_mono_time) { - auto events = can->events(); - auto it = std::lower_bound(events->begin(), events->end(), max_mono_time, [=](auto &e, uint64_t ts) { - return e->mono_time < ts; - }); - if (it == events->end() || it == events->begin()) - return {}; - +template +std::deque HistoryLogModel::fetchData(InputIt first, InputIt last, uint64_t min_time) { std::deque msgs; const auto [src, address] = DBCManager::parseId(msg_id); - uint32_t cnt = 0; QVector values(sigs.size()); - for (--it; it != events->begin() && (*it)->mono_time > min_mono_time; --it) { + for (auto it = first; it != last && (*it)->mono_time > min_time; ++it) { if ((*it)->which == cereal::Event::Which::CAN) { for (const auto &c : (*it)->event.getCan()) { if (src == c.getSrc() && address == c.getAddress()) { @@ -120,7 +116,7 @@ std::deque HistoryLogModel::fetchData(uint64_t min_mon m.mono_time = (*it)->mono_time; m.data = toHex(QByteArray((char *)dat.begin(), dat.size())); m.sig_values = values; - if (++cnt >= batch_size && min_mono_time == 0) + if (msgs.size() >= batch_size && min_time == 0) return msgs; } } @@ -129,6 +125,25 @@ std::deque HistoryLogModel::fetchData(uint64_t min_mon } return msgs; } +template std::deque HistoryLogModel::fetchData<>(std::vector::iterator first, std::vector::iterator last, uint64_t min_time); +template std::deque HistoryLogModel::fetchData<>(std::vector::reverse_iterator first, std::vector::reverse_iterator last, uint64_t min_time); + +std::deque HistoryLogModel::fetchData(uint64_t from_time, uint64_t min_time) { + auto events = can->events(); + if (dynamic_mode) { + auto it = std::lower_bound(events->rbegin(), events->rend(), from_time, [=](auto &e, uint64_t ts) { + return e->mono_time > ts; + }); + if (it != events->rend()) ++it; + return fetchData(it, events->rend(), min_time); + } else { + assert(min_time == 0); + auto it = std::upper_bound(events->begin(), events->end(), from_time, [=](uint64_t ts, auto &e) { + return ts < e->mono_time; + }); + return fetchData(it, events->end(), 0); + } +} // HeaderView @@ -166,9 +181,9 @@ HistoryLog::HistoryLog(QWidget *parent) : QTableView(parent) { LogsWidget::LogsWidget(QWidget *parent) : QWidget(parent) { QVBoxLayout *main_layout = new QVBoxLayout(this); - filter_container = new QWidget(this); - QHBoxLayout *h = new QHBoxLayout(filter_container); + QHBoxLayout *h = new QHBoxLayout(); signals_cb = new QComboBox(this); + signals_cb->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); h->addWidget(signals_cb); comp_box = new QComboBox(); comp_box->addItems({">", "=", "!=", "<"}); @@ -177,7 +192,9 @@ LogsWidget::LogsWidget(QWidget *parent) : QWidget(parent) { value_edit->setClearButtonEnabled(true); value_edit->setValidator(new QDoubleValidator(-500000, 500000, 6, this)); h->addWidget(value_edit); - main_layout->addWidget(filter_container); + dynamic_mode = new QCheckBox(tr("Dynamic")); + h->addWidget(dynamic_mode, 0, Qt::AlignRight); + main_layout->addLayout(h); model = new HistoryLogModel(this); logs = new HistoryLog(this); @@ -185,33 +202,38 @@ LogsWidget::LogsWidget(QWidget *parent) : QWidget(parent) { main_layout->addWidget(logs); QObject::connect(logs, &QTableView::doubleClicked, this, &LogsWidget::doubleClicked); - QObject::connect(signals_cb, SIGNAL(currentIndexChanged(int)), this, SLOT(setFilter())); - QObject::connect(comp_box, SIGNAL(currentIndexChanged(int)), this, SLOT(setFilter())); + QObject::connect(signals_cb, SIGNAL(activated(int)), this, SLOT(setFilter())); + QObject::connect(comp_box, SIGNAL(activated(int)), this, SLOT(setFilter())); QObject::connect(value_edit, &QLineEdit::textChanged, this, &LogsWidget::setFilter); + QObject::connect(dynamic_mode, &QCheckBox::stateChanged, model, &HistoryLogModel::setDynamicMode); + QObject::connect(can, &CANMessages::seekedTo, model, &HistoryLogModel::refresh); + QObject::connect(can, &CANMessages::eventsMerged, model, &HistoryLogModel::segmentsMerged); } void LogsWidget::setMessage(const QString &message_id) { - blockSignals(true); + model->setMessage(message_id); + cur_filter_text = ""; value_edit->setText(""); signals_cb->clear(); comp_box->setCurrentIndex(0); - sigs.clear(); - if (auto dbc_msg = dbc()->msg(message_id)) { - sigs = dbc_msg->getSignals(); - for (auto s : sigs) { + bool has_signals = model->sigs.size() > 0; + if (has_signals) { + for (auto s : model->sigs) { signals_cb->addItem(s->name.c_str()); } } - filter_container->setVisible(!sigs.empty()); - model->setMessage(message_id); - blockSignals(false); + comp_box->setVisible(has_signals); + value_edit->setVisible(has_signals); + signals_cb->setVisible(has_signals); } -static bool not_equal(double l, double r) { - return l != r; -} +static bool not_equal(double l, double r) { return l != r; } void LogsWidget::setFilter() { + if (cur_filter_text.isEmpty() && value_edit->text().isEmpty()) { + return; + } + std::function cmp; switch (comp_box->currentIndex()) { case 0: cmp = std::greater{}; break; @@ -220,6 +242,19 @@ void LogsWidget::setFilter() { case 3: cmp = std::less{}; break; } model->setFilter(signals_cb->currentIndex(), value_edit->text(), cmp); + cur_filter_text = value_edit->text(); +} + +void LogsWidget::showEvent(QShowEvent *event) { + if (dynamic_mode->isChecked()) { + model->refresh(); + } +} + +void LogsWidget::updateState() { + if (dynamic_mode->isChecked()) { + model->updateState(); + } } void LogsWidget::doubleClicked(const QModelIndex &index) { diff --git a/tools/cabana/historylog.h b/tools/cabana/historylog.h index c636f9b48f..f20f51637a 100644 --- a/tools/cabana/historylog.h +++ b/tools/cabana/historylog.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -17,8 +18,10 @@ public: }; class HistoryLogModel : public QAbstractTableModel { + Q_OBJECT + public: - HistoryLogModel(QObject *parent); + HistoryLogModel(QObject *parent) : QAbstractTableModel(parent) {} void setMessage(const QString &message_id); void updateState(); void setFilter(int sig_idx, const QString &value, std::function cmp); @@ -28,6 +31,9 @@ public: inline bool canFetchMore(const QModelIndex &parent) const override { return has_more_data; } int rowCount(const QModelIndex &parent = QModelIndex()) const override { return messages.size(); } int columnCount(const QModelIndex &parent = QModelIndex()) const override { return std::max(1ul, sigs.size()) + 1; } + void setDynamicMode(int state); + void segmentsMerged(); + void refresh(); struct Message { uint64_t mono_time = 0; @@ -35,15 +41,20 @@ public: QString data; }; - std::deque fetchData(uint64_t min_mono_time, uint64_t max_mono_time); + template + std::deque fetchData(InputIt first, InputIt last, uint64_t min_time); + std::deque fetchData(uint64_t from_time, uint64_t min_time = 0); + QString msg_id; bool has_more_data = true; const int batch_size = 50; int filter_sig_idx = -1; double filter_value = 0; + uint64_t last_fetch_time = 0; std::function filter_cmp = nullptr; std::deque messages; std::vector sigs; + bool dynamic_mode = false; }; class HistoryLog : public QTableView { @@ -58,7 +69,7 @@ class LogsWidget : public QWidget { public: LogsWidget(QWidget *parent); void setMessage(const QString &message_id); - void updateState() { model->updateState(); } + void updateState(); signals: void openChart(const QString &msg_id, const Signal *sig); @@ -68,12 +79,12 @@ private slots: private: void doubleClicked(const QModelIndex &index); - void showEvent(QShowEvent *event) override { model->setMessage(model->msg_id); }; + void showEvent(QShowEvent *event) override; HistoryLog *logs; HistoryLogModel *model; - QWidget *filter_container; + QCheckBox *dynamic_mode; QComboBox *signals_cb, *comp_box; QLineEdit *value_edit; - std::vector sigs; + QString cur_filter_text; }; From 28d0459c69a6f9bcf511fe982ba573cb7842e385 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 30 Dec 2022 14:44:59 -0800 Subject: [PATCH 014/484] update_requirements: skip pre-commit install on mac --- update_requirements.sh | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/update_requirements.sh b/update_requirements.sh index e5dedea7a6..b430df59e5 100755 --- a/update_requirements.sh +++ b/update_requirements.sh @@ -69,11 +69,13 @@ else RUN="poetry run" fi -echo "pre-commit hooks install..." -shopt -s nullglob -for f in .pre-commit-config.yaml */.pre-commit-config.yaml; do - cd $DIR/$(dirname $f) - if [ -e ".git" ]; then - $RUN pre-commit install - fi -done +if [ "$(uname)" != "Darwin" ]; then + echo "pre-commit hooks install..." + shopt -s nullglob + for f in .pre-commit-config.yaml */.pre-commit-config.yaml; do + cd $DIR/$(dirname $f) + if [ -e ".git" ]; then + $RUN pre-commit install + fi + done +fi From a7155a43b726266084554e7cfa11d8a7bba88238 Mon Sep 17 00:00:00 2001 From: Erich Moraga <33645296+ErichMoraga@users.noreply.github.com> Date: Fri, 30 Dec 2022 18:16:24 -0600 Subject: [PATCH 015/484] Add several missing HIGHLANDERH_TSS2 firmwares (#26844) * Add several missing HIGHLANDERH_TSS2 firmwares `@joeswisher#3240` 2023 Highlander Hybrid DongleID/route 1cdd18b56163c309|2022-12-29--23-18-05 * docs.py gen'd CARS.md w/ 2023 Highlander Hybrid update --- docs/CARS.md | 2 +- selfdrive/car/toyota/values.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index ce067609aa..f3667c4cea 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -182,7 +182,7 @@ A supported vehicle is one that just works when you install a comma three. All s |Toyota|Highlander 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| |Toyota|Highlander 2020-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| |Toyota|Highlander Hybrid 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Toyota|Highlander Hybrid 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Highlander Hybrid 2020-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| |Toyota|Mirai 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| |Toyota|Prius 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| |Toyota|Prius 2017-20|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index 80c0ccbb83..64a0bed0b2 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -131,7 +131,7 @@ CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = { CAR.HIGHLANDER: ToyotaCarInfo("Toyota Highlander 2017-19", video_link="https://www.youtube.com/watch?v=0wS0wXSLzoo"), CAR.HIGHLANDER_TSS2: ToyotaCarInfo("Toyota Highlander 2020-23"), CAR.HIGHLANDERH: ToyotaCarInfo("Toyota Highlander Hybrid 2017-19"), - CAR.HIGHLANDERH_TSS2: ToyotaCarInfo("Toyota Highlander Hybrid 2020-22"), + CAR.HIGHLANDERH_TSS2: ToyotaCarInfo("Toyota Highlander Hybrid 2020-23"), CAR.PRIUS: [ ToyotaCarInfo("Toyota Prius 2016", "Toyota Safety Sense P", "https://www.youtube.com/watch?v=8zopPJI8XQ0"), ToyotaCarInfo("Toyota Prius 2017-20", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0"), @@ -1023,11 +1023,13 @@ FW_VERSIONS = { (Ecu.eps, 0x7a1, None): [ b'8965B48241\x00\x00\x00\x00\x00\x00', b'8965B48310\x00\x00\x00\x00\x00\x00', + b'8965B48400\x00\x00\x00\x00\x00\x00', ], (Ecu.abs, 0x7b0, None): [ b'\x01F15264872300\x00\x00\x00\x00', b'\x01F15264872400\x00\x00\x00\x00', b'\x01F15264872500\x00\x00\x00\x00', + b'\x01F15264872600\x00\x00\x00\x00', b'\x01F15264873500\x00\x00\x00\x00', b'\x01F152648C6300\x00\x00\x00\x00', b'\x01F152648J4000\x00\x00\x00\x00', @@ -1041,6 +1043,7 @@ FW_VERSIONS = { b'\x01896630EE4100\x00\x00\x00\x00', b'\x01896630EE5000\x00\x00\x00\x00', b'\x01896630EE6000\x00\x00\x00\x00', + b'\x01896630EF8000\x00\x00\x00\x00', b'\x02896630E66000\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', b'\x02896630E66100\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', b'\x01896630EA1000\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', @@ -1056,6 +1059,7 @@ FW_VERSIONS = { b'\x028646F0E02100\x00\x00\x00\x008646G2601200\x00\x00\x00\x00', b'\x028646F4803000\x00\x00\x00\x008646G5301200\x00\x00\x00\x00', b'\x028646F4803000\x00\x00\x00\x008646G3304000\x00\x00\x00\x00', + b'\x028646F4803200\x00\x00\x00\x008646G3304000\x00\x00\x00\x00', ], }, CAR.LEXUS_IS: { From 52b72dd71b77e1937691174a790fc8cc348298a3 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 30 Dec 2022 17:16:22 -0800 Subject: [PATCH 016/484] LGTM.com is deprecated (#26846) --- README.md | 3 --- lgtm.yml | 19 ------------------- 2 files changed, 22 deletions(-) delete mode 100644 lgtm.yml diff --git a/README.md b/README.md index cfeb625bfe..88b7aeb2bb 100755 --- a/README.md +++ b/README.md @@ -143,7 +143,4 @@ NO WARRANTY EXPRESSED OR IMPLIED.** [![openpilot tests](https://github.com/commaai/openpilot/workflows/openpilot%20tests/badge.svg?event=push)](https://github.com/commaai/openpilot/actions) -[![Total alerts](https://img.shields.io/lgtm/alerts/g/commaai/openpilot.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/commaai/openpilot/alerts/) -[![Language grade: Python](https://img.shields.io/lgtm/grade/python/g/commaai/openpilot.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/commaai/openpilot/context:python) -[![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/commaai/openpilot.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/commaai/openpilot/context:cpp) [![codecov](https://codecov.io/gh/commaai/openpilot/branch/master/graph/badge.svg)](https://codecov.io/gh/commaai/openpilot) diff --git a/lgtm.yml b/lgtm.yml deleted file mode 100644 index 6ce9353562..0000000000 --- a/lgtm.yml +++ /dev/null @@ -1,19 +0,0 @@ -path_classifiers: - library: - - external - - third_party - - pyextra - - tools/lib/mkvparse -extraction: - cpp: - after_prepare: - - "pip3 install --upgrade --user pkgconfig cython setuptools wheel" - - "pip3 install --upgrade --user jinja2 pyyaml cython pycapnp numpy sympy tqdm\ - \ cffi logentries zmq scons" - - "export PATH=/opt/work/.local/bin:$PATH" - index: - build_command: "scons" - javascript: - index: - filters: - - exclude: "*" From e3a41b3c255a5c72064f66c4d2504e1655bf2ac9 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 30 Dec 2022 20:25:33 -0800 Subject: [PATCH 017/484] add spidev2 package (#26847) --- poetry.lock | 43 ++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index f7bc4669a3..9ab86329d7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1352,6 +1352,14 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "ioctl-opt" +version = "1.2.2" +description = "Functions to compute fnctl.ioctl's opt argument" +category = "main" +optional = false +python-versions = "*" + [[package]] name = "ipykernel" version = "6.16.1" @@ -3881,6 +3889,17 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "spidev2" +version = "0.9.0" +description = "Pure-python interface to Linux spidev." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +ioctl-opt = "*" + [[package]] name = "sqlalchemy" version = "1.4.42" @@ -4425,7 +4444,7 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] lock-version = "1.1" python-versions = "~3.8" -content-hash = "d894380c87f5558e032708cc1230aed172ad3e1db9aa112e2e105bebefff4e20" +content-hash = "2ab9d6aee6cc2c7bb5ba9763a54779ef9535cb752ec0038736a442992a04d459" [metadata.files] adal = [ @@ -5624,6 +5643,9 @@ inputs = [ {file = "inputs-0.5-py2.py3-none-any.whl", hash = "sha256:13f894564e52134cf1e3862b1811da034875eb1f2b62e6021e3776e9669a96ec"}, {file = "inputs-0.5.tar.gz", hash = "sha256:a31d5b96a3525f1232f326be9e7ce8ccaf873c6b1fb84d9f3c9bc3d79b23eae4"}, ] +ioctl-opt = [ + {file = "ioctl-opt-1.2.2.tar.gz", hash = "sha256:9628bbd6728f90d019759f54d20b741ddbf9f8db8d41976da4332492f669d643"}, +] ipykernel = [ {file = "ipykernel-6.16.1-py3-none-any.whl", hash = "sha256:32eb7bdc5af57185e9a42b0dcef66413ef91a0490b378eae46cbdf0d4e0b5912"}, {file = "ipykernel-6.16.1.tar.gz", hash = "sha256:3a27a550c1d682e7825f0f7732b0142b79ef1b21cd2e713cacac0c9847535f13"}, @@ -6429,6 +6451,7 @@ onnx = [ ] onnx2torch = [ {file = "onnx2torch-1.5.4-py3-none-any.whl", hash = "sha256:fd1a0fe05072bfb9f3d86d9330299b130b41f11bd4ae634db17078974e711725"}, + {file = "onnx2torch-1.5.4.tar.gz", hash = "sha256:df837b557a63540223d85fde4a1d679fde0ca8d8bb89d5379c030b01eddc9c24"}, ] onnxoptimizer = [ {file = "onnxoptimizer-0.3.1-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:e73a5e2e3ca4db9bff54f7131768749c861677b97ee811a136fcf1a52783cf6e"}, @@ -6810,6 +6833,7 @@ pycryptodome = [ {file = "pycryptodome-3.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:7c9ed8aa31c146bef65d89a1b655f5f4eab5e1120f55fc297713c89c9e56ff0b"}, {file = "pycryptodome-3.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:5099c9ca345b2f252f0c28e96904643153bae9258647585e5e6f649bb7a1844a"}, {file = "pycryptodome-3.15.0-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:2ec709b0a58b539a4f9d33fb8508264c3678d7edb33a68b8906ba914f71e8c13"}, + {file = "pycryptodome-3.15.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:2ae53125de5b0d2c95194d957db9bb2681da8c24d0fb0fe3b056de2bcaf5d837"}, {file = "pycryptodome-3.15.0-cp27-cp27m-win32.whl", hash = "sha256:fd2184aae6ee2a944aaa49113e6f5787cdc5e4db1eb8edb1aea914bd75f33a0c"}, {file = "pycryptodome-3.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:7e3a8f6ee405b3bd1c4da371b93c31f7027944b2bcce0697022801db93120d83"}, {file = "pycryptodome-3.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:b9c5b1a1977491533dfd31e01550ee36ae0249d78aae7f632590db833a5012b8"}, @@ -6817,12 +6841,14 @@ pycryptodome = [ {file = "pycryptodome-3.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:2aa55aae81f935a08d5a3c2042eb81741a43e044bd8a81ea7239448ad751f763"}, {file = "pycryptodome-3.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:c3640deff4197fa064295aaac10ab49a0d55ef3d6a54ae1499c40d646655c89f"}, {file = "pycryptodome-3.15.0-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:045d75527241d17e6ef13636d845a12e54660aa82e823b3b3341bcf5af03fa79"}, + {file = "pycryptodome-3.15.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:eb6fce570869e70cc8ebe68eaa1c26bed56d40ad0f93431ee61d400525433c54"}, {file = "pycryptodome-3.15.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9ee40e2168f1348ae476676a2e938ca80a2f57b14a249d8fe0d3cdf803e5a676"}, {file = "pycryptodome-3.15.0-cp35-abi3-manylinux1_i686.whl", hash = "sha256:4c3ccad74eeb7b001f3538643c4225eac398c77d617ebb3e57571a897943c667"}, {file = "pycryptodome-3.15.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:1b22bcd9ec55e9c74927f6b1f69843cb256fb5a465088ce62837f793d9ffea88"}, {file = "pycryptodome-3.15.0-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:57f565acd2f0cf6fb3e1ba553d0cb1f33405ec1f9c5ded9b9a0a5320f2c0bd3d"}, {file = "pycryptodome-3.15.0-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:4b52cb18b0ad46087caeb37a15e08040f3b4c2d444d58371b6f5d786d95534c2"}, {file = "pycryptodome-3.15.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:092a26e78b73f2530b8bd6b3898e7453ab2f36e42fd85097d705d6aba2ec3e5e"}, + {file = "pycryptodome-3.15.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:50ca7e587b8e541eb6c192acf92449d95377d1f88908c0a32ac5ac2703ebe28b"}, {file = "pycryptodome-3.15.0-cp35-abi3-win32.whl", hash = "sha256:e244ab85c422260de91cda6379e8e986405b4f13dc97d2876497178707f87fc1"}, {file = "pycryptodome-3.15.0-cp35-abi3-win_amd64.whl", hash = "sha256:c77126899c4b9c9827ddf50565e93955cb3996813c18900c16b2ea0474e130e9"}, {file = "pycryptodome-3.15.0-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:9eaadc058106344a566dc51d3d3a758ab07f8edde013712bc8d22032a86b264f"}, @@ -7417,6 +7443,18 @@ setproctitle = [ {file = "setproctitle-1.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7f2719a398e1a2c01c2a63bf30377a34d0b6ef61946ab9cf4d550733af8f1ef1"}, {file = "setproctitle-1.3.2-cp310-cp310-win32.whl", hash = "sha256:e425be62524dc0c593985da794ee73eb8a17abb10fe692ee43bb39e201d7a099"}, {file = "setproctitle-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:e85e50b9c67854f89635a86247412f3ad66b132a4d8534ac017547197c88f27d"}, + {file = "setproctitle-1.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2a97d51c17d438cf5be284775a322d57b7ca9505bb7e118c28b1824ecaf8aeaa"}, + {file = "setproctitle-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:587c7d6780109fbd8a627758063d08ab0421377c0853780e5c356873cdf0f077"}, + {file = "setproctitle-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d17c8bd073cbf8d141993db45145a70b307385b69171d6b54bcf23e5d644de"}, + {file = "setproctitle-1.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e932089c35a396dc31a5a1fc49889dd559548d14cb2237adae260382a090382e"}, + {file = "setproctitle-1.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e4f8f12258a8739c565292a551c3db62cca4ed4f6b6126664e2381acb4931bf"}, + {file = "setproctitle-1.3.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:570d255fd99c7f14d8f91363c3ea96bd54f8742275796bca67e1414aeca7d8c3"}, + {file = "setproctitle-1.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a8e0881568c5e6beff91ef73c0ec8ac2a9d3ecc9edd6bd83c31ca34f770910c4"}, + {file = "setproctitle-1.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4bba3be4c1fabf170595b71f3af46c6d482fbe7d9e0563999b49999a31876f77"}, + {file = "setproctitle-1.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:37ece938110cab2bb3957e3910af8152ca15f2b6efdf4f2612e3f6b7e5459b80"}, + {file = "setproctitle-1.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db684d6bbb735a80bcbc3737856385b55d53f8a44ce9b46e9a5682c5133a9bf7"}, + {file = "setproctitle-1.3.2-cp311-cp311-win32.whl", hash = "sha256:ca58cd260ea02759238d994cfae844fc8b1e206c684beb8f38877dcab8451dfc"}, + {file = "setproctitle-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:88486e6cce2a18a033013d17b30a594f1c5cb42520c49c19e6ade40b864bb7ff"}, {file = "setproctitle-1.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:92c626edc66169a1b09e9541b9c0c9f10488447d8a2b1d87c8f0672e771bc927"}, {file = "setproctitle-1.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:710e16fa3bade3b026907e4a5e841124983620046166f355bbb84be364bf2a02"}, {file = "setproctitle-1.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f29b75e86260b0ab59adb12661ef9f113d2f93a59951373eb6d68a852b13e83"}, @@ -7611,6 +7649,9 @@ spidev = [ {file = "spidev-3.6-cp39-cp39-linux_armv7l.whl", hash = "sha256:280abc00a1ef7780ef62c3f294f52a2527b6c47d8c269fea98664970bcaf6da5"}, {file = "spidev-3.6.tar.gz", hash = "sha256:14dbc37594a4aaef85403ab617985d3c3ef464d62bc9b769ef552db53701115b"}, ] +spidev2 = [ + {file = "spidev2-0.9.0.tar.gz", hash = "sha256:152da2911a8660283ceac3a75dd869953379bcbcf079e5436af5aae736876086"}, +] sqlalchemy = [ {file = "SQLAlchemy-1.4.42-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:28e881266a172a4d3c5929182fde6bb6fba22ac93f137d5380cc78a11a9dd124"}, {file = "SQLAlchemy-1.4.42-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ca9389a00f639383c93ed00333ed763812f80b5ae9e772ea32f627043f8c9c88"}, diff --git a/pyproject.toml b/pyproject.toml index e99b278341..af9fcc6e6e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,6 +51,7 @@ six = "^1.16.0" smbus2 = "^0.4.2" sounddevice = "^0.4.5" spidev = { version = "^3.6", platform = "linux" } +spidev2 = { version = "^0.9.0", platform = "linux" } sympy = "^1.10.1" timezonefinder = "^6.0.1" tqdm = "^4.64.0" From 65414bcd8d2fa23d0c6dce8070fb468c08a85dde Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sat, 31 Dec 2022 11:30:28 -0800 Subject: [PATCH 018/484] controlsd: remove laikad from ignored process list --- selfdrive/controls/controlsd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 36f0b559c2..fea19adbdb 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -38,7 +38,7 @@ REPLAY = "REPLAY" in os.environ SIMULATION = "SIMULATION" in os.environ NOSENSOR = "NOSENSOR" in os.environ IGNORE_PROCESSES = {"uploader", "deleter", "loggerd", "logmessaged", "tombstoned", "statsd", - "logcatd", "proclogd", "clocksd", "updated", "timezoned", "manage_athenad", "laikad"} | \ + "logcatd", "proclogd", "clocksd", "updated", "timezoned", "manage_athenad"} | \ {k for k, v in managed_processes.items() if not v.enabled} ThermalStatus = log.DeviceState.ThermalStatus From 304796bafe0c0608e40b20fc44923f9edf462240 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 31 Dec 2022 19:51:03 -0800 Subject: [PATCH 019/484] ui: draw radarState leads (#26852) use radarState --- selfdrive/ui/qt/onroad.cc | 20 +++++++++++--------- selfdrive/ui/qt/onroad.h | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/selfdrive/ui/qt/onroad.cc b/selfdrive/ui/qt/onroad.cc index 95ad813dff..1b4b880fbf 100644 --- a/selfdrive/ui/qt/onroad.cc +++ b/selfdrive/ui/qt/onroad.cc @@ -498,13 +498,13 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) { painter.restore(); } -void AnnotatedCameraWidget::drawLead(QPainter &painter, const cereal::ModelDataV2::LeadDataV3::Reader &lead_data, const QPointF &vd) { +void AnnotatedCameraWidget::drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd) { painter.save(); const float speedBuff = 10.; const float leadBuff = 40.; - const float d_rel = lead_data.getX()[0]; - const float v_rel = lead_data.getV()[0]; + const float d_rel = lead_data.getDRel(); + const float v_rel = lead_data.getVRel(); float fillAlpha = 0; if (d_rel < leadBuff) { @@ -539,6 +539,7 @@ void AnnotatedCameraWidget::paintGL() { SubMaster &sm = *(s->sm); const double start_draw_t = millis_since_boot(); const cereal::ModelDataV2::Reader &model = sm["modelV2"].getModelV2(); + const cereal::RadarState::Reader &radar_state = sm["radarState"].getRadarState(); // draw camera frame { @@ -589,19 +590,20 @@ void AnnotatedCameraWidget::paintGL() { if (sm.rcv_frame("modelV2") > s->scene.started_frame) { update_model(s, sm["modelV2"].getModelV2()); if (sm.rcv_frame("radarState") > s->scene.started_frame) { - update_leads(s, sm["radarState"].getRadarState(), sm["modelV2"].getModelV2().getPosition()); + update_leads(s, radar_state, sm["modelV2"].getModelV2().getPosition()); } } drawLaneLines(painter, s); if (s->scene.longitudinal_control) { - const auto leads = model.getLeadsV3(); - if (leads[0].getProb() > .5) { - drawLead(painter, leads[0], s->scene.lead_vertices[0]); + auto lead_one = radar_state.getLeadOne(); + auto lead_two = radar_state.getLeadTwo(); + if (lead_one.getStatus()) { + drawLead(painter, lead_one, s->scene.lead_vertices[0]); } - if (leads[1].getProb() > .5 && (std::abs(leads[1].getX()[0] - leads[0].getX()[0]) > 3.0)) { - drawLead(painter, leads[1], s->scene.lead_vertices[1]); + if (lead_two.getStatus() && (std::abs(lead_one.getDRel() - lead_two.getDRel()) > 3.0)) { + drawLead(painter, lead_two, s->scene.lead_vertices[1]); } } } diff --git a/selfdrive/ui/qt/onroad.h b/selfdrive/ui/qt/onroad.h index 9e18355970..3dbb05b674 100644 --- a/selfdrive/ui/qt/onroad.h +++ b/selfdrive/ui/qt/onroad.h @@ -80,7 +80,7 @@ protected: void showEvent(QShowEvent *event) override; void updateFrameMat() override; void drawLaneLines(QPainter &painter, const UIState *s); - void drawLead(QPainter &painter, const cereal::ModelDataV2::LeadDataV3::Reader &lead_data, const QPointF &vd); + void drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd); void drawHud(QPainter &p); inline QColor redColor(int alpha = 255) { return QColor(201, 34, 49, alpha); } inline QColor whiteColor(int alpha = 255) { return QColor(255, 255, 255, alpha); } From cd8e03d53ed210aec46a2ff728cb4a830314a21a Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sat, 31 Dec 2022 21:00:50 -0800 Subject: [PATCH 020/484] move all third party stuff into third_party/ (#26853) * mv fastcluster * move msm_kgsl.h * camerad include * update path * mv pyextra * fix tici build * add acados_template to release build Co-authored-by: Comma Device --- .pre-commit-config.yaml | 12 ++++++------ Dockerfile.openpilot | 1 - README.md | 1 - SConstruct | 5 ++--- docs/Makefile | 2 +- docs/docker/Dockerfile | 1 - launch_chffrplus.sh | 2 +- mypy.ini | 2 +- release/files_common | 11 ++++------- selfdrive/controls/lib/lateral_mpc_lib/SConscript | 6 +++--- selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py | 2 +- .../controls/lib/longitudinal_mpc_lib/SConscript | 6 +++--- .../controls/lib/longitudinal_mpc_lib/long_mpc.py | 2 +- selfdrive/controls/radard.py | 2 +- selfdrive/controls/tests/test_clustering.py | 4 ++-- selfdrive/manager/manager.py | 2 -- selfdrive/modeld/thneed/thneed.h | 2 +- system/camerad/SConscript | 14 +++++--------- {pyextra => third_party}/.gitignore | 0 third_party/SConscript | 2 ++ .../acados}/acados_template/.gitignore | 0 .../acados}/acados_template/__init__.py | 0 .../acados}/acados_template/acados_layout.json | 0 .../acados}/acados_template/acados_model.py | 0 .../acados}/acados_template/acados_ocp.py | 0 .../acados}/acados_template/acados_ocp_solver.py | 0 .../acados_template/acados_ocp_solver_pyx.pyx | 0 .../acados}/acados_template/acados_sim.py | 0 .../acados}/acados_template/acados_sim_layout.json | 0 .../acados}/acados_template/acados_sim_solver.py | 0 .../acados_template/acados_solver_common.pxd | 0 .../acados}/acados_template/builders.py | 0 .../c_templates_tera/CMakeLists.in.txt | 0 .../acados_template/c_templates_tera/CPPLINT.cfg | 0 .../acados_template/c_templates_tera/Makefile.in | 0 .../c_templates_tera/acados_mex_create.in.c | 0 .../c_templates_tera/acados_mex_free.in.c | 0 .../c_templates_tera/acados_mex_set.in.c | 0 .../c_templates_tera/acados_mex_solve.in.c | 0 .../c_templates_tera/acados_sim_solver.in.c | 0 .../c_templates_tera/acados_sim_solver.in.h | 0 .../c_templates_tera/acados_sim_solver_sfun.in.c | 0 .../c_templates_tera/acados_solver.in.c | 0 .../c_templates_tera/acados_solver.in.h | 0 .../c_templates_tera/acados_solver.in.pxd | 0 .../c_templates_tera/acados_solver_sfun.in.c | 0 .../c_templates_tera/cost_y_0_fun.in.h | 0 .../c_templates_tera/cost_y_e_fun.in.h | 0 .../c_templates_tera/cost_y_fun.in.h | 0 .../c_templates_tera/external_cost.in.h | 0 .../c_templates_tera/external_cost_0.in.h | 0 .../c_templates_tera/external_cost_e.in.h | 0 .../c_templates_tera/h_constraint.in.h | 0 .../c_templates_tera/h_e_constraint.in.h | 0 .../acados_template/c_templates_tera/main.in.c | 0 .../acados_template/c_templates_tera/main_mex.in.c | 0 .../acados_template/c_templates_tera/main_sim.in.c | 0 .../c_templates_tera/make_main_mex.in.m | 0 .../acados_template/c_templates_tera/make_mex.in.m | 0 .../c_templates_tera/make_sfun.in.m | 0 .../c_templates_tera/make_sfun_sim.in.m | 0 .../c_templates_tera/mex_solver.in.m | 0 .../acados_template/c_templates_tera/model.in.h | 0 .../c_templates_tera/phi_constraint.in.h | 0 .../c_templates_tera/phi_e_constraint.in.h | 0 .../acados_template/generate_c_code_constraint.py | 0 .../generate_c_code_discrete_dynamics.py | 0 .../generate_c_code_explicit_ode.py | 0 .../generate_c_code_external_cost.py | 0 .../acados_template/generate_c_code_gnsf.py | 0 .../generate_c_code_implicit_ode.py | 0 .../acados_template/generate_c_code_nls_cost.py | 0 .../acados_template/simulink_default_opts.json | 0 .../acados}/acados_template/utils.py | 0 third_party/acados/build.sh | 5 ++--- .../lib => third_party}/cluster/.gitignore | 0 .../controls/lib => third_party}/cluster/LICENSE | 0 .../controls/lib => third_party}/cluster/README | 0 .../lib => third_party}/cluster/SConscript | 0 .../lib => third_party}/cluster/__init__.py | 0 .../lib => third_party}/cluster/fastcluster.cpp | 0 .../lib => third_party}/cluster/fastcluster.h | 0 .../cluster/fastcluster_R_dm.cpp | 0 .../lib => third_party}/cluster/fastcluster_dm.cpp | 0 .../lib => third_party}/cluster/fastcluster_py.py | 0 .../controls/lib => third_party}/cluster/test.cpp | 0 .../linux}/include/media/cam_cpas.h | 0 .../linux}/include/media/cam_defs.h | 0 .../linux}/include/media/cam_fd.h | 0 .../linux}/include/media/cam_icp.h | 0 .../linux}/include/media/cam_isp.h | 0 .../linux}/include/media/cam_isp_ife.h | 0 .../linux}/include/media/cam_isp_vfe.h | 0 .../linux}/include/media/cam_jpeg.h | 0 .../linux}/include/media/cam_lrme.h | 0 .../linux}/include/media/cam_req_mgr.h | 0 .../linux}/include/media/cam_sensor.h | 0 .../linux}/include/media/cam_sensor_cmn_header.h | 0 .../linux}/include/media/cam_sync.h | 0 .../linux}/include/msm_cam_sensor.h | 0 .../linux}/include/msm_camsensor_sdk.h | 0 .../linux}/include/msm_kgsl.h | 0 .../linux}/include/msmb_camera.h | 0 .../linux}/include/msmb_isp.h | 0 .../linux}/include/msmb_ispif.h | 0 tools/gpstest/fuzzy_testing.py | 2 +- tools/sim/Dockerfile.sim | 1 - 107 files changed, 37 insertions(+), 50 deletions(-) rename {pyextra => third_party}/.gitignore (100%) rename {pyextra => third_party/acados}/acados_template/.gitignore (100%) rename {pyextra => third_party/acados}/acados_template/__init__.py (100%) rename {pyextra => third_party/acados}/acados_template/acados_layout.json (100%) rename {pyextra => third_party/acados}/acados_template/acados_model.py (100%) rename {pyextra => third_party/acados}/acados_template/acados_ocp.py (100%) rename {pyextra => third_party/acados}/acados_template/acados_ocp_solver.py (100%) rename {pyextra => third_party/acados}/acados_template/acados_ocp_solver_pyx.pyx (100%) rename {pyextra => third_party/acados}/acados_template/acados_sim.py (100%) rename {pyextra => third_party/acados}/acados_template/acados_sim_layout.json (100%) rename {pyextra => third_party/acados}/acados_template/acados_sim_solver.py (100%) rename {pyextra => third_party/acados}/acados_template/acados_solver_common.pxd (100%) rename {pyextra => third_party/acados}/acados_template/builders.py (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/CMakeLists.in.txt (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/CPPLINT.cfg (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/Makefile.in (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/acados_mex_create.in.c (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/acados_mex_free.in.c (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/acados_mex_set.in.c (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/acados_mex_solve.in.c (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/acados_sim_solver.in.c (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/acados_sim_solver.in.h (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/acados_sim_solver_sfun.in.c (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/acados_solver.in.c (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/acados_solver.in.h (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/acados_solver.in.pxd (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/acados_solver_sfun.in.c (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/cost_y_0_fun.in.h (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/cost_y_e_fun.in.h (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/cost_y_fun.in.h (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/external_cost.in.h (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/external_cost_0.in.h (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/external_cost_e.in.h (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/h_constraint.in.h (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/h_e_constraint.in.h (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/main.in.c (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/main_mex.in.c (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/main_sim.in.c (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/make_main_mex.in.m (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/make_mex.in.m (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/make_sfun.in.m (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/make_sfun_sim.in.m (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/mex_solver.in.m (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/model.in.h (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/phi_constraint.in.h (100%) rename {pyextra => third_party/acados}/acados_template/c_templates_tera/phi_e_constraint.in.h (100%) rename {pyextra => third_party/acados}/acados_template/generate_c_code_constraint.py (100%) rename {pyextra => third_party/acados}/acados_template/generate_c_code_discrete_dynamics.py (100%) rename {pyextra => third_party/acados}/acados_template/generate_c_code_explicit_ode.py (100%) rename {pyextra => third_party/acados}/acados_template/generate_c_code_external_cost.py (100%) rename {pyextra => third_party/acados}/acados_template/generate_c_code_gnsf.py (100%) rename {pyextra => third_party/acados}/acados_template/generate_c_code_implicit_ode.py (100%) rename {pyextra => third_party/acados}/acados_template/generate_c_code_nls_cost.py (100%) rename {pyextra => third_party/acados}/acados_template/simulink_default_opts.json (100%) rename {pyextra => third_party/acados}/acados_template/utils.py (100%) rename {selfdrive/controls/lib => third_party}/cluster/.gitignore (100%) rename {selfdrive/controls/lib => third_party}/cluster/LICENSE (100%) rename {selfdrive/controls/lib => third_party}/cluster/README (100%) rename {selfdrive/controls/lib => third_party}/cluster/SConscript (100%) rename {selfdrive/controls/lib => third_party}/cluster/__init__.py (100%) rename {selfdrive/controls/lib => third_party}/cluster/fastcluster.cpp (100%) rename {selfdrive/controls/lib => third_party}/cluster/fastcluster.h (100%) rename {selfdrive/controls/lib => third_party}/cluster/fastcluster_R_dm.cpp (100%) rename {selfdrive/controls/lib => third_party}/cluster/fastcluster_dm.cpp (100%) rename {selfdrive/controls/lib => third_party}/cluster/fastcluster_py.py (100%) rename {selfdrive/controls/lib => third_party}/cluster/test.cpp (100%) rename {system/camerad => third_party/linux}/include/media/cam_cpas.h (100%) rename {system/camerad => third_party/linux}/include/media/cam_defs.h (100%) rename {system/camerad => third_party/linux}/include/media/cam_fd.h (100%) rename {system/camerad => third_party/linux}/include/media/cam_icp.h (100%) rename {system/camerad => third_party/linux}/include/media/cam_isp.h (100%) rename {system/camerad => third_party/linux}/include/media/cam_isp_ife.h (100%) rename {system/camerad => third_party/linux}/include/media/cam_isp_vfe.h (100%) rename {system/camerad => third_party/linux}/include/media/cam_jpeg.h (100%) rename {system/camerad => third_party/linux}/include/media/cam_lrme.h (100%) rename {system/camerad => third_party/linux}/include/media/cam_req_mgr.h (100%) rename {system/camerad => third_party/linux}/include/media/cam_sensor.h (100%) rename {system/camerad => third_party/linux}/include/media/cam_sensor_cmn_header.h (100%) rename {system/camerad => third_party/linux}/include/media/cam_sync.h (100%) rename {system/camerad => third_party/linux}/include/msm_cam_sensor.h (100%) rename {system/camerad => third_party/linux}/include/msm_camsensor_sdk.h (100%) rename {selfdrive/modeld/thneed => third_party/linux}/include/msm_kgsl.h (100%) rename {system/camerad => third_party/linux}/include/msmb_camera.h (100%) rename {system/camerad => third_party/linux}/include/msmb_isp.h (100%) rename {system/camerad => third_party/linux}/include/msmb_ispif.h (100%) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 91b9c61628..edbca52fed 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,7 @@ repos: rev: v4.1.0 hooks: - id: check-ast - exclude: '^(pyextra)/' + exclude: '^(third_party)/' - id: check-json - id: check-xml - id: check-yaml @@ -19,7 +19,7 @@ repos: rev: v2.2.1 hooks: - id: codespell - exclude: '^(pyextra/)|(third_party/)|(body/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(opendbc/)|(laika_repo/)|(rednose_repo/)|(include/)|(selfdrive/ui/translations/.*.ts)|(selfdrive/controls/lib/cluster)' + exclude: '^(third_party/)|(body/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(opendbc/)|(laika_repo/)|(rednose_repo/)|(selfdrive/ui/translations/.*.ts)' args: # if you've got a short variable name that's getting flagged, add it here - -L bu,ro,te,ue,alo,hda,ois,nam,nams,ned,som,parm,setts,inout,warmup @@ -31,12 +31,12 @@ repos: entry: mypy language: system types: [python] - exclude: '^(pyextra/)|(cereal/)|(opendbc/)|(panda/)|(laika/)|(laika_repo/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(xx/)' + exclude: '^(third_party/)|(cereal/)|(opendbc/)|(panda/)|(laika/)|(laika_repo/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(xx/)' - repo: https://github.com/PyCQA/flake8 rev: 4.0.1 hooks: - id: flake8 - exclude: '^(pyextra/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(opendbc/)|(laika_repo/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(selfdrive/debug/)/' + exclude: '^(third_party/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(opendbc/)|(laika_repo/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(selfdrive/debug/)/' additional_dependencies: ['flake8-no-implicit-concat'] args: - --indent-size=2 @@ -51,7 +51,7 @@ repos: entry: pylint language: system types: [python] - exclude: '^(pyextra/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(laika_repo/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)' + exclude: '^(third_party/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(laika_repo/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)' args: - -j0 - -rn @@ -64,7 +64,7 @@ repos: entry: cppcheck language: system types: [c++] - exclude: '^(third_party/)|(pyextra/)|(cereal/)|(body/)|(rednose/)|(rednose_repo/)|(opendbc/)|(panda/)|(tools/)|(selfdrive/modeld/thneed/debug/)|(selfdrive/modeld/test/)|(selfdrive/camerad/test/)|(installer/)' + exclude: '^(third_party/)|(cereal/)|(body/)|(rednose/)|(rednose_repo/)|(opendbc/)|(panda/)|(tools/)|(selfdrive/modeld/thneed/debug/)|(selfdrive/modeld/test/)|(selfdrive/camerad/test/)|(installer/)' args: - --error-exitcode=1 - --language=c++ diff --git a/Dockerfile.openpilot b/Dockerfile.openpilot index 102da78d7d..acc0fcc784 100644 --- a/Dockerfile.openpilot +++ b/Dockerfile.openpilot @@ -10,7 +10,6 @@ WORKDIR ${OPENPILOT_PATH} COPY SConstruct ${OPENPILOT_PATH} -COPY ./pyextra ${OPENPILOT_PATH}/pyextra COPY ./third_party ${OPENPILOT_PATH}/third_party COPY ./site_scons ${OPENPILOT_PATH}/site_scons COPY ./laika ${OPENPILOT_PATH}/laika diff --git a/README.md b/README.md index 88b7aeb2bb..4896d9ff72 100755 --- a/README.md +++ b/README.md @@ -103,7 +103,6 @@ Directory Structure ├── opendbc # Files showing how to interpret data from cars ├── panda # Code used to communicate on CAN ├── third_party # External libraries - ├── pyextra # Extra python packages └── system # Generic services ├── camerad # Driver to capture images from the camera sensors ├── clocksd # Broadcasts current time diff --git a/SConstruct b/SConstruct index 54b008004e..b148a9116a 100644 --- a/SConstruct +++ b/SConstruct @@ -71,10 +71,10 @@ if arch == "aarch64" and AGNOS: lenv = { "PATH": os.environ['PATH'], "LD_LIBRARY_PATH": [Dir(f"#third_party/acados/{arch}/lib").abspath], - "PYTHONPATH": Dir("#").abspath + ":" + Dir("#pyextra/").abspath, + "PYTHONPATH": Dir("#").abspath, "ACADOS_SOURCE_DIR": Dir("#third_party/acados/include/acados").abspath, - "ACADOS_PYTHON_INTERFACE_PATH": Dir("#pyextra/acados_template").abspath, + "ACADOS_PYTHON_INTERFACE_PATH": Dir("#third_party/acados/acados_template").abspath, "TERA_PATH": Dir("#").abspath + f"/third_party/acados/{arch}/t_renderer" } @@ -422,7 +422,6 @@ SConscript(['common/transformations/SConscript']) SConscript(['selfdrive/modeld/SConscript']) -SConscript(['selfdrive/controls/lib/cluster/SConscript']) SConscript(['selfdrive/controls/lib/lateral_mpc_lib/SConscript']) SConscript(['selfdrive/controls/lib/longitudinal_mpc_lib/SConscript']) diff --git a/docs/Makefile b/docs/Makefile index d0aa841c4d..dee660f770 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -41,7 +41,7 @@ clean: @echo "Building rst files..." sphinx-apidoc -o "$(DOCSBUILDDIR)" ../ \ - ../xx ../laika_repo ../rednose_repo ../pyextra ../notebooks ../panda_jungle \ + ../xx ../laika_repo ../rednose_repo ../notebooks ../panda_jungle \ ../third_party \ ../panda/examples \ ../scripts \ diff --git a/docs/docker/Dockerfile b/docs/docker/Dockerfile index 0d5883f566..a1cdbbeaf0 100644 --- a/docs/docker/Dockerfile +++ b/docs/docker/Dockerfile @@ -11,7 +11,6 @@ WORKDIR ${OPENPILOT_PATH} COPY SConstruct ${OPENPILOT_PATH} -COPY ./pyextra ${OPENPILOT_PATH}/pyextra COPY ./third_party ${OPENPILOT_PATH}/third_party COPY ./site_scons ${OPENPILOT_PATH}/site_scons COPY ./laika ${OPENPILOT_PATH}/laika diff --git a/launch_chffrplus.sh b/launch_chffrplus.sh index 911774a4eb..9fe9b1bd15 100755 --- a/launch_chffrplus.sh +++ b/launch_chffrplus.sh @@ -74,7 +74,7 @@ function launch { # handle pythonpath ln -sfn $(pwd) /data/pythonpath - export PYTHONPATH="$PWD:$PWD/pyextra" + export PYTHONPATH="$PWD" # hardware specific init agnos_init diff --git a/mypy.ini b/mypy.ini index 39b1b007a7..9b6d00d2c4 100644 --- a/mypy.ini +++ b/mypy.ini @@ -2,7 +2,7 @@ python_version = 3.8 plugins = numpy.typing.mypy_plugin files = body, common, docs, scripts, selfdrive, site_scons, system, tools -exclude = ^(pyextra/)|(cereal/)|(opendbc/)|(panda/)|(laika/)|(laika_repo/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(xx/) +exclude = ^(cereal/)|(opendbc/)|(panda/)|(laika/)|(laika_repo/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(xx/) ; third-party packages ignore_missing_imports = True diff --git a/release/files_common b/release/files_common index a06543abbf..d0afa30dea 100644 --- a/release/files_common +++ b/release/files_common @@ -192,8 +192,6 @@ selfdrive/controls/lib/pid.py selfdrive/controls/lib/radar_helpers.py selfdrive/controls/lib/vehicle_model.py -selfdrive/controls/lib/cluster/* - selfdrive/controls/lib/lateral_mpc_lib/.gitignore selfdrive/controls/lib/longitudinal_mpc_lib/.gitignore selfdrive/controls/lib/lateral_mpc_lib/* @@ -328,7 +326,6 @@ system/camerad/SConscript system/camerad/main.cc system/camerad/snapshot/* -system/camerad/include/* system/camerad/cameras/camera_common.h system/camerad/cameras/camera_common.cc system/camerad/cameras/sensor2_i2c.h @@ -384,7 +381,6 @@ selfdrive/modeld/thneed/thneed.h selfdrive/modeld/thneed/thneed_common.cc selfdrive/modeld/thneed/thneed_qcom2.cc selfdrive/modeld/thneed/serialize.cc -selfdrive/modeld/thneed/include/* selfdrive/modeld/runners/snpemodel.cc selfdrive/modeld/runners/snpemodel.h @@ -412,8 +408,11 @@ selfdrive/assets/offroad/* selfdrive/assets/sounds/* selfdrive/assets/training/* +third_party/.gitignore third_party/SConscript +third_party/cluster/* + third_party/linux/** third_party/opencl/** @@ -436,15 +435,13 @@ third_party/snpe/dsp** third_party/acados/x86_64/** third_party/acados/larch64/** third_party/acados/include/** +third_party/acados/acados_template/** third_party/qt5/larch64/bin/** scripts/update_now.sh scripts/stop_updater.sh -pyextra/.gitignore -pyextra/acados_template/** - rednose/.gitignore rednose/** laika/** diff --git a/selfdrive/controls/lib/lateral_mpc_lib/SConscript b/selfdrive/controls/lib/lateral_mpc_lib/SConscript index df1e2a2a1a..868b5a873c 100644 --- a/selfdrive/controls/lib/lateral_mpc_lib/SConscript +++ b/selfdrive/controls/lib/lateral_mpc_lib/SConscript @@ -44,7 +44,7 @@ generated_files = [ ] + build_files acados_dir = '#third_party/acados' -acados_templates_dir = '#pyextra/acados_template/c_templates_tera' +acados_templates_dir = '#third_party/acados/acados_template/c_templates_tera' source_list = ['lat_mpc.py', f'{acados_dir}/include/acados_c/ocp_nlp_interface.h', @@ -70,8 +70,8 @@ lib_solver = lenv.SharedLibrary(f"{gen}/acados_ocp_solver_lat", LIBS=['m', 'acados', 'hpipm', 'blasfeo', 'qpOASES_e']) # generate cython stuff -acados_ocp_solver_pyx = File("#pyextra/acados_template/acados_ocp_solver_pyx.pyx") -acados_ocp_solver_common = File("#pyextra/acados_template/acados_solver_common.pxd") +acados_ocp_solver_pyx = File("#third_party/acados/acados_template/acados_ocp_solver_pyx.pyx") +acados_ocp_solver_common = File("#third_party/acados/acados_template/acados_solver_common.pxd") libacados_ocp_solver_pxd = File(f'{gen}/acados_solver.pxd') libacados_ocp_solver_c = File(f'{gen}/acados_ocp_solver_pyx.c') diff --git a/selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py b/selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py index 9607532ace..536f436fce 100755 --- a/selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py +++ b/selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py @@ -8,7 +8,7 @@ from common.realtime import sec_since_boot from selfdrive.modeld.constants import T_IDXS if __name__ == '__main__': # generating code - from pyextra.acados_template import AcadosModel, AcadosOcp, AcadosOcpSolver + from third_party.acados.acados_template import AcadosModel, AcadosOcp, AcadosOcpSolver else: from selfdrive.controls.lib.lateral_mpc_lib.c_generated_code.acados_ocp_solver_pyx import AcadosOcpSolverCython # pylint: disable=no-name-in-module, import-error diff --git a/selfdrive/controls/lib/longitudinal_mpc_lib/SConscript b/selfdrive/controls/lib/longitudinal_mpc_lib/SConscript index 5a9e69c297..e5b2360607 100644 --- a/selfdrive/controls/lib/longitudinal_mpc_lib/SConscript +++ b/selfdrive/controls/lib/longitudinal_mpc_lib/SConscript @@ -51,7 +51,7 @@ generated_files = [ ] + build_files acados_dir = '#third_party/acados' -acados_templates_dir = '#pyextra/acados_template/c_templates_tera' +acados_templates_dir = '#third_party/acados/acados_template/c_templates_tera' source_list = ['long_mpc.py', f'{acados_dir}/include/acados_c/ocp_nlp_interface.h', @@ -77,8 +77,8 @@ lib_solver = lenv.SharedLibrary(f"{gen}/acados_ocp_solver_long", LIBS=['m', 'acados', 'hpipm', 'blasfeo', 'qpOASES_e']) # generate cython stuff -acados_ocp_solver_pyx = File("#pyextra/acados_template/acados_ocp_solver_pyx.pyx") -acados_ocp_solver_common = File("#pyextra/acados_template/acados_solver_common.pxd") +acados_ocp_solver_pyx = File("#third_party/acados/acados_template/acados_ocp_solver_pyx.pyx") +acados_ocp_solver_common = File("#third_party/acados/acados_template/acados_solver_common.pxd") libacados_ocp_solver_pxd = File(f'{gen}/acados_solver.pxd') libacados_ocp_solver_c = File(f'{gen}/acados_ocp_solver_pyx.c') diff --git a/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py b/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py index 79a9ec4f0c..dc27fd27a9 100644 --- a/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py +++ b/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py @@ -9,7 +9,7 @@ from selfdrive.modeld.constants import index_function from selfdrive.controls.lib.radar_helpers import _LEAD_ACCEL_TAU if __name__ == '__main__': # generating code - from pyextra.acados_template import AcadosModel, AcadosOcp, AcadosOcpSolver + from third_party.acados.acados_template import AcadosModel, AcadosOcp, AcadosOcpSolver else: from selfdrive.controls.lib.longitudinal_mpc_lib.c_generated_code.acados_ocp_solver_pyx import AcadosOcpSolverCython # pylint: disable=no-name-in-module, import-error diff --git a/selfdrive/controls/radard.py b/selfdrive/controls/radard.py index 3d958139d6..db87604e98 100755 --- a/selfdrive/controls/radard.py +++ b/selfdrive/controls/radard.py @@ -8,9 +8,9 @@ from cereal import car from common.numpy_fast import interp from common.params import Params from common.realtime import Ratekeeper, Priority, config_realtime_process -from selfdrive.controls.lib.cluster.fastcluster_py import cluster_points_centroid from selfdrive.controls.lib.radar_helpers import Cluster, Track, RADAR_TO_CAMERA from system.swaglog import cloudlog +from third_party.cluster.fastcluster_py import cluster_points_centroid class KalmanParams(): diff --git a/selfdrive/controls/tests/test_clustering.py b/selfdrive/controls/tests/test_clustering.py index 290b267029..99c24d5938 100644 --- a/selfdrive/controls/tests/test_clustering.py +++ b/selfdrive/controls/tests/test_clustering.py @@ -7,8 +7,8 @@ from fastcluster import linkage_vector from scipy.cluster import _hierarchy from scipy.spatial.distance import pdist -from selfdrive.controls.lib.cluster.fastcluster_py import hclust, ffi -from selfdrive.controls.lib.cluster.fastcluster_py import cluster_points_centroid +from third_party.cluster.fastcluster_py import hclust, ffi +from third_party.cluster.fastcluster_py import cluster_points_centroid def fcluster(Z, t, criterion='inconsistent', depth=2, R=None, monocrit=None): diff --git a/selfdrive/manager/manager.py b/selfdrive/manager/manager.py index 928507f65b..369c529626 100755 --- a/selfdrive/manager/manager.py +++ b/selfdrive/manager/manager.py @@ -23,8 +23,6 @@ from system.version import is_dirty, get_commit, get_version, get_origin, get_sh terms_version, training_version, is_tested_branch -sys.path.append(os.path.join(BASEDIR, "pyextra")) - def manager_init() -> None: # update system time from panda diff --git a/selfdrive/modeld/thneed/thneed.h b/selfdrive/modeld/thneed/thneed.h index 65475ccf7f..6475577734 100644 --- a/selfdrive/modeld/thneed/thneed.h +++ b/selfdrive/modeld/thneed/thneed.h @@ -12,7 +12,7 @@ #include -#include "selfdrive/modeld/thneed/include/msm_kgsl.h" +#include "msm_kgsl.h" using namespace std; diff --git a/system/camerad/SConscript b/system/camerad/SConscript index ddc763b53d..3ecc3f6d72 100644 --- a/system/camerad/SConscript +++ b/system/camerad/SConscript @@ -2,17 +2,13 @@ Import('env', 'arch', 'cereal', 'messaging', 'common', 'gpucommon', 'visionipc') libs = ['m', 'pthread', common, 'jpeg', 'OpenCL', 'yuv', cereal, messaging, 'zmq', 'capnp', 'kj', visionipc, gpucommon, 'atomic'] -cenv = env.Clone() -cenv['CPPPATH'].append('include/') - -camera_obj = cenv.Object(['cameras/camera_qcom2.cc', 'cameras/camera_common.cc', 'cameras/camera_util.cc']) -cenv.Program('camerad', [ +camera_obj = env.Object(['cameras/camera_qcom2.cc', 'cameras/camera_common.cc', 'cameras/camera_util.cc']) +env.Program('camerad', [ 'main.cc', camera_obj, ], LIBS=libs) if GetOption("test") and arch == "x86_64": - cenv.Program('test/ae_gray_test', [ - 'test/ae_gray_test.cc', - camera_obj, - ], LIBS=libs) + env.Program('test/ae_gray_test', + ['test/ae_gray_test.cc', camera_obj], + LIBS=libs) diff --git a/pyextra/.gitignore b/third_party/.gitignore similarity index 100% rename from pyextra/.gitignore rename to third_party/.gitignore diff --git a/third_party/SConscript b/third_party/SConscript index e5bbfaa07a..e8d1789ee0 100644 --- a/third_party/SConscript +++ b/third_party/SConscript @@ -4,3 +4,5 @@ env.Library('json11', ['json11/json11.cpp'], CCFLAGS=env['CCFLAGS'] + ['-Wno-unq env.Append(CPPPATH=[Dir('json11')]) env.Library('kaitai', ['kaitai/kaitaistream.cpp'], CPPDEFINES=['KS_STR_ENCODING_NONE']) + +SConscript(['cluster/SConscript']) diff --git a/pyextra/acados_template/.gitignore b/third_party/acados/acados_template/.gitignore similarity index 100% rename from pyextra/acados_template/.gitignore rename to third_party/acados/acados_template/.gitignore diff --git a/pyextra/acados_template/__init__.py b/third_party/acados/acados_template/__init__.py similarity index 100% rename from pyextra/acados_template/__init__.py rename to third_party/acados/acados_template/__init__.py diff --git a/pyextra/acados_template/acados_layout.json b/third_party/acados/acados_template/acados_layout.json similarity index 100% rename from pyextra/acados_template/acados_layout.json rename to third_party/acados/acados_template/acados_layout.json diff --git a/pyextra/acados_template/acados_model.py b/third_party/acados/acados_template/acados_model.py similarity index 100% rename from pyextra/acados_template/acados_model.py rename to third_party/acados/acados_template/acados_model.py diff --git a/pyextra/acados_template/acados_ocp.py b/third_party/acados/acados_template/acados_ocp.py similarity index 100% rename from pyextra/acados_template/acados_ocp.py rename to third_party/acados/acados_template/acados_ocp.py diff --git a/pyextra/acados_template/acados_ocp_solver.py b/third_party/acados/acados_template/acados_ocp_solver.py similarity index 100% rename from pyextra/acados_template/acados_ocp_solver.py rename to third_party/acados/acados_template/acados_ocp_solver.py diff --git a/pyextra/acados_template/acados_ocp_solver_pyx.pyx b/third_party/acados/acados_template/acados_ocp_solver_pyx.pyx similarity index 100% rename from pyextra/acados_template/acados_ocp_solver_pyx.pyx rename to third_party/acados/acados_template/acados_ocp_solver_pyx.pyx diff --git a/pyextra/acados_template/acados_sim.py b/third_party/acados/acados_template/acados_sim.py similarity index 100% rename from pyextra/acados_template/acados_sim.py rename to third_party/acados/acados_template/acados_sim.py diff --git a/pyextra/acados_template/acados_sim_layout.json b/third_party/acados/acados_template/acados_sim_layout.json similarity index 100% rename from pyextra/acados_template/acados_sim_layout.json rename to third_party/acados/acados_template/acados_sim_layout.json diff --git a/pyextra/acados_template/acados_sim_solver.py b/third_party/acados/acados_template/acados_sim_solver.py similarity index 100% rename from pyextra/acados_template/acados_sim_solver.py rename to third_party/acados/acados_template/acados_sim_solver.py diff --git a/pyextra/acados_template/acados_solver_common.pxd b/third_party/acados/acados_template/acados_solver_common.pxd similarity index 100% rename from pyextra/acados_template/acados_solver_common.pxd rename to third_party/acados/acados_template/acados_solver_common.pxd diff --git a/pyextra/acados_template/builders.py b/third_party/acados/acados_template/builders.py similarity index 100% rename from pyextra/acados_template/builders.py rename to third_party/acados/acados_template/builders.py diff --git a/pyextra/acados_template/c_templates_tera/CMakeLists.in.txt b/third_party/acados/acados_template/c_templates_tera/CMakeLists.in.txt similarity index 100% rename from pyextra/acados_template/c_templates_tera/CMakeLists.in.txt rename to third_party/acados/acados_template/c_templates_tera/CMakeLists.in.txt diff --git a/pyextra/acados_template/c_templates_tera/CPPLINT.cfg b/third_party/acados/acados_template/c_templates_tera/CPPLINT.cfg similarity index 100% rename from pyextra/acados_template/c_templates_tera/CPPLINT.cfg rename to third_party/acados/acados_template/c_templates_tera/CPPLINT.cfg diff --git a/pyextra/acados_template/c_templates_tera/Makefile.in b/third_party/acados/acados_template/c_templates_tera/Makefile.in similarity index 100% rename from pyextra/acados_template/c_templates_tera/Makefile.in rename to third_party/acados/acados_template/c_templates_tera/Makefile.in diff --git a/pyextra/acados_template/c_templates_tera/acados_mex_create.in.c b/third_party/acados/acados_template/c_templates_tera/acados_mex_create.in.c similarity index 100% rename from pyextra/acados_template/c_templates_tera/acados_mex_create.in.c rename to third_party/acados/acados_template/c_templates_tera/acados_mex_create.in.c diff --git a/pyextra/acados_template/c_templates_tera/acados_mex_free.in.c b/third_party/acados/acados_template/c_templates_tera/acados_mex_free.in.c similarity index 100% rename from pyextra/acados_template/c_templates_tera/acados_mex_free.in.c rename to third_party/acados/acados_template/c_templates_tera/acados_mex_free.in.c diff --git a/pyextra/acados_template/c_templates_tera/acados_mex_set.in.c b/third_party/acados/acados_template/c_templates_tera/acados_mex_set.in.c similarity index 100% rename from pyextra/acados_template/c_templates_tera/acados_mex_set.in.c rename to third_party/acados/acados_template/c_templates_tera/acados_mex_set.in.c diff --git a/pyextra/acados_template/c_templates_tera/acados_mex_solve.in.c b/third_party/acados/acados_template/c_templates_tera/acados_mex_solve.in.c similarity index 100% rename from pyextra/acados_template/c_templates_tera/acados_mex_solve.in.c rename to third_party/acados/acados_template/c_templates_tera/acados_mex_solve.in.c diff --git a/pyextra/acados_template/c_templates_tera/acados_sim_solver.in.c b/third_party/acados/acados_template/c_templates_tera/acados_sim_solver.in.c similarity index 100% rename from pyextra/acados_template/c_templates_tera/acados_sim_solver.in.c rename to third_party/acados/acados_template/c_templates_tera/acados_sim_solver.in.c diff --git a/pyextra/acados_template/c_templates_tera/acados_sim_solver.in.h b/third_party/acados/acados_template/c_templates_tera/acados_sim_solver.in.h similarity index 100% rename from pyextra/acados_template/c_templates_tera/acados_sim_solver.in.h rename to third_party/acados/acados_template/c_templates_tera/acados_sim_solver.in.h diff --git a/pyextra/acados_template/c_templates_tera/acados_sim_solver_sfun.in.c b/third_party/acados/acados_template/c_templates_tera/acados_sim_solver_sfun.in.c similarity index 100% rename from pyextra/acados_template/c_templates_tera/acados_sim_solver_sfun.in.c rename to third_party/acados/acados_template/c_templates_tera/acados_sim_solver_sfun.in.c diff --git a/pyextra/acados_template/c_templates_tera/acados_solver.in.c b/third_party/acados/acados_template/c_templates_tera/acados_solver.in.c similarity index 100% rename from pyextra/acados_template/c_templates_tera/acados_solver.in.c rename to third_party/acados/acados_template/c_templates_tera/acados_solver.in.c diff --git a/pyextra/acados_template/c_templates_tera/acados_solver.in.h b/third_party/acados/acados_template/c_templates_tera/acados_solver.in.h similarity index 100% rename from pyextra/acados_template/c_templates_tera/acados_solver.in.h rename to third_party/acados/acados_template/c_templates_tera/acados_solver.in.h diff --git a/pyextra/acados_template/c_templates_tera/acados_solver.in.pxd b/third_party/acados/acados_template/c_templates_tera/acados_solver.in.pxd similarity index 100% rename from pyextra/acados_template/c_templates_tera/acados_solver.in.pxd rename to third_party/acados/acados_template/c_templates_tera/acados_solver.in.pxd diff --git a/pyextra/acados_template/c_templates_tera/acados_solver_sfun.in.c b/third_party/acados/acados_template/c_templates_tera/acados_solver_sfun.in.c similarity index 100% rename from pyextra/acados_template/c_templates_tera/acados_solver_sfun.in.c rename to third_party/acados/acados_template/c_templates_tera/acados_solver_sfun.in.c diff --git a/pyextra/acados_template/c_templates_tera/cost_y_0_fun.in.h b/third_party/acados/acados_template/c_templates_tera/cost_y_0_fun.in.h similarity index 100% rename from pyextra/acados_template/c_templates_tera/cost_y_0_fun.in.h rename to third_party/acados/acados_template/c_templates_tera/cost_y_0_fun.in.h diff --git a/pyextra/acados_template/c_templates_tera/cost_y_e_fun.in.h b/third_party/acados/acados_template/c_templates_tera/cost_y_e_fun.in.h similarity index 100% rename from pyextra/acados_template/c_templates_tera/cost_y_e_fun.in.h rename to third_party/acados/acados_template/c_templates_tera/cost_y_e_fun.in.h diff --git a/pyextra/acados_template/c_templates_tera/cost_y_fun.in.h b/third_party/acados/acados_template/c_templates_tera/cost_y_fun.in.h similarity index 100% rename from pyextra/acados_template/c_templates_tera/cost_y_fun.in.h rename to third_party/acados/acados_template/c_templates_tera/cost_y_fun.in.h diff --git a/pyextra/acados_template/c_templates_tera/external_cost.in.h b/third_party/acados/acados_template/c_templates_tera/external_cost.in.h similarity index 100% rename from pyextra/acados_template/c_templates_tera/external_cost.in.h rename to third_party/acados/acados_template/c_templates_tera/external_cost.in.h diff --git a/pyextra/acados_template/c_templates_tera/external_cost_0.in.h b/third_party/acados/acados_template/c_templates_tera/external_cost_0.in.h similarity index 100% rename from pyextra/acados_template/c_templates_tera/external_cost_0.in.h rename to third_party/acados/acados_template/c_templates_tera/external_cost_0.in.h diff --git a/pyextra/acados_template/c_templates_tera/external_cost_e.in.h b/third_party/acados/acados_template/c_templates_tera/external_cost_e.in.h similarity index 100% rename from pyextra/acados_template/c_templates_tera/external_cost_e.in.h rename to third_party/acados/acados_template/c_templates_tera/external_cost_e.in.h diff --git a/pyextra/acados_template/c_templates_tera/h_constraint.in.h b/third_party/acados/acados_template/c_templates_tera/h_constraint.in.h similarity index 100% rename from pyextra/acados_template/c_templates_tera/h_constraint.in.h rename to third_party/acados/acados_template/c_templates_tera/h_constraint.in.h diff --git a/pyextra/acados_template/c_templates_tera/h_e_constraint.in.h b/third_party/acados/acados_template/c_templates_tera/h_e_constraint.in.h similarity index 100% rename from pyextra/acados_template/c_templates_tera/h_e_constraint.in.h rename to third_party/acados/acados_template/c_templates_tera/h_e_constraint.in.h diff --git a/pyextra/acados_template/c_templates_tera/main.in.c b/third_party/acados/acados_template/c_templates_tera/main.in.c similarity index 100% rename from pyextra/acados_template/c_templates_tera/main.in.c rename to third_party/acados/acados_template/c_templates_tera/main.in.c diff --git a/pyextra/acados_template/c_templates_tera/main_mex.in.c b/third_party/acados/acados_template/c_templates_tera/main_mex.in.c similarity index 100% rename from pyextra/acados_template/c_templates_tera/main_mex.in.c rename to third_party/acados/acados_template/c_templates_tera/main_mex.in.c diff --git a/pyextra/acados_template/c_templates_tera/main_sim.in.c b/third_party/acados/acados_template/c_templates_tera/main_sim.in.c similarity index 100% rename from pyextra/acados_template/c_templates_tera/main_sim.in.c rename to third_party/acados/acados_template/c_templates_tera/main_sim.in.c diff --git a/pyextra/acados_template/c_templates_tera/make_main_mex.in.m b/third_party/acados/acados_template/c_templates_tera/make_main_mex.in.m similarity index 100% rename from pyextra/acados_template/c_templates_tera/make_main_mex.in.m rename to third_party/acados/acados_template/c_templates_tera/make_main_mex.in.m diff --git a/pyextra/acados_template/c_templates_tera/make_mex.in.m b/third_party/acados/acados_template/c_templates_tera/make_mex.in.m similarity index 100% rename from pyextra/acados_template/c_templates_tera/make_mex.in.m rename to third_party/acados/acados_template/c_templates_tera/make_mex.in.m diff --git a/pyextra/acados_template/c_templates_tera/make_sfun.in.m b/third_party/acados/acados_template/c_templates_tera/make_sfun.in.m similarity index 100% rename from pyextra/acados_template/c_templates_tera/make_sfun.in.m rename to third_party/acados/acados_template/c_templates_tera/make_sfun.in.m diff --git a/pyextra/acados_template/c_templates_tera/make_sfun_sim.in.m b/third_party/acados/acados_template/c_templates_tera/make_sfun_sim.in.m similarity index 100% rename from pyextra/acados_template/c_templates_tera/make_sfun_sim.in.m rename to third_party/acados/acados_template/c_templates_tera/make_sfun_sim.in.m diff --git a/pyextra/acados_template/c_templates_tera/mex_solver.in.m b/third_party/acados/acados_template/c_templates_tera/mex_solver.in.m similarity index 100% rename from pyextra/acados_template/c_templates_tera/mex_solver.in.m rename to third_party/acados/acados_template/c_templates_tera/mex_solver.in.m diff --git a/pyextra/acados_template/c_templates_tera/model.in.h b/third_party/acados/acados_template/c_templates_tera/model.in.h similarity index 100% rename from pyextra/acados_template/c_templates_tera/model.in.h rename to third_party/acados/acados_template/c_templates_tera/model.in.h diff --git a/pyextra/acados_template/c_templates_tera/phi_constraint.in.h b/third_party/acados/acados_template/c_templates_tera/phi_constraint.in.h similarity index 100% rename from pyextra/acados_template/c_templates_tera/phi_constraint.in.h rename to third_party/acados/acados_template/c_templates_tera/phi_constraint.in.h diff --git a/pyextra/acados_template/c_templates_tera/phi_e_constraint.in.h b/third_party/acados/acados_template/c_templates_tera/phi_e_constraint.in.h similarity index 100% rename from pyextra/acados_template/c_templates_tera/phi_e_constraint.in.h rename to third_party/acados/acados_template/c_templates_tera/phi_e_constraint.in.h diff --git a/pyextra/acados_template/generate_c_code_constraint.py b/third_party/acados/acados_template/generate_c_code_constraint.py similarity index 100% rename from pyextra/acados_template/generate_c_code_constraint.py rename to third_party/acados/acados_template/generate_c_code_constraint.py diff --git a/pyextra/acados_template/generate_c_code_discrete_dynamics.py b/third_party/acados/acados_template/generate_c_code_discrete_dynamics.py similarity index 100% rename from pyextra/acados_template/generate_c_code_discrete_dynamics.py rename to third_party/acados/acados_template/generate_c_code_discrete_dynamics.py diff --git a/pyextra/acados_template/generate_c_code_explicit_ode.py b/third_party/acados/acados_template/generate_c_code_explicit_ode.py similarity index 100% rename from pyextra/acados_template/generate_c_code_explicit_ode.py rename to third_party/acados/acados_template/generate_c_code_explicit_ode.py diff --git a/pyextra/acados_template/generate_c_code_external_cost.py b/third_party/acados/acados_template/generate_c_code_external_cost.py similarity index 100% rename from pyextra/acados_template/generate_c_code_external_cost.py rename to third_party/acados/acados_template/generate_c_code_external_cost.py diff --git a/pyextra/acados_template/generate_c_code_gnsf.py b/third_party/acados/acados_template/generate_c_code_gnsf.py similarity index 100% rename from pyextra/acados_template/generate_c_code_gnsf.py rename to third_party/acados/acados_template/generate_c_code_gnsf.py diff --git a/pyextra/acados_template/generate_c_code_implicit_ode.py b/third_party/acados/acados_template/generate_c_code_implicit_ode.py similarity index 100% rename from pyextra/acados_template/generate_c_code_implicit_ode.py rename to third_party/acados/acados_template/generate_c_code_implicit_ode.py diff --git a/pyextra/acados_template/generate_c_code_nls_cost.py b/third_party/acados/acados_template/generate_c_code_nls_cost.py similarity index 100% rename from pyextra/acados_template/generate_c_code_nls_cost.py rename to third_party/acados/acados_template/generate_c_code_nls_cost.py diff --git a/pyextra/acados_template/simulink_default_opts.json b/third_party/acados/acados_template/simulink_default_opts.json similarity index 100% rename from pyextra/acados_template/simulink_default_opts.json rename to third_party/acados/acados_template/simulink_default_opts.json diff --git a/pyextra/acados_template/utils.py b/third_party/acados/acados_template/utils.py similarity index 100% rename from pyextra/acados_template/utils.py rename to third_party/acados/acados_template/utils.py diff --git a/third_party/acados/build.sh b/third_party/acados/build.sh index 0481e8159b..9b18b2b67d 100755 --- a/third_party/acados/build.sh +++ b/third_party/acados/build.sh @@ -43,11 +43,10 @@ mkdir -p $INSTALL_DIR rm $DIR/acados_repo/lib/*.json -rm -rf $DIR/include +rm -rf $DIR/include $DIR/acados_template cp -r $DIR/acados_repo/include $DIR cp -r $DIR/acados_repo/lib $INSTALL_DIR -rm -rf $DIR/../../pyextra/acados_template -cp -r $DIR/acados_repo/interfaces/acados_template/acados_template $DIR/../../pyextra +cp -r $DIR/acados_repo/interfaces/acados_template/acados_template $DIR/ #pip3 install -e $DIR/acados/interfaces/acados_template # build tera diff --git a/selfdrive/controls/lib/cluster/.gitignore b/third_party/cluster/.gitignore similarity index 100% rename from selfdrive/controls/lib/cluster/.gitignore rename to third_party/cluster/.gitignore diff --git a/selfdrive/controls/lib/cluster/LICENSE b/third_party/cluster/LICENSE similarity index 100% rename from selfdrive/controls/lib/cluster/LICENSE rename to third_party/cluster/LICENSE diff --git a/selfdrive/controls/lib/cluster/README b/third_party/cluster/README similarity index 100% rename from selfdrive/controls/lib/cluster/README rename to third_party/cluster/README diff --git a/selfdrive/controls/lib/cluster/SConscript b/third_party/cluster/SConscript similarity index 100% rename from selfdrive/controls/lib/cluster/SConscript rename to third_party/cluster/SConscript diff --git a/selfdrive/controls/lib/cluster/__init__.py b/third_party/cluster/__init__.py similarity index 100% rename from selfdrive/controls/lib/cluster/__init__.py rename to third_party/cluster/__init__.py diff --git a/selfdrive/controls/lib/cluster/fastcluster.cpp b/third_party/cluster/fastcluster.cpp similarity index 100% rename from selfdrive/controls/lib/cluster/fastcluster.cpp rename to third_party/cluster/fastcluster.cpp diff --git a/selfdrive/controls/lib/cluster/fastcluster.h b/third_party/cluster/fastcluster.h similarity index 100% rename from selfdrive/controls/lib/cluster/fastcluster.h rename to third_party/cluster/fastcluster.h diff --git a/selfdrive/controls/lib/cluster/fastcluster_R_dm.cpp b/third_party/cluster/fastcluster_R_dm.cpp similarity index 100% rename from selfdrive/controls/lib/cluster/fastcluster_R_dm.cpp rename to third_party/cluster/fastcluster_R_dm.cpp diff --git a/selfdrive/controls/lib/cluster/fastcluster_dm.cpp b/third_party/cluster/fastcluster_dm.cpp similarity index 100% rename from selfdrive/controls/lib/cluster/fastcluster_dm.cpp rename to third_party/cluster/fastcluster_dm.cpp diff --git a/selfdrive/controls/lib/cluster/fastcluster_py.py b/third_party/cluster/fastcluster_py.py similarity index 100% rename from selfdrive/controls/lib/cluster/fastcluster_py.py rename to third_party/cluster/fastcluster_py.py diff --git a/selfdrive/controls/lib/cluster/test.cpp b/third_party/cluster/test.cpp similarity index 100% rename from selfdrive/controls/lib/cluster/test.cpp rename to third_party/cluster/test.cpp diff --git a/system/camerad/include/media/cam_cpas.h b/third_party/linux/include/media/cam_cpas.h similarity index 100% rename from system/camerad/include/media/cam_cpas.h rename to third_party/linux/include/media/cam_cpas.h diff --git a/system/camerad/include/media/cam_defs.h b/third_party/linux/include/media/cam_defs.h similarity index 100% rename from system/camerad/include/media/cam_defs.h rename to third_party/linux/include/media/cam_defs.h diff --git a/system/camerad/include/media/cam_fd.h b/third_party/linux/include/media/cam_fd.h similarity index 100% rename from system/camerad/include/media/cam_fd.h rename to third_party/linux/include/media/cam_fd.h diff --git a/system/camerad/include/media/cam_icp.h b/third_party/linux/include/media/cam_icp.h similarity index 100% rename from system/camerad/include/media/cam_icp.h rename to third_party/linux/include/media/cam_icp.h diff --git a/system/camerad/include/media/cam_isp.h b/third_party/linux/include/media/cam_isp.h similarity index 100% rename from system/camerad/include/media/cam_isp.h rename to third_party/linux/include/media/cam_isp.h diff --git a/system/camerad/include/media/cam_isp_ife.h b/third_party/linux/include/media/cam_isp_ife.h similarity index 100% rename from system/camerad/include/media/cam_isp_ife.h rename to third_party/linux/include/media/cam_isp_ife.h diff --git a/system/camerad/include/media/cam_isp_vfe.h b/third_party/linux/include/media/cam_isp_vfe.h similarity index 100% rename from system/camerad/include/media/cam_isp_vfe.h rename to third_party/linux/include/media/cam_isp_vfe.h diff --git a/system/camerad/include/media/cam_jpeg.h b/third_party/linux/include/media/cam_jpeg.h similarity index 100% rename from system/camerad/include/media/cam_jpeg.h rename to third_party/linux/include/media/cam_jpeg.h diff --git a/system/camerad/include/media/cam_lrme.h b/third_party/linux/include/media/cam_lrme.h similarity index 100% rename from system/camerad/include/media/cam_lrme.h rename to third_party/linux/include/media/cam_lrme.h diff --git a/system/camerad/include/media/cam_req_mgr.h b/third_party/linux/include/media/cam_req_mgr.h similarity index 100% rename from system/camerad/include/media/cam_req_mgr.h rename to third_party/linux/include/media/cam_req_mgr.h diff --git a/system/camerad/include/media/cam_sensor.h b/third_party/linux/include/media/cam_sensor.h similarity index 100% rename from system/camerad/include/media/cam_sensor.h rename to third_party/linux/include/media/cam_sensor.h diff --git a/system/camerad/include/media/cam_sensor_cmn_header.h b/third_party/linux/include/media/cam_sensor_cmn_header.h similarity index 100% rename from system/camerad/include/media/cam_sensor_cmn_header.h rename to third_party/linux/include/media/cam_sensor_cmn_header.h diff --git a/system/camerad/include/media/cam_sync.h b/third_party/linux/include/media/cam_sync.h similarity index 100% rename from system/camerad/include/media/cam_sync.h rename to third_party/linux/include/media/cam_sync.h diff --git a/system/camerad/include/msm_cam_sensor.h b/third_party/linux/include/msm_cam_sensor.h similarity index 100% rename from system/camerad/include/msm_cam_sensor.h rename to third_party/linux/include/msm_cam_sensor.h diff --git a/system/camerad/include/msm_camsensor_sdk.h b/third_party/linux/include/msm_camsensor_sdk.h similarity index 100% rename from system/camerad/include/msm_camsensor_sdk.h rename to third_party/linux/include/msm_camsensor_sdk.h diff --git a/selfdrive/modeld/thneed/include/msm_kgsl.h b/third_party/linux/include/msm_kgsl.h similarity index 100% rename from selfdrive/modeld/thneed/include/msm_kgsl.h rename to third_party/linux/include/msm_kgsl.h diff --git a/system/camerad/include/msmb_camera.h b/third_party/linux/include/msmb_camera.h similarity index 100% rename from system/camerad/include/msmb_camera.h rename to third_party/linux/include/msmb_camera.h diff --git a/system/camerad/include/msmb_isp.h b/third_party/linux/include/msmb_isp.h similarity index 100% rename from system/camerad/include/msmb_isp.h rename to third_party/linux/include/msmb_isp.h diff --git a/system/camerad/include/msmb_ispif.h b/third_party/linux/include/msmb_ispif.h similarity index 100% rename from system/camerad/include/msmb_ispif.h rename to third_party/linux/include/msmb_ispif.h diff --git a/tools/gpstest/fuzzy_testing.py b/tools/gpstest/fuzzy_testing.py index df6691c558..bd204e7ae7 100755 --- a/tools/gpstest/fuzzy_testing.py +++ b/tools/gpstest/fuzzy_testing.py @@ -69,7 +69,7 @@ rc_p: Any = None def exec_remote_checker(lat, lon, duration, ip_addr): global rc_p # TODO: good enough for testing - remote_cmd = "export PYTHONPATH=/data/pythonpath:/data/pythonpath/pyextra && " + remote_cmd = "export PYTHONPATH=/data/pythonpath && " remote_cmd += "cd /data/openpilot && " remote_cmd += f"timeout {duration} /usr/local/pyenv/shims/python tools/gpstest/remote_checker.py " remote_cmd += f"{lat} {lon}" diff --git a/tools/sim/Dockerfile.sim b/tools/sim/Dockerfile.sim index 3692d48e0f..be16f8c863 100644 --- a/tools/sim/Dockerfile.sim +++ b/tools/sim/Dockerfile.sim @@ -15,7 +15,6 @@ RUN mkdir -p $HOME/openpilot COPY SConstruct $HOME/openpilot/ COPY ./third_party $HOME/openpilot/third_party -COPY ./pyextra $HOME/openpilot/pyextra COPY ./site_scons $HOME/openpilot/site_scons COPY ./rednose $HOME/openpilot/rednose COPY ./laika $HOME/openpilot/laika From 3d44b6b3ac6d1f207e01d4cbf225bd929255ca1e Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 1 Jan 2023 19:15:44 -0800 Subject: [PATCH 021/484] alt amp config (#26858) * alt amp config * fix * not executable * comment * consistency Co-authored-by: Comma Device --- system/hardware/tici/amplifier.py | 52 ++++++++++++++++++++++++------- system/hardware/tici/hardware.py | 14 +++++++-- 2 files changed, 53 insertions(+), 13 deletions(-) mode change 100755 => 100644 system/hardware/tici/amplifier.py diff --git a/system/hardware/tici/amplifier.py b/system/hardware/tici/amplifier.py old mode 100755 new mode 100644 index a8b2798630..8233834d11 --- a/system/hardware/tici/amplifier.py +++ b/system/hardware/tici/amplifier.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python from smbus2 import SMBus from collections import namedtuple @@ -63,11 +62,43 @@ BASE_CONFIG = [ AmpConfig("Zero-crossing detection disabled", 0b0, 0x49, 5, 0b00100000), ] -BASE_CONFIG += configs_from_eq_params(0x84, EQParams(0x274F, 0xC0FF, 0x3BF9, 0x0B3C, 0x1656)) -BASE_CONFIG += configs_from_eq_params(0x8E, EQParams(0x1009, 0xC6BF, 0x2952, 0x1C97, 0x30DF)) -BASE_CONFIG += configs_from_eq_params(0x98, EQParams(0x0F75, 0xCBE5, 0x0ED2, 0x2528, 0x3E42)) -BASE_CONFIG += configs_from_eq_params(0xA2, EQParams(0x091F, 0x3D4C, 0xCE11, 0x1266, 0x2807)) -BASE_CONFIG += configs_from_eq_params(0xAC, EQParams(0x0A9E, 0x3F20, 0xE573, 0x0A8B, 0x3A3B)) +CONFIGS = { + "tici": [ + *configs_from_eq_params(0x84, EQParams(0x274F, 0xC0FF, 0x3BF9, 0x0B3C, 0x1656)), + *configs_from_eq_params(0x8E, EQParams(0x1009, 0xC6BF, 0x2952, 0x1C97, 0x30DF)), + *configs_from_eq_params(0x98, EQParams(0x0F75, 0xCBE5, 0x0ED2, 0x2528, 0x3E42)), + *configs_from_eq_params(0xA2, EQParams(0x091F, 0x3D4C, 0xCE11, 0x1266, 0x2807)), + *configs_from_eq_params(0xAC, EQParams(0x0A9E, 0x3F20, 0xE573, 0x0A8B, 0x3A3B)), + ], + "tizi": [ + AmpConfig("Left speaker output from left DAC", 0b1, 0x2B, 0, 0b11111111), + AmpConfig("Left Speaker Mixer Gain", 0b00, 0x2D, 0, 0b00000011), + AmpConfig("Left speaker output volume", 0x1F, 0x3D, 0, 0b00011111), + AmpConfig("Right speaker output volume", 0x1F, 0x3E, 0, 0b00011111), + AmpConfig("DAI1 attenuation (DV1)", 0x4, 0x2F, 0, 0b00001111), + AmpConfig("DAI2 attenuation (DV2)", 0x4, 0x31, 0, 0b00001111), + AmpConfig("DAI2: DC blocking", 0b0, 0x20, 0, 0b00000001), + AmpConfig("ALC enable", 0b0, 0x43, 7, 0b10000000), + AmpConfig("DAI2 EQ attenuation", 0x2, 0x32, 0, 0b00001111), + AmpConfig("Excursion limiter upper corner freq", 0b001, 0x41, 4, 0b01110000), + AmpConfig("Excursion limiter threshold", 0b100, 0x42, 0, 0b00001111), + AmpConfig("Distortion limit (THDCLP)", 0x0, 0x46, 4, 0b11110000), + AmpConfig("Distortion limiter release time constant", 0b1, 0x46, 0, 0b00000001), + AmpConfig("Left DAC input mixer: DAI1 left", 0b0, 0x22, 7, 0b10000000), + AmpConfig("Left DAC input mixer: DAI1 right", 0b0, 0x22, 6, 0b01000000), + AmpConfig("Left DAC input mixer: DAI2 left", 0b1, 0x22, 5, 0b00100000), + AmpConfig("Left DAC input mixer: DAI2 right", 0b0, 0x22, 4, 0b00010000), + AmpConfig("Right DAC input mixer: DAI2 left", 0b0, 0x22, 1, 0b00000010), + AmpConfig("Right DAC input mixer: DAI2 right", 0b1, 0x22, 0, 0b00000001), + AmpConfig("Volume adjustment smoothing disabled", 0b1, 0x49, 6, 0b01000000), + + *configs_from_eq_params(0x84, EQParams(0x3084, 0xC023, 0x3D60, 0x042B, 0x1222)), + *configs_from_eq_params(0x8E, EQParams(0x2FB2, 0xC05C, 0x3BD3, 0x06C5, 0x16BB)), + *configs_from_eq_params(0x98, EQParams(0x21F5, 0xDF73, 0x2DFE, 0x371A, 0x2C80)), + *configs_from_eq_params(0xA2, EQParams(0x2A5A, 0x0AD0, 0x14FA, 0x3F14, 0x3C76)), + *configs_from_eq_params(0xAC, EQParams(0x1577, 0x3FAE, 0xEE60, 0x0664, 0x3D86)), + ], +} class Amplifier: AMP_I2C_BUS = 0 @@ -91,14 +122,13 @@ class Amplifier: def set_global_shutdown(self, amp_disabled): self.set_config(AmpConfig("Global shutdown", 0b0 if amp_disabled else 0b1, 0x51, 7, 0b10000000)) - def initialize_configuration(self): + def initialize_configuration(self, model): self.set_global_shutdown(amp_disabled=True) for config in BASE_CONFIG: self.set_config(config) - self.set_global_shutdown(amp_disabled=False) - + for config in CONFIGS[model]: + self.set_config(config) -if __name__ == "__main__": - Amplifier(debug=True).initialize_configuration() + self.set_global_shutdown(amp_disabled=False) diff --git a/system/hardware/tici/hardware.py b/system/hardware/tici/hardware.py index b5f5e00410..c0fbc66fae 100644 --- a/system/hardware/tici/hardware.py +++ b/system/hardware/tici/hardware.py @@ -85,6 +85,16 @@ class Tici(HardwareBase): def amplifier(self): return Amplifier() + @cached_property + def model(self): + with open("/sys/firmware/devicetree/base/model") as f: + model = f.read().strip('\x00') + model = model.split('comma ')[-1] + # TODO: remove this with AGNOS 7+ + if model.startswith('Qualcomm'): + model = 'tici' + return model + def get_os_version(self): with open("/VERSION") as f: return f.read().strip() @@ -401,7 +411,7 @@ class Tici(HardwareBase): # amplifier, 100mW at idle self.amplifier.set_global_shutdown(amp_disabled=powersave_enabled) if not powersave_enabled: - self.amplifier.initialize_configuration() + self.amplifier.initialize_configuration(self.model) # *** CPU config *** @@ -430,7 +440,7 @@ class Tici(HardwareBase): return 0 def initialize_hardware(self): - self.amplifier.initialize_configuration() + self.amplifier.initialize_configuration(self.model) # Allow thermald to write engagement status to kmsg os.system("sudo chmod a+w /dev/kmsg") From 922bedaf47effb448c056a1844c13ecd7a26a244 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 1 Jan 2023 19:47:02 -0800 Subject: [PATCH 022/484] tici: remove hardcoded max brightness (#26859) * tici: remove hardcoded max brightness * fix that one * cleanup Co-authored-by: Comma Device --- system/hardware/tici/hardware.h | 4 +++- system/hardware/tici/hardware.py | 11 +++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/system/hardware/tici/hardware.h b/system/hardware/tici/hardware.h index d388f9c48a..521a257627 100644 --- a/system/hardware/tici/hardware.h +++ b/system/hardware/tici/hardware.h @@ -25,9 +25,11 @@ public: static void reboot() { std::system("sudo reboot"); }; static void poweroff() { std::system("sudo poweroff"); }; static void set_brightness(int percent) { + std::string max = util::read_file("/sys/class/backlight/panel0-backlight/max_brightness"); + std::ofstream brightness_control("/sys/class/backlight/panel0-backlight/brightness"); if (brightness_control.is_open()) { - brightness_control << (percent * (int)(1023/100.)) << "\n"; + brightness_control << (int)(percent * (std::stof(max)/100.)) << "\n"; brightness_control.close(); } }; diff --git a/system/hardware/tici/hardware.py b/system/hardware/tici/hardware.py index c0fbc66fae..9c1cc930c1 100644 --- a/system/hardware/tici/hardware.py +++ b/system/hardware/tici/hardware.py @@ -395,15 +395,22 @@ class Tici(HardwareBase): def set_screen_brightness(self, percentage): try: + with open("/sys/class/backlight/panel0-backlight/max_brightness") as f: + max_brightness = float(f.read().strip()) + + val = int(percentage * (max_brightness / 100.)) with open("/sys/class/backlight/panel0-backlight/brightness", "w") as f: - f.write(str(int(percentage * 10.23))) + f.write(str(val)) except Exception: pass def get_screen_brightness(self): try: + with open("/sys/class/backlight/panel0-backlight/max_brightness") as f: + max_brightness = float(f.read().strip()) + with open("/sys/class/backlight/panel0-backlight/brightness") as f: - return int(float(f.read()) / 10.23) + return int(float(f.read()) / (max_brightness / 100.)) except Exception: return 0 From b5a2dfa93ed053b5e1e04f6a3b76735126c1e00f Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 3 Jan 2023 03:57:51 +0800 Subject: [PATCH 023/484] replay: only keep one init_data in merged events (#26863) don't merge init_data --- tools/replay/replay.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/replay/replay.cc b/tools/replay/replay.cc index a01371abe1..998c93b938 100644 --- a/tools/replay/replay.cc +++ b/tools/replay/replay.cc @@ -269,8 +269,12 @@ void Replay::mergeSegments(const SegmentMap::iterator &begin, const SegmentMap:: new_events_->reserve(new_events_size); for (int n : segments_need_merge) { const auto &e = segments_[n]->log->events; - auto middle = new_events_->insert(new_events_->end(), e.begin(), e.end()); - std::inplace_merge(new_events_->begin(), middle, new_events_->end(), Event::lessThan()); + if (e.size() > 0) { + auto insert_from = e.begin(); + if (new_events_->size() > 0 && (*insert_from)->which == cereal::Event::Which::INIT_DATA) ++insert_from; + auto middle = new_events_->insert(new_events_->end(), insert_from, e.end()); + std::inplace_merge(new_events_->begin(), middle, new_events_->end(), Event::lessThan()); + } } updateEvents([&]() { From 88fd2ea84ff7c465a2c2f7de13635f112d604637 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 3 Jan 2023 05:31:08 +0800 Subject: [PATCH 024/484] replay: add option --prefix to set OPENPILOT_PREFIX (#26862) --- tools/replay/main.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/replay/main.cc b/tools/replay/main.cc index 8621a9b978..6b624aa1fa 100644 --- a/tools/replay/main.cc +++ b/tools/replay/main.cc @@ -1,6 +1,7 @@ #include #include +#include "common/prefix.h" #include "tools/replay/consoleui.h" #include "tools/replay/replay.h" @@ -27,6 +28,7 @@ int main(int argc, char *argv[]) { parser.addOption({{"s", "start"}, "start from ", "seconds"}); parser.addOption({"demo", "use a demo route instead of providing your own"}); parser.addOption({"data_dir", "local directory with routes", "data_dir"}); + parser.addOption({"prefix", "set OPENPILOT_PREFIX", "prefix"}); for (auto &[name, _, desc] : flags) { parser.addOption({name, desc}); } @@ -47,6 +49,13 @@ int main(int argc, char *argv[]) { replay_flags |= flag; } } + + std::unique_ptr op_prefix; + auto prefix = parser.value("prefix"); + if (!prefix.isEmpty()) { + op_prefix.reset(new OpenpilotPrefix(prefix.toStdString())); + } + Replay *replay = new Replay(route, allow, block, nullptr, replay_flags, parser.value("data_dir"), &app); if (!parser.value("c").isEmpty()) { replay->setSegmentCacheLimit(parser.value("c").toInt()); From 8b4c5631a4c8f259fe7e7db1789556c2b26e0f7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Mon, 2 Jan 2023 22:25:39 -0800 Subject: [PATCH 025/484] Ev6 upper jerk limit (#26868) --- opendbc | 2 +- selfdrive/car/hyundai/hyundaicanfd.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/opendbc b/opendbc index 4a7ad636ff..d585a9bf29 160000 --- a/opendbc +++ b/opendbc @@ -1 +1 @@ -Subproject commit 4a7ad636ff806146a93f7ae541e463a7dfa5696d +Subproject commit d585a9bf2908b2c83bf02b567b9e1f5bfc587a01 diff --git a/selfdrive/car/hyundai/hyundaicanfd.py b/selfdrive/car/hyundai/hyundaicanfd.py index c492e6a5ff..43c9642342 100644 --- a/selfdrive/car/hyundai/hyundaicanfd.py +++ b/selfdrive/car/hyundai/hyundaicanfd.py @@ -83,6 +83,7 @@ def create_acc_control(packer, CP, enabled, accel_last, accel, stopping, gas_ove "aReqRaw": a_raw, "VSetDis": set_speed, "JerkLowerLimit": jerk if enabled else 1, + "JerkUpperLimit": 3.0, "ACC_ObjDist": 1, "ObjValid": 0, @@ -90,7 +91,6 @@ def create_acc_control(packer, CP, enabled, accel_last, accel, stopping, gas_ove "SET_ME_2": 0x4, "SET_ME_3": 0x3, "SET_ME_TMP_64": 0x64, - "NEW_SIGNAL_10": 4, "DISTANCE_SETTING": 4, } From 2c293bf728f59a36830898e0f0d4111cd6708619 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 4 Jan 2023 12:06:01 -0800 Subject: [PATCH 026/484] Hyundai CAN-FD: set request accel when stopping (#26877) Update hyundaicanfd.py --- selfdrive/car/hyundai/hyundaicanfd.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/selfdrive/car/hyundai/hyundaicanfd.py b/selfdrive/car/hyundai/hyundaicanfd.py index 43c9642342..af7239571c 100644 --- a/selfdrive/car/hyundai/hyundaicanfd.py +++ b/selfdrive/car/hyundai/hyundaicanfd.py @@ -72,8 +72,6 @@ def create_acc_control(packer, CP, enabled, accel_last, accel, stopping, gas_ove else: a_raw = accel a_val = clip(accel, accel_last - jn, accel_last + jn) - if stopping: - a_raw = 0 values = { "ACCMode": 0 if not enabled else (2 if gas_override else 1), From b48b2f09b203db3e6bb26c671dd7e9b328207bc4 Mon Sep 17 00:00:00 2001 From: Luckst4r <67570890+Luckst4r@users.noreply.github.com> Date: Wed, 4 Jan 2023 17:17:01 -0600 Subject: [PATCH 027/484] Genesis GV70 2023: add missing FW versions (#26876) * Update values.py Fingerprint GV70 * Update selfdrive/car/hyundai/values.py Co-authored-by: Shane Smiskol --- selfdrive/car/hyundai/values.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 4d3f44d65f..782c3989a7 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -1517,9 +1517,11 @@ FW_VERSIONS = { CAR.GENESIS_GV70_1ST_GEN: { (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00JK1 MFC AT USA LHD 1.00 1.04 99211-AR000 210204', + b'\xf1\x00JK1 MFC AT USA LHD 1.00 1.01 99211-AR200 220125', ], (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00JK1_ SCC FHCUP 1.00 1.02 99110-AR000 ', + b'\xf1\x00JK1_ SCC FHCUP 1.00 1.00 99110-AR200 ', ], }, CAR.GENESIS_GV60_EV_1ST_GEN: { From 083bcb6f0271518537ccfc69bdcc20ae50efd600 Mon Sep 17 00:00:00 2001 From: mitchellgoffpc Date: Fri, 6 Jan 2023 12:08:22 -0800 Subject: [PATCH 028/484] Added omegaconf to xx dependencies --- poetry.lock | 29 ++++++++++++++++++++++++++++- pyproject.toml | 1 + 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 9ab86329d7..8edfbb868c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -80,6 +80,14 @@ develop = ["imgaug (>=0.4.0)", "pytest"] imgaug = ["imgaug (>=0.4.0)"] tests = ["pytest"] +[[package]] +name = "antlr4-python3-runtime" +version = "4.9.3" +description = "ANTLR 4.9.3 runtime for Python 3.7" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "anyio" version = "3.6.2" @@ -2431,6 +2439,18 @@ rsa = ["cryptography (>=3.0.0)"] signals = ["blinker (>=1.4.0)"] signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] +[[package]] +name = "omegaconf" +version = "2.3.0" +description = "A flexible configuration library" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +antlr4-python3-runtime = ">=4.9.0,<4.10.0" +PyYAML = ">=5.1.0" + [[package]] name = "onnx" version = "1.12.0" @@ -4444,7 +4464,7 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] lock-version = "1.1" python-versions = "~3.8" -content-hash = "2ab9d6aee6cc2c7bb5ba9763a54779ef9535cb752ec0038736a442992a04d459" +content-hash = "3c2597d2199a29ef79dd4d5fbc5e2350d86c82a3fe6716d13b93ebc268053eb9" [metadata.files] adal = [ @@ -4557,6 +4577,9 @@ albumentations = [ {file = "albumentations-1.3.0-py3-none-any.whl", hash = "sha256:294165d87d03bc8323e484927f0a5c1a3c64b0e7b9c32a979582a6c93c363bdf"}, {file = "albumentations-1.3.0.tar.gz", hash = "sha256:be1af36832c8893314f2a5550e8ac19801e04770734c1b70fa3c996b41f37bed"}, ] +antlr4-python3-runtime = [ + {file = "antlr4-python3-runtime-4.9.3.tar.gz", hash = "sha256:f224469b4168294902bb1efa80a8bf7855f24c99aef99cbefc1bcd3cce77881b"}, +] anyio = [ {file = "anyio-3.6.2-py3-none-any.whl", hash = "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"}, {file = "anyio-3.6.2.tar.gz", hash = "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421"}, @@ -6426,6 +6449,10 @@ oauthlib = [ {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, ] +omegaconf = [ + {file = "omegaconf-2.3.0-py3-none-any.whl", hash = "sha256:7b4df175cdb08ba400f45cae3bdcae7ba8365db4d165fc65fd04b050ab63b46b"}, + {file = "omegaconf-2.3.0.tar.gz", hash = "sha256:d5d4b6d29955cc50ad50c46dc269bcd92c6e00f5f90d23ab5fee7bfca4ba4cc7"}, +] onnx = [ {file = "onnx-1.12.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:bdbd2578424c70836f4d0f9dda16c21868ddb07cc8192f9e8a176908b43d694b"}, {file = "onnx-1.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213e73610173f6b2e99f99a4b0636f80b379c417312079d603806e48ada4ca8b"}, diff --git a/pyproject.toml b/pyproject.toml index af9fcc6e6e..ed3a821e29 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -171,6 +171,7 @@ torchvision = { url = "https://download.pytorch.org/whl/cu113/torchvision-0.12.0 triton = "^1.1.1" Werkzeug = "^2.1.2" zerorpc = { git = "https://github.com/commaai/zerorpc-python.git", branch = "master" } +omegaconf = "^2.3.0" [build-system] From 18b011636ef623023e53bd7326d9871c4ce07f97 Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Fri, 6 Jan 2023 19:31:24 -0500 Subject: [PATCH 029/484] =?UTF-8?q?VW=20MQB:=20Add=20FW=20for=202019=20?= =?UTF-8?q?=C5=A0koda=20Octavia=20(#26886)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- selfdrive/car/volkswagen/values.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py index d1f6bfb02c..24ea0a03ba 100755 --- a/selfdrive/car/volkswagen/values.py +++ b/selfdrive/car/volkswagen/values.py @@ -1023,6 +1023,7 @@ FW_VERSIONS = { }, CAR.SKODA_OCTAVIA_MK3: { (Ecu.engine, 0x7e0, None): [ + b'\xf1\x8704C906025L \xf1\x896198', b'\xf1\x8704E906016ER\xf1\x895823', b'\xf1\x8704E906027HD\xf1\x893742', b'\xf1\x8704E906027MH\xf1\x894786', @@ -1034,6 +1035,7 @@ FW_VERSIONS = { b'\xf1\x870CW300041L \xf1\x891601', b'\xf1\x870CW300041N \xf1\x891605', b'\xf1\x870CW300043B \xf1\x891601', + b'\xf1\x870CW300043P \xf1\x891605', b'\xf1\x870D9300041C \xf1\x894936', b'\xf1\x870D9300041J \xf1\x894902', b'\xf1\x870D9300041P \xf1\x894507', @@ -1043,6 +1045,7 @@ FW_VERSIONS = { b'\xf1\x873Q0959655AQ\xf1\x890200\xf1\x82\r11120011100010312212113100', b'\xf1\x873Q0959655AS\xf1\x890200\xf1\x82\r11120011100010022212110200', b'\xf1\x873Q0959655BH\xf1\x890703\xf1\x82\0163221003221002105755331052100', + b'\xf1\x873Q0959655CM\xf1\x890720\xf1\x82\x0e3221003221002105755331052100', b'\xf1\x873Q0959655CN\xf1\x890720\xf1\x82\x0e3221003221002105755331052100', b'\xf1\x875QD959655 \xf1\x890388\xf1\x82\x111101000011110006110411111111119111', ], From b994b93b164bb9dc2a8689dff0f271d2d68ced55 Mon Sep 17 00:00:00 2001 From: Eric Brown Date: Fri, 6 Jan 2023 17:46:21 -0700 Subject: [PATCH 030/484] GM: check radarOffCan in radar_interface (#26885) Remove hardcoded vehicle list, replace with radarOffCan check --- selfdrive/car/gm/radar_interface.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/selfdrive/car/gm/radar_interface.py b/selfdrive/car/gm/radar_interface.py index 6904e6f899..5cb211812d 100755 --- a/selfdrive/car/gm/radar_interface.py +++ b/selfdrive/car/gm/radar_interface.py @@ -3,7 +3,7 @@ import math from cereal import car from common.conversions import Conversions as CV from opendbc.can.parser import CANParser -from selfdrive.car.gm.values import DBC, CAR, CanBus +from selfdrive.car.gm.values import DBC, CanBus from selfdrive.car.interfaces import RadarInterfaceBase RADAR_HEADER_MSG = 1120 @@ -16,9 +16,6 @@ LAST_RADAR_MSG = RADAR_HEADER_MSG + NUM_SLOTS def create_radar_can_parser(car_fingerprint): - if car_fingerprint not in (CAR.VOLT, CAR.MALIBU, CAR.HOLDEN_ASTRA, CAR.ACADIA, CAR.CADILLAC_ATS, CAR.ESCALADE_ESV): - return None - # C1A-ARS3-A by Continental radar_targets = list(range(SLOT_1_MSG, SLOT_1_MSG + NUM_SLOTS)) signals = list(zip(['FLRRNumValidTargets', @@ -34,11 +31,12 @@ def create_radar_can_parser(car_fingerprint): return CANParser(DBC[car_fingerprint]['radar'], signals, checks, CanBus.OBSTACLE) + class RadarInterface(RadarInterfaceBase): def __init__(self, CP): super().__init__(CP) - self.rcp = create_radar_can_parser(CP.carFingerprint) + self.rcp = None if CP.radarOffCan else create_radar_can_parser(CP.carFingerprint) self.trigger_msg = LAST_RADAR_MSG self.updated_messages = set() From 4145bc837948f530eb17c3f93728a1347e4eceb3 Mon Sep 17 00:00:00 2001 From: Mauricio Alvarez Leon <65101411+BBBmau@users.noreply.github.com> Date: Fri, 6 Jan 2023 16:49:27 -0800 Subject: [PATCH 031/484] Hyundai: add FW versions for Sonata 2023 (#26880) * Update Hyundai Sonata year to 2023 * update docs * update docs * update docs * Update values.py * and engine Co-authored-by: Laptop Researcher Co-authored-by: Shane Smiskol --- docs/CARS.md | 2 +- selfdrive/car/hyundai/values.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index f3667c4cea..d53ea39893 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -82,7 +82,7 @@ A supported vehicle is one that just works when you install a comma three. All s |Hyundai|Santa Fe Hybrid 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L| |Hyundai|Santa Fe Plug-in Hybrid 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L| |Hyundai|Sonata 2018-19|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E| -|Hyundai|Sonata 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A| +|Hyundai|Sonata 2020-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A| |Hyundai|Sonata Hybrid 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A| |Hyundai|Tucson 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L| |Hyundai|Tucson 2022[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N| diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 782c3989a7..dd7f347863 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -160,7 +160,7 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { CAR.SANTA_FE_2022: HyundaiCarInfo("Hyundai Santa Fe 2021-22", "All", "https://youtu.be/VnHzSTygTS4", harness=Harness.hyundai_l), CAR.SANTA_FE_HEV_2022: HyundaiCarInfo("Hyundai Santa Fe Hybrid 2022", "All", harness=Harness.hyundai_l), CAR.SANTA_FE_PHEV_2022: HyundaiCarInfo("Hyundai Santa Fe Plug-in Hybrid 2022", "All", harness=Harness.hyundai_l), - CAR.SONATA: HyundaiCarInfo("Hyundai Sonata 2020-22", "All", "https://www.youtube.com/watch?v=ix63r9kE3Fw", harness=Harness.hyundai_a), + CAR.SONATA: HyundaiCarInfo("Hyundai Sonata 2020-23", "All", "https://www.youtube.com/watch?v=ix63r9kE3Fw", harness=Harness.hyundai_a), CAR.SONATA_LF: HyundaiCarInfo("Hyundai Sonata 2018-19", harness=Harness.hyundai_e), CAR.TUCSON: [ HyundaiCarInfo("Hyundai Tucson 2021", min_enable_speed=19 * CV.MPH_TO_MS, harness=Harness.hyundai_l), @@ -512,6 +512,7 @@ FW_VERSIONS = { b'HM6M1_0a0_G20', b'HM6M2_0a0_BD0', b'\xf1\x8739110-2S278\xf1\x82DNDVD5GMCCXXXL5B', + b'\xf1\x8739110-2S041\xf1\x81HM6M1_0a0_M00', ], (Ecu.eps, 0x7d4, None): [ b'\xf1\x00DN8 MDPS C 1,00 1,01 56310L0010\x00 4DNAC101', # modified firmware @@ -529,6 +530,7 @@ FW_VERSIONS = { b'\xf1\x8756310L0210\x00\xf1\x00DN8 MDPS C 1.00 1.01 56310L0210\x00 4DNAC101', b'\xf1\x8757700-L0000\xf1\x00DN8 MDPS R 1.00 1.00 57700-L0000 4DNAP100', b'\xf1\x00DN8 MDPS R 1.00 1.00 57700-L0000 4DNAP101', + b'\xf1\x00DN8 MDPS C 1.00 1.01 56310-L0210 4DNAC102', ], (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00DN8 MFC AT KOR LHD 1.00 1.02 99211-L1000 190422', @@ -538,6 +540,7 @@ FW_VERSIONS = { b'\xf1\x00DN8 MFC AT USA LHD 1.00 1.03 99211-L0000 210603', b'\xf1\x00DN8 MFC AT USA LHD 1.00 1.05 99211-L1000 201109', b'\xf1\x00DN8 MFC AT USA LHD 1.00 1.06 99211-L1000 210325', + b'\xf1\x00DN8 MFC AT USA LHD 1.00 1.07 99211-L1000 211223', ], (Ecu.transmission, 0x7e1, None): [ b'\xf1\x00bcsh8p54 U903\x00\x00\x00\x00\x00\x00SDN8T16NB0z{\xd4v', @@ -548,6 +551,7 @@ FW_VERSIONS = { b'\xf1\x00HT6WA250BLHT6WA910A1SDN8G25NB1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'\xf1\x00HT6WA250BLHT6WA910A1SDN8G25NB1\x00\x00\x00\x00\x00\x00\x96\xa1\xf1\x92', b'\xf1\x00HT6WA280BLHT6WAD10A1SDN8G25NB2\x00\x00\x00\x00\x00\x00\x08\xc9O:', + b'\xf1\x00HT6WA280BLHT6WAD10A1SDN8G25NB4\x00\x00\x00\x00\x00\x00g!l[', b'\xf1\x00T02601BL T02730A1 VDN8T25XXX730NS5\xf7_\x92\xf5', b'\xf1\x00T02601BL T02832A1 VDN8T25XXX832NS8G\x0e\xfeE', b'\xf1\x87954A02N060\x00\x00\x00\x00\x00\xf1\x81T02730A1 \xf1\x00T02601BL T02730A1 VDN8T25XXX730NS5\xf7_\x92\xf5', From b74f2d102daa6acea69d0c673dbb0172f31897f3 Mon Sep 17 00:00:00 2001 From: Jason Wen <47793918+sunnyhaibin@users.noreply.github.com> Date: Fri, 6 Jan 2023 19:54:56 -0500 Subject: [PATCH 032/484] Hyundai: Add FW Versions for Tucson 2023 (#26887) --- selfdrive/car/hyundai/values.py | 1 + 1 file changed, 1 insertion(+) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index dd7f347863..1adf78aa84 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -1477,6 +1477,7 @@ FW_VERSIONS = { CAR.TUCSON_4TH_GEN: { (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.01 99211-N9240 14T', + b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.00 99211-CW010 14X', ], (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00NX4__ 1.01 1.00 99110-N9100 ', From 0d85a2c373c3e139237c6bc969be79312a99bd63 Mon Sep 17 00:00:00 2001 From: Rafael Pinilla <80814874+Rafale83@users.noreply.github.com> Date: Sat, 7 Jan 2023 18:40:52 +0100 Subject: [PATCH 033/484] European Lexus UX250h 2019: add engine FW (#26895) * Update values.py Added mention to all 2019 Lexus UX250h Fw in CORROLLAH section * Update values.py Syntax error in REMs --- selfdrive/car/toyota/values.py | 1 + 1 file changed, 1 insertion(+) diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index 64a0bed0b2..e945130e90 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -831,6 +831,7 @@ FW_VERSIONS = { b'\x01896630ZJ1000\x00\x00\x00\x00', b'\x01896630ZU8000\x00\x00\x00\x00', b'\x01896637621000\x00\x00\x00\x00', + b'\x01896637623000\x00\x00\x00\x00', b'\x01896637624000\x00\x00\x00\x00', b'\x01896637626000\x00\x00\x00\x00', b'\x01896637639000\x00\x00\x00\x00', From bf34110572cb801564346ffb2f234cd152a7b100 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sat, 7 Jan 2023 11:05:56 -0800 Subject: [PATCH 034/484] docs: update CAN FD footnote (#26896) * docs: update CAN FD footnote * touch up --- docs/CARS.md | 2 +- selfdrive/car/hyundai/values.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index d53ea39893..930b52c8a2 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -236,7 +236,7 @@ A supported vehicle is one that just works when you install a comma three. All s 2By default, this car will use the stock Adaptive Cruise Control (ACC) for longitudinal control. If the Driver Support Unit (DSU) is disconnected, openpilot ACC will replace stock ACC. NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).
3Requires a community built ASCM harness. NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).
42019 Honda Civic 1.6L Diesel Sedan does not have ALC below 12mph.
-5Requires a red panda, additional harness box, additional OBD-C cable, USB-A to USB-A cable, and a USB-A to USB-C OTG dongle.
+5Requires a red panda for this CAN FD. All the hardware needed is sold in the CAN FD kit.
6openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control.
7Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform.
8Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets.
diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 1adf78aa84..dc19c5511d 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -123,9 +123,9 @@ class CAR: class Footnote(Enum): CANFD = CarFootnote( - "Requires a red panda, additional harness box, " + - "additional OBD-C cable, USB-A to USB-A cable, and a USB-A to USB-C OTG dongle.", - Column.MODEL, shop_footnote=True) + "Requires a red panda for this CAN FD. " + + "All the hardware needed is sold in the CAN FD kit.", + Column.MODEL, docs_only=True) @dataclass From 2c7df6efc87f4f16aa0801ed40bc0e2542578031 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sun, 8 Jan 2023 03:07:30 +0800 Subject: [PATCH 035/484] replay: show absolute time (#26869) --- tools/replay/consoleui.cc | 12 ++---------- tools/replay/replay.h | 1 + tools/replay/route.cc | 1 + tools/replay/route.h | 3 +++ 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/tools/replay/consoleui.cc b/tools/replay/consoleui.cc index 77357a2873..5ad702590c 100644 --- a/tools/replay/consoleui.cc +++ b/tools/replay/consoleui.cc @@ -50,14 +50,6 @@ void add_str(WINDOW *w, const char *str, Color color = Color::Default, bool bold if (color != Color::Default) wattroff(w, COLOR_PAIR(color)); } -std::string format_seconds(int s) { - int total_minutes = s / 60; - int seconds = s % 60; - int hours = total_minutes / 60; - int minutes = total_minutes % 60; - return util::string_format("%02d:%02d:%02d", hours, minutes, seconds); -} - } // namespace ConsoleUI::ConsoleUI(Replay *replay, QObject *parent) : replay(replay), sm({"carState", "liveParameters"}), QObject(parent) { @@ -177,8 +169,8 @@ void ConsoleUI::updateStatus() { } auto [status_str, status_color] = status_text[status]; write_item(0, 0, "STATUS: ", status_str, " ", false, status_color); - std::string suffix = " / " + format_seconds(replay->totalSeconds()); - write_item(0, 25, "TIME: ", format_seconds(replay->currentSeconds()), suffix, true); + std::string current_segment = " - " + std::to_string((int)(replay->currentSeconds() / 60)); + write_item(0, 25, "TIME: ", replay->currentDateTime().toString("ddd MMMM dd hh:mm:ss").toStdString(), current_segment, true); auto p = sm["liveParameters"].getLiveParameters(); write_item(1, 0, "STIFFNESS: ", util::string_format("%.2f %%", p.getStiffnessFactor() * 100), " "); diff --git a/tools/replay/replay.h b/tools/replay/replay.h index 2c68443df0..6788a97d03 100644 --- a/tools/replay/replay.h +++ b/tools/replay/replay.h @@ -64,6 +64,7 @@ public: inline void removeFlag(REPLAY_FLAGS flag) { flags_ &= ~flag; } inline const Route* route() const { return route_.get(); } inline double currentSeconds() const { return double(cur_mono_time_ - route_start_ts_) / 1e9; } + inline QDateTime currentDateTime() const { return route_->datetime().addSecs(currentSeconds()); } inline uint64_t routeStartTime() const { return route_start_ts_; } inline int toSeconds(uint64_t mono_time) const { return (mono_time - route_start_ts_) / 1e9; } inline int totalSeconds() const { return segments_.size() * 60; } diff --git a/tools/replay/route.cc b/tools/replay/route.cc index f0d6ec5a12..9d57b9118e 100644 --- a/tools/replay/route.cc +++ b/tools/replay/route.cc @@ -31,6 +31,7 @@ bool Route::load() { rInfo("invalid route format"); return false; } + date_time_ = QDateTime::fromString(route_.timestamp, "yyyy-MM-dd--HH-mm-ss"); return data_dir_.isEmpty() ? loadFromServer() : loadFromLocal(); } diff --git a/tools/replay/route.h b/tools/replay/route.h index 6b78ebad87..86adf6a14d 100644 --- a/tools/replay/route.h +++ b/tools/replay/route.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include "tools/replay/framereader.h" @@ -27,6 +28,7 @@ public: Route(const QString &route, const QString &data_dir = {}); bool load(); inline const QString &name() const { return route_.str; } + inline const QDateTime datetime() const { return date_time_; } inline const QString &dir() const { return data_dir_; } inline const RouteIdentifier &identifier() const { return route_; } inline const std::map &segments() const { return segments_; } @@ -41,6 +43,7 @@ protected: RouteIdentifier route_ = {}; QString data_dir_; std::map segments_; + QDateTime date_time_; }; class Segment : public QObject { From ae256dceba4fff8c036777b118873d2fbcfab42c Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Sat, 7 Jan 2023 13:18:28 -0800 Subject: [PATCH 036/484] revert: docs: update CAN FD footnote (#26896) partial revert, we still need the footnote to be exported for the shop --- selfdrive/car/hyundai/values.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index dc19c5511d..16f1f42679 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -122,10 +122,11 @@ class CAR: class Footnote(Enum): + # footnotes which mention "red panda" will be replaced with the CAN FD panda kit on the shop page CANFD = CarFootnote( "Requires a red panda for this CAN FD. " + "All the hardware needed is sold in the CAN FD kit.", - Column.MODEL, docs_only=True) + Column.MODEL, shop_footnote=True) @dataclass From b6440304d59d13b918842f68ce9f3417e3e0ecbc Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Sat, 7 Jan 2023 18:53:14 -0700 Subject: [PATCH 037/484] Navigation: add destination marker (#26873) * add navigation destination marker * fix removal * update default marker icon * update default marker Co-authored-by: Kurt Nistelberger --- .../assets/navigation/default_marker.svg | 5 +++++ selfdrive/ui/qt/maps/map.cc | 19 +++++++++++++++++-- selfdrive/ui/qt/maps/map.h | 2 ++ 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 selfdrive/assets/navigation/default_marker.svg diff --git a/selfdrive/assets/navigation/default_marker.svg b/selfdrive/assets/navigation/default_marker.svg new file mode 100644 index 0000000000..116a45e251 --- /dev/null +++ b/selfdrive/assets/navigation/default_marker.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/selfdrive/ui/qt/maps/map.cc b/selfdrive/ui/qt/maps/map.cc index b5519daccf..3e1d08740d 100644 --- a/selfdrive/ui/qt/maps/map.cc +++ b/selfdrive/ui/qt/maps/map.cc @@ -47,7 +47,7 @@ MapWindow::MapWindow(const QMapboxGLSettings &settings) : m_settings(settings), map_eta->setVisible(false); auto last_gps_position = coordinate_from_param("LastGPSPosition"); - if (last_gps_position) { + if (last_gps_position.has_value()) { last_position = *last_gps_position; } @@ -82,6 +82,7 @@ void MapWindow::initLayers() { m_map->setPaintProperty("navLayer", "line-color", QColor("#31a1ee")); m_map->setPaintProperty("navLayer", "line-width", 7.5); m_map->setLayoutProperty("navLayer", "line-cap", "round"); + m_map->addAnnotationIcon("default_marker", QImage("../assets/navigation/default_marker.svg")); } if (!m_map->layerExists("carPosLayer")) { qDebug() << "Initializing carPosLayer"; @@ -220,7 +221,6 @@ void MapWindow::updateState(const UIState &s) { emit instructionsChanged(i); } } else { - m_map->setPitch(MIN_PITCH); clearRoute(); } } @@ -237,6 +237,7 @@ void MapWindow::updateState(const UIState &s) { m_map->setLayoutProperty("navLayer", "visibility", "visible"); route_rcv_frame = sm.rcv_frame("navRoute"); + update_destination_marker(); } } @@ -274,6 +275,7 @@ void MapWindow::clearRoute() { if (!m_map.isNull()) { m_map->setLayoutProperty("navLayer", "visibility", "none"); m_map->setPitch(MIN_PITCH); + update_destination_marker(); } map_instructions->hideIfNoError(); @@ -361,6 +363,19 @@ void MapWindow::offroadTransition(bool offroad) { last_bearing = {}; } +void MapWindow::update_destination_marker() { + if (marker_id != -1) { + m_map->removeAnnotation(marker_id); + marker_id = -1; + } + + auto nav_dest = coordinate_from_param("NavDestination"); + if (nav_dest.has_value()) { + auto ano = QMapbox::SymbolAnnotation {*nav_dest, "default_marker"}; + marker_id = m_map->addAnnotation(QVariant::fromValue(ano)); + } +} + MapInstructions::MapInstructions(QWidget * parent) : QWidget(parent) { is_rhd = Params().getBool("IsRhdDetected"); QHBoxLayout *main_layout = new QHBoxLayout(this); diff --git a/selfdrive/ui/qt/maps/map.h b/selfdrive/ui/qt/maps/map.h index c3d5e92530..8151bdecfd 100644 --- a/selfdrive/ui/qt/maps/map.h +++ b/selfdrive/ui/qt/maps/map.h @@ -79,6 +79,7 @@ private: QMapboxGLSettings m_settings; QScopedPointer m_map; + QMapbox::AnnotationID marker_id = -1; void initLayers(); @@ -111,6 +112,7 @@ private: MapETA* map_eta; void clearRoute(); + void update_destination_marker(); uint64_t route_rcv_frame = 0; private slots: From 2b611862becd0cafbe6949961da04dfa4d252b4e Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Sat, 7 Jan 2023 17:56:16 -0800 Subject: [PATCH 038/484] match style --- selfdrive/ui/qt/maps/map.cc | 6 +++--- selfdrive/ui/qt/maps/map.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/selfdrive/ui/qt/maps/map.cc b/selfdrive/ui/qt/maps/map.cc index 3e1d08740d..c625564f1d 100644 --- a/selfdrive/ui/qt/maps/map.cc +++ b/selfdrive/ui/qt/maps/map.cc @@ -237,7 +237,7 @@ void MapWindow::updateState(const UIState &s) { m_map->setLayoutProperty("navLayer", "visibility", "visible"); route_rcv_frame = sm.rcv_frame("navRoute"); - update_destination_marker(); + updateDestinationMarker(); } } @@ -275,7 +275,7 @@ void MapWindow::clearRoute() { if (!m_map.isNull()) { m_map->setLayoutProperty("navLayer", "visibility", "none"); m_map->setPitch(MIN_PITCH); - update_destination_marker(); + updateDestinationMarker(); } map_instructions->hideIfNoError(); @@ -363,7 +363,7 @@ void MapWindow::offroadTransition(bool offroad) { last_bearing = {}; } -void MapWindow::update_destination_marker() { +void MapWindow::updateDestinationMarker() { if (marker_id != -1) { m_map->removeAnnotation(marker_id); marker_id = -1; diff --git a/selfdrive/ui/qt/maps/map.h b/selfdrive/ui/qt/maps/map.h index 8151bdecfd..0d8b93a5f4 100644 --- a/selfdrive/ui/qt/maps/map.h +++ b/selfdrive/ui/qt/maps/map.h @@ -112,7 +112,7 @@ private: MapETA* map_eta; void clearRoute(); - void update_destination_marker(); + void updateDestinationMarker(); uint64_t route_rcv_frame = 0; private slots: From 2204a7153f3f63716eedf81a8aeee9f2ed3e3c2e Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 9 Jan 2023 07:51:08 +0800 Subject: [PATCH 039/484] replay: remove need for fake dongle id (#26899) --- tools/replay/route.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/replay/route.cc b/tools/replay/route.cc index 9d57b9118e..619aeb3f5f 100644 --- a/tools/replay/route.cc +++ b/tools/replay/route.cc @@ -19,15 +19,15 @@ Route::Route(const QString &route, const QString &data_dir) : data_dir_(data_dir } RouteIdentifier Route::parseRoute(const QString &str) { - QRegExp rx(R"(^([a-z0-9]{16})([|_/])(\d{4}-\d{2}-\d{2}--\d{2}-\d{2}-\d{2})(?:(--|/)(\d*))?$)"); + QRegExp rx(R"(^(?:([a-z0-9]{16})([|_/]))?(\d{4}-\d{2}-\d{2}--\d{2}-\d{2}-\d{2})(?:(--|/)(\d*))?$)"); if (rx.indexIn(str) == -1) return {}; const QStringList list = rx.capturedTexts(); - return {list[1], list[3], list[5].toInt(), list[1] + "|" + list[3]}; + return {.dongle_id = list[1], .timestamp = list[3], .segment_id = list[5].toInt(), .str = list[1] + "|" + list[3]}; } bool Route::load() { - if (route_.str.isEmpty()) { + if (route_.str.isEmpty() || (data_dir_.isEmpty() && route_.dongle_id.isEmpty())) { rInfo("invalid route format"); return false; } From 19a15eeb539c8e8f53166b8885e09d6276d32fd8 Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Mon, 9 Jan 2023 19:30:30 +0100 Subject: [PATCH 040/484] MacOS: homebrew arm-none-eabi-gcc works again (#26903) Revert "Fix gcc-arm-embedded for m1 mac (#24515)" This reverts commit 01a237ef02cf989a3e2dc8a529d5a0338a09e123. --- tools/mac_setup.sh | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tools/mac_setup.sh b/tools/mac_setup.sh index e85292da03..dd2041e0cb 100755 --- a/tools/mac_setup.sh +++ b/tools/mac_setup.sh @@ -52,14 +52,9 @@ brew "zeromq" brew "protobuf" brew "protobuf-c" brew "swig" +cask "gcc-arm-embedded" EOS -# Install gcc-arm-embedded 10.3-2021.10. 11.x is broken on M1 Macs with Xcode 13.3~ -brew uninstall gcc-arm-embedded || true -curl -L https://github.com/Homebrew/homebrew-cask/raw/d407663b8017a0a062c7fc0b929faf2e16abd1ff/Casks/gcc-arm-embedded.rb > /tmp/gcc-arm-embedded.rb -brew install --cask /tmp/gcc-arm-embedded.rb -rm /tmp/gcc-arm-embedded.rb - echo "[ ] finished brew install t=$SECONDS" BREW_PREFIX=$(brew --prefix) From a421c9464eec3a6b994df5ec010ff51b27aa4bbc Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 10 Jan 2023 05:48:04 +0800 Subject: [PATCH 041/484] replay: sync vision buffer (#26904) --- tools/replay/camera.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/replay/camera.cc b/tools/replay/camera.cc index 72b385dca8..66898c9244 100644 --- a/tools/replay/camera.cc +++ b/tools/replay/camera.cc @@ -56,7 +56,7 @@ void CameraServer::cameraThread(Camera &cam) { .timestamp_eof = eidx.getTimestampEof(), }; yuv->set_frame_id(eidx.getFrameId()); - vipc_server_->send(yuv, &extra, false); + vipc_server_->send(yuv, &extra); } else { rError("camera[%d] failed to get frame: %lu", cam.type, eidx.getSegmentId()); } From a40efbdfcc3619528a2634d6b291c928c2374b4b Mon Sep 17 00:00:00 2001 From: Tim Wilson Date: Mon, 9 Jan 2023 16:01:55 -0700 Subject: [PATCH 042/484] GM camera ACC: always set long tune (#26892) * GM: set long tune for camera car w/o exp. mode * same tuning * update refs Co-authored-by: Shane Smiskol --- selfdrive/car/gm/interface.py | 18 +++++++++--------- selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index 195df36a7f..856dcbaae5 100755 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -68,20 +68,20 @@ class CarInterface(CarInterfaceBase): ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM ret.minEnableSpeed = 5 * CV.KPH_TO_MS + # Tuning for experimental long + ret.longitudinalTuning.kpV = [2.0, 1.5] + ret.longitudinalTuning.kiV = [0.72] + ret.stopAccel = -2.0 + ret.stoppingDecelRate = 2.0 # reach brake quickly after enabling + ret.vEgoStopping = 0.25 + ret.vEgoStarting = 0.25 + ret.longitudinalActuatorDelayUpperBound = 0.5 + if experimental_long: ret.pcmCruise = False ret.openpilotLongitudinalControl = True ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM_LONG - # Tuning - ret.longitudinalTuning.kpV = [2.0, 1.5] - ret.longitudinalTuning.kiV = [0.72] - ret.stopAccel = -2.0 - ret.stoppingDecelRate = 2.0 # reach brake quickly after enabling - ret.vEgoStopping = 0.25 - ret.vEgoStarting = 0.25 - ret.longitudinalActuatorDelayUpperBound = 0.5 - else: # ASCM, OBD-II harness ret.openpilotLongitudinalControl = True ret.networkLocation = NetworkLocation.gateway diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 603d2b2d96..24ec1c62de 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -557ffbf5a1c9cafba1ff8d6f3e2642df98b2d6e6 \ No newline at end of file +6d30c77af7b3210b03f65b433c0a043a96ee39bc \ No newline at end of file From dac7a24677ab680a0ff8fe209cc73cb14d6672a5 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 9 Jan 2023 16:21:47 -0800 Subject: [PATCH 043/484] car interfaces: test longitudinal params are set correctly (#26894) * add long unit tests and fix * revert * remove debug * cleanup Co-authored-by: Laptop Researcher --- selfdrive/car/tests/test_car_interfaces.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/selfdrive/car/tests/test_car_interfaces.py b/selfdrive/car/tests/test_car_interfaces.py index 48d85584b3..11e7be7f44 100755 --- a/selfdrive/car/tests/test_car_interfaces.py +++ b/selfdrive/car/tests/test_car_interfaces.py @@ -35,6 +35,12 @@ class TestCarInterfaces(unittest.TestCase): self.assertGreater(car_params.centerToFront, 0) self.assertGreater(car_params.maxLateralAccel, 0) + # Longitudinal sanity checks + self.assertEqual(len(car_params.longitudinalTuning.kpV), len(car_params.longitudinalTuning.kpBP)) + self.assertEqual(len(car_params.longitudinalTuning.kiV), len(car_params.longitudinalTuning.kiBP)) + self.assertEqual(len(car_params.longitudinalTuning.deadzoneV), len(car_params.longitudinalTuning.deadzoneBP)) + + # Lateral sanity checks if car_params.steerControlType != car.CarParams.SteerControlType.angle: tune = car_params.lateralTuning if tune.which() == 'pid': From 9b9c2a657ed0ba326727339269654f9497997a70 Mon Sep 17 00:00:00 2001 From: David Peterson Date: Mon, 9 Jan 2023 20:25:43 -0500 Subject: [PATCH 044/484] Car docs: add video for Nissan Leaf (#26901) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * CAR.LEAF: NissanCarInfo("Nissan Leaf 2018-22”), video_link= "https://youtu.be/vaMbtAh_0cY&t=0s"), Add Youtube video 2019 Leaf * Update selfdrive/car/nissan/values.py Co-authored-by: Shane Smiskol --- selfdrive/car/nissan/values.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/car/nissan/values.py b/selfdrive/car/nissan/values.py index 6371b56be8..e9af828e2b 100644 --- a/selfdrive/car/nissan/values.py +++ b/selfdrive/car/nissan/values.py @@ -39,7 +39,7 @@ class NissanCarInfo(CarInfo): CAR_INFO: Dict[str, Optional[Union[NissanCarInfo, List[NissanCarInfo]]]] = { CAR.XTRAIL: NissanCarInfo("Nissan X-Trail 2017"), - CAR.LEAF: NissanCarInfo("Nissan Leaf 2018-22"), + CAR.LEAF: NissanCarInfo("Nissan Leaf 2018-22", video_link="https://youtu.be/vaMbtAh_0cY"), CAR.LEAF_IC: None, # same platforms CAR.ROGUE: NissanCarInfo("Nissan Rogue 2018-20"), CAR.ALTIMA: NissanCarInfo("Nissan Altima 2019-20", harness=Harness.nissan_b), From 1a907b4c7a3167ad21b992fd74e2439019163086 Mon Sep 17 00:00:00 2001 From: Jason Wen <47793918+sunnyhaibin@users.noreply.github.com> Date: Mon, 9 Jan 2023 21:14:02 -0500 Subject: [PATCH 045/484] HKG: add FW Versions for 2019 Kia Stinger (#26897) * HKG: Add FW Versions for 2019 Kia Stinger * Update selfdrive/car/hyundai/values.py * fix * use shorter query Co-authored-by: Shane Smiskol --- selfdrive/car/hyundai/values.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 16f1f42679..736ebbb553 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -799,6 +799,7 @@ FW_VERSIONS = { b'\xf1\x00CK__ SCC F_CUP 1.00 1.01 96400-J5100 ', b'\xf1\x00CK__ SCC F_CUP 1.00 1.03 96400-J5100 ', b'\xf1\x00CK__ SCC F_CUP 1.00 1.01 96400-J5000 ', + b'\xf1\x00CK__ SCC F_CUP 1.00 1.02 96400-J5100 ', ], (Ecu.engine, 0x7e0, None): [ b'\xf1\x81606DE051\x00\x00\x00\x00\x00\x00\x00\x00', @@ -806,6 +807,7 @@ FW_VERSIONS = { b'\xf1\x82CKJN3TMSDE0B\x00\x00\x00\x00', b'\xf1\x82CKKN3TMD_H0A\x00\x00\x00\x00', b'\xe0\x19\xff\xe7\xe7g\x01\xa2\x00\x0f\x00\x9e\x00\x06\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\x0f\x0e\x0f\x0f\x0e\r\x00\x00\x7f\x02.\xff\x00\x00~p\x00\x00\x00\x00u\xff\xf9\xff\x00\x00\x00\x00V\t\xd5\x01\xc0\x00\x00\x00\x007\xfb\xfc\x0b\x8d\x00', + b'\xf1\x81640H0051\x00\x00\x00\x00\x00\x00\x00\x00', ], (Ecu.eps, 0x7d4, None): [ b'\xf1\x00CK MDPS R 1.00 1.04 57700-J5200 4C2CL104', @@ -813,6 +815,7 @@ FW_VERSIONS = { b'\xf1\x00CK MDPS R 1.00 1.04 57700-J5420 4C4VL104', b'\xf1\x00CK MDPS R 1.00 1.06 57700-J5420 4C4VL106', b'\xf1\x00CK MDPS R 1.00 1.07 57700-J5220 4C2VL107', + b'\xf1\x00CK MDPS R 1.00 1.06 57700-J5220 4C2VL106', ], (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00CK MFC AT USA LHD 1.00 1.03 95740-J5000 170822', @@ -820,6 +823,7 @@ FW_VERSIONS = { b'\xf1\x00CK MFC AT EUR LHD 1.00 1.03 95740-J5000 170822', ], (Ecu.transmission, 0x7e1, None): [ + b'\xf1\x00bcsh8p54 E25\x00\x00\x00\x00\x00\x00\x00SCK0T33NB2\xb3\xee\xba\xdc', b'\xf1\x87VCJLE17622572DK0vd6D\x99\x98y\x97vwVffUfvfC%CuT&Dx\x87o\xff{\x1c\xf1\x81E21\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E21\x00\x00\x00\x00\x00\x00\x00SCK0T33NB0\x88\xa2\xe6\xf0', b'\xf1\x87VDHLG17000192DK2xdFffT\xa5VUD$DwT\x86wveVeeD&T\x99\xba\x8f\xff\xcc\x99\xf1\x81E21\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E21\x00\x00\x00\x00\x00\x00\x00SCK0T33NB0\x88\xa2\xe6\xf0', b'\xf1\x87VDHLG17000192DK2xdFffT\xa5VUD$DwT\x86wveVeeD&T\x99\xba\x8f\xff\xcc\x99\xf1\x89E21\x00\x00\x00\x00\x00\x00\x00\xf1\x82SCK0T33NB0', From d12146fbd657042bb6a74caa4c10b08a09ba074d Mon Sep 17 00:00:00 2001 From: Erich Moraga <33645296+ErichMoraga@users.noreply.github.com> Date: Mon, 9 Jan 2023 20:57:51 -0600 Subject: [PATCH 046/484] Add several missing LEXUS_RC firmwares (#26905) * Add several missing LEXUS_RC firmwares `@Vranesh#9912` 2020 RC350 DongleID/route d6303cdeea512a4e|2023-01-09--17-11-55 * lexus now has two engines * todo Co-authored-by: Shane Smiskol --- selfdrive/car/fw_query_definitions.py | 1 + selfdrive/car/toyota/values.py | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/selfdrive/car/fw_query_definitions.py b/selfdrive/car/fw_query_definitions.py index c7e4d4eb30..a1695733fa 100755 --- a/selfdrive/car/fw_query_definitions.py +++ b/selfdrive/car/fw_query_definitions.py @@ -62,6 +62,7 @@ class Request: @dataclass class FwQueryConfig: requests: List[Request] + # TODO: make this automatic and remove hardcoded lists, or do fingerprinting with ecus # Overrides and removes from essential ecus for specific models and ecus (exact matching) non_essential_ecus: Dict[capnp.lib.capnp._EnumModule, List[str]] = field(default_factory=dict) # Ecus added for data collection, not to be fingerprinted on diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index e945130e90..586b1a835e 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -230,7 +230,7 @@ FW_QUERY_CONFIG = FwQueryConfig( # FIXME: On some models, abs can sometimes be missing Ecu.abs: [CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.SIENNA, CAR.LEXUS_IS], # On some models, the engine can show on two different addresses - Ecu.engine: [CAR.CAMRY, CAR.COROLLA_TSS2, CAR.CHR, CAR.LEXUS_IS], + Ecu.engine: [CAR.CAMRY, CAR.COROLLA_TSS2, CAR.CHR, CAR.LEXUS_IS, CAR.LEXUS_RC], } ) @@ -1738,22 +1738,29 @@ FW_VERSIONS = { ], }, CAR.LEXUS_RC: { + (Ecu.engine, 0x700, None): [ + b'\x01896632478200\x00\x00\x00\x00', + ], (Ecu.engine, 0x7e0, None): [ b'\x0232484000\x00\x00\x00\x00\x00\x00\x00\x0052422000\x00\x00\x00\x00\x00\x00\x00\x00', ], (Ecu.abs, 0x7b0, None): [ + b'F152624150\x00\x00\x00\x00\x00\x00', b'F152624221\x00\x00\x00\x00\x00\x00', ], (Ecu.dsu, 0x791, None): [ + b'881512407000\x00\x00\x00\x00', b'881512409100\x00\x00\x00\x00', ], (Ecu.eps, 0x7a1, None): [ b'8965B24081\x00\x00\x00\x00\x00\x00', + b'8965B24320\x00\x00\x00\x00\x00\x00', ], (Ecu.fwdRadar, 0x750, 0xf): [ b'8821F4702300\x00\x00\x00\x00', ], (Ecu.fwdCamera, 0x750, 0x6d): [ + b'8646F2401200\x00\x00\x00\x00', b'8646F2402200\x00\x00\x00\x00', ], }, From 9e364ff76f9c0b23ebe557b95b4a584dff052e97 Mon Sep 17 00:00:00 2001 From: Kyle Dibble-Dabney <6963660+dibble9012@users.noreply.github.com> Date: Mon, 9 Jan 2023 22:01:03 -0500 Subject: [PATCH 047/484] Added fingerprint for 2023 EV6, fwdCamera (#26900) --- selfdrive/car/hyundai/values.py | 1 + 1 file changed, 1 insertion(+) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 736ebbb553..1a81e1b167 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -1466,6 +1466,7 @@ FW_VERSIONS = { b'\xf1\x00CV1 MFC AT EUR LHD 1.00 1.05 99210-CV000 211027', b'\xf1\x00CV1 MFC AT EUR LHD 1.00 1.06 99210-CV000 220328', b'\xf1\x00CV1 MFC AT EUR RHD 1.00 1.00 99210-CV100 220630', + b'\xf1\x00CV1 MFC AT USA LHD 1.00 1.00 99210-CV100 220630', ], }, CAR.IONIQ_5: { From 4e9bddee5cbabe4807e690a93408a23ce66225d5 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 10 Jan 2023 13:02:43 -0800 Subject: [PATCH 048/484] longcontrol: enter stopping state immediately (#26879) * enter stopping state immediately * Update selfdrive/controls/lib/longcontrol.py --- selfdrive/controls/lib/longcontrol.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/selfdrive/controls/lib/longcontrol.py b/selfdrive/controls/lib/longcontrol.py index 545a4c43ff..e8095813f2 100644 --- a/selfdrive/controls/lib/longcontrol.py +++ b/selfdrive/controls/lib/longcontrol.py @@ -30,10 +30,8 @@ def long_control_state_trans(CP, active, long_control_state, v_ego, v_target, long_control_state = LongCtrlState.off else: - if long_control_state == LongCtrlState.off: + if long_control_state in (LongCtrlState.off, LongCtrlState.pid): long_control_state = LongCtrlState.pid - - elif long_control_state == LongCtrlState.pid: if stopping_condition: long_control_state = LongCtrlState.stopping From b45dda2d0a365946e4cfc3c4a08023c05e2d58c7 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 10 Jan 2023 14:46:43 -0800 Subject: [PATCH 049/484] Longitudinal tests: test forceDecel (#26765) * test with forceDecel * test all combos * fix * fix * fix * ... * remove print * clean up * just set cruise to 0 * update ref commit Co-authored-by: Bruce Wayne --- .../lib/longitudinal_mpc_lib/long_mpc.py | 2 +- .../controls/lib/longitudinal_planner.py | 7 +- .../test/longitudinal_maneuvers/maneuver.py | 6 ++ .../test/longitudinal_maneuvers/plant.py | 4 +- .../test_longitudinal.py | 68 +++++++++++-------- selfdrive/test/process_replay/ref_commit | 2 +- 6 files changed, 51 insertions(+), 38 deletions(-) diff --git a/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py b/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py index dc27fd27a9..c017951232 100644 --- a/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py +++ b/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py @@ -306,7 +306,7 @@ class LongitudinalMpc: self.cruise_min_a = min_a self.max_a = max_a - def update(self, carstate, radarstate, v_cruise, x, v, a, j): + def update(self, radarstate, v_cruise, x, v, a, j): v_ego = self.x0[1] self.status = radarstate.leadOne.status or radarstate.leadTwo.status diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index a0f6318323..0febfbafd9 100755 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -15,7 +15,6 @@ from selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX, CONTROL_N from system.swaglog import cloudlog LON_MPC_STEP = 0.2 # first step is 0.2s -AWARENESS_DECEL = -0.2 # car smoothly decel at .2m/s^2 when user is distracted A_CRUISE_MIN = -1.2 A_CRUISE_MAX_VALS = [1.6, 1.2, 0.8, 0.6] A_CRUISE_MAX_BP = [0., 10.0, 25., 40.] @@ -111,9 +110,7 @@ class LongitudinalPlanner: self.v_model_error = sm['modelV2'].temporalPose.trans[0] - v_ego if force_slow_decel: - # if required so, force a smooth deceleration - accel_limits_turns[1] = min(accel_limits_turns[1], AWARENESS_DECEL) - accel_limits_turns[0] = min(accel_limits_turns[0], accel_limits_turns[1]) + v_cruise = 0.0 # clip limits, cannot init MPC outside of bounds accel_limits_turns[0] = min(accel_limits_turns[0], self.a_desired + 0.05) accel_limits_turns[1] = max(accel_limits_turns[1], self.a_desired - 0.05) @@ -122,7 +119,7 @@ class LongitudinalPlanner: self.mpc.set_accel_limits(accel_limits_turns[0], accel_limits_turns[1]) self.mpc.set_cur_state(self.v_desired_filter.x, self.a_desired) x, v, a, j = self.parse_model(sm['modelV2'], self.v_model_error) - self.mpc.update(sm['carState'], sm['radarState'], v_cruise, x, v, a, j) + self.mpc.update(sm['radarState'], v_cruise, x, v, a, j) self.v_desired_trajectory = np.interp(T_IDXS[:CONTROL_N], T_IDXS_MPC, self.mpc.v_solution) self.a_desired_trajectory = np.interp(T_IDXS[:CONTROL_N], T_IDXS_MPC, self.mpc.a_solution) diff --git a/selfdrive/test/longitudinal_maneuvers/maneuver.py b/selfdrive/test/longitudinal_maneuvers/maneuver.py index 65935f8979..00ddfe627e 100644 --- a/selfdrive/test/longitudinal_maneuvers/maneuver.py +++ b/selfdrive/test/longitudinal_maneuvers/maneuver.py @@ -19,6 +19,7 @@ class Maneuver: self.ensure_start = kwargs.get("ensure_start", False) self.enabled = kwargs.get("enabled", True) self.e2e = kwargs.get("e2e", False) + self.force_decel = kwargs.get("force_decel", False) self.duration = duration self.title = title @@ -32,6 +33,7 @@ class Maneuver: only_lead2=self.only_lead2, only_radar=self.only_radar, e2e=self.e2e, + force_decel=self.force_decel, ) valid = True @@ -60,6 +62,10 @@ class Maneuver: if self.ensure_start and log['v_rel'] > 0 and log['speeds'][-1] <= 0.1: print('LongitudinalPlanner not starting!') valid = False + if self.force_decel and log['speed'] > 1e-1 and log['acceleration'] > -0.04: + print('Not stopping with force decel') + valid = False + print("maneuver end", valid) return valid, np.array(logs) diff --git a/selfdrive/test/longitudinal_maneuvers/plant.py b/selfdrive/test/longitudinal_maneuvers/plant.py index 8e150d800c..59cb0e1fe3 100755 --- a/selfdrive/test/longitudinal_maneuvers/plant.py +++ b/selfdrive/test/longitudinal_maneuvers/plant.py @@ -15,7 +15,7 @@ class Plant: messaging_initialized = False def __init__(self, lead_relevancy=False, speed=0.0, distance_lead=2.0, - enabled=True, only_lead2=False, only_radar=False, e2e=False): + enabled=True, only_lead2=False, only_radar=False, e2e=False, force_decel=False): self.rate = 1. / DT_MDL if not Plant.messaging_initialized: @@ -39,6 +39,7 @@ class Plant: self.only_lead2 = only_lead2 self.only_radar = only_radar self.e2e = e2e + self.force_decel = force_decel self.rk = Ratekeeper(self.rate, print_delay_threshold=100.0) self.ts = 1. / self.rate @@ -111,6 +112,7 @@ class Plant: control.controlsState.longControlState = LongCtrlState.pid if self.enabled else LongCtrlState.off control.controlsState.vCruise = float(v_cruise * 3.6) control.controlsState.experimentalMode = self.e2e + control.controlsState.forceDecel = self.force_decel car_state.carState.vEgo = float(self.speed) car_state.carState.standstill = self.speed < 0.01 diff --git a/selfdrive/test/longitudinal_maneuvers/test_longitudinal.py b/selfdrive/test/longitudinal_maneuvers/test_longitudinal.py index 686b35e456..bc477ca9fe 100755 --- a/selfdrive/test/longitudinal_maneuvers/test_longitudinal.py +++ b/selfdrive/test/longitudinal_maneuvers/test_longitudinal.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 +import itertools import os -from parameterized import parameterized +from parameterized import parameterized_class import unittest from common.params import Params @@ -9,8 +10,8 @@ from selfdrive.test.longitudinal_maneuvers.maneuver import Maneuver # TODO: make new FCW tests -def create_maneuvers(e2e): - return [ +def create_maneuvers(kwargs): + maneuvers = [ Maneuver( 'approach stopped car at 25m/s, initial distance: 120m', duration=20., @@ -19,7 +20,7 @@ def create_maneuvers(e2e): initial_distance_lead=120., speed_lead_values=[30., 0.], breakpoints=[0., 1.], - e2e=e2e, + **kwargs, ), Maneuver( 'approach stopped car at 20m/s, initial distance 90m', @@ -29,7 +30,7 @@ def create_maneuvers(e2e): initial_distance_lead=90., speed_lead_values=[20., 0.], breakpoints=[0., 1.], - e2e=e2e, + **kwargs, ), Maneuver( 'steady state following a car at 20m/s, then lead decel to 0mph at 1m/s^2', @@ -39,7 +40,7 @@ def create_maneuvers(e2e): initial_distance_lead=35., speed_lead_values=[20., 20., 0.], breakpoints=[0., 15., 35.0], - e2e=e2e, + **kwargs, ), Maneuver( 'steady state following a car at 20m/s, then lead decel to 0mph at 2m/s^2', @@ -49,7 +50,7 @@ def create_maneuvers(e2e): initial_distance_lead=35., speed_lead_values=[20., 20., 0.], breakpoints=[0., 15., 25.0], - e2e=e2e, + **kwargs, ), Maneuver( 'steady state following a car at 20m/s, then lead decel to 0mph at 3m/s^2', @@ -59,7 +60,7 @@ def create_maneuvers(e2e): initial_distance_lead=35., speed_lead_values=[20., 20., 0.], breakpoints=[0., 15., 21.66], - e2e=e2e, + **kwargs, ), Maneuver( 'steady state following a car at 20m/s, then lead decel to 0mph at 3+m/s^2', @@ -71,7 +72,7 @@ def create_maneuvers(e2e): prob_lead_values=[0., 1., 1.], cruise_values=[20., 20., 20.], breakpoints=[2., 2.01, 8.8], - e2e=e2e, + **kwargs, ), Maneuver( "approach stopped car at 20m/s, with prob_lead_values", @@ -83,7 +84,7 @@ def create_maneuvers(e2e): prob_lead_values=[0.0, 0., 1.], cruise_values=[20., 20., 20.], breakpoints=[0.0, 2., 2.01], - e2e=e2e, + **kwargs, ), Maneuver( "approach slower cut-in car at 20m/s", @@ -94,7 +95,7 @@ def create_maneuvers(e2e): speed_lead_values=[15., 15.], breakpoints=[1., 11.], only_lead2=True, - e2e=e2e, + **kwargs, ), Maneuver( "stay stopped behind radar override lead", @@ -106,7 +107,7 @@ def create_maneuvers(e2e): prob_lead_values=[0., 0.], breakpoints=[1., 11.], only_radar=True, - e2e=e2e, + **kwargs, ), Maneuver( "NaN recovery", @@ -117,10 +118,20 @@ def create_maneuvers(e2e): speed_lead_values=[0., 0., 0.0], breakpoints=[1., 1.01, 11.], cruise_values=[float("nan"), 15., 15.], - e2e=e2e, + **kwargs, ), - # controls relies on planner commanding to move for stock-ACC resume spamming Maneuver( + 'cruising at 25 m/s while disabled', + duration=20., + initial_speed=25., + lead_relevancy=False, + enabled=False, + **kwargs, + ), + ] + if not kwargs['force_decel']: + # controls relies on planner commanding to move for stock-ACC resume spamming + maneuvers.append(Maneuver( "resume from a stop", duration=20., initial_speed=0., @@ -129,20 +140,16 @@ def create_maneuvers(e2e): speed_lead_values=[0., 0., 2.], breakpoints=[1., 10., 15.], ensure_start=True, - e2e=e2e, - ), - Maneuver( - 'cruising at 25 m/s while disabled', - duration=20., - initial_speed=25., - lead_relevancy=False, - enabled=False, - e2e=e2e, - ), - ] + **kwargs, + )) + return maneuvers +@parameterized_class(("e2e", "force_decel"), itertools.product([True, False], repeat=2)) class LongitudinalControl(unittest.TestCase): + e2e: bool + force_decel: bool + @classmethod def setUpClass(cls): os.environ['SIMULATION'] = "1" @@ -154,11 +161,12 @@ class LongitudinalControl(unittest.TestCase): params.put_bool("Passive", bool(os.getenv("PASSIVE"))) params.put_bool("OpenpilotEnabledToggle", True) - @parameterized.expand([(man,) for e2e in [True, False] for man in create_maneuvers(e2e)]) - def test_maneuver(self, maneuver): - print(maneuver.title, f'in {"e2e" if maneuver.e2e else "acc"} mode') - valid, _ = maneuver.evaluate() - self.assertTrue(valid, msg=maneuver.title) + def test_maneuver(self): + for maneuver in create_maneuvers({"e2e": self.e2e, "force_decel": self.force_decel}): + with self.subTest(title=maneuver.title, e2e=maneuver.e2e, force_decel=maneuver.force_decel): + print(maneuver.title, f'in {"e2e" if maneuver.e2e else "acc"} mode') + valid, _ = maneuver.evaluate() + self.assertTrue(valid) if __name__ == "__main__": diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 24ec1c62de..c23ff8a140 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -6d30c77af7b3210b03f65b433c0a043a96ee39bc \ No newline at end of file +c39b74fdc14bb22a1c95cd5deedd4f4fcadf8494 From 5f66a9a6213f6c42161584979a2f760f9ad9df47 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 10 Jan 2023 16:20:27 -0800 Subject: [PATCH 050/484] jenkins fixups --- Jenkinsfile | 3 ++- selfdrive/test/process_replay/model_replay.py | 2 +- selfdrive/test/setup_device_ci.sh | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 37621dde5c..f49ccf4315 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -90,7 +90,8 @@ pipeline { } } steps { - sh "git config --global --add safe.directory ${WORKSPACE}" + sh "git config --global --add safe.directory '*'" + sh "git submodule update --init --recursive" sh "git lfs pull" lock(resource: "", label: "simulator", inversePrecedence: true, quantity: 1) { sh "${WORKSPACE}/tools/sim/build_container.sh" diff --git a/selfdrive/test/process_replay/model_replay.py b/selfdrive/test/process_replay/model_replay.py index 324801fead..68cbb28aaf 100755 --- a/selfdrive/test/process_replay/model_replay.py +++ b/selfdrive/test/process_replay/model_replay.py @@ -223,7 +223,7 @@ if __name__ == "__main__": try: expected_msgs = 2*MAX_FRAMES if not NO_NAV: - expected_msgs += NAV_FRAMES*2 + expected_msgs += NAV_FRAMES*3 cmp_log = list(LogReader(BASE_URL + log_fn))[:expected_msgs] ignore = [ diff --git a/selfdrive/test/setup_device_ci.sh b/selfdrive/test/setup_device_ci.sh index 9e06b98662..b7d586a83b 100755 --- a/selfdrive/test/setup_device_ci.sh +++ b/selfdrive/test/setup_device_ci.sh @@ -56,7 +56,7 @@ cd $SOURCE_DIR rm -f .git/index.lock git reset --hard -git fetch --verbose origin $GIT_COMMIT +git fetch --no-tags --no-recurse-submodules -j4 --verbose --depth 1 origin $GIT_COMMIT find . -maxdepth 1 -not -path './.git' -not -name '.' -not -name '..' -exec rm -rf '{}' \; git reset --hard $GIT_COMMIT git checkout $GIT_COMMIT @@ -69,6 +69,7 @@ git lfs pull (ulimit -n 65535 && git lfs prune) echo "git checkout done, t=$SECONDS" +du -hs $SOURCE_DIR $SOURCE_DIR/.git rsync -a --delete $SOURCE_DIR $TEST_DIR From f0d0d999813ee3c5cc49c2b47623296c70d37318 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 10 Jan 2023 19:59:39 -0800 Subject: [PATCH 051/484] process replay: test body with joystick mode (#26916) * no need to check sm * fix * Update ref_commit * revert --- selfdrive/controls/controlsd.py | 2 +- selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index fea19adbdb..b5d5e3d8a2 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -103,7 +103,7 @@ class Controls: else: self.CI, self.CP = CI, CI.CP - self.joystick_mode = self.params.get_bool("JoystickDebugMode") or (self.CP.notCar and sm is None) + self.joystick_mode = self.params.get_bool("JoystickDebugMode") or self.CP.notCar # set alternative experiences from parameters self.disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator") diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index c23ff8a140..76f2ef06ba 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -c39b74fdc14bb22a1c95cd5deedd4f4fcadf8494 +d9432dcfb875a76be051137969e371c9926611e9 From cb88b3ed6528a7ad840be7bcd33fe7023b4f3719 Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Tue, 10 Jan 2023 23:51:10 -0500 Subject: [PATCH 052/484] controlsd: set latActive with max minimum steer speed (#26805) * refactor minimum lateral speed handling * rename for clarity * simplify without joystick at standstill * intermediate standstill variable, check notCar * check joystick for now * cmt Co-authored-by: Shane Smiskol --- selfdrive/controls/controlsd.py | 6 ++++-- selfdrive/controls/lib/latcontrol.py | 2 +- selfdrive/controls/lib/latcontrol_angle.py | 4 ++-- selfdrive/controls/lib/latcontrol_indi.py | 4 ++-- selfdrive/controls/lib/latcontrol_pid.py | 4 ++-- selfdrive/controls/lib/latcontrol_torque.py | 4 ++-- 6 files changed, 13 insertions(+), 11 deletions(-) diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index b5d5e3d8a2..1d92cd4fb2 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -17,7 +17,7 @@ from selfdrive.boardd.boardd import can_list_to_can_capnp from selfdrive.car.car_helpers import get_car, get_startup_event, get_one_can from selfdrive.controls.lib.lateral_planner import CAMERA_OFFSET from selfdrive.controls.lib.drive_helpers import VCruiseHelper, get_lag_adjusted_curvature -from selfdrive.controls.lib.latcontrol import LatControl +from selfdrive.controls.lib.latcontrol import LatControl, MIN_LATERAL_CONTROL_SPEED from selfdrive.controls.lib.longcontrol import LongControl from selfdrive.controls.lib.latcontrol_pid import LatControlPID from selfdrive.controls.lib.latcontrol_indi import LatControlINDI @@ -569,9 +569,11 @@ class Controls: CC = car.CarControl.new_message() CC.enabled = self.enabled + # Check which actuators can be enabled + standstill = CS.vEgo <= max(self.CP.minSteerSpeed, MIN_LATERAL_CONTROL_SPEED) or CS.standstill CC.latActive = self.active and not CS.steerFaultTemporary and not CS.steerFaultPermanent and \ - CS.vEgo > self.CP.minSteerSpeed and not CS.standstill + (not standstill or self.joystick_mode) CC.longActive = self.enabled and not self.events.any(ET.OVERRIDE_LONGITUDINAL) and self.CP.openpilotLongitudinalControl actuators = CC.actuators diff --git a/selfdrive/controls/lib/latcontrol.py b/selfdrive/controls/lib/latcontrol.py index cefbc1078c..d38959c560 100644 --- a/selfdrive/controls/lib/latcontrol.py +++ b/selfdrive/controls/lib/latcontrol.py @@ -3,7 +3,7 @@ from abc import abstractmethod, ABC from common.numpy_fast import clip from common.realtime import DT_CTRL -MIN_STEER_SPEED = 0.3 +MIN_LATERAL_CONTROL_SPEED = 0.3 # m/s class LatControl(ABC): diff --git a/selfdrive/controls/lib/latcontrol_angle.py b/selfdrive/controls/lib/latcontrol_angle.py index d692f80b4b..9ed140d38e 100644 --- a/selfdrive/controls/lib/latcontrol_angle.py +++ b/selfdrive/controls/lib/latcontrol_angle.py @@ -1,7 +1,7 @@ import math from cereal import log -from selfdrive.controls.lib.latcontrol import LatControl, MIN_STEER_SPEED +from selfdrive.controls.lib.latcontrol import LatControl STEER_ANGLE_SATURATION_THRESHOLD = 2.5 # Degrees @@ -14,7 +14,7 @@ class LatControlAngle(LatControl): def update(self, active, CS, VM, params, last_actuators, steer_limited, desired_curvature, desired_curvature_rate, llk): angle_log = log.ControlsState.LateralAngleState.new_message() - if CS.vEgo < MIN_STEER_SPEED or not active: + if not active: angle_log.active = False angle_steers_des = float(CS.steeringAngleDeg) else: diff --git a/selfdrive/controls/lib/latcontrol_indi.py b/selfdrive/controls/lib/latcontrol_indi.py index 2bc3cef76b..dca82c6729 100644 --- a/selfdrive/controls/lib/latcontrol_indi.py +++ b/selfdrive/controls/lib/latcontrol_indi.py @@ -5,7 +5,7 @@ from cereal import log from common.filter_simple import FirstOrderFilter from common.numpy_fast import clip, interp from common.realtime import DT_CTRL -from selfdrive.controls.lib.latcontrol import LatControl, MIN_STEER_SPEED +from selfdrive.controls.lib.latcontrol import LatControl class LatControlINDI(LatControl): @@ -82,7 +82,7 @@ class LatControlINDI(LatControl): rate_des = VM.get_steer_from_curvature(-desired_curvature_rate, CS.vEgo, 0) indi_log.steeringRateDesiredDeg = math.degrees(rate_des) - if CS.vEgo < MIN_STEER_SPEED or not active: + if not active: indi_log.active = False self.steer_filter.x = 0.0 output_steer = 0 diff --git a/selfdrive/controls/lib/latcontrol_pid.py b/selfdrive/controls/lib/latcontrol_pid.py index 6bd678073e..6696d2e304 100644 --- a/selfdrive/controls/lib/latcontrol_pid.py +++ b/selfdrive/controls/lib/latcontrol_pid.py @@ -1,7 +1,7 @@ import math from cereal import log -from selfdrive.controls.lib.latcontrol import LatControl, MIN_STEER_SPEED +from selfdrive.controls.lib.latcontrol import LatControl from selfdrive.controls.lib.pid import PIDController @@ -28,7 +28,7 @@ class LatControlPID(LatControl): pid_log.steeringAngleDesiredDeg = angle_steers_des pid_log.angleError = error - if CS.vEgo < MIN_STEER_SPEED or not active: + if not active: output_steer = 0.0 pid_log.active = False self.pid.reset() diff --git a/selfdrive/controls/lib/latcontrol_torque.py b/selfdrive/controls/lib/latcontrol_torque.py index d10d39d945..2f56094379 100644 --- a/selfdrive/controls/lib/latcontrol_torque.py +++ b/selfdrive/controls/lib/latcontrol_torque.py @@ -2,7 +2,7 @@ import math from cereal import log from common.numpy_fast import interp -from selfdrive.controls.lib.latcontrol import LatControl, MIN_STEER_SPEED +from selfdrive.controls.lib.latcontrol import LatControl from selfdrive.controls.lib.pid import PIDController from selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY @@ -39,7 +39,7 @@ class LatControlTorque(LatControl): def update(self, active, CS, VM, params, last_actuators, steer_limited, desired_curvature, desired_curvature_rate, llk): pid_log = log.ControlsState.LateralTorqueState.new_message() - if CS.vEgo < MIN_STEER_SPEED or not active: + if not active: output_torque = 0.0 pid_log.active = False else: From 29470d2d2d5f53d4b26a220192a08adbde5eeb5d Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 10 Jan 2023 22:28:54 -0800 Subject: [PATCH 053/484] VW: match panda standstill check (#25761) * test models: check panda standstill * match panda * reverse exception * check == 0 * bumppanda Co-authored-by: Adeeb Shihadeh --- panda | 2 +- selfdrive/car/tests/test_models.py | 2 +- selfdrive/car/volkswagen/carstate.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/panda b/panda index 345147fe2b..0b3b906036 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 345147fe2bc3a06c44709426f9fcd298588b9fe4 +Subproject commit 0b3b9060365739eeeddb1528cfe4018fec4efe7a diff --git a/selfdrive/car/tests/test_models.py b/selfdrive/car/tests/test_models.py index 641c109316..b6f78c4b47 100755 --- a/selfdrive/car/tests/test_models.py +++ b/selfdrive/car/tests/test_models.py @@ -238,7 +238,7 @@ class TestCarModelBase(unittest.TestCase): # TODO: check rest of panda's carstate (steering, ACC main on, etc.) checks['gasPressed'] += CS.gasPressed != self.safety.get_gas_pressed_prev() - if self.CP.carName not in ("hyundai", "volkswagen", "body"): + if self.CP.carName not in ("hyundai", "body"): # TODO: fix standstill mismatches for other makes checks['standstill'] += CS.standstill == self.safety.get_vehicle_moving() diff --git a/selfdrive/car/volkswagen/carstate.py b/selfdrive/car/volkswagen/carstate.py index 7ea9aa871b..64d1246880 100644 --- a/selfdrive/car/volkswagen/carstate.py +++ b/selfdrive/car/volkswagen/carstate.py @@ -44,7 +44,7 @@ class CarState(CarStateBase): ret.vEgoRaw = float(np.mean([ret.wheelSpeeds.fl, ret.wheelSpeeds.fr, ret.wheelSpeeds.rl, ret.wheelSpeeds.rr])) ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw) - ret.standstill = ret.vEgo < 0.1 + ret.standstill = ret.vEgoRaw == 0 # Update steering angle, rate, yaw rate, and driver input torque. VW send # the sign/direction in a separate signal so they must be recombined. @@ -160,7 +160,7 @@ class CarState(CarStateBase): # vEgo obtained from Bremse_1 vehicle speed rather than Bremse_3 wheel speeds because Bremse_3 isn't present on NSF ret.vEgoRaw = pt_cp.vl["Bremse_1"]["Geschwindigkeit_neu__Bremse_1_"] * CV.KPH_TO_MS ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw) - ret.standstill = ret.vEgo < 0.1 + ret.standstill = ret.vEgoRaw == 0 # Update steering angle, rate, yaw rate, and driver input torque. VW send # the sign/direction in a separate signal so they must be recombined. From f41caec2dec08aa3bbf8e0b3b6efa7600a8f6f0f Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 11 Jan 2023 15:02:24 -0800 Subject: [PATCH 054/484] bump cereal, can in qlogs --- cereal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cereal b/cereal index f200875ca3..c0d9abf6f7 160000 --- a/cereal +++ b/cereal @@ -1 +1 @@ -Subproject commit f200875ca300d3a7b9293c4effcc9456e359e505 +Subproject commit c0d9abf6f7c7de140c41af10e322e226d900ef99 From 9884957e3e07e09df6c021222949a4a77e2bd8ff Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 12 Jan 2023 07:32:13 +0800 Subject: [PATCH 055/484] cabana: sync play button state (#26917) sync play button --- tools/cabana/canmessages.cc | 5 +++++ tools/cabana/canmessages.h | 4 +++- tools/cabana/videowidget.cc | 13 +++++-------- tools/cabana/videowidget.h | 1 - 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/tools/cabana/canmessages.cc b/tools/cabana/canmessages.cc index ad3ee80e3d..6e19eb440f 100644 --- a/tools/cabana/canmessages.cc +++ b/tools/cabana/canmessages.cc @@ -91,6 +91,11 @@ void CANMessages::seekTo(double ts) { emit updated(); } +void CANMessages::pause(bool pause) { + replay->pause(pause); + emit (pause ? paused() : resume()); +} + void CANMessages::settingChanged() { replay->setSegmentCacheLimit(settings.cached_segment_limit); } diff --git a/tools/cabana/canmessages.h b/tools/cabana/canmessages.h index 4dac4fe9df..ea43933565 100644 --- a/tools/cabana/canmessages.h +++ b/tools/cabana/canmessages.h @@ -38,10 +38,12 @@ public: inline const std::vector *events() const { return replay->events(); } inline void setSpeed(float speed) { replay->setSpeed(speed); } inline bool isPaused() const { return replay->isPaused(); } - inline void pause(bool pause) { replay->pause(pause); } + void pause(bool pause); inline const std::vector> getTimeline() { return replay->getTimeline(); } signals: + void paused(); + void resume(); void seekedTo(double sec); void streamStarted(); void eventsMerged(); diff --git a/tools/cabana/videowidget.cc b/tools/cabana/videowidget.cc index 7e40ba2adb..ed4354ce65 100644 --- a/tools/cabana/videowidget.cc +++ b/tools/cabana/videowidget.cc @@ -63,22 +63,19 @@ VideoWidget::VideoWidget(QWidget *parent) : QFrame(parent) { } main_layout->addLayout(control_layout); - QObject::connect(can, &CANMessages::updated, this, &VideoWidget::updateState); QObject::connect(slider, &QSlider::sliderReleased, [this]() { can->seekTo(slider->value() / 1000.0); }); QObject::connect(slider, &QSlider::valueChanged, [=](int value) { time_label->setText(formatTime(value / 1000)); }); - QObject::connect(cam_widget, &CameraWidget::clicked, [this]() { pause(!can->isPaused()); }); - QObject::connect(play_btn, &QPushButton::clicked, [=]() { pause(!can->isPaused()); }); + QObject::connect(cam_widget, &CameraWidget::clicked, []() { can->pause(!can->isPaused()); }); + QObject::connect(play_btn, &QPushButton::clicked, []() { can->pause(!can->isPaused()); }); + QObject::connect(can, &CANMessages::updated, this, &VideoWidget::updateState); + QObject::connect(can, &CANMessages::paused, [this]() { play_btn->setText("▶"); }); + QObject::connect(can, &CANMessages::resume, [this]() { play_btn->setText("⏸"); }); QObject::connect(can, &CANMessages::streamStarted, [this]() { end_time_label->setText(formatTime(can->totalSeconds())); slider->setRange(0, can->totalSeconds() * 1000); }); } -void VideoWidget::pause(bool pause) { - play_btn->setText(!pause ? "⏸" : "▶"); - can->pause(pause); -} - void VideoWidget::rangeChanged(double min, double max, bool is_zoomed) { if (!is_zoomed) { min = 0; diff --git a/tools/cabana/videowidget.h b/tools/cabana/videowidget.h index 86cdc6f114..ec8bc4bec4 100644 --- a/tools/cabana/videowidget.h +++ b/tools/cabana/videowidget.h @@ -44,7 +44,6 @@ public: protected: void updateState(); - void pause(bool pause); CameraWidget *cam_widget; QLabel *end_time_label; From 35626303b16734b17cb3dd266c5dd6f18f8941a9 Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Wed, 11 Jan 2023 18:45:15 -0500 Subject: [PATCH 056/484] VW MQB: Add FW for 2019 Volkswagen Golf (#26920) --- selfdrive/car/volkswagen/values.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py index 24ea0a03ba..af73683e22 100755 --- a/selfdrive/car/volkswagen/values.py +++ b/selfdrive/car/volkswagen/values.py @@ -373,6 +373,7 @@ FW_VERSIONS = { b'\xf1\x8704L906056CR\xf1\x895813', b'\xf1\x8704L906056HE\xf1\x893758', b'\xf1\x8704L906056HN\xf1\x896590', + b'\xf1\x8704L906056HT\xf1\x896591', b'\xf1\x870EA906016A \xf1\x898343', b'\xf1\x870EA906016E \xf1\x894219', b'\xf1\x870EA906016F \xf1\x894238', @@ -405,6 +406,7 @@ FW_VERSIONS = { b'\xf1\x870CW300041H \xf1\x891010', b'\xf1\x870CW300042F \xf1\x891604', b'\xf1\x870CW300043B \xf1\x891601', + b'\xf1\x870CW300043E \xf1\x891603', b'\xf1\x870CW300044S \xf1\x894530', b'\xf1\x870CW300044T \xf1\x895245', b'\xf1\x870CW300045 \xf1\x894531', @@ -442,6 +444,7 @@ FW_VERSIONS = { b'\xf1\x875Q0959655AA\xf1\x890388\xf1\x82\x111413001113120053114317121C111C9113', b'\xf1\x875Q0959655BH\xf1\x890336\xf1\x82\x1314160011123300314211012230229333463100', b'\xf1\x875Q0959655BS\xf1\x890403\xf1\x82\x1314160011123300314240012250229333463100', + b'\xf1\x875Q0959655BT\xf1\x890403\xf1\x82\x13141600111233003142404A2251229333463100', b'\xf1\x875Q0959655BT\xf1\x890403\xf1\x82\x13141600111233003142404A2252229333463100', b'\xf1\x875Q0959655BT\xf1\x890403\xf1\x82\x13141600111233003142405A2252229333463100', b'\xf1\x875Q0959655C \xf1\x890361\xf1\x82\x111413001112120004110415121610169112', From ccb43271977174e1a6de11b15ad07cd04c2ed056 Mon Sep 17 00:00:00 2001 From: Jason Wen <47793918+sunnyhaibin@users.noreply.github.com> Date: Wed, 11 Jan 2023 19:14:05 -0500 Subject: [PATCH 057/484] HKG CAN-FD: set cruise state available with car signal (#26812) * HKG CAN-FD: set cruise state available with car signal * Whoops * Check this signal * No should be in SCC_CONTROL * use TCS * add back * match CAN * think these are missing Co-authored-by: Shane Smiskol --- selfdrive/car/hyundai/carstate.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/selfdrive/car/hyundai/carstate.py b/selfdrive/car/hyundai/carstate.py index fc196528d7..53389279f7 100644 --- a/selfdrive/car/hyundai/carstate.py +++ b/selfdrive/car/hyundai/carstate.py @@ -156,6 +156,9 @@ class CarState(CarStateBase): def update_canfd(self, cp, cp_cam): ret = car.CarState.new_message() + self.is_metric = cp.vl["CRUISE_BUTTONS_ALT"]["DISTANCE_UNIT"] != 1 + speed_factor = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS + if self.CP.carFingerprint in (EV_CAR | HYBRID_CAR): if self.CP.carFingerprint in EV_CAR: ret.gas = cp.vl["ACCELERATOR"]["ACCELERATOR_PEDAL"] / 255. @@ -197,14 +200,18 @@ class CarState(CarStateBase): ret.leftBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"]["FL_INDICATOR"] != 0 ret.rightBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"]["FR_INDICATOR"] != 0 - ret.cruiseState.available = True - self.is_metric = cp.vl["CRUISE_BUTTONS_ALT"]["DISTANCE_UNIT"] != 1 - if not self.CP.openpilotLongitudinalControl: - speed_factor = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS + # cruise state + if self.CP.openpilotLongitudinalControl: + # These are not used for engage/disengage since openpilot keeps track of state using the buttons + ret.cruiseState.available = cp.vl["TCS"]["ACCEnable"] == 0 + ret.cruiseState.enabled = cp.vl["TCS"]["ACC_REQ"] == 1 + ret.cruiseState.standstill = False + else: cp_cruise_info = cp_cam if self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC else cp - ret.cruiseState.speed = cp_cruise_info.vl["SCC_CONTROL"]["VSetDis"] * speed_factor - ret.cruiseState.standstill = cp_cruise_info.vl["SCC_CONTROL"]["CRUISE_STANDSTILL"] == 1 + ret.cruiseState.available = cp_cruise_info.vl["SCC_CONTROL"]["MainMode_ACC"] == 1 ret.cruiseState.enabled = cp_cruise_info.vl["SCC_CONTROL"]["ACCMode"] in (1, 2) + ret.cruiseState.standstill = cp_cruise_info.vl["SCC_CONTROL"]["CRUISE_STANDSTILL"] == 1 + ret.cruiseState.speed = cp_cruise_info.vl["SCC_CONTROL"]["VSetDis"] * speed_factor self.cruise_info = copy.copy(cp_cruise_info.vl["SCC_CONTROL"]) cruise_btn_msg = "CRUISE_BUTTONS_ALT" if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS else "CRUISE_BUTTONS" @@ -435,6 +442,7 @@ class CarState(CarStateBase): ("DriverBraking", "TCS"), ("ACCEnable", "TCS"), + ("ACC_REQ", "TCS"), ("COUNTER", cruise_btn_msg), ("CRUISE_BUTTONS", cruise_btn_msg), @@ -476,6 +484,7 @@ class CarState(CarStateBase): ("ACCMode", "SCC_CONTROL"), ("VSetDis", "SCC_CONTROL"), ("CRUISE_STANDSTILL", "SCC_CONTROL"), + ("MainMode_ACC", "SCC_CONTROL"), ] checks += [ ("SCC_CONTROL", 50), From a24afa18d3b43f9ea02a15d99ba8eb07b8cc9709 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 11 Jan 2023 17:22:24 -0800 Subject: [PATCH 058/484] Hyundai CAN-FD: set available from TCS ACC available (#26921) * available on TCS signal always * cmt enh * better comment * better --- selfdrive/car/hyundai/carstate.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/selfdrive/car/hyundai/carstate.py b/selfdrive/car/hyundai/carstate.py index 53389279f7..47621d3c2c 100644 --- a/selfdrive/car/hyundai/carstate.py +++ b/selfdrive/car/hyundai/carstate.py @@ -201,14 +201,14 @@ class CarState(CarStateBase): ret.rightBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"]["FR_INDICATOR"] != 0 # cruise state + # CAN FD cars enable on main button press, set available if no TCS faults preventing engagement + ret.cruiseState.available = cp.vl["TCS"]["ACCEnable"] == 0 if self.CP.openpilotLongitudinalControl: # These are not used for engage/disengage since openpilot keeps track of state using the buttons - ret.cruiseState.available = cp.vl["TCS"]["ACCEnable"] == 0 ret.cruiseState.enabled = cp.vl["TCS"]["ACC_REQ"] == 1 ret.cruiseState.standstill = False else: cp_cruise_info = cp_cam if self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC else cp - ret.cruiseState.available = cp_cruise_info.vl["SCC_CONTROL"]["MainMode_ACC"] == 1 ret.cruiseState.enabled = cp_cruise_info.vl["SCC_CONTROL"]["ACCMode"] in (1, 2) ret.cruiseState.standstill = cp_cruise_info.vl["SCC_CONTROL"]["CRUISE_STANDSTILL"] == 1 ret.cruiseState.speed = cp_cruise_info.vl["SCC_CONTROL"]["VSetDis"] * speed_factor @@ -484,7 +484,6 @@ class CarState(CarStateBase): ("ACCMode", "SCC_CONTROL"), ("VSetDis", "SCC_CONTROL"), ("CRUISE_STANDSTILL", "SCC_CONTROL"), - ("MainMode_ACC", "SCC_CONTROL"), ] checks += [ ("SCC_CONTROL", 50), From 257a45dd18d30971dce0a02f285874423b2bfe9d Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 11 Jan 2023 20:25:01 -0800 Subject: [PATCH 059/484] HKG: filter on steering pressed (#26924) * filter on can steering pressed * Update ref_commit --- selfdrive/car/hyundai/carstate.py | 2 +- selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/car/hyundai/carstate.py b/selfdrive/car/hyundai/carstate.py index 47621d3c2c..da1a7bfa78 100644 --- a/selfdrive/car/hyundai/carstate.py +++ b/selfdrive/car/hyundai/carstate.py @@ -86,7 +86,7 @@ class CarState(CarStateBase): 50, cp.vl["CGW1"]["CF_Gway_TurnSigLh"], cp.vl["CGW1"]["CF_Gway_TurnSigRh"]) ret.steeringTorque = cp.vl["MDPS12"]["CR_Mdps_StrColTq"] ret.steeringTorqueEps = cp.vl["MDPS12"]["CR_Mdps_OutTq"] - ret.steeringPressed = abs(ret.steeringTorque) > self.params.STEER_THRESHOLD + ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > self.params.STEER_THRESHOLD, 5) ret.steerFaultTemporary = cp.vl["MDPS12"]["CF_Mdps_ToiUnavail"] != 0 or cp.vl["MDPS12"]["CF_Mdps_ToiFlt"] != 0 # cruise state diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 76f2ef06ba..157d146ade 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -d9432dcfb875a76be051137969e371c9926611e9 +ff0348b1957c8f992125df77a36b3c0553a26e6a From 2d403cbf9c14ed51e1e6d65bd33330a5775dec66 Mon Sep 17 00:00:00 2001 From: Niels Ole Salscheider Date: Thu, 12 Jan 2023 05:33:45 +0100 Subject: [PATCH 060/484] VW MQB: Add FW for 2017 Seat Leon (#26883) * VW MQB: Add FW for 2017 Seat Leon * missing srs Co-authored-by: Comma Device Co-authored-by: Shane Smiskol --- selfdrive/car/volkswagen/values.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py index af73683e22..f627e517be 100755 --- a/selfdrive/car/volkswagen/values.py +++ b/selfdrive/car/volkswagen/values.py @@ -937,19 +937,23 @@ FW_VERSIONS = { b'\xf1\x8704L906026BP\xf1\x891198', b'\xf1\x8704L906026BP\xf1\x897608', b'\xf1\x8705E906018AS\xf1\x899596', + b'\xf1\x878V0906264H \xf1\x890005', ], (Ecu.transmission, 0x7e1, None): [ + b'\xf1\x870CW300041G \xf1\x891003', b'\xf1\x870CW300050J \xf1\x891908', b'\xf1\x870D9300042M \xf1\x895016', ], (Ecu.srs, 0x715, None): [ b'\xf1\x873Q0959655AC\xf1\x890189\xf1\x82\r11110011110011021511110200', b'\xf1\x873Q0959655AS\xf1\x890200\xf1\x82\r12110012120012021612110200', + b'\xf1\x873Q0959655BH\xf1\x890703\xf1\x82\x0e1312001313001305171311052900', b'\xf1\x873Q0959655CM\xf1\x890720\xf1\x82\0161312001313001305171311052900', ], (Ecu.eps, 0x712, None): [ b'\xf1\x875Q0909144AB\xf1\x891082\xf1\x82\00521N01342A1', b'\xf1\x875Q0909144P \xf1\x891043\xf1\x82\00511N01805A0', + b'\xf1\x875Q0909144T \xf1\x891072\xf1\x82\x0521N01309A1', b'\xf1\x875Q0909144T \xf1\x891072\xf1\x82\00521N05808A1', ], (Ecu.fwdRadar, 0x757, None): [ From 69f8ac0b6508559549e99c5f12b7d9105ce1baa8 Mon Sep 17 00:00:00 2001 From: Jason Wen <47793918+sunnyhaibin@users.noreply.github.com> Date: Thu, 12 Jan 2023 00:17:58 -0500 Subject: [PATCH 061/484] HKG: Car Port for Kia Sorento 2022 (#26874) * HKG: Car Port for Kia Sorento 2022 * Harness K * SCC is on bus 4 * Add test route * seems reasonable * more interesting segment Co-authored-by: Shane Smiskol --- RELEASES.md | 1 + docs/CARS.md | 3 ++- selfdrive/car/hyundai/interface.py | 9 ++++++--- selfdrive/car/hyundai/values.py | 17 ++++++++++++++--- selfdrive/car/tests/routes.py | 1 + selfdrive/car/torque_data/override.yaml | 1 + 6 files changed, 25 insertions(+), 7 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index eea69d295f..1b5ef8f7a7 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -6,6 +6,7 @@ Version 0.9.1 (2022-12-XX) * Chevrolet Bolt EV 2022-23 support thanks to JasonJShuler! * Genesis GV60 2023 support thanks to sunnyhaibin! * Hyundai Tucson 2022-23 support +* Kia Sorento 2022-23 support thanks to sunnyhaibin! * Kia Sorento Plug-in Hybrid 2022 support thanks to sunnyhaibin! Version 0.9.0 (2022-11-21) diff --git a/docs/CARS.md b/docs/CARS.md index 930b52c8a2..ac374501dc 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,7 +4,7 @@ A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system. -# 222 Supported Cars +# 223 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:| @@ -110,6 +110,7 @@ A supported vehicle is one that just works when you install a comma three. All s |Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A| |Kia|Sorento 2018|Advanced Smart Cruise Control|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C| |Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E| +|Kia|Sorento 2022-23[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K| |Kia|Sorento Plug-in Hybrid 2022-23[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A| |Kia|Sportage 2023[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N| |Kia|Sportage Hybrid 2023[5](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N| diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py index 6881be5a33..6d6d9833df 100644 --- a/selfdrive/car/hyundai/interface.py +++ b/selfdrive/car/hyundai/interface.py @@ -185,10 +185,13 @@ class CarInterface(CarInterfaceBase): ret.mass = 1767. + STD_CARGO_KG # SX Prestige trim support only ret.wheelbase = 2.756 ret.steerRatio = 13.6 - elif candidate == CAR.KIA_SORENTO_PHEV_4TH_GEN: - ret.mass = 4095.8 * CV.LB_TO_KG + STD_CARGO_KG # weight from EX and above trims, average of FWD and AWD versions (EX, X-Line EX AWD, SX, SX Pestige, X-Line SX Prestige AWD) + elif candidate in (CAR.KIA_SORENTO_4TH_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN): ret.wheelbase = 2.81 - ret.steerRatio = 13.27 # steering ratio according to Kia News https://www.kiamedia.com/us/en/models/sorento-phev/2022/specifications + ret.steerRatio = 13.5 # average of the platforms + if candidate == CAR.KIA_SORENTO_4TH_GEN: + ret.mass = 3957 * CV.LB_TO_KG + STD_CARGO_KG + else: + ret.mass = 4537 * CV.LB_TO_KG + STD_CARGO_KG # Genesis elif candidate == CAR.GENESIS_GV60_EV_1ST_GEN: diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 1a81e1b167..229ca992d5 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -105,6 +105,7 @@ class CAR: KIA_SELTOS = "KIA SELTOS 2021" KIA_SPORTAGE_5TH_GEN = "KIA SPORTAGE 5TH GEN" KIA_SORENTO = "KIA SORENTO GT LINE 2018" + KIA_SORENTO_4TH_GEN = "KIA SORENTO 4TH GEN" KIA_SORENTO_PHEV_4TH_GEN = "KIA SORENTO PLUG-IN HYBRID 4TH GEN" KIA_SPORTAGE_HYBRID_5TH_GEN = "KIA SPORTAGE HYBRID 5TH GEN" KIA_STINGER = "KIA STINGER GT2 2018" @@ -211,7 +212,8 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { HyundaiCarInfo("Kia Sorento 2018", "Advanced Smart Cruise Control", "https://www.youtube.com/watch?v=Fkh3s6WHJz8", harness=Harness.hyundai_c), HyundaiCarInfo("Kia Sorento 2019", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", harness=Harness.hyundai_e), ], - CAR.KIA_SORENTO_PHEV_4TH_GEN: HyundaiCarInfo("Kia Sorento Plug-in Hybrid 2022-23", "Smart Cruise Control (SCC)", harness=Harness.hyundai_a), + CAR.KIA_SORENTO_4TH_GEN: HyundaiCarInfo("Kia Sorento 2022-23", harness=Harness.hyundai_k), + CAR.KIA_SORENTO_PHEV_4TH_GEN: HyundaiCarInfo("Kia Sorento Plug-in Hybrid 2022-23", harness=Harness.hyundai_a), CAR.KIA_SPORTAGE_HYBRID_5TH_GEN: HyundaiCarInfo("Kia Sportage Hybrid 2023", harness=Harness.hyundai_n), CAR.KIA_STINGER: HyundaiCarInfo("Kia Stinger 2018-20", video_link="https://www.youtube.com/watch?v=MJ94qoofYw0", harness=Harness.hyundai_c), CAR.KIA_STINGER_2022: HyundaiCarInfo("Kia Stinger 2022", "All", harness=Harness.hyundai_k), @@ -1543,6 +1545,14 @@ FW_VERSIONS = { b'\xf1\x00JW1_ RDR ----- 1.00 1.00 99110-CU000 ', ], }, + CAR.KIA_SORENTO_4TH_GEN: { + (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00MQ4 MFC AT USA LHD 1.00 1.05 99210-R5000 210623', + ], + (Ecu.fwdRadar, 0x7d0, None): [ + b'\xf1\x00MQ4_ SCC FHCUP 1.00 1.06 99110-P2000 ', + ], + }, } CHECKSUM = { @@ -1560,10 +1570,10 @@ FEATURES = { "use_fca": {CAR.SONATA, CAR.SONATA_HYBRID, CAR.ELANTRA, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.KIA_STINGER, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_FORTE, CAR.KIA_NIRO_EV, CAR.PALISADE, CAR.GENESIS_G70, CAR.GENESIS_G70_2020, CAR.KONA, CAR.SANTA_FE, CAR.KIA_SELTOS, CAR.KONA_HEV, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.TUCSON, CAR.KONA_EV_2022, CAR.KIA_STINGER_2022}, } -CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.TUCSON_4TH_GEN, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, CAR.SANTA_CRUZ_1ST_GEN, CAR.KIA_SPORTAGE_5TH_GEN, CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.GENESIS_GV60_EV_1ST_GEN} +CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.TUCSON_4TH_GEN, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, CAR.SANTA_CRUZ_1ST_GEN, CAR.KIA_SPORTAGE_5TH_GEN, CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.GENESIS_GV60_EV_1ST_GEN, CAR.KIA_SORENTO_4TH_GEN} # The radar does SCC on these cars when HDA I, rather than the camera -CANFD_RADAR_SCC_CAR = {CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN} +CANFD_RADAR_SCC_CAR = {CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.KIA_SORENTO_4TH_GEN} # The camera does SCC on these cars, rather than the radar CAMERA_SCC_CAR = {CAR.KONA_EV_2022, } @@ -1628,4 +1638,5 @@ DBC = { CAR.GENESIS_GV70_1ST_GEN: dbc_dict('hyundai_canfd', None), CAR.KIA_SORENTO_PHEV_4TH_GEN: dbc_dict('hyundai_canfd', None), CAR.GENESIS_GV60_EV_1ST_GEN: dbc_dict('hyundai_canfd', None), + CAR.KIA_SORENTO_4TH_GEN: dbc_dict('hyundai_canfd', None), } diff --git a/selfdrive/car/tests/routes.py b/selfdrive/car/tests/routes.py index 3edc406e19..ac0521cc68 100644 --- a/selfdrive/car/tests/routes.py +++ b/selfdrive/car/tests/routes.py @@ -100,6 +100,7 @@ routes = [ CarTestRoute("db68bbe12250812c|2022-12-05--00-54-12", HYUNDAI.TUCSON_4TH_GEN), # 2023 CarTestRoute("36e10531feea61a4|2022-07-25--13-37-42", HYUNDAI.TUCSON_HYBRID_4TH_GEN), CarTestRoute("5875672fc1d4bf57|2020-07-23--21-33-28", HYUNDAI.KIA_SORENTO), + CarTestRoute("1d0d000db3370fd0|2023-01-04--22-28-42", HYUNDAI.KIA_SORENTO_4TH_GEN, segment=5), CarTestRoute("628935d7d3e5f4f7|2022-11-30--01-12-46", HYUNDAI.KIA_SORENTO_PHEV_4TH_GEN), CarTestRoute("9c917ba0d42ffe78|2020-04-17--12-43-19", HYUNDAI.PALISADE), CarTestRoute("05a8f0197fdac372|2022-10-19--14-14-09", HYUNDAI.IONIQ_5), # HDA2 diff --git a/selfdrive/car/torque_data/override.yaml b/selfdrive/car/torque_data/override.yaml index d7b2ec4079..3a9f92c046 100644 --- a/selfdrive/car/torque_data/override.yaml +++ b/selfdrive/car/torque_data/override.yaml @@ -36,6 +36,7 @@ KIA SPORTAGE HYBRID 5TH GEN: [2.5, 2.5, 0.1] GENESIS GV70 1ST GEN: [2.42, 2.42, 0.1] KIA SORENTO PLUG-IN HYBRID 4TH GEN: [2.5, 2.5, 0.1] GENESIS GV60 ELECTRIC 1ST GEN: [2.5, 2.5, 0.1] +KIA SORENTO 4TH GEN: [2.5, 2.5, 0.1] # Dashcam or fallback configured as ideal car mock: [10.0, 10, 0.0] From f3fa4d759d5967e8590492005b8ede8bd17a6981 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 12 Jan 2023 13:21:11 +0800 Subject: [PATCH 062/484] cabana: fix find similar bits (#26918) * dynamic find bit * change * Revert "change" This reverts commit 12cf513e2725a52ee40b8999521adcd6ecb00221. * change sorting and headers * make perc a float Co-authored-by: Shane Smiskol --- tools/cabana/tools/findsimilarbits.cc | 44 ++++++++++++++++++++------- tools/cabana/tools/findsimilarbits.h | 9 ++++-- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/tools/cabana/tools/findsimilarbits.cc b/tools/cabana/tools/findsimilarbits.cc index 5fa5bcf7b8..cb5dbc0845 100644 --- a/tools/cabana/tools/findsimilarbits.cc +++ b/tools/cabana/tools/findsimilarbits.cc @@ -25,14 +25,28 @@ FindSimilarBitsDlg::FindSimilarBitsDlg(QWidget *parent) : QDialog(parent) { } bus_combo->model()->sort(0); bus_combo->setCurrentIndex(0); + + msg_cb = new QComboBox(this); + for (auto &[address, msg] : dbc()->messages()) { + msg_cb->addItem(msg.name, address); + } + msg_cb->model()->sort(0); + msg_cb->setCurrentIndex(0); + + byte_idx_sb = new QSpinBox(this); + byte_idx_sb->setRange(0, 63); + + bit_idx_sb = new QSpinBox(this); + bit_idx_sb->setRange(0, 7); + form_layout->addWidget(new QLabel("Bus")); form_layout->addWidget(bus_combo); + form_layout->addWidget(msg_cb); + form_layout->addWidget(new QLabel("Byte Index")); + form_layout->addWidget(byte_idx_sb); + form_layout->addWidget(new QLabel("Bit Index")); + form_layout->addWidget(bit_idx_sb); - bit_combo = new QComboBox(this); - bit_combo->addItems({"0", "1"}); - bit_combo->setCurrentIndex(1); - form_layout->addWidget(new QLabel("Bit")); - form_layout->addWidget(bit_combo); min_msgs = new QLineEdit(this); min_msgs->setValidator(new QIntValidator(this)); @@ -56,10 +70,11 @@ FindSimilarBitsDlg::FindSimilarBitsDlg(QWidget *parent) : QDialog(parent) { void FindSimilarBitsDlg::find() { search_btn->setEnabled(false); table->clear(); - auto msg_mismatched = calcBits(bus_combo->currentText().toUInt(), bit_combo->currentIndex(), min_msgs->text().toInt()); + uint32_t selected_address = msg_cb->currentData().toUInt(); + auto msg_mismatched = calcBits(bus_combo->currentText().toUInt(), selected_address, byte_idx_sb->value(), bit_idx_sb->value(), min_msgs->text().toInt()); table->setRowCount(msg_mismatched.size()); table->setColumnCount(6); - table->setHorizontalHeaderLabels({"address", "byte idx", "bit idx", "mismatches", "total", "perc%"}); + table->setHorizontalHeaderLabels({"address", "byte idx", "bit idx", "mismatches", "total msgs", "% mismatched"}); for (int i = 0; i < msg_mismatched.size(); ++i) { auto &m = msg_mismatched[i]; table->setItem(i, 0, new QTableWidgetItem(QString("%1").arg(m.address, 1, 16))); @@ -72,18 +87,25 @@ void FindSimilarBitsDlg::find() { search_btn->setEnabled(true); } -QList FindSimilarBitsDlg::calcBits(uint8_t bus, int bit_to_find, int min_msgs_cnt) { +QList FindSimilarBitsDlg::calcBits(uint8_t bus, uint32_t selected_address, int byte_idx, int bit_idx, int min_msgs_cnt) { QHash> mismatches; QHash msg_count; auto events = can->events(); + qDebug() << bus << selected_address << byte_idx << bit_idx; + int bit_to_find = -1; for (auto e : *events) { if (e->which == cereal::Event::Which::CAN) { for (const auto &c : e->event.getCan()) { if (c.getSrc() == bus) { + const auto dat = c.getDat(); uint32_t address = c.getAddress(); + if (address == selected_address && dat.size() > byte_idx) { + bit_to_find = ((dat[byte_idx] >> (7 - bit_idx)) & 1) != 0; + } ++msg_count[address]; + if (bit_to_find == -1) continue; + auto &mismatched = mismatches[address]; - const auto dat = c.getDat(); if (mismatched.size() < dat.size() * 8) { mismatched.resize(dat.size() * 8); } @@ -104,12 +126,12 @@ QList FindSimilarBitsDlg::calcBits(uint8_ if (auto cnt = msg_count[it.key()]; cnt > min_msgs_cnt) { auto &mismatched = it.value(); for (int i = 0; i < mismatched.size(); ++i) { - if (uint32_t perc = (mismatched[i] / (double)cnt) * 100; perc < 50) { + if (float perc = (mismatched[i] / (double)cnt) * 100; perc < 50) { result.push_back({it.key(), (uint32_t)i / 8, (uint32_t)i % 8, mismatched[i], cnt, perc}); } } } } - std::sort(result.begin(), result.end(), [](auto &l, auto &r) { return l.perc > r.perc; }); + std::sort(result.begin(), result.end(), [](auto &l, auto &r) { return l.perc < r.perc; }); return result; } diff --git a/tools/cabana/tools/findsimilarbits.h b/tools/cabana/tools/findsimilarbits.h index 79db4a1c69..8083999909 100644 --- a/tools/cabana/tools/findsimilarbits.h +++ b/tools/cabana/tools/findsimilarbits.h @@ -3,6 +3,7 @@ #include #include #include +#include #include class FindSimilarBitsDlg : public QDialog { @@ -11,13 +12,15 @@ public: private: struct mismatched_struct { - uint32_t address, byte_idx, bit_idx, mismatches, total, perc; + uint32_t address, byte_idx, bit_idx, mismatches, total; + float perc; }; - QList calcBits(uint8_t bus, int bit_to_find, int min_msgs_cnt); + QList calcBits(uint8_t bus, uint32_t selected_address, int byte_idx, int bit_idx, int min_msgs_cnt); void find(); QTableWidget *table; - QComboBox *bus_combo, *bit_combo; + QComboBox *bus_combo, *msg_cb; + QSpinBox *byte_idx_sb, *bit_idx_sb; QPushButton *search_btn; QLineEdit *min_msgs; }; From a0f55f72fcc119f7a2ff902c1e347b3779c2c318 Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Wed, 11 Jan 2023 22:31:25 -0800 Subject: [PATCH 063/484] increase nav default marker --- selfdrive/assets/navigation/default_marker.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/assets/navigation/default_marker.svg b/selfdrive/assets/navigation/default_marker.svg index 116a45e251..43d5290a96 100644 --- a/selfdrive/assets/navigation/default_marker.svg +++ b/selfdrive/assets/navigation/default_marker.svg @@ -1,5 +1,5 @@ - + From de6f9010fa6e3daf87c7cbe855af98b73fdc6d56 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 11 Jan 2023 23:30:11 -0800 Subject: [PATCH 064/484] Car docs: show video link (#26907) * add videos to gh docs * fix * add icon * fix * try this * white inner triangle * width * try height * use video_icon in template * clean up * rename * smaller --- docs/CARS.md | 450 +++++++++++++++--------------- docs/assets/icon-youtube.svg | 12 + selfdrive/car/CARS_template.md | 3 +- selfdrive/car/docs_definitions.py | 6 +- 4 files changed, 244 insertions(+), 227 deletions(-) create mode 100644 docs/assets/icon-youtube.svg diff --git a/docs/CARS.md b/docs/CARS.md index ac374501dc..1b76069f6a 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -6,231 +6,231 @@ A supported vehicle is one that just works when you install a comma three. All s # 223 Supported Cars -|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness| -|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:| -|Acura|ILX 2016-19|AcuraWatch Plus|openpilot|25 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec| -|Acura|RDX 2016-18|AcuraWatch Plus|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec| -|Acura|RDX 2019-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A| -|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Cadillac|Escalade ESV 2016[3](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II| -|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM| -|Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM| -|Chevrolet|Silverado 1500 2020-21|Safety Package II|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM| -|Chevrolet|Volt 2017-18[3](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II| -|Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA| -|Chrysler|Pacifica 2019-20|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA| -|Chrysler|Pacifica 2021|All|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA| -|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA| -|Chrysler|Pacifica Hybrid 2019-22|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA| -|comma|body|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|None| -|Genesis|G70 2018-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F| -|Genesis|G70 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F| -|Genesis|G80 2017-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H| -|Genesis|G90 2017-18|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C| -|Genesis|GV60 2023[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K| -|Genesis|GV70 2022-23[5](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L| -|GMC|Acadia 2018[3](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II| -|GMC|Sierra 1500 2020-21|Driver Alert Package II|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM| -|Honda|Accord 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A| -|Honda|Accord Hybrid 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A| -|Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec| -|Honda|Civic 2019-21|All|openpilot available[1](#footnotes)|0 mph|2 mph[4](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A| -|Honda|Civic 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch B| -|Honda|Civic Hatchback 2017-21|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A| -|Honda|Civic Hatchback 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch B| -|Honda|CR-V 2015-16|Touring Trim|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec| -|Honda|CR-V 2017-22|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A| -|Honda|CR-V Hybrid 2017-19|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A| -|Honda|e 2020|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A| -|Honda|Fit 2018-20|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec| -|Honda|Freed 2020|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec| -|Honda|HR-V 2019-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec| -|Honda|Insight 2019-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A| -|Honda|Inspire 2018|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A| -|Honda|Odyssey 2018-20|Honda Sensing|openpilot|25 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec| -|Honda|Passport 2019-21|All|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec| -|Honda|Pilot 2016-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec| -|Honda|Ridgeline 2017-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec| -|Hyundai|Elantra 2017-19|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai B| -|Hyundai|Elantra 2021-22|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K| -|Hyundai|Elantra GT 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E| -|Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K| -|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|Stock|19 mph|37 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai J| -|Hyundai|i30 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E| -|Hyundai|Ioniq 5 (Southeast Asia only) 2022-23[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai Q| -|Hyundai|Ioniq 5 (with HDA II) 2022-23[5](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai Q| -|Hyundai|Ioniq 5 (without HDA II) 2022-23[5](#footnotes)|Highway Driving Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K| -|Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C| -|Hyundai|Ioniq Electric 2020|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H| -|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C| -|Hyundai|Ioniq Hybrid 2020-22|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H| -|Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C| -|Hyundai|Ioniq Plug-in Hybrid 2020-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H| -|Hyundai|Kona 2020|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai B| -|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai G| -|Hyundai|Kona Electric 2022|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai O| -|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai I| -|Hyundai|Palisade 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H| -|Hyundai|Santa Cruz 2021-22[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N| -|Hyundai|Santa Fe 2019-20|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai D| -|Hyundai|Santa Fe 2021-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L| -|Hyundai|Santa Fe Hybrid 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L| -|Hyundai|Santa Fe Plug-in Hybrid 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L| -|Hyundai|Sonata 2018-19|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E| -|Hyundai|Sonata 2020-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A| -|Hyundai|Sonata Hybrid 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A| -|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L| -|Hyundai|Tucson 2022[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N| -|Hyundai|Tucson 2023[5](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N| -|Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L| -|Hyundai|Tucson Hybrid 2022[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N| -|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E| -|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA| -|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA| -|Kia|Ceed 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E| -|Kia|EV6 (Southeast Asia only) 2022-23[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai P| -|Kia|EV6 (with HDA II) 2022[5](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai P| -|Kia|EV6 (without HDA II) 2022[5](#footnotes)|Highway Driving Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L| -|Kia|Forte 2019-21|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai G| -|Kia|K5 2021-22|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A| -|Kia|Niro EV 2019|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H| -|Kia|Niro EV 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F| -|Kia|Niro EV 2021|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C| -|Kia|Niro EV 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H| -|Kia|Niro Hybrid 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F| -|Kia|Niro Hybrid 2022|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H| -|Kia|Niro Plug-in Hybrid 2018-19|All|openpilot available[1](#footnotes)|10 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C| -|Kia|Optima 2017|Advanced Smart Cruise Control|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai B| -|Kia|Optima 2019-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai G| -|Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A| -|Kia|Sorento 2018|Advanced Smart Cruise Control|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C| -|Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E| -|Kia|Sorento 2022-23[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K| -|Kia|Sorento Plug-in Hybrid 2022-23[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A| -|Kia|Sportage 2023[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N| -|Kia|Sportage Hybrid 2023[5](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N| -|Kia|Stinger 2018-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C| -|Kia|Stinger 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K| -|Kia|Telluride 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H| -|Lexus|CT Hybrid 2017-18|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| -|Lexus|ES 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Lexus|ES Hybrid 2017-18|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Lexus|ES Hybrid 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Lexus|IS 2017-19|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| -|Lexus|NX 2018-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| -|Lexus|NX 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Lexus|NX Hybrid 2018-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| -|Lexus|NX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Lexus|RC 2017-20|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| -|Lexus|RX 2016|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| -|Lexus|RX 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| -|Lexus|RX 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Lexus|RX Hybrid 2016|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| -|Lexus|RX Hybrid 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| -|Lexus|RX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Lexus|UX Hybrid 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Mazda|CX-5 2022-23|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Mazda| -|Mazda|CX-9 2021-23|All|Stock|0 mph|28 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Mazda| -|Nissan|Altima 2019-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Nissan B| -|Nissan|Leaf 2018-22|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Nissan A| -|Nissan|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Nissan A| -|Nissan|X-Trail 2017|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Nissan A| -|Ram|1500 2019-22|Adaptive Cruise Control (ACC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Ram| -|SEAT|Ateca 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Subaru|Ascent 2019-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A| -|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A| -|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A| -|Subaru|Forester 2019-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A| -|Subaru|Impreza 2017-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A| -|Subaru|Impreza 2020-22|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A| -|Subaru|Legacy 2020-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru B| -|Subaru|Outback 2020-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru B| -|Subaru|XV 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A| -|Subaru|XV 2020-21|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A| -|Škoda|Kamiq 2021[7](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[10](#footnotes)| -|Škoda|Karoq 2019-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Škoda|Kodiaq 2018-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Škoda|Octavia 2015, 2018-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Škoda|Octavia RS 2016|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Škoda|Scala 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[10](#footnotes)| -|Škoda|Superb 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Toyota|Avalon 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| -|Toyota|Avalon 2017-18|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| -|Toyota|Avalon 2019-21|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| -|Toyota|Avalon 2022|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Toyota|Avalon Hybrid 2019-21|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| -|Toyota|Avalon Hybrid 2022|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Toyota|C-HR 2017-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| -|Toyota|C-HR Hybrid 2017-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| -|Toyota|Camry 2018-20|All|Stock|0 mph[6](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| -|Toyota|Camry 2021-22|All|openpilot|0 mph[6](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| -|Toyota|Camry Hybrid 2021-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Toyota|Corolla 2017-19|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| -|Toyota|Corolla 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Toyota|Corolla Cross (Non-US only) 2020-23|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Toyota|Corolla Cross Hybrid (Non-US only) 2020-22|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Toyota|Corolla Hatchback 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Toyota|Corolla Hybrid 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Toyota|Highlander 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Toyota|Highlander 2020-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Toyota|Highlander Hybrid 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Toyota|Highlander Hybrid 2020-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Toyota|Mirai 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Toyota|Prius 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| -|Toyota|Prius 2017-20|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| -|Toyota|Prius 2021-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Toyota|Prius Prime 2017-20|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| -|Toyota|Prius Prime 2021-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Toyota|Prius v 2017|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Toyota|RAV4 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| -|Toyota|RAV4 2017-18|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| -|Toyota|RAV4 2019-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Toyota|RAV4 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| -|Toyota|RAV4 Hybrid 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Toyota|RAV4 Hybrid 2017-18|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Toyota|RAV4 Hybrid 2019-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Toyota|RAV4 Hybrid 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| -|Toyota|Sienna 2018-20|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| -|Volkswagen|Arteon 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|Arteon eHybrid 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|Arteon R 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|Atlas Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|California 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|Jetta 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|Jetta GLI 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|Passat 2015-22[8](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|Passat Alltrack 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|Passat GTE 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|Polo 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[10](#footnotes)| -|Volkswagen|Polo GTI 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[10](#footnotes)| -|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[10](#footnotes)| -|Volkswagen|T-Roc 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[10](#footnotes)| -|Volkswagen|Taos 2022|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|Tiguan 2019-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| -|Volkswagen|Touran 2017|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| +|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness|Video| +|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +|Acura|ILX 2016-19|AcuraWatch Plus|openpilot|25 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| +|Acura|RDX 2016-18|AcuraWatch Plus|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| +|Acura|RDX 2019-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| +|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Cadillac|Escalade ESV 2016[3](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|| +|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|| +|Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|| +|Chevrolet|Silverado 1500 2020-21|Safety Package II|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|| +|Chevrolet|Volt 2017-18[3](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|| +|Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|| +|Chrysler|Pacifica 2019-20|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|| +|Chrysler|Pacifica 2021|All|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|| +|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|| +|Chrysler|Pacifica Hybrid 2019-22|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|| +|comma|body|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|None|| +|Genesis|G70 2018-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F|| +|Genesis|G70 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F|| +|Genesis|G80 2017-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| +|Genesis|G90 2017-18|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| +|Genesis|GV60 2023[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| +|Genesis|GV70 2022-23[5](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| +|GMC|Acadia 2018[3](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|| +|GMC|Sierra 1500 2020-21|Driver Alert Package II|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|| +|Honda|Accord 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| +|Honda|Accord Hybrid 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| +|Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| +|Honda|Civic 2019-21|All|openpilot available[1](#footnotes)|0 mph|2 mph[4](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| +|Honda|Civic 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch B|| +|Honda|Civic Hatchback 2017-21|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| +|Honda|Civic Hatchback 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch B|| +|Honda|CR-V 2015-16|Touring Trim|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| +|Honda|CR-V 2017-22|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| +|Honda|CR-V Hybrid 2017-19|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| +|Honda|e 2020|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| +|Honda|Fit 2018-20|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| +|Honda|Freed 2020|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| +|Honda|HR-V 2019-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| +|Honda|Insight 2019-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| +|Honda|Inspire 2018|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| +|Honda|Odyssey 2018-20|Honda Sensing|openpilot|25 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| +|Honda|Passport 2019-21|All|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| +|Honda|Pilot 2016-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| +|Honda|Ridgeline 2017-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| +|Hyundai|Elantra 2017-19|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai B|| +|Hyundai|Elantra 2021-22|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| +|Hyundai|Elantra GT 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|| +|Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| +|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|Stock|19 mph|37 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai J|| +|Hyundai|i30 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|| +|Hyundai|Ioniq 5 (Southeast Asia only) 2022-23[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai Q|| +|Hyundai|Ioniq 5 (with HDA II) 2022-23[5](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai Q|| +|Hyundai|Ioniq 5 (without HDA II) 2022-23[5](#footnotes)|Highway Driving Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| +|Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| +|Hyundai|Ioniq Electric 2020|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| +|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| +|Hyundai|Ioniq Hybrid 2020-22|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| +|Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| +|Hyundai|Ioniq Plug-in Hybrid 2020-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| +|Hyundai|Kona 2020|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai B|| +|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai G|| +|Hyundai|Kona Electric 2022|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai O|| +|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai I|| +|Hyundai|Palisade 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| +|Hyundai|Santa Cruz 2021-22[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|| +|Hyundai|Santa Fe 2019-20|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai D|| +|Hyundai|Santa Fe 2021-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| +|Hyundai|Santa Fe Hybrid 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| +|Hyundai|Santa Fe Plug-in Hybrid 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| +|Hyundai|Sonata 2018-19|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|| +|Hyundai|Sonata 2020-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|| +|Hyundai|Sonata Hybrid 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|| +|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| +|Hyundai|Tucson 2022[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|| +|Hyundai|Tucson 2023[5](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|| +|Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| +|Hyundai|Tucson Hybrid 2022[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|| +|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|| +|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|| +|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|| +|Kia|Ceed 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|| +|Kia|EV6 (Southeast Asia only) 2022-23[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai P|| +|Kia|EV6 (with HDA II) 2022[5](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai P|| +|Kia|EV6 (without HDA II) 2022[5](#footnotes)|Highway Driving Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| +|Kia|Forte 2019-21|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai G|| +|Kia|K5 2021-22|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|| +|Kia|Niro EV 2019|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| +|Kia|Niro EV 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F|| +|Kia|Niro EV 2021|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| +|Kia|Niro EV 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| +|Kia|Niro Hybrid 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F|| +|Kia|Niro Hybrid 2022|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| +|Kia|Niro Plug-in Hybrid 2018-19|All|openpilot available[1](#footnotes)|10 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| +|Kia|Optima 2017|Advanced Smart Cruise Control|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai B|| +|Kia|Optima 2019-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai G|| +|Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|| +|Kia|Sorento 2018|Advanced Smart Cruise Control|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| +|Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|| +|Kia|Sorento 2022-23[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| +|Kia|Sorento Plug-in Hybrid 2022-23[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|| +|Kia|Sportage 2023[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|| +|Kia|Sportage Hybrid 2023[5](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|| +|Kia|Stinger 2018-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| +|Kia|Stinger 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| +|Kia|Telluride 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| +|Lexus|CT Hybrid 2017-18|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Lexus|ES 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Lexus|ES Hybrid 2017-18|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Lexus|ES Hybrid 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Lexus|IS 2017-19|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Lexus|NX 2018-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Lexus|NX 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Lexus|NX Hybrid 2018-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Lexus|NX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Lexus|RC 2017-20|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Lexus|RX 2016|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Lexus|RX 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Lexus|RX 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Lexus|RX Hybrid 2016|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Lexus|RX Hybrid 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Lexus|RX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Lexus|UX Hybrid 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Mazda|CX-5 2022-23|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Mazda|| +|Mazda|CX-9 2021-23|All|Stock|0 mph|28 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Mazda|| +|Nissan|Altima 2019-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Nissan B|| +|Nissan|Leaf 2018-22|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Nissan A|| +|Nissan|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Nissan A|| +|Nissan|X-Trail 2017|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Nissan A|| +|Ram|1500 2019-22|Adaptive Cruise Control (ACC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Ram|| +|SEAT|Ateca 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Subaru|Ascent 2019-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|| +|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|| +|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|| +|Subaru|Forester 2019-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|| +|Subaru|Impreza 2017-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|| +|Subaru|Impreza 2020-22|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|| +|Subaru|Legacy 2020-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru B|| +|Subaru|Outback 2020-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru B|| +|Subaru|XV 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|| +|Subaru|XV 2020-21|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|| +|Škoda|Kamiq 2021[7](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[10](#footnotes)|| +|Škoda|Karoq 2019-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Škoda|Kodiaq 2018-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Škoda|Octavia 2015, 2018-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Škoda|Octavia RS 2016|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Škoda|Scala 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[10](#footnotes)|| +|Škoda|Superb 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Avalon 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|Avalon 2017-18|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|Avalon 2019-21|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|Avalon 2022|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Avalon Hybrid 2019-21|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|Avalon Hybrid 2022|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|C-HR 2017-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|C-HR Hybrid 2017-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|Camry 2018-20|All|Stock|0 mph[6](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|Camry 2021-22|All|openpilot|0 mph[6](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|Camry Hybrid 2021-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Corolla 2017-19|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|Corolla 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Corolla Cross (Non-US only) 2020-23|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Corolla Cross Hybrid (Non-US only) 2020-22|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Corolla Hatchback 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Corolla Hybrid 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Highlander 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Highlander 2020-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Highlander Hybrid 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Highlander Hybrid 2020-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Mirai 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Prius 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|Prius 2017-20|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|Prius 2021-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Prius Prime 2017-20|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|Prius Prime 2021-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Prius v 2017|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|RAV4 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|RAV4 2017-18|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|RAV4 2019-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|RAV4 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|RAV4 Hybrid 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|RAV4 Hybrid 2017-18|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|RAV4 Hybrid 2019-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|RAV4 Hybrid 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|Sienna 2018-20|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Volkswagen|Arteon 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Arteon eHybrid 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Arteon R 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Atlas Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|California 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Jetta 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Jetta GLI 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Passat 2015-22[8](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Passat Alltrack 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Passat GTE 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Polo 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[10](#footnotes)|| +|Volkswagen|Polo GTI 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[10](#footnotes)|| +|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[10](#footnotes)|| +|Volkswagen|T-Roc 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[10](#footnotes)|| +|Volkswagen|Taos 2022|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Tiguan 2019-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Touran 2017|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| 1Experimental openpilot longitudinal control is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `master-ci`.
diff --git a/docs/assets/icon-youtube.svg b/docs/assets/icon-youtube.svg new file mode 100644 index 0000000000..4e2c9fdfa9 --- /dev/null +++ b/docs/assets/icon-youtube.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/selfdrive/car/CARS_template.md b/selfdrive/car/CARS_template.md index 2fce0f9036..1c80ea3d93 100644 --- a/selfdrive/car/CARS_template.md +++ b/selfdrive/car/CARS_template.md @@ -1,5 +1,6 @@ {% set footnote_tag = '[{}](#footnotes)' -%} {% set star_icon = '[![star](assets/icon-star-{}.svg)](##)' -%} +{% set video_icon = '' -%} @@ -12,7 +13,7 @@ A supported vehicle is one that just works when you install a comma three. All s |{{Column | map(attribute='value') | join('|')}}| |---|---|---|{% for _ in range((Column | length) - 3) %}{{':---:|'}}{% endfor +%} {% for car_info in all_car_info %} -|{% for column in Column %}{{car_info.get_column(column, star_icon, footnote_tag)}}|{% endfor %} +|{% for column in Column %}{{car_info.get_column(column, star_icon, video_icon, footnote_tag)}}|{% endfor %} {% endfor %} diff --git a/selfdrive/car/docs_definitions.py b/selfdrive/car/docs_definitions.py index 03e9e721b5..c3063bc47c 100644 --- a/selfdrive/car/docs_definitions.py +++ b/selfdrive/car/docs_definitions.py @@ -21,6 +21,7 @@ class Column(Enum): STEERING_TORQUE = "Steering Torque" AUTO_RESUME = "Resume from stop" HARNESS = "Harness" + VIDEO = "Video" class Star(Enum): @@ -159,6 +160,7 @@ class CarInfo: Column.STEERING_TORQUE: Star.EMPTY, Column.AUTO_RESUME: Star.FULL if CP.autoResumeSng else Star.EMPTY, Column.HARNESS: self.harness.value, + Column.VIDEO: self.video_link if self.video_link is not None else "", # replaced with an image and link from template in get_column } # Set steering torque star from max lateral acceleration @@ -202,12 +204,14 @@ class CarInfo: else: raise Exception(f"This notCar does not have a detail sentence: {CP.carFingerprint}") - def get_column(self, column: Column, star_icon: str, footnote_tag: str) -> str: + def get_column(self, column: Column, star_icon: str, video_icon: str, footnote_tag: str) -> str: item: Union[str, Star] = self.row[column] if isinstance(item, Star): item = star_icon.format(item.value) elif column == Column.MODEL and len(self.years): item += f" {self.years}" + elif column == Column.VIDEO and len(item) > 0: + item = video_icon.format(item) footnotes = get_footnotes(self.footnotes, column) if len(footnotes): From b68dabb689104ef0819a6c881b800076deb866f6 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 11 Jan 2023 23:44:50 -0800 Subject: [PATCH 065/484] Car interface: require fingerprint and FW versions to get params (#26766) * require fingerprint and FW versions * add get_non_essential_params() * comment * all required * classmethod, need to allow subclasses to override _get_params * fix that * fix --- selfdrive/car/docs.py | 2 +- selfdrive/car/interfaces.py | 13 +++++++------ selfdrive/car/tests/test_car_interfaces.py | 2 +- selfdrive/car/tests/test_fw_fingerprint.py | 2 +- selfdrive/controls/lib/tests/test_latcontrol.py | 2 +- selfdrive/controls/lib/tests/test_vehicle_model.py | 2 +- selfdrive/controls/tests/test_state_machine.py | 2 +- selfdrive/debug/cycle_alerts.py | 2 +- selfdrive/debug/internal/test_paramsd.py | 2 +- selfdrive/test/longitudinal_maneuvers/plant.py | 2 +- selfdrive/test/process_replay/process_replay.py | 2 +- 11 files changed, 17 insertions(+), 16 deletions(-) diff --git a/selfdrive/car/docs.py b/selfdrive/car/docs.py index 03313e2ff6..bc03619d0d 100755 --- a/selfdrive/car/docs.py +++ b/selfdrive/car/docs.py @@ -29,7 +29,7 @@ def get_all_car_info() -> List[CarInfo]: all_car_info: List[CarInfo] = [] footnotes = get_all_footnotes() for model, car_info in get_interface_attr("CAR_INFO", combine_brands=True).items(): - CP = interfaces[model][0].get_params(model, fingerprint=gen_empty_fingerprint(), car_fw=[car.CarParams.CarFw(ecu="unknown")]) + CP = interfaces[model][0].get_params(model, fingerprint=gen_empty_fingerprint(), car_fw=[car.CarParams.CarFw(ecu="unknown")], experimental_long=False) if CP.dashcamOnly or car_info is None: continue diff --git a/selfdrive/car/interfaces.py b/selfdrive/car/interfaces.py index ad1fd22b90..7192f5252c 100644 --- a/selfdrive/car/interfaces.py +++ b/selfdrive/car/interfaces.py @@ -88,13 +88,14 @@ class CarInterfaceBase(ABC): return ACCEL_MIN, ACCEL_MAX @classmethod - def get_params(cls, candidate: str, fingerprint: Optional[Dict[int, Dict[int, int]]] = None, car_fw: Optional[List[car.CarParams.CarFw]] = None, experimental_long: bool = False): - if fingerprint is None: - fingerprint = gen_empty_fingerprint() - - if car_fw is None: - car_fw = list() + def get_non_essential_params(cls, candidate: str): + """ + Parameters essential to controlling the car may be incomplete or wrong without FW versions or fingerprints. + """ + return cls.get_params(candidate, gen_empty_fingerprint(), list(), False) + @classmethod + def get_params(cls, candidate: str, fingerprint: Dict[int, Dict[int, int]], car_fw: List[car.CarParams.CarFw], experimental_long: bool): ret = CarInterfaceBase.get_std_params(candidate) ret = cls._get_params(ret, candidate, fingerprint, car_fw, experimental_long) diff --git a/selfdrive/car/tests/test_car_interfaces.py b/selfdrive/car/tests/test_car_interfaces.py index 11e7be7f44..deb0454b9c 100755 --- a/selfdrive/car/tests/test_car_interfaces.py +++ b/selfdrive/car/tests/test_car_interfaces.py @@ -25,7 +25,7 @@ class TestCarInterfaces(unittest.TestCase): car_fw = [] - car_params = CarInterface.get_params(car_name, fingerprints, car_fw) + car_params = CarInterface.get_params(car_name, fingerprints, car_fw, experimental_long=False) car_interface = CarInterface(car_params, CarController, CarState) assert car_params assert car_interface diff --git a/selfdrive/car/tests/test_fw_fingerprint.py b/selfdrive/car/tests/test_fw_fingerprint.py index 2e43103852..e9f23145cd 100755 --- a/selfdrive/car/tests/test_fw_fingerprint.py +++ b/selfdrive/car/tests/test_fw_fingerprint.py @@ -67,7 +67,7 @@ class TestFwFingerprint(unittest.TestCase): blacklisted_addrs = (0x7c4, 0x7d0) # includes A/C ecu and an unknown ecu for car_model, ecus in FW_VERSIONS.items(): with self.subTest(car_model=car_model): - CP = interfaces[car_model][0].get_params(car_model) + CP = interfaces[car_model][0].get_non_essential_params(car_model) if CP.carName == 'subaru': for ecu in ecus.keys(): self.assertNotIn(ecu[1], blacklisted_addrs, f'{car_model}: Blacklisted ecu: (Ecu.{ECU_NAME[ecu[0]]}, {hex(ecu[1])})') diff --git a/selfdrive/controls/lib/tests/test_latcontrol.py b/selfdrive/controls/lib/tests/test_latcontrol.py index f15ab2fa56..993774f719 100755 --- a/selfdrive/controls/lib/tests/test_latcontrol.py +++ b/selfdrive/controls/lib/tests/test_latcontrol.py @@ -20,7 +20,7 @@ class TestLatControl(unittest.TestCase): @parameterized.expand([(HONDA.CIVIC, LatControlPID), (TOYOTA.RAV4, LatControlTorque), (TOYOTA.PRIUS, LatControlINDI), (NISSAN.LEAF, LatControlAngle)]) def test_saturation(self, car_name, controller): CarInterface, CarController, CarState = interfaces[car_name] - CP = CarInterface.get_params(car_name) + CP = CarInterface.get_non_essential_params(car_name) CI = CarInterface(CP, CarController, CarState) VM = VehicleModel(CP) diff --git a/selfdrive/controls/lib/tests/test_vehicle_model.py b/selfdrive/controls/lib/tests/test_vehicle_model.py index 3e08cb0aa0..03d97a7e3f 100755 --- a/selfdrive/controls/lib/tests/test_vehicle_model.py +++ b/selfdrive/controls/lib/tests/test_vehicle_model.py @@ -12,7 +12,7 @@ from selfdrive.controls.lib.vehicle_model import VehicleModel, dyn_ss_sol, creat class TestVehicleModel(unittest.TestCase): def setUp(self): - CP = CarInterface.get_params(CAR.CIVIC) + CP = CarInterface.get_non_essential_params(CAR.CIVIC) self.VM = VehicleModel(CP) def test_round_trip_yaw_rate(self): diff --git a/selfdrive/controls/tests/test_state_machine.py b/selfdrive/controls/tests/test_state_machine.py index 8f263a98e7..d5f468f214 100755 --- a/selfdrive/controls/tests/test_state_machine.py +++ b/selfdrive/controls/tests/test_state_machine.py @@ -31,7 +31,7 @@ class TestStateMachine(unittest.TestCase): def setUp(self): CarInterface, CarController, CarState = interfaces["mock"] - CP = CarInterface.get_params("mock") + CP = CarInterface.get_non_essential_params("mock") CI = CarInterface(CP, CarController, CarState) self.controlsd = Controls(CI=CI) diff --git a/selfdrive/debug/cycle_alerts.py b/selfdrive/debug/cycle_alerts.py index b40c8e304c..71c7b34be5 100755 --- a/selfdrive/debug/cycle_alerts.py +++ b/selfdrive/debug/cycle_alerts.py @@ -51,7 +51,7 @@ def cycle_alerts(duration=200, is_metric=False): cameras = ['roadCameraState', 'wideRoadCameraState', 'driverCameraState'] CS = car.CarState.new_message() - CP = CarInterface.get_params("HONDA CIVIC 2016") + CP = CarInterface.get_non_essential_params("HONDA CIVIC 2016") sm = messaging.SubMaster(['deviceState', 'pandaStates', 'roadCameraState', 'modelV2', 'liveCalibration', 'driverMonitoringState', 'longitudinalPlan', 'lateralPlan', 'liveLocationKalman', 'managerState'] + cameras) diff --git a/selfdrive/debug/internal/test_paramsd.py b/selfdrive/debug/internal/test_paramsd.py index 3d8e422c35..81524496f7 100755 --- a/selfdrive/debug/internal/test_paramsd.py +++ b/selfdrive/debug/internal/test_paramsd.py @@ -20,7 +20,7 @@ T_SIM = 5 * 60 # s DT = 0.01 -CP = CarInterface.get_params(CAR.CIVIC) +CP = CarInterface.get_non_essential_params(CAR.CIVIC) VM = VehicleModel(CP) x, y = 0, 0 # m, m diff --git a/selfdrive/test/longitudinal_maneuvers/plant.py b/selfdrive/test/longitudinal_maneuvers/plant.py index 59cb0e1fe3..bd0556aa07 100755 --- a/selfdrive/test/longitudinal_maneuvers/plant.py +++ b/selfdrive/test/longitudinal_maneuvers/plant.py @@ -49,7 +49,7 @@ class Plant: from selfdrive.car.honda.values import CAR from selfdrive.car.honda.interface import CarInterface - self.planner = LongitudinalPlanner(CarInterface.get_params(CAR.CIVIC), init_v=self.speed) + self.planner = LongitudinalPlanner(CarInterface.get_non_essential_params(CAR.CIVIC), init_v=self.speed) @property def current_time(self): diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index c5465fc025..22ec099285 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -180,7 +180,7 @@ def fingerprint(msgs, fsm, can_sock, fingerprint): def get_car_params(msgs, fsm, can_sock, fingerprint): if fingerprint: CarInterface, _, _ = interfaces[fingerprint] - CP = CarInterface.get_params(fingerprint) + CP = CarInterface.get_non_essential_params(fingerprint) else: can = FakeSocket(wait=False) sendcan = FakeSocket(wait=False) From 97b931a12ae94567062891586496e7a73bd3ebbd Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 13 Jan 2023 03:27:20 +0800 Subject: [PATCH 066/484] cabana: fix chart glitches (#26928) --- tools/cabana/chartswidget.cc | 8 ++++++-- tools/cabana/chartswidget.h | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index cf01533aaa..952ed2d83f 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -47,6 +47,10 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { QApplication::style()->standardPalette().color(QPalette::Background).value(); updateToolBar(); + align_charts_timer = new QTimer(this); + align_charts_timer->setSingleShot(true); + align_charts_timer->callOnTimeout(this, &ChartsWidget::alignCharts); + QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &ChartsWidget::removeAll); QObject::connect(can, &CANMessages::eventsMerged, this, &ChartsWidget::eventsMerged); QObject::connect(can, &CANMessages::updated, this, &ChartsWidget::updateState); @@ -159,7 +163,7 @@ void ChartsWidget::showChart(const QString &id, const Signal *sig, bool show, bo QObject::connect(chart, &ChartView::zoomReset, this, &ChartsWidget::zoomReset); QObject::connect(chart, &ChartView::seriesRemoved, this, &ChartsWidget::seriesChanged); QObject::connect(chart, &ChartView::seriesAdded, this, &ChartsWidget::seriesChanged); - QObject::connect(chart, &ChartView::axisYUpdated, this, &ChartsWidget::alignCharts); + QObject::connect(chart, &ChartView::axisYUpdated, [this]() { align_charts_timer->start(100); }); charts_layout->insertWidget(0, chart); charts.push_back(chart); } @@ -453,7 +457,7 @@ void ChartView::updateAxisY() { double range = max_y - min_y; applyNiceNumbers(min_y - range * 0.05, max_y + range * 0.05); } - QTimer::singleShot(0, this, &ChartView::axisYUpdated); + emit axisYUpdated(); } void ChartView::applyNiceNumbers(qreal min, qreal max) { diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index 7a8325382d..819432920b 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -111,6 +112,7 @@ private: QAction *dock_btn; QAction *reset_zoom_btn; QAction *remove_all_btn; + QTimer *align_charts_timer; QVBoxLayout *charts_layout; QList charts; uint32_t max_chart_range = 0; From f14deae475f32af5b04fc2ef3744a77445ad8177 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 12 Jan 2023 11:28:04 -0800 Subject: [PATCH 067/484] Revert "Car interface: require fingerprint and FW versions to get params (#26766)" This reverts commit b68dabb689104ef0819a6c881b800076deb866f6. --- selfdrive/car/docs.py | 2 +- selfdrive/car/interfaces.py | 13 ++++++------- selfdrive/car/tests/test_car_interfaces.py | 2 +- selfdrive/car/tests/test_fw_fingerprint.py | 2 +- selfdrive/controls/lib/tests/test_latcontrol.py | 2 +- selfdrive/controls/lib/tests/test_vehicle_model.py | 2 +- selfdrive/controls/tests/test_state_machine.py | 2 +- selfdrive/debug/cycle_alerts.py | 2 +- selfdrive/debug/internal/test_paramsd.py | 2 +- selfdrive/test/longitudinal_maneuvers/plant.py | 2 +- selfdrive/test/process_replay/process_replay.py | 2 +- 11 files changed, 16 insertions(+), 17 deletions(-) diff --git a/selfdrive/car/docs.py b/selfdrive/car/docs.py index bc03619d0d..03313e2ff6 100755 --- a/selfdrive/car/docs.py +++ b/selfdrive/car/docs.py @@ -29,7 +29,7 @@ def get_all_car_info() -> List[CarInfo]: all_car_info: List[CarInfo] = [] footnotes = get_all_footnotes() for model, car_info in get_interface_attr("CAR_INFO", combine_brands=True).items(): - CP = interfaces[model][0].get_params(model, fingerprint=gen_empty_fingerprint(), car_fw=[car.CarParams.CarFw(ecu="unknown")], experimental_long=False) + CP = interfaces[model][0].get_params(model, fingerprint=gen_empty_fingerprint(), car_fw=[car.CarParams.CarFw(ecu="unknown")]) if CP.dashcamOnly or car_info is None: continue diff --git a/selfdrive/car/interfaces.py b/selfdrive/car/interfaces.py index 7192f5252c..ad1fd22b90 100644 --- a/selfdrive/car/interfaces.py +++ b/selfdrive/car/interfaces.py @@ -88,14 +88,13 @@ class CarInterfaceBase(ABC): return ACCEL_MIN, ACCEL_MAX @classmethod - def get_non_essential_params(cls, candidate: str): - """ - Parameters essential to controlling the car may be incomplete or wrong without FW versions or fingerprints. - """ - return cls.get_params(candidate, gen_empty_fingerprint(), list(), False) + def get_params(cls, candidate: str, fingerprint: Optional[Dict[int, Dict[int, int]]] = None, car_fw: Optional[List[car.CarParams.CarFw]] = None, experimental_long: bool = False): + if fingerprint is None: + fingerprint = gen_empty_fingerprint() + + if car_fw is None: + car_fw = list() - @classmethod - def get_params(cls, candidate: str, fingerprint: Dict[int, Dict[int, int]], car_fw: List[car.CarParams.CarFw], experimental_long: bool): ret = CarInterfaceBase.get_std_params(candidate) ret = cls._get_params(ret, candidate, fingerprint, car_fw, experimental_long) diff --git a/selfdrive/car/tests/test_car_interfaces.py b/selfdrive/car/tests/test_car_interfaces.py index deb0454b9c..11e7be7f44 100755 --- a/selfdrive/car/tests/test_car_interfaces.py +++ b/selfdrive/car/tests/test_car_interfaces.py @@ -25,7 +25,7 @@ class TestCarInterfaces(unittest.TestCase): car_fw = [] - car_params = CarInterface.get_params(car_name, fingerprints, car_fw, experimental_long=False) + car_params = CarInterface.get_params(car_name, fingerprints, car_fw) car_interface = CarInterface(car_params, CarController, CarState) assert car_params assert car_interface diff --git a/selfdrive/car/tests/test_fw_fingerprint.py b/selfdrive/car/tests/test_fw_fingerprint.py index e9f23145cd..2e43103852 100755 --- a/selfdrive/car/tests/test_fw_fingerprint.py +++ b/selfdrive/car/tests/test_fw_fingerprint.py @@ -67,7 +67,7 @@ class TestFwFingerprint(unittest.TestCase): blacklisted_addrs = (0x7c4, 0x7d0) # includes A/C ecu and an unknown ecu for car_model, ecus in FW_VERSIONS.items(): with self.subTest(car_model=car_model): - CP = interfaces[car_model][0].get_non_essential_params(car_model) + CP = interfaces[car_model][0].get_params(car_model) if CP.carName == 'subaru': for ecu in ecus.keys(): self.assertNotIn(ecu[1], blacklisted_addrs, f'{car_model}: Blacklisted ecu: (Ecu.{ECU_NAME[ecu[0]]}, {hex(ecu[1])})') diff --git a/selfdrive/controls/lib/tests/test_latcontrol.py b/selfdrive/controls/lib/tests/test_latcontrol.py index 993774f719..f15ab2fa56 100755 --- a/selfdrive/controls/lib/tests/test_latcontrol.py +++ b/selfdrive/controls/lib/tests/test_latcontrol.py @@ -20,7 +20,7 @@ class TestLatControl(unittest.TestCase): @parameterized.expand([(HONDA.CIVIC, LatControlPID), (TOYOTA.RAV4, LatControlTorque), (TOYOTA.PRIUS, LatControlINDI), (NISSAN.LEAF, LatControlAngle)]) def test_saturation(self, car_name, controller): CarInterface, CarController, CarState = interfaces[car_name] - CP = CarInterface.get_non_essential_params(car_name) + CP = CarInterface.get_params(car_name) CI = CarInterface(CP, CarController, CarState) VM = VehicleModel(CP) diff --git a/selfdrive/controls/lib/tests/test_vehicle_model.py b/selfdrive/controls/lib/tests/test_vehicle_model.py index 03d97a7e3f..3e08cb0aa0 100755 --- a/selfdrive/controls/lib/tests/test_vehicle_model.py +++ b/selfdrive/controls/lib/tests/test_vehicle_model.py @@ -12,7 +12,7 @@ from selfdrive.controls.lib.vehicle_model import VehicleModel, dyn_ss_sol, creat class TestVehicleModel(unittest.TestCase): def setUp(self): - CP = CarInterface.get_non_essential_params(CAR.CIVIC) + CP = CarInterface.get_params(CAR.CIVIC) self.VM = VehicleModel(CP) def test_round_trip_yaw_rate(self): diff --git a/selfdrive/controls/tests/test_state_machine.py b/selfdrive/controls/tests/test_state_machine.py index d5f468f214..8f263a98e7 100755 --- a/selfdrive/controls/tests/test_state_machine.py +++ b/selfdrive/controls/tests/test_state_machine.py @@ -31,7 +31,7 @@ class TestStateMachine(unittest.TestCase): def setUp(self): CarInterface, CarController, CarState = interfaces["mock"] - CP = CarInterface.get_non_essential_params("mock") + CP = CarInterface.get_params("mock") CI = CarInterface(CP, CarController, CarState) self.controlsd = Controls(CI=CI) diff --git a/selfdrive/debug/cycle_alerts.py b/selfdrive/debug/cycle_alerts.py index 71c7b34be5..b40c8e304c 100755 --- a/selfdrive/debug/cycle_alerts.py +++ b/selfdrive/debug/cycle_alerts.py @@ -51,7 +51,7 @@ def cycle_alerts(duration=200, is_metric=False): cameras = ['roadCameraState', 'wideRoadCameraState', 'driverCameraState'] CS = car.CarState.new_message() - CP = CarInterface.get_non_essential_params("HONDA CIVIC 2016") + CP = CarInterface.get_params("HONDA CIVIC 2016") sm = messaging.SubMaster(['deviceState', 'pandaStates', 'roadCameraState', 'modelV2', 'liveCalibration', 'driverMonitoringState', 'longitudinalPlan', 'lateralPlan', 'liveLocationKalman', 'managerState'] + cameras) diff --git a/selfdrive/debug/internal/test_paramsd.py b/selfdrive/debug/internal/test_paramsd.py index 81524496f7..3d8e422c35 100755 --- a/selfdrive/debug/internal/test_paramsd.py +++ b/selfdrive/debug/internal/test_paramsd.py @@ -20,7 +20,7 @@ T_SIM = 5 * 60 # s DT = 0.01 -CP = CarInterface.get_non_essential_params(CAR.CIVIC) +CP = CarInterface.get_params(CAR.CIVIC) VM = VehicleModel(CP) x, y = 0, 0 # m, m diff --git a/selfdrive/test/longitudinal_maneuvers/plant.py b/selfdrive/test/longitudinal_maneuvers/plant.py index bd0556aa07..59cb0e1fe3 100755 --- a/selfdrive/test/longitudinal_maneuvers/plant.py +++ b/selfdrive/test/longitudinal_maneuvers/plant.py @@ -49,7 +49,7 @@ class Plant: from selfdrive.car.honda.values import CAR from selfdrive.car.honda.interface import CarInterface - self.planner = LongitudinalPlanner(CarInterface.get_non_essential_params(CAR.CIVIC), init_v=self.speed) + self.planner = LongitudinalPlanner(CarInterface.get_params(CAR.CIVIC), init_v=self.speed) @property def current_time(self): diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 22ec099285..c5465fc025 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -180,7 +180,7 @@ def fingerprint(msgs, fsm, can_sock, fingerprint): def get_car_params(msgs, fsm, can_sock, fingerprint): if fingerprint: CarInterface, _, _ = interfaces[fingerprint] - CP = CarInterface.get_non_essential_params(fingerprint) + CP = CarInterface.get_params(fingerprint) else: can = FakeSocket(wait=False) sendcan = FakeSocket(wait=False) From b00bc4f57e2c9a3acb9e3b10528b7e30cbde43f1 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 12 Jan 2023 12:25:24 -0800 Subject: [PATCH 068/484] Car interface: require fingerprint and FW versions to get params (#26932) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * require fingerprint and FW versions * add get_non_essential_params() * comment * all required * classmethod, need to allow subclasses to override _get_params * fix that * fix * fix * wrong fix 🤦 --- selfdrive/car/docs.py | 2 +- selfdrive/car/interfaces.py | 13 +++++++------ selfdrive/car/tests/test_car_interfaces.py | 2 +- selfdrive/car/tests/test_fw_fingerprint.py | 2 +- selfdrive/car/tests/test_lateral_limits.py | 2 +- selfdrive/controls/lib/tests/test_latcontrol.py | 2 +- selfdrive/controls/lib/tests/test_vehicle_model.py | 2 +- selfdrive/controls/tests/test_state_machine.py | 2 +- selfdrive/debug/cycle_alerts.py | 2 +- selfdrive/debug/internal/test_paramsd.py | 2 +- selfdrive/test/longitudinal_maneuvers/plant.py | 2 +- selfdrive/test/process_replay/process_replay.py | 2 +- 12 files changed, 18 insertions(+), 17 deletions(-) diff --git a/selfdrive/car/docs.py b/selfdrive/car/docs.py index 03313e2ff6..bc03619d0d 100755 --- a/selfdrive/car/docs.py +++ b/selfdrive/car/docs.py @@ -29,7 +29,7 @@ def get_all_car_info() -> List[CarInfo]: all_car_info: List[CarInfo] = [] footnotes = get_all_footnotes() for model, car_info in get_interface_attr("CAR_INFO", combine_brands=True).items(): - CP = interfaces[model][0].get_params(model, fingerprint=gen_empty_fingerprint(), car_fw=[car.CarParams.CarFw(ecu="unknown")]) + CP = interfaces[model][0].get_params(model, fingerprint=gen_empty_fingerprint(), car_fw=[car.CarParams.CarFw(ecu="unknown")], experimental_long=False) if CP.dashcamOnly or car_info is None: continue diff --git a/selfdrive/car/interfaces.py b/selfdrive/car/interfaces.py index ad1fd22b90..7192f5252c 100644 --- a/selfdrive/car/interfaces.py +++ b/selfdrive/car/interfaces.py @@ -88,13 +88,14 @@ class CarInterfaceBase(ABC): return ACCEL_MIN, ACCEL_MAX @classmethod - def get_params(cls, candidate: str, fingerprint: Optional[Dict[int, Dict[int, int]]] = None, car_fw: Optional[List[car.CarParams.CarFw]] = None, experimental_long: bool = False): - if fingerprint is None: - fingerprint = gen_empty_fingerprint() - - if car_fw is None: - car_fw = list() + def get_non_essential_params(cls, candidate: str): + """ + Parameters essential to controlling the car may be incomplete or wrong without FW versions or fingerprints. + """ + return cls.get_params(candidate, gen_empty_fingerprint(), list(), False) + @classmethod + def get_params(cls, candidate: str, fingerprint: Dict[int, Dict[int, int]], car_fw: List[car.CarParams.CarFw], experimental_long: bool): ret = CarInterfaceBase.get_std_params(candidate) ret = cls._get_params(ret, candidate, fingerprint, car_fw, experimental_long) diff --git a/selfdrive/car/tests/test_car_interfaces.py b/selfdrive/car/tests/test_car_interfaces.py index 11e7be7f44..deb0454b9c 100755 --- a/selfdrive/car/tests/test_car_interfaces.py +++ b/selfdrive/car/tests/test_car_interfaces.py @@ -25,7 +25,7 @@ class TestCarInterfaces(unittest.TestCase): car_fw = [] - car_params = CarInterface.get_params(car_name, fingerprints, car_fw) + car_params = CarInterface.get_params(car_name, fingerprints, car_fw, experimental_long=False) car_interface = CarInterface(car_params, CarController, CarState) assert car_params assert car_interface diff --git a/selfdrive/car/tests/test_fw_fingerprint.py b/selfdrive/car/tests/test_fw_fingerprint.py index 2e43103852..e9f23145cd 100755 --- a/selfdrive/car/tests/test_fw_fingerprint.py +++ b/selfdrive/car/tests/test_fw_fingerprint.py @@ -67,7 +67,7 @@ class TestFwFingerprint(unittest.TestCase): blacklisted_addrs = (0x7c4, 0x7d0) # includes A/C ecu and an unknown ecu for car_model, ecus in FW_VERSIONS.items(): with self.subTest(car_model=car_model): - CP = interfaces[car_model][0].get_params(car_model) + CP = interfaces[car_model][0].get_non_essential_params(car_model) if CP.carName == 'subaru': for ecu in ecus.keys(): self.assertNotIn(ecu[1], blacklisted_addrs, f'{car_model}: Blacklisted ecu: (Ecu.{ECU_NAME[ecu[0]]}, {hex(ecu[1])})') diff --git a/selfdrive/car/tests/test_lateral_limits.py b/selfdrive/car/tests/test_lateral_limits.py index a82c20ce01..3efdd7404e 100755 --- a/selfdrive/car/tests/test_lateral_limits.py +++ b/selfdrive/car/tests/test_lateral_limits.py @@ -40,7 +40,7 @@ class TestLateralLimits(unittest.TestCase): @classmethod def setUpClass(cls): CarInterface, _, _ = interfaces[cls.car_model] - CP = CarInterface.get_params(cls.car_model) + CP = CarInterface.get_non_essential_params(cls.car_model) if CP.dashcamOnly: raise unittest.SkipTest("Platform is behind dashcamOnly") diff --git a/selfdrive/controls/lib/tests/test_latcontrol.py b/selfdrive/controls/lib/tests/test_latcontrol.py index f15ab2fa56..993774f719 100755 --- a/selfdrive/controls/lib/tests/test_latcontrol.py +++ b/selfdrive/controls/lib/tests/test_latcontrol.py @@ -20,7 +20,7 @@ class TestLatControl(unittest.TestCase): @parameterized.expand([(HONDA.CIVIC, LatControlPID), (TOYOTA.RAV4, LatControlTorque), (TOYOTA.PRIUS, LatControlINDI), (NISSAN.LEAF, LatControlAngle)]) def test_saturation(self, car_name, controller): CarInterface, CarController, CarState = interfaces[car_name] - CP = CarInterface.get_params(car_name) + CP = CarInterface.get_non_essential_params(car_name) CI = CarInterface(CP, CarController, CarState) VM = VehicleModel(CP) diff --git a/selfdrive/controls/lib/tests/test_vehicle_model.py b/selfdrive/controls/lib/tests/test_vehicle_model.py index 3e08cb0aa0..03d97a7e3f 100755 --- a/selfdrive/controls/lib/tests/test_vehicle_model.py +++ b/selfdrive/controls/lib/tests/test_vehicle_model.py @@ -12,7 +12,7 @@ from selfdrive.controls.lib.vehicle_model import VehicleModel, dyn_ss_sol, creat class TestVehicleModel(unittest.TestCase): def setUp(self): - CP = CarInterface.get_params(CAR.CIVIC) + CP = CarInterface.get_non_essential_params(CAR.CIVIC) self.VM = VehicleModel(CP) def test_round_trip_yaw_rate(self): diff --git a/selfdrive/controls/tests/test_state_machine.py b/selfdrive/controls/tests/test_state_machine.py index 8f263a98e7..d5f468f214 100755 --- a/selfdrive/controls/tests/test_state_machine.py +++ b/selfdrive/controls/tests/test_state_machine.py @@ -31,7 +31,7 @@ class TestStateMachine(unittest.TestCase): def setUp(self): CarInterface, CarController, CarState = interfaces["mock"] - CP = CarInterface.get_params("mock") + CP = CarInterface.get_non_essential_params("mock") CI = CarInterface(CP, CarController, CarState) self.controlsd = Controls(CI=CI) diff --git a/selfdrive/debug/cycle_alerts.py b/selfdrive/debug/cycle_alerts.py index b40c8e304c..71c7b34be5 100755 --- a/selfdrive/debug/cycle_alerts.py +++ b/selfdrive/debug/cycle_alerts.py @@ -51,7 +51,7 @@ def cycle_alerts(duration=200, is_metric=False): cameras = ['roadCameraState', 'wideRoadCameraState', 'driverCameraState'] CS = car.CarState.new_message() - CP = CarInterface.get_params("HONDA CIVIC 2016") + CP = CarInterface.get_non_essential_params("HONDA CIVIC 2016") sm = messaging.SubMaster(['deviceState', 'pandaStates', 'roadCameraState', 'modelV2', 'liveCalibration', 'driverMonitoringState', 'longitudinalPlan', 'lateralPlan', 'liveLocationKalman', 'managerState'] + cameras) diff --git a/selfdrive/debug/internal/test_paramsd.py b/selfdrive/debug/internal/test_paramsd.py index 3d8e422c35..81524496f7 100755 --- a/selfdrive/debug/internal/test_paramsd.py +++ b/selfdrive/debug/internal/test_paramsd.py @@ -20,7 +20,7 @@ T_SIM = 5 * 60 # s DT = 0.01 -CP = CarInterface.get_params(CAR.CIVIC) +CP = CarInterface.get_non_essential_params(CAR.CIVIC) VM = VehicleModel(CP) x, y = 0, 0 # m, m diff --git a/selfdrive/test/longitudinal_maneuvers/plant.py b/selfdrive/test/longitudinal_maneuvers/plant.py index 59cb0e1fe3..bd0556aa07 100755 --- a/selfdrive/test/longitudinal_maneuvers/plant.py +++ b/selfdrive/test/longitudinal_maneuvers/plant.py @@ -49,7 +49,7 @@ class Plant: from selfdrive.car.honda.values import CAR from selfdrive.car.honda.interface import CarInterface - self.planner = LongitudinalPlanner(CarInterface.get_params(CAR.CIVIC), init_v=self.speed) + self.planner = LongitudinalPlanner(CarInterface.get_non_essential_params(CAR.CIVIC), init_v=self.speed) @property def current_time(self): diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index c5465fc025..22ec099285 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -180,7 +180,7 @@ def fingerprint(msgs, fsm, can_sock, fingerprint): def get_car_params(msgs, fsm, can_sock, fingerprint): if fingerprint: CarInterface, _, _ = interfaces[fingerprint] - CP = CarInterface.get_params(fingerprint) + CP = CarInterface.get_non_essential_params(fingerprint) else: can = FakeSocket(wait=False) sendcan = FakeSocket(wait=False) From 66d0d4c7d7035375bc17bdc9da404c2a5e77d453 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 13 Jan 2023 05:27:46 +0800 Subject: [PATCH 069/484] cabana: filtering in both address and msg name (#26929) search in address and name Co-authored-by: Shane Smiskol --- tools/cabana/messageswidget.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index dbf87a3da2..a5224e108b 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -80,11 +80,11 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const { void MessageListModel::setFilterString(const QString &string) { filter_str = string; - bool search_id = filter_str.contains(':'); msgs.clear(); for (auto it = can->can_msgs.begin(); it != can->can_msgs.end(); ++it) { - if ((search_id ? it.key() : msgName(it.key())).contains(filter_str, Qt::CaseInsensitive)) + if (it.key().contains(filter_str, Qt::CaseInsensitive) || msgName(it.key()).contains(filter_str, Qt::CaseInsensitive)) { msgs.push_back(it.key()); + } } sortMessages(); } From 53ec07edbd591296602658080df71d91e8629540 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 13 Jan 2023 05:39:51 +0800 Subject: [PATCH 070/484] cabana: double click on an item in FindSimilarBitsDlg to open the message in detailView (#26930) * double click to open message * remove qDebug --- tools/cabana/mainwin.cc | 1 + tools/cabana/messageswidget.cc | 11 +++++++---- tools/cabana/messageswidget.h | 2 ++ tools/cabana/tools/findsimilarbits.cc | 8 +++++++- tools/cabana/tools/findsimilarbits.h | 5 +++++ 5 files changed, 22 insertions(+), 5 deletions(-) diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index 43aa330029..46ef8f10f9 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -281,5 +281,6 @@ void MainWindow::setOption() { void MainWindow::findSimilarBits() { FindSimilarBitsDlg dlg(this); + QObject::connect(&dlg, &FindSimilarBitsDlg::openMessage, messages_widget, &MessagesWidget::selectMessage); dlg.exec(); } diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index a5224e108b..fd0bc68514 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -39,6 +39,7 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { QObject::connect(dbc(), &DBCManager::DBCFileChanged, model, &MessageListModel::sortMessages); QObject::connect(dbc(), &DBCManager::msgUpdated, model, &MessageListModel::sortMessages); QObject::connect(dbc(), &DBCManager::msgRemoved, model, &MessageListModel::sortMessages); + QObject::connect(model, &MessageListModel::modelReset, [this]() { selectMessage(current_msg_id); }); QObject::connect(table_widget->selectionModel(), &QItemSelectionModel::currentChanged, [=](const QModelIndex ¤t, const QModelIndex &previous) { if (current.isValid() && current.row() < model->msgs.size()) { if (model->msgs[current.row()] != current_msg_id) { @@ -47,10 +48,12 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { } } }); - QObject::connect(model, &MessageListModel::modelReset, [this]() { - if (int row = model->msgs.indexOf(current_msg_id); row != -1) - table_widget->selectionModel()->setCurrentIndex(model->index(row, 0), QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect); - }); +} + +void MessagesWidget::selectMessage(const QString &msg_id) { + if (int row = model->msgs.indexOf(msg_id); row != -1) { + table_widget->selectionModel()->setCurrentIndex(model->index(row, 0), QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect); + } } // MessageListModel diff --git a/tools/cabana/messageswidget.h b/tools/cabana/messageswidget.h index 3a42bed4be..62aa23c02e 100644 --- a/tools/cabana/messageswidget.h +++ b/tools/cabana/messageswidget.h @@ -31,6 +31,8 @@ class MessagesWidget : public QWidget { public: MessagesWidget(QWidget *parent); + void selectMessage(const QString &message_id); + signals: void msgSelectionChanged(const QString &message_id); diff --git a/tools/cabana/tools/findsimilarbits.cc b/tools/cabana/tools/findsimilarbits.cc index cb5dbc0845..5c9623c7e4 100644 --- a/tools/cabana/tools/findsimilarbits.cc +++ b/tools/cabana/tools/findsimilarbits.cc @@ -59,12 +59,19 @@ FindSimilarBitsDlg::FindSimilarBitsDlg(QWidget *parent) : QDialog(parent) { main_layout->addLayout(form_layout); table = new QTableWidget(this); + table->setSelectionBehavior(QAbstractItemView::SelectRows); + table->setSelectionMode(QAbstractItemView::SingleSelection); table->setEditTriggers(QAbstractItemView::NoEditTriggers); table->horizontalHeader()->setStretchLastSection(true); main_layout->addWidget(table); setMinimumSize({700, 500}); QObject::connect(search_btn, &QPushButton::clicked, this, &FindSimilarBitsDlg::find); + QObject::connect(table, &QTableWidget::doubleClicked, [this](const QModelIndex &index) { + if (index.isValid()) { + emit openMessage(bus_combo->currentText() + ":" + table->item(index.row(), 0)->text()); + } + }); } void FindSimilarBitsDlg::find() { @@ -91,7 +98,6 @@ QList FindSimilarBitsDlg::calcBits(uint8_ QHash> mismatches; QHash msg_count; auto events = can->events(); - qDebug() << bus << selected_address << byte_idx << bit_idx; int bit_to_find = -1; for (auto e : *events) { if (e->which == cereal::Event::Which::CAN) { diff --git a/tools/cabana/tools/findsimilarbits.h b/tools/cabana/tools/findsimilarbits.h index 8083999909..30d78f0dea 100644 --- a/tools/cabana/tools/findsimilarbits.h +++ b/tools/cabana/tools/findsimilarbits.h @@ -7,9 +7,14 @@ #include class FindSimilarBitsDlg : public QDialog { + Q_OBJECT + public: FindSimilarBitsDlg(QWidget *parent); +signals: + void openMessage(const QString &msg_id); + private: struct mismatched_struct { uint32_t address, byte_idx, bit_idx, mismatches, total; From df394004d574d0c9c63216da56bc965da27af508 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 12 Jan 2023 14:58:47 -0800 Subject: [PATCH 071/484] red panda footnote: finish thought (#26933) missing `car` --- docs/CARS.md | 2 +- selfdrive/car/hyundai/values.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index 1b76069f6a..1e862a2f45 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -237,7 +237,7 @@ A supported vehicle is one that just works when you install a comma three. All s 2By default, this car will use the stock Adaptive Cruise Control (ACC) for longitudinal control. If the Driver Support Unit (DSU) is disconnected, openpilot ACC will replace stock ACC. NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).
3Requires a community built ASCM harness. NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).
42019 Honda Civic 1.6L Diesel Sedan does not have ALC below 12mph.
-5Requires a red panda for this CAN FD. All the hardware needed is sold in the CAN FD kit.
+5Requires a red panda for this CAN FD car. All the hardware needed is sold in the CAN FD kit.
6openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control.
7Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform.
8Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets.
diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 229ca992d5..34c66623c3 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -125,7 +125,7 @@ class CAR: class Footnote(Enum): # footnotes which mention "red panda" will be replaced with the CAN FD panda kit on the shop page CANFD = CarFootnote( - "Requires a red panda for this CAN FD. " + + "Requires a red panda for this CAN FD car. " + "All the hardware needed is sold in the CAN FD kit.", Column.MODEL, shop_footnote=True) From 47cca343ec6ae611871d53d7b0660647f816f1c9 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 12 Jan 2023 15:07:14 -0800 Subject: [PATCH 072/484] docs: open footnotes in new tab --- docs/CARS.md | 4 ++-- selfdrive/car/gm/values.py | 2 +- selfdrive/car/hyundai/values.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index 1e862a2f45..48145d78a9 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -235,9 +235,9 @@ A supported vehicle is one that just works when you install a comma three. All s 1Experimental openpilot longitudinal control is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `master-ci`.
2By default, this car will use the stock Adaptive Cruise Control (ACC) for longitudinal control. If the Driver Support Unit (DSU) is disconnected, openpilot ACC will replace stock ACC. NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).
-3Requires a community built ASCM harness. NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).
+3Requires a community built ASCM harness. NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).
42019 Honda Civic 1.6L Diesel Sedan does not have ALC below 12mph.
-5Requires a red panda for this CAN FD car. All the hardware needed is sold in the CAN FD kit.
+5Requires a red panda for this CAN FD car. All the hardware needed is sold in the CAN FD kit.
6openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control.
7Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform.
8Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets.
diff --git a/selfdrive/car/gm/values.py b/selfdrive/car/gm/values.py index ece128c253..e633ec5f62 100644 --- a/selfdrive/car/gm/values.py +++ b/selfdrive/car/gm/values.py @@ -75,7 +75,7 @@ class CAR: class Footnote(Enum): OBD_II = CarFootnote( - 'Requires a community built ASCM harness. ' + + 'Requires a community built ASCM harness. ' + 'NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).', Column.MODEL) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 34c66623c3..eace661266 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -125,8 +125,8 @@ class CAR: class Footnote(Enum): # footnotes which mention "red panda" will be replaced with the CAN FD panda kit on the shop page CANFD = CarFootnote( - "Requires a red panda for this CAN FD car. " + - "All the hardware needed is sold in the CAN FD kit.", + "Requires a red panda for this CAN FD car. " + + "All the hardware needed is sold in the CAN FD kit.", Column.MODEL, shop_footnote=True) From 8aac07bf86897ff9dee8f82ddd361fe39aed5063 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 12 Jan 2023 15:20:37 -0800 Subject: [PATCH 073/484] model replay: disable nav for now --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index f49ccf4315..3b16b3f112 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -190,7 +190,7 @@ pipeline { steps { phone_steps("tici-common", [ ["build", "cd selfdrive/manager && ./build.py"], - ["model replay", "cd selfdrive/test/process_replay && ./model_replay.py"], + ["model replay", "cd selfdrive/test/process_replay && NO_NAV=1 ./model_replay.py"], ]) } } From 1d91913aae95fceade6afd7c808e2188ce6e2985 Mon Sep 17 00:00:00 2001 From: Jason Wen <47793918+sunnyhaibin@users.noreply.github.com> Date: Fri, 13 Jan 2023 14:21:51 -0500 Subject: [PATCH 074/484] Hyundai: Add FW Version for 2021 Sonata N Line (#26945) * Hyundai: Add FW Version for 2021 Sonata N Line * missing engine Co-authored-by: Shane Smiskol --- selfdrive/car/hyundai/values.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index eace661266..c4fda42b63 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -516,6 +516,7 @@ FW_VERSIONS = { b'HM6M2_0a0_BD0', b'\xf1\x8739110-2S278\xf1\x82DNDVD5GMCCXXXL5B', b'\xf1\x8739110-2S041\xf1\x81HM6M1_0a0_M00', + b'\xf1\x81HM6M1_0a0_G20', ], (Ecu.eps, 0x7d4, None): [ b'\xf1\x00DN8 MDPS C 1,00 1,01 56310L0010\x00 4DNAC101', # modified firmware @@ -606,6 +607,7 @@ FW_VERSIONS = { b'\xf1\x87SALFBA7460044GJ2gx\x87\x88Vf\x86hx\x88\x87\x88wwwwgw\x86wd?\xfa\xff\x86U_\xff\xaf\x1f\xf1\x81U913\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U913\x00\x00\x00\x00\x00\x00SDN8T16NB2\n\xdd^\xbc', b'\xf1\x87SAMFBA8105254GJ2wx\x87\x88Vf\x86hx\x88\x87\x88wwwwwwww\x86O\xfa\xff\x99\x88\x7f\xffZG\xf1\x81U913\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U913\x00\x00\x00\x00\x00\x00SDN8T16NB2\n\xdd^\xbc', b'\xf1\x87SANFB45889451GC7wx\x87\x88gw\x87x\x88\x88x\x88\x87wxw\x87wxw\x87\x8f\xfc\xffeU\x8f\xff+Q\xf1\x81U913\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U913\x00\x00\x00\x00\x00\x00SDN8T16NB2\n\xdd^\xbc', + b'\xf1\x00T02601BL T02900A1 VDN8T25XXX900NSA\xb9\x13\xf9p', ], }, CAR.SONATA_LF: { From aaed3537780b5082d427667d6ab4070db2e7e21f Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 14 Jan 2023 03:33:03 +0800 Subject: [PATCH 075/484] cabana: use Qt::Window flag for FindSimilarBitsDlg (#26938) use Qt::Window flag for FindSimilarBitsDlg --- tools/cabana/mainwin.cc | 6 +++--- tools/cabana/tools/findsimilarbits.cc | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index 46ef8f10f9..9df5894b90 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -280,7 +280,7 @@ void MainWindow::setOption() { } void MainWindow::findSimilarBits() { - FindSimilarBitsDlg dlg(this); - QObject::connect(&dlg, &FindSimilarBitsDlg::openMessage, messages_widget, &MessagesWidget::selectMessage); - dlg.exec(); + FindSimilarBitsDlg *dlg = new FindSimilarBitsDlg(this); + QObject::connect(dlg, &FindSimilarBitsDlg::openMessage, messages_widget, &MessagesWidget::selectMessage); + dlg->show(); } diff --git a/tools/cabana/tools/findsimilarbits.cc b/tools/cabana/tools/findsimilarbits.cc index 5c9623c7e4..8a05e3e236 100644 --- a/tools/cabana/tools/findsimilarbits.cc +++ b/tools/cabana/tools/findsimilarbits.cc @@ -10,8 +10,10 @@ #include "tools/cabana/canmessages.h" #include "tools/cabana/dbcmanager.h" -FindSimilarBitsDlg::FindSimilarBitsDlg(QWidget *parent) : QDialog(parent) { +FindSimilarBitsDlg::FindSimilarBitsDlg(QWidget *parent) : QDialog(parent, Qt::WindowFlags() | Qt::Window) { setWindowTitle(tr("Find similar bits")); + setAttribute(Qt::WA_DeleteOnClose); + QVBoxLayout *main_layout = new QVBoxLayout(this); QHBoxLayout *form_layout = new QHBoxLayout(); From 88423e25df8bfbd8e6a3275eb086f468cf9a59f1 Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Fri, 13 Jan 2023 12:37:38 -0700 Subject: [PATCH 076/484] Laikad: set active (#26850) * laikad update, renaming * update locationd * fix naming * address PR comments * upsi * . * draft to fix replay * fix process relay to allow no response for messages * final fix for process replay * . * bump cereal * update process replay ref commit * reduce wait time * . * last ref change * move laikad helpers to laika * . * fix ublox test * update refs * add proper qcom replay support * fix gnss support if both is available * update refs * remove left over * revert laikad msg * move laika back to master * init * fix gps valid flag * change time * add gnss to ignore * remove gps_valid flag * . * adopt orientation reset threshold * . * update laikad * . * fix stanstill KF resets * test orienation reset count * update laika * bump cereal * fix process replay * update laika repo * remove handle gps * add extra logging for cache * . * add more log * . * . * update laika * dont remove gps code * inc min satellite count * update magic vals and add acc drop * update laika * upsi * rem * bump laika * use nav and correct * more fixes * use sftp * No more glonass * Revert "No more glonass" This reverts commit a76124da50a1e25f423ad1137c7a046e1d57811d. * nump laika * back support old ephemeris cache * add health to ephemeris message * bump laika * remove print * fix laikad tests * clean * remove extra log * bump laika * inc timeout for plotjuggler build * rem cache clear * . * enable gps after checks Co-authored-by: Kurt Nistelberger Co-authored-by: Bruce Wayne Co-authored-by: Shane Smiskol --- .github/workflows/tools_tests.yaml | 2 +- laika_repo | 2 +- selfdrive/locationd/laikad.py | 85 +++++++------- selfdrive/locationd/liblocationd.cc | 4 +- selfdrive/locationd/locationd.cc | 71 ++++++------ selfdrive/locationd/locationd.h | 6 +- .../locationd/test/_test_locationd_lib.py | 4 +- selfdrive/locationd/test/test_laikad.py | 104 +++++++++--------- selfdrive/locationd/test/test_locationd.py | 36 +++--- selfdrive/locationd/ublox_msg.cc | 1 + .../test/process_replay/process_replay.py | 2 +- selfdrive/test/process_replay/ref_commit | 2 +- 12 files changed, 169 insertions(+), 150 deletions(-) diff --git a/.github/workflows/tools_tests.yaml b/.github/workflows/tools_tests.yaml index 94cc3c2580..7ad0ce35b1 100644 --- a/.github/workflows/tools_tests.yaml +++ b/.github/workflows/tools_tests.yaml @@ -38,7 +38,7 @@ jobs: - name: Build Docker image run: eval "$BUILD" - name: Unit test - timeout-minutes: 2 + timeout-minutes: 6 run: | ${{ env.RUN }} "scons -j$(nproc) --directory=/tmp/openpilot/cereal && \ apt-get update && \ diff --git a/laika_repo b/laika_repo index 5eb0c3c259..73bf110ae0 160000 --- a/laika_repo +++ b/laika_repo @@ -1 +1 @@ -Subproject commit 5eb0c3c2596dd12a232b83bdb057a716810e89cf +Subproject commit 73bf110ae0093ad86755bf5eb6a03e46ff5c239d diff --git a/selfdrive/locationd/laikad.py b/selfdrive/locationd/laikad.py index 1f13c2b69a..b8d86c63fd 100755 --- a/selfdrive/locationd/laikad.py +++ b/selfdrive/locationd/laikad.py @@ -3,6 +3,7 @@ import json import math import os import time +import shutil from collections import defaultdict from concurrent.futures import Future, ProcessPoolExecutor from datetime import datetime @@ -29,17 +30,17 @@ from system.swaglog import cloudlog MAX_TIME_GAP = 10 EPHEMERIS_CACHE = 'LaikadEphemeris' DOWNLOADS_CACHE_FOLDER = "/tmp/comma_download_cache/" -CACHE_VERSION = 0.1 +CACHE_VERSION = 0.2 POS_FIX_RESIDUAL_THRESHOLD = 100.0 class Laikad: - def __init__(self, valid_const=("GPS", "GLONASS"), auto_fetch_orbits=True, auto_update=False, - valid_ephem_types=(EphemerisType.ULTRA_RAPID_ORBIT, EphemerisType.NAV), + def __init__(self, valid_const=("GPS", "GLONASS"), auto_fetch_navs=True, auto_update=False, + valid_ephem_types=(EphemerisType.NAV,), save_ephemeris=False, use_qcom=False): """ valid_const: GNSS constellation which can be used - auto_fetch_orbits: If true fetch orbits from internet when needed + auto_fetch_navs: If true fetch navs from internet when needed auto_update: If true download AstroDog will download all files needed. This can be ephemeris or correction data like ionosphere. valid_ephem_types: Valid ephemeris types to be used by AstroDog save_ephemeris: If true saves and loads nav and orbit ephemeris to cache. @@ -47,11 +48,11 @@ class Laikad: self.astro_dog = AstroDog(valid_const=valid_const, auto_update=auto_update, valid_ephem_types=valid_ephem_types, clear_old_ephemeris=True, cache_dir=DOWNLOADS_CACHE_FOLDER) self.gnss_kf = GNSSKalman(GENERATED_DIR, cython=True, erratic_clock=use_qcom) - self.auto_fetch_orbits = auto_fetch_orbits + self.auto_fetch_navs = auto_fetch_navs self.orbit_fetch_executor: Optional[ProcessPoolExecutor] = None self.orbit_fetch_future: Optional[Future] = None - self.last_fetch_orbits_t = None + self.last_fetch_navs_t = None self.got_first_gnss_msg = False self.last_cached_t = None self.save_ephemeris = save_ephemeris @@ -74,29 +75,30 @@ class Laikad: try: cache = json.loads(cache, object_hook=deserialize_hook) - self.astro_dog.add_orbits(cache['orbits']) - self.astro_dog.add_navs(cache['nav']) - self.last_fetch_orbits_t = cache['last_fetch_orbits_t'] + if cache['version'] == CACHE_VERSION: + self.astro_dog.add_navs(cache['navs']) + self.last_fetch_navs_t = cache['last_fetch_navs_t'] + else: + cache['navs'] = {} except json.decoder.JSONDecodeError: cloudlog.exception("Error parsing cache") - timestamp = self.last_fetch_orbits_t.as_datetime() if self.last_fetch_orbits_t is not None else 'Nan' + timestamp = self.last_fetch_navs_t.as_datetime() if self.last_fetch_navs_t is not None else 'Nan' cloudlog.debug( - f"Loaded nav ({sum([len(v) for v in cache['nav']])}) and orbits ({sum([len(v) for v in cache['orbits']])}) cache with timestamp: {timestamp}. Unique orbit and nav sats: {list(cache['orbits'].keys())} {list(cache['nav'].keys())} " + - f"With time range: {[f'{start.as_datetime()}, {end.as_datetime()}' for (start,end) in self.astro_dog.orbit_fetched_times._ranges]}") + f"Loaded navs ({sum([len(v) for v in cache['navs']])}) cache with timestamp: {timestamp}. Unique orbit and nav sats: {list(cache['navs'].keys())} " + + f"With time range: {[f'{start.as_datetime()}, {end.as_datetime()}' for (start,end) in self.astro_dog.navs_fetched_times._ranges]}") def cache_ephemeris(self, t: GPSTime): if self.save_ephemeris and (self.last_cached_t is None or t - self.last_cached_t > SECS_IN_MIN): put_nonblocking(EPHEMERIS_CACHE, json.dumps( - {'version': CACHE_VERSION, 'last_fetch_orbits_t': self.last_fetch_orbits_t, 'orbits': self.astro_dog.orbits, 'nav': self.astro_dog.nav}, + {'version': CACHE_VERSION, 'last_fetch_navs_t': self.last_fetch_navs_t, 'navs': self.astro_dog.navs}, cls=CacheSerializer)) cloudlog.debug("Cache saved") self.last_cached_t = t def get_lsq_fix(self, t, measurements): if self.last_fix_t is None or abs(self.last_fix_t - t) > 0: - min_measurements = 6 if any(p.constellation_id == ConstellationId.GLONASS for p in measurements) else 5 + min_measurements = 7 if any(p.constellation_id == ConstellationId.GLONASS for p in measurements) else 6 position_solution, pr_residuals = calc_pos_fix(measurements, self.posfix_functions, min_measurements=min_measurements) - if len(position_solution) < 3: return None position_estimate = position_solution[:3] @@ -155,8 +157,13 @@ class Laikad: # Filter measurements with unexpected pseudoranges for GPS and GLONASS satellites new_meas = [m for m in new_meas if 1e7 < m.observables['C1C'] < 3e7] processed_measurements = process_measurements(new_meas, self.astro_dog) - - instant_fix = self.get_lsq_fix(t, processed_measurements) + if self.last_fix_pos is not None: + corrected_measurements = correct_measurements(processed_measurements, self.last_fix_pos, self.astro_dog) + instant_fix = self.get_lsq_fix(t, corrected_measurements) + #instant_fix = self.get_lsq_fix(t, processed_measurements) + else: + corrected_measurements = [] + instant_fix = self.get_lsq_fix(t, processed_measurements) if instant_fix is None: return None else: @@ -164,8 +171,6 @@ class Laikad: self.last_fix_t = t self.last_fix_pos = position_estimate self.lat_fix_pos_std = position_std - - corrected_measurements = correct_measurements(processed_measurements, position_estimate, self.astro_dog) if (t*1e9) % 10 == 0: cloudlog.debug(f"Measurements Incoming/Processed/Corrected: {len(new_meas), len(processed_measurements), len(corrected_measurements)}") return position_estimate, position_std, velocity_estimate, velocity_std, corrected_measurements, processed_measurements @@ -185,8 +190,8 @@ class Laikad: if week > 0: self.got_first_gnss_msg = True latest_msg_t = GPSTime(week, tow) - if self.auto_fetch_orbits: - self.fetch_orbits(latest_msg_t, block) + if self.auto_fetch_navs: + self.fetch_navs(latest_msg_t, block) output = self.process_report(new_meas, t) if output is None: @@ -254,9 +259,9 @@ class Laikad: p_initial_diag[GStates.ECEF_POS] = 1000 ** 2 self.gnss_kf.init_state(x_initial, covs_diag=p_initial_diag) - def fetch_orbits(self, t: GPSTime, block): - # Download new orbits if 1 hour of orbits data left - if t + SECS_IN_HR not in self.astro_dog.orbit_fetched_times and (self.last_fetch_orbits_t is None or abs(t - self.last_fetch_orbits_t) > SECS_IN_MIN): + def fetch_navs(self, t: GPSTime, block): + # Download new navs if 1 hour of navs data left + if t + SECS_IN_HR not in self.astro_dog.navs_fetched_times and (self.last_fetch_navs_t is None or abs(t - self.last_fetch_navs_t) > SECS_IN_MIN): astro_dog_vars = self.astro_dog.valid_const, self.astro_dog.auto_update, self.astro_dog.valid_ephem_types, self.astro_dog.cache_dir ret = None @@ -271,22 +276,22 @@ class Laikad: if ret is not None: if ret[0] is None: - self.last_fetch_orbits_t = ret[2] + self.last_fetch_navs_t = ret[2] else: - self.astro_dog.orbits, self.astro_dog.orbit_fetched_times, self.last_fetch_orbits_t = ret + self.astro_dog.navs, self.astro_dog.navs_fetched_times, self.last_fetch_navs_t = ret self.cache_ephemeris(t=t) def get_orbit_data(t: GPSTime, valid_const, auto_update, valid_ephem_types, cache_dir): astro_dog = AstroDog(valid_const=valid_const, auto_update=auto_update, valid_ephem_types=valid_ephem_types, cache_dir=cache_dir) - cloudlog.info(f"Start to download/parse orbits for time {t.as_datetime()}") + cloudlog.info(f"Start to download/parse navs for time {t.as_datetime()}") start_time = time.monotonic() try: - astro_dog.get_orbit_data(t, only_predictions=True) - cloudlog.info(f"Done parsing orbits. Took {time.monotonic() - start_time:.1f}s") - cloudlog.debug(f"Downloaded orbits ({sum([len(v) for v in astro_dog.orbits])}): {list(astro_dog.orbits.keys())}" + + astro_dog.get_navs(t) + cloudlog.info(f"Done parsing navs. Took {time.monotonic() - start_time:.1f}s") + cloudlog.debug(f"Downloaded navs ({sum([len(v) for v in astro_dog.navs])}): {list(astro_dog.navs.keys())}" + f"With time range: {[f'{start.as_datetime()}, {end.as_datetime()}' for (start,end) in astro_dog.orbit_fetched_times._ranges]}") - return astro_dog.orbits, astro_dog.orbit_fetched_times, t + return astro_dog.navs, astro_dog.navs_fetched_times, t except (DownloadFailed, RuntimeError, ValueError, IOError) as e: cloudlog.warning(f"No orbit data found or parsing failure: {e}") return None, None, t @@ -385,7 +390,15 @@ def process_msg(laikad, gnss_msg, mono_time, block=False): return laikad.process_gnss_msg(gnss_msg, mono_time, block=block) +def clear_tmp_cache(): + if os.path.exists(DOWNLOADS_CACHE_FOLDER): + shutil.rmtree(DOWNLOADS_CACHE_FOLDER) + os.mkdir(DOWNLOADS_CACHE_FOLDER) + + def main(sm=None, pm=None, qc=None): + #clear_tmp_cache() + use_qcom = not Params().get_bool("UbloxAvailable", block=True) if use_qcom or (qc is not None and qc): raw_gnss_socket = "qcomGnss" @@ -399,7 +412,7 @@ def main(sm=None, pm=None, qc=None): replay = "REPLAY" in os.environ use_internet = "LAIKAD_NO_INTERNET" not in os.environ - laikad = Laikad(save_ephemeris=not replay, auto_fetch_orbits=use_internet, use_qcom=use_qcom) + laikad = Laikad(save_ephemeris=not replay, auto_fetch_navs=use_internet, use_qcom=use_qcom) while True: sm.update() @@ -409,17 +422,15 @@ def main(sm=None, pm=None, qc=None): msg = process_msg(laikad, gnss_msg, sm.logMonoTime[raw_gnss_socket], replay) if msg is None: + # TODO: beautify this, locationd needs a valid message msg = messaging.new_message("gnssMeasurements") - msg.valid = False - pm.send('gnssMeasurements', msg) if not laikad.got_first_gnss_msg and sm.updated['clocks']: clocks_msg = sm['clocks'] t = GPSTime.from_datetime(datetime.utcfromtimestamp(clocks_msg.wallTimeNanos * 1E-9)) - if laikad.auto_fetch_orbits: - laikad.fetch_orbits(t, block=replay) - + if laikad.auto_fetch_navs: + laikad.fetch_navs(t, block=replay) if __name__ == "__main__": main() diff --git a/selfdrive/locationd/liblocationd.cc b/selfdrive/locationd/liblocationd.cc index da57fb7ff4..32fec7724a 100755 --- a/selfdrive/locationd/liblocationd.cc +++ b/selfdrive/locationd/liblocationd.cc @@ -3,8 +3,8 @@ extern "C" { typedef Localizer* Localizer_t; - Localizer *localizer_init() { - return new Localizer(); + Localizer *localizer_init(bool has_ublox) { + return new Localizer(has_ublox); } void localizer_get_message_bytes(Localizer *localizer, bool inputsOK, bool sensorsOK, bool gpsOK, bool msgValid, diff --git a/selfdrive/locationd/locationd.cc b/selfdrive/locationd/locationd.cc index 442adcd347..928e19081e 100755 --- a/selfdrive/locationd/locationd.cc +++ b/selfdrive/locationd/locationd.cc @@ -30,10 +30,11 @@ const double GPS_UBLOX_SENSOR_TIME_OFFSET = 0.095; // s const float GPS_MUL_FACTOR = 10.0; const float GPS_POS_STD_THRESHOLD = 50.0; const float GPS_VEL_STD_THRESHOLD = 5.0; -const float GPS_POS_ERROR_RESET_THRESHOLD = 200.0; -const float GPS_POS_STD_RESET_THRESHOLD = 5.0; +const float GPS_POS_ERROR_RESET_THRESHOLD = 300.0; +const float GPS_POS_STD_RESET_THRESHOLD = 2.0; const float GPS_VEL_STD_RESET_THRESHOLD = 0.5; -const float GPS_ORIENTATION_ERROR_RESET_THRESHOLD = 5.0; +const float GPS_ORIENTATION_ERROR_RESET_THRESHOLD = 1.0; +const int GPS_ORIENTATION_ERROR_RESET_CNT = 3; static VectorXd floatlist2vector(const capnp::List::Reader& floatlist) { VectorXd res(floatlist.size()); @@ -84,6 +85,8 @@ Localizer::Localizer() { this->converter = std::make_unique((ECEF) { .x = ecef_pos[0], .y = ecef_pos[1], .z = ecef_pos[2] }); } +Localizer::Localizer(bool has_ublox) : Localizer() { ublox_available = has_ublox; } + void Localizer::build_live_location(cereal::LiveLocationKalman::Builder& fix) { VectorXd predicted_state = this->kf->get_x(); MatrixXdr predicted_cov = this->kf->get_P(); @@ -227,7 +230,7 @@ void Localizer::handle_sensor(double current_time, const cereal::SensorEventData } double sensor_time = 1e-9 * log.getTimestamp(); - + // sensor time and log time should be close if (std::abs(current_time - sensor_time) > 0.1) { LOGE("Sensor reading ignored, sensor timestamp more than 100ms off from log time"); @@ -308,7 +311,7 @@ void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::R } if (gps_invalid_flag || gps_unreasonable || gps_accuracy_insane || gps_lat_lng_alt_insane || gps_vel_insane || gps_accuracy_insane_quectel) { - this->gps_valid = false; + //this->gps_valid = false; this->determine_gps_mode(current_time); return; } @@ -316,7 +319,7 @@ void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::R double sensor_time = current_time - sensor_time_offset; // Process message - this->gps_valid = true; + //this->gps_valid = true; this->gps_mode = true; Geodetic geodetic = { log.getLatitude(), log.getLongitude(), log.getAltitude() }; this->converter = std::make_unique(geodetic); @@ -357,12 +360,10 @@ void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::R void Localizer::handle_gnss(double current_time, const cereal::GnssMeasurements::Reader& log) { - this->gps_valid = log.getPositionECEF().getValid() && log.getVelocityECEF().getValid(); - if (!this->gps_valid) { + if(!log.getPositionECEF().getValid() || !log.getVelocityECEF().getValid()) { this->determine_gps_mode(current_time); return; } - this->gps_mode = true; double sensor_time = log.getMeasTime() * 1e-9; if (ublox_available) @@ -406,29 +407,42 @@ void Localizer::handle_gnss(double current_time, const cereal::GnssMeasurements: VectorXd initial_pose_ecef_quat = quat2vector(euler2quat(ecef_euler_from_ned({ ecef_pos(0), ecef_pos(1), ecef_pos(2) }, orientation_ned_gps))); if (ecef_pos_std > GPS_POS_STD_THRESHOLD || ecef_vel_std > GPS_VEL_STD_THRESHOLD) { - this->gps_valid = false; this->determine_gps_mode(current_time); return; } + // prevent jumping gnss measurements (covered lots, standstill...) + bool orientation_reset = ecef_vel_std < GPS_VEL_STD_RESET_THRESHOLD; + orientation_reset &= orientation_error.norm() > GPS_ORIENTATION_ERROR_RESET_THRESHOLD; + orientation_reset &= !this->standstill; + if (orientation_reset) { + this->orientation_reset_count++; + } + else { + this->orientation_reset_count = 0; + } + if ((gps_est_error > GPS_POS_ERROR_RESET_THRESHOLD && ecef_pos_std < GPS_POS_STD_RESET_THRESHOLD) || this->last_gps_msg == 0) { // always reset on first gps message and if the location is off but the accuracy is high LOGE("Locationd vs gnssMeasurement position difference too large, kalman reset"); this->reset_kalman(NAN, initial_pose_ecef_quat, ecef_pos, ecef_vel, ecef_pos_R, ecef_vel_R); - } else if (ecef_vel_std < GPS_VEL_STD_RESET_THRESHOLD && orientation_error.norm() > GPS_ORIENTATION_ERROR_RESET_THRESHOLD) { + } else if (orientation_reset_count > GPS_ORIENTATION_ERROR_RESET_CNT) { LOGE("Locationd vs gnssMeasurement orientation difference too large, kalman reset"); this->reset_kalman(NAN, initial_pose_ecef_quat, ecef_pos, ecef_vel, ecef_pos_R, ecef_vel_R); this->kf->predict_and_observe(sensor_time, OBSERVATION_ECEF_ORIENTATION_FROM_GPS, { initial_pose_ecef_quat }); + this->orientation_reset_count = 0; } - this->last_gps_msg = current_time; + this->gps_mode = true; + this->last_gps_msg = sensor_time; this->kf->predict_and_observe(sensor_time, OBSERVATION_ECEF_POS, { ecef_pos }, { ecef_pos_R }); this->kf->predict_and_observe(sensor_time, OBSERVATION_ECEF_VEL, { ecef_vel }, { ecef_vel_R }); } void Localizer::handle_car_state(double current_time, const cereal::CarState::Reader& log) { this->car_speed = std::abs(log.getVEgo()); - if (log.getStandstill()) { + this->standstill = log.getStandstill(); + if (this->standstill) { this->kf->predict_and_observe(current_time, OBSERVATION_NO_ROT, { Vector3d(0.0, 0.0, 0.0) }); this->kf->predict_and_observe(current_time, OBSERVATION_NO_ACCEL, { Vector3d(0.0, 0.0, 0.0) }); } @@ -437,7 +451,7 @@ void Localizer::handle_car_state(double current_time, const cereal::CarState::Re void Localizer::handle_cam_odo(double current_time, const cereal::CameraOdometry::Reader& log) { VectorXd rot_device = this->device_from_calib * floatlist2vector(log.getRot()); VectorXd trans_device = this->device_from_calib * floatlist2vector(log.getTrans()); - + if (!this->is_timestamp_valid(current_time)) { this->observation_timings_invalid = true; return; @@ -574,12 +588,12 @@ void Localizer::handle_msg(const cereal::Event::Reader& log) { this->handle_sensor(t, log.getAccelerometer()); } else if (log.isGyroscope()) { this->handle_sensor(t, log.getGyroscope()); - } else if (log.isGpsLocation()) { - this->handle_gps(t, log.getGpsLocation(), GPS_QUECTEL_SENSOR_TIME_OFFSET); - } else if (log.isGpsLocationExternal()) { - this->handle_gps(t, log.getGpsLocationExternal(), GPS_UBLOX_SENSOR_TIME_OFFSET); - //} else if (log.isGnssMeasurements()) { - // this->handle_gnss(t, log.getGnssMeasurements()); + //} else if (log.isGpsLocation()) { + // this->handle_gps(t, log.getGpsLocation(), GPS_QUECTEL_SENSOR_TIME_OFFSET); + //} else if (log.isGpsLocationExternal()) { + // this->handle_gps(t, log.getGpsLocationExternal(), GPS_UBLOX_SENSOR_TIME_OFFSET); + } else if (log.isGnssMeasurements()) { + this->handle_gnss(t, log.getGnssMeasurements()); } else if (log.isCarState()) { this->handle_car_state(t, log.getCarState()); } else if (log.isCameraOdometry()) { @@ -604,7 +618,7 @@ kj::ArrayPtr Localizer::get_message_bytes(MessageBuilder& msg_build } bool Localizer::is_gps_ok() { - return this->gps_valid; + return (this->kf->get_filter_time() - this->last_gps_msg) < 1.0; } bool Localizer::critical_services_valid(std::map critical_services) { @@ -616,7 +630,7 @@ bool Localizer::critical_services_valid(std::map critical_s return true; } -bool Localizer::is_timestamp_valid(double current_time) { +bool Localizer::is_timestamp_valid(double current_time) { double filter_time = this->kf->get_filter_time(); if (!std::isnan(filter_time) && ((filter_time - current_time) > MAX_FILTER_REWIND_TIME)) { LOGE("Observation timestamp is older than the max rewind threshold of the filter"); @@ -643,19 +657,12 @@ void Localizer::determine_gps_mode(double current_time) { int Localizer::locationd_thread() { ublox_available = Params().getBool("UbloxAvailable", true); - - const char* gps_location_socket; - if (ublox_available) { - gps_location_socket = "gpsLocationExternal"; - } else { - gps_location_socket = "gpsLocation"; - } - const std::initializer_list service_list = {gps_location_socket, "cameraOdometry", "liveCalibration", + const std::initializer_list service_list = {"gnssMeasurements", "cameraOdometry", "liveCalibration", "carState", "carParams", "accelerometer", "gyroscope"}; - PubMaster pm({"liveLocationKalman"}); // TODO: remove carParams once we're always sending at 100Hz - SubMaster sm(service_list, {}, nullptr, {gps_location_socket, "carParams"}); + SubMaster sm(service_list, {}, nullptr, {"gnssMeasurements", "carParams"}); + PubMaster pm({"liveLocationKalman"}); uint64_t cnt = 0; bool filterInitialized = false; diff --git a/selfdrive/locationd/locationd.h b/selfdrive/locationd/locationd.h index 7a282be9d7..a292a3c936 100755 --- a/selfdrive/locationd/locationd.h +++ b/selfdrive/locationd/locationd.h @@ -24,6 +24,7 @@ class Localizer { public: Localizer(); + Localizer(bool has_ublox); int locationd_thread(); @@ -77,9 +78,10 @@ private: double reset_tracker = 0.0; bool device_fell = false; bool gps_mode = false; - bool gps_valid = false; double last_gps_msg = 0; bool ublox_available = true; bool observation_timings_invalid = false; - std::map observation_values_invalid; + std::map observation_values_invalid; + bool standstill = true; + int32_t orientation_reset_count = 0; }; diff --git a/selfdrive/locationd/test/_test_locationd_lib.py b/selfdrive/locationd/test/_test_locationd_lib.py index c4a053bbc6..819bb1570e 100755 --- a/selfdrive/locationd/test/_test_locationd_lib.py +++ b/selfdrive/locationd/test/_test_locationd_lib.py @@ -19,7 +19,7 @@ LIBLOCATIOND_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '../ class TestLocationdLib(unittest.TestCase): def setUp(self): header = '''typedef ...* Localizer_t; -Localizer_t localizer_init(); +Localizer_t localizer_init(bool has_ublox); void localizer_get_message_bytes(Localizer_t localizer, bool inputsOK, bool sensorsOK, bool gpsOK, bool msgValid, char *buff, size_t buff_size); void localizer_handle_msg_bytes(Localizer_t localizer, const char *data, size_t size);''' @@ -27,7 +27,7 @@ void localizer_handle_msg_bytes(Localizer_t localizer, const char *data, size_t self.ffi.cdef(header) self.lib = self.ffi.dlopen(LIBLOCATIOND_PATH) - self.localizer = self.lib.localizer_init() + self.localizer = self.lib.localizer_init(True) # default to ublox self.buff_size = 2048 self.msg_buff = self.ffi.new(f'char[{self.buff_size}]') diff --git a/selfdrive/locationd/test/test_laikad.py b/selfdrive/locationd/test/test_laikad.py index 198ecfe935..6059eab68f 100755 --- a/selfdrive/locationd/test/test_laikad.py +++ b/selfdrive/locationd/test/test_laikad.py @@ -4,7 +4,7 @@ import unittest from collections import defaultdict from datetime import datetime from unittest import mock -from unittest.mock import Mock, patch +from unittest.mock import patch from common.params import Params from laika.constants import SECS_IN_DAY @@ -67,47 +67,47 @@ class TestLaikad(unittest.TestCase): def setUp(self): Params().remove(EPHEMERIS_CACHE) - def test_fetch_orbits_non_blocking(self): + def test_fetch_navs_non_blocking(self): gpstime = GPSTime.from_datetime(datetime(2021, month=3, day=1)) laikad = Laikad() - laikad.fetch_orbits(gpstime, block=False) + laikad.fetch_navs(gpstime, block=False) laikad.orbit_fetch_future.result(30) # Get results and save orbits to laikad: - laikad.fetch_orbits(gpstime, block=False) + laikad.fetch_navs(gpstime, block=False) - ephem = laikad.astro_dog.orbits['G01'][0] + ephem = laikad.astro_dog.navs['G01'][0] self.assertIsNotNone(ephem) - laikad.fetch_orbits(gpstime+2*SECS_IN_DAY, block=False) + laikad.fetch_navs(gpstime+2*SECS_IN_DAY, block=False) laikad.orbit_fetch_future.result(30) # Get results and save orbits to laikad: - laikad.fetch_orbits(gpstime + 2 * SECS_IN_DAY, block=False) + laikad.fetch_navs(gpstime + 2 * SECS_IN_DAY, block=False) - ephem2 = laikad.astro_dog.orbits['G01'][0] + ephem2 = laikad.astro_dog.navs['G01'][0] self.assertIsNotNone(ephem) self.assertNotEqual(ephem, ephem2) - def test_fetch_orbits_with_wrong_clocks(self): + def test_fetch_navs_with_wrong_clocks(self): laikad = Laikad() - def check_has_orbits(): - self.assertGreater(len(laikad.astro_dog.orbits), 0) - ephem = laikad.astro_dog.orbits['G01'][0] + def check_has_navs(): + self.assertGreater(len(laikad.astro_dog.navs), 0) + ephem = laikad.astro_dog.navs['G01'][0] self.assertIsNotNone(ephem) real_current_time = GPSTime.from_datetime(datetime(2021, month=3, day=1)) wrong_future_clock_time = real_current_time + SECS_IN_DAY - laikad.fetch_orbits(wrong_future_clock_time, block=True) - check_has_orbits() - self.assertEqual(laikad.last_fetch_orbits_t, wrong_future_clock_time) + laikad.fetch_navs(wrong_future_clock_time, block=True) + check_has_navs() + self.assertEqual(laikad.last_fetch_navs_t, wrong_future_clock_time) # Test fetching orbits with earlier time - assert real_current_time < laikad.last_fetch_orbits_t + assert real_current_time < laikad.last_fetch_navs_t laikad.astro_dog.orbits = {} - laikad.fetch_orbits(real_current_time, block=True) - check_has_orbits() - self.assertEqual(laikad.last_fetch_orbits_t, real_current_time) + laikad.fetch_navs(real_current_time, block=True) + check_has_navs() + self.assertEqual(laikad.last_fetch_navs_t, real_current_time) def test_ephemeris_source_in_msg(self): data_mock = defaultdict(str) @@ -115,22 +115,22 @@ class TestLaikad(unittest.TestCase): gpstime = GPS_TIME_PREDICTION_ORBITS_RUSSIAN_SRC laikad = Laikad() - laikad.fetch_orbits(gpstime, block=True) - meas = get_measurement_mock(gpstime, laikad.astro_dog.orbits['R01'][0]) + laikad.fetch_navs(gpstime, block=True) + meas = get_measurement_mock(gpstime, laikad.astro_dog.navs['R01'][0]) msg = create_measurement_msg(meas) - self.assertEqual(msg.ephemerisSource.type.raw, EphemerisSourceType.glonassIacUltraRapid) + self.assertEqual(msg.ephemerisSource.type.raw, EphemerisSourceType.nav) # Verify gps satellite returns same source - meas = get_measurement_mock(gpstime, laikad.astro_dog.orbits['R01'][0]) + meas = get_measurement_mock(gpstime, laikad.astro_dog.navs['R01'][0]) msg = create_measurement_msg(meas) - self.assertEqual(msg.ephemerisSource.type.raw, EphemerisSourceType.glonassIacUltraRapid) + self.assertEqual(msg.ephemerisSource.type.raw, EphemerisSourceType.nav) # Test nasa source by using older date gpstime = GPSTime.from_datetime(datetime(2021, month=3, day=1)) laikad = Laikad() - laikad.fetch_orbits(gpstime, block=True) - meas = get_measurement_mock(gpstime, laikad.astro_dog.orbits['G01'][0]) + laikad.fetch_navs(gpstime, block=True) + meas = get_measurement_mock(gpstime, laikad.astro_dog.navs['G01'][0]) msg = create_measurement_msg(meas) - self.assertEqual(msg.ephemerisSource.type.raw, EphemerisSourceType.nasaUltraRapid) + self.assertEqual(msg.ephemerisSource.type.raw, EphemerisSourceType.nav) # Test nav source type ephem = GPSEphemeris(data_mock, gpstime) @@ -142,7 +142,7 @@ class TestLaikad(unittest.TestCase): laikad = Laikad(auto_update=True, valid_ephem_types=EphemerisType.ULTRA_RAPID_ORBIT) correct_msgs = verify_messages(self.logs, laikad) - correct_msgs_expected = 555 + correct_msgs_expected = 554 self.assertEqual(correct_msgs_expected, len(correct_msgs)) self.assertEqual(correct_msgs_expected, len([m for m in correct_msgs if m.gnssMeasurements.positionECEF.valid])) @@ -161,9 +161,8 @@ class TestLaikad(unittest.TestCase): def test_laika_online_nav_only(self): laikad = Laikad(auto_update=True, valid_ephem_types=EphemerisType.NAV) # Disable fetch_orbits to test NAV only - laikad.fetch_orbits = Mock() correct_msgs = verify_messages(self.logs, laikad) - correct_msgs_expected = 559 + correct_msgs_expected = 554 self.assertEqual(correct_msgs_expected, len(correct_msgs)) self.assertEqual(correct_msgs_expected, len([m for m in correct_msgs if m.gnssMeasurements.positionECEF.valid])) @@ -171,45 +170,45 @@ class TestLaikad(unittest.TestCase): def test_laika_offline(self, downloader_mock): downloader_mock.side_effect = DownloadFailed("Mock download failed") laikad = Laikad(auto_update=False) - laikad.fetch_orbits(GPS_TIME_PREDICTION_ORBITS_RUSSIAN_SRC, block=True) + laikad.fetch_navs(GPS_TIME_PREDICTION_ORBITS_RUSSIAN_SRC, block=True) @mock.patch('laika.downloader.download_and_cache_file') def test_download_failed_russian_source(self, downloader_mock): downloader_mock.side_effect = DownloadFailed laikad = Laikad(auto_update=False) correct_msgs = verify_messages(self.logs, laikad) - self.assertEqual(16, len(correct_msgs)) - self.assertEqual(16, len([m for m in correct_msgs if m.gnssMeasurements.positionECEF.valid])) + self.assertEqual(0, len(correct_msgs)) + self.assertEqual(0, len([m for m in correct_msgs if m.gnssMeasurements.positionECEF.valid])) def test_laika_get_orbits(self): laikad = Laikad(auto_update=False) # Pretend process has loaded the orbits on startup by using the time of the first gps message. - laikad.fetch_orbits(self.first_gps_time, block=True) - self.dict_has_values(laikad.astro_dog.orbits) + laikad.fetch_navs(self.first_gps_time, block=True) + self.dict_has_values(laikad.astro_dog.navs) @unittest.skip("Use to debug live data") - def test_laika_get_orbits_now(self): + def test_laika_get_navs_now(self): laikad = Laikad(auto_update=False) - laikad.fetch_orbits(GPSTime.from_datetime(datetime.utcnow()), block=True) + laikad.fetch_navs(GPSTime.from_datetime(datetime.utcnow()), block=True) prn = "G01" - self.assertGreater(len(laikad.astro_dog.orbits[prn]), 0) + self.assertGreater(len(laikad.astro_dog.navs[prn]), 0) prn = "R01" - self.assertGreater(len(laikad.astro_dog.orbits[prn]), 0) - print(min(laikad.astro_dog.orbits[prn], key=lambda e: e.epoch).epoch.as_datetime()) + self.assertGreater(len(laikad.astro_dog.navs[prn]), 0) + print(min(laikad.astro_dog.navs[prn], key=lambda e: e.epoch).epoch.as_datetime()) - def test_get_orbits_in_process(self): + def test_get_navs_in_process(self): laikad = Laikad(auto_update=False) - has_orbits = False + has_navs = False for m in self.logs: laikad.process_gnss_msg(m.ubloxGnss, m.logMonoTime, block=False) if laikad.orbit_fetch_future is not None: laikad.orbit_fetch_future.result() - vals = laikad.astro_dog.orbits.values() - has_orbits = len(vals) > 0 and max([len(v) for v in vals]) > 0 - if has_orbits: + vals = laikad.astro_dog.navs.values() + has_navs = len(vals) > 0 and max([len(v) for v in vals]) > 0 + if has_navs: break - self.assertTrue(has_orbits) - self.assertGreater(len(laikad.astro_dog.orbit_fetched_times._ranges), 0) + self.assertTrue(has_navs) + self.assertGreater(len(laikad.astro_dog.navs_fetched_times._ranges), 0) self.assertEqual(None, laikad.orbit_fetch_future) def test_cache(self): @@ -228,17 +227,16 @@ class TestLaikad(unittest.TestCase): wait_for_cache() Params().remove(EPHEMERIS_CACHE) - laikad.astro_dog.get_navs(self.first_gps_time) - laikad.fetch_orbits(self.first_gps_time, block=True) + #laikad.astro_dog.get_navs(self.first_gps_time) + laikad.fetch_navs(self.first_gps_time, block=True) # Wait for cache to save wait_for_cache() # Check both nav and orbits separate laikad = Laikad(auto_update=False, valid_ephem_types=EphemerisType.NAV, save_ephemeris=True) - # Verify orbits and nav are loaded from cache - self.dict_has_values(laikad.astro_dog.orbits) - self.dict_has_values(laikad.astro_dog.nav) + # Verify navs are loaded from cache + self.dict_has_values(laikad.astro_dog.navs) # Verify cache is working for only nav by running a segment msg = verify_messages(self.logs, laikad, return_one_success=True) self.assertIsNotNone(msg) @@ -246,7 +244,7 @@ class TestLaikad(unittest.TestCase): with patch('selfdrive.locationd.laikad.get_orbit_data', return_value=None) as mock_method: # Verify no orbit downloads even if orbit fetch times is reset since the cache has recently been saved and we don't want to download high frequently laikad.astro_dog.orbit_fetched_times = TimeRangeHolder() - laikad.fetch_orbits(self.first_gps_time, block=False) + laikad.fetch_navs(self.first_gps_time, block=False) mock_method.assert_not_called() # Verify cache is working for only orbits by running a segment diff --git a/selfdrive/locationd/test/test_locationd.py b/selfdrive/locationd/test/test_locationd.py index 7569530211..6e65acaaf9 100755 --- a/selfdrive/locationd/test/test_locationd.py +++ b/selfdrive/locationd/test/test_locationd.py @@ -8,13 +8,14 @@ import capnp import cereal.messaging as messaging from cereal.services import service_list from common.params import Params +from common.transformations.coordinates import ecef2geodetic from selfdrive.manager.process_config import managed_processes class TestLocationdProc(unittest.TestCase): MAX_WAITS = 1000 - LLD_MSGS = ['gpsLocationExternal', 'cameraOdometry', 'carState', 'liveCalibration', + LLD_MSGS = ['gnssMeasurements', 'cameraOdometry', 'carState', 'liveCalibration', 'accelerometer', 'gyroscope', 'magnetometer'] def setUp(self): @@ -45,16 +46,14 @@ class TestLocationdProc(unittest.TestCase): except capnp.lib.capnp.KjException: msg = messaging.new_message(name, 0) - if name == "gpsLocationExternal": - msg.gpsLocationExternal.flags = 1 - msg.gpsLocationExternal.verticalAccuracy = 1.0 - msg.gpsLocationExternal.speedAccuracy = 1.0 - msg.gpsLocationExternal.bearingAccuracyDeg = 1.0 - msg.gpsLocationExternal.vNED = [0.0, 0.0, 0.0] - msg.gpsLocationExternal.latitude = self.lat - msg.gpsLocationExternal.longitude = self.lon - msg.gpsLocationExternal.unixTimestampMillis = t * 1e6 - msg.gpsLocationExternal.altitude = self.alt + if name == "gnssMeasurements": + msg.gnssMeasurements.measTime = t + msg.gnssMeasurements.positionECEF.value = [self.x , self.y, self.z] + msg.gnssMeasurements.positionECEF.std = [0,0,0] + msg.gnssMeasurements.positionECEF.valid = True + msg.gnssMeasurements.velocityECEF.value = [] + msg.gnssMeasurements.velocityECEF.std = [0,0,0] + msg.gnssMeasurements.velocityECEF.valid = True elif name == 'cameraOdometry': msg.cameraOdometry.rot = [0.0, 0.0, 0.0] msg.cameraOdometry.rotStd = [0.0, 0.0, 0.0] @@ -67,9 +66,11 @@ class TestLocationdProc(unittest.TestCase): # first reset params Params().remove('LastGPSPosition') - self.lat = 30 + (random.random() * 10.0) - self.lon = -70 + (random.random() * 10.0) - self.alt = 5 + (random.random() * 10.0) + self.x = -2710700 + (random.random() * 1e5) + self.y = -4280600 + (random.random() * 1e5) + self.z = 3850300 + (random.random() * 1e5) + self.lat, self.lon, self.alt = ecef2geodetic([self.x, self.y, self.z]) + self.fake_duration = 90 # secs # get fake messages at the correct frequency, listed in services.py fake_msgs = [] @@ -83,10 +84,9 @@ class TestLocationdProc(unittest.TestCase): time.sleep(1) # wait for async params write lastGPS = json.loads(Params().get('LastGPSPosition')) - - self.assertAlmostEqual(lastGPS['latitude'], self.lat, places=3) - self.assertAlmostEqual(lastGPS['longitude'], self.lon, places=3) - self.assertAlmostEqual(lastGPS['altitude'], self.alt, places=3) + self.assertAlmostEqual(lastGPS['latitude'], self.lat, places=4) + self.assertAlmostEqual(lastGPS['longitude'], self.lon, places=4) + self.assertAlmostEqual(lastGPS['altitude'], self.alt, places=4) if __name__ == "__main__": diff --git a/selfdrive/locationd/ublox_msg.cc b/selfdrive/locationd/ublox_msg.cc index b45dffae33..6431e9d48b 100644 --- a/selfdrive/locationd/ublox_msg.cc +++ b/selfdrive/locationd/ublox_msg.cc @@ -192,6 +192,7 @@ kj::Array UbloxMsgParser::gen_rxm_sfrbx(ubx_t::rxm_sfrbx_t *msg) { eph.setAf2(subframe_1->af_2() * pow(2, -55)); eph.setAf1(subframe_1->af_1() * pow(2, -43)); eph.setAf0(subframe_1->af_0() * pow(2, -31)); + eph.setSvHealth(subframe_1->sv_health()); } // Subframe 2 diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 22ec099285..24ee87e471 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -331,7 +331,7 @@ CONFIGS = [ pub_sub={ "cameraOdometry": ["liveLocationKalman"], "accelerometer": [], "gyroscope": [], - "gpsLocationExternal": [], "liveCalibration": [], "carState": [], + "gnssMeasurements": [], "liveCalibration": [], "carState": [], }, ignore=["logMonoTime", "valid"], init_callback=get_car_params, diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 157d146ade..58b6139d3e 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -ff0348b1957c8f992125df77a36b3c0553a26e6a +e41c3dd90cf61d0a630ba53c7e866cb2be3fc1a8 \ No newline at end of file From c1b9c3d3d1c928df929bce0cd1959d06e1091ccf Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 14 Jan 2023 03:40:34 +0800 Subject: [PATCH 077/484] Cabana: save signal on editingFinished (#26940) save signal on editingFinished --- tools/cabana/signaledit.cc | 32 ++++++++++++++++---------------- tools/cabana/signaledit.h | 7 +++---- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/tools/cabana/signaledit.cc b/tools/cabana/signaledit.cc index b2491d5b1f..3845e72be1 100644 --- a/tools/cabana/signaledit.cc +++ b/tools/cabana/signaledit.cc @@ -54,12 +54,20 @@ SignalForm::SignalForm(QWidget *parent) : QWidget(parent) { val_desc = new QLineEdit(); form_layout->addRow(tr("Value descriptions"), val_desc); - QObject::connect(name, &QLineEdit::textEdited, this, &SignalForm::changed); - QObject::connect(factor, &QLineEdit::textEdited, this, &SignalForm::changed); - QObject::connect(offset, &QLineEdit::textEdited, this, &SignalForm::changed); + QObject::connect(name, &QLineEdit::editingFinished, this, &SignalForm::textBoxEditingFinished); + QObject::connect(factor, &QLineEdit::editingFinished, this, &SignalForm::textBoxEditingFinished); + QObject::connect(offset, &QLineEdit::editingFinished, this, &SignalForm::textBoxEditingFinished); + QObject::connect(size, &QSpinBox::editingFinished, this, &SignalForm::changed); QObject::connect(sign, SIGNAL(activated(int)), SIGNAL(changed())); QObject::connect(endianness, SIGNAL(activated(int)), SIGNAL(changed())); - QObject::connect(size, SIGNAL(valueChanged(int)), SIGNAL(changed())); +} + +void SignalForm::textBoxEditingFinished() { + QLineEdit *edit = qobject_cast(QObject::sender()); + if (edit && edit->isModified()) { + edit->setModified(false); + emit changed(); + } } // SignalEdit @@ -108,17 +116,12 @@ SignalEdit::SignalEdit(int index, QWidget *parent) : form_idx(index), QWidget(pa hline->setFrameShadow(QFrame::Sunken); main_layout->addWidget(hline); - save_timer = new QTimer(this); - save_timer->setInterval(300); - save_timer->setSingleShot(true); - save_timer->callOnTimeout(this, &SignalEdit::saveSignal); - QObject::connect(title, &ElidedLabel::clicked, [this]() { emit showFormClicked(sig); }); QObject::connect(plot_btn, &QToolButton::clicked, [this](bool checked) { emit showChart(msg_id, sig, checked, QGuiApplication::keyboardModifiers() & Qt::ShiftModifier); }); - QObject::connect(remove_btn, &QToolButton::clicked, [this]() { emit remove(sig); }); - QObject::connect(form, &SignalForm::changed, [this]() { save_timer->start(); }); + QObject::connect(remove_btn, &QToolButton::clicked, [this]() { emit remove(sig); }); + QObject::connect(form, &SignalForm::changed, this, &SignalEdit::saveSignal); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); } @@ -133,8 +136,6 @@ void SignalEdit::setSignal(const QString &message_id, const Signal *signal) { } void SignalEdit::saveSignal() { - if (!sig || !form->changed_by_user) return; - Signal s = *sig; s.name = form->name->text().toStdString(); s.size = form->size->text().toInt(); @@ -160,8 +161,9 @@ void SignalEdit::saveSignal() { s.lsb = bigEndianStartBitsIndex(bigEndianBitIndex(s.start_bit) + s.size - 1); s.msb = s.start_bit; } - if (s != *sig) + if (s != *sig) { emit save(this->sig, s); + } } void SignalEdit::setChartOpened(bool opened) { @@ -171,7 +173,6 @@ void SignalEdit::setChartOpened(bool opened) { void SignalEdit::updateForm(bool visible) { if (visible && sig) { - form->changed_by_user = false; if (form->name->text() != sig->name.c_str()) { form->name->setText(sig->name.c_str()); } @@ -182,7 +183,6 @@ void SignalEdit::updateForm(bool visible) { form->msb->setText(QString::number(sig->msb)); form->lsb->setText(QString::number(sig->lsb)); form->size->setValue(sig->size); - form->changed_by_user = true; } form->setVisible(visible); icon->setText(visible ? "▼ " : "> "); diff --git a/tools/cabana/signaledit.h b/tools/cabana/signaledit.h index eec943226e..d7b9084e7f 100644 --- a/tools/cabana/signaledit.h +++ b/tools/cabana/signaledit.h @@ -4,7 +4,6 @@ #include #include #include -#include #include #include "selfdrive/ui/qt/widgets/controls.h" @@ -15,13 +14,14 @@ class SignalForm : public QWidget { Q_OBJECT public: SignalForm(QWidget *parent); + void textBoxEditingFinished(); + QLineEdit *name, *unit, *comment, *val_desc, *offset, *factor, *min_val, *max_val; QLabel *lsb, *msb; QSpinBox *size; QComboBox *sign, *endianness; - bool changed_by_user = false; - signals: +signals: void changed(); }; @@ -55,5 +55,4 @@ protected: QLabel *icon; int form_idx = 0; QToolButton *plot_btn; - QTimer *save_timer; }; From 83d619e457a27f7ac2f1a7fc7c297dc257aa4430 Mon Sep 17 00:00:00 2001 From: Erich Moraga <33645296+ErichMoraga@users.noreply.github.com> Date: Fri, 13 Jan 2023 13:49:33 -0600 Subject: [PATCH 078/484] Add missing COROLLA_TSS2 engine f/w (#26936) add missing engine Co-authored-by: Shane Smiskol --- selfdrive/car/toyota/values.py | 1 + 1 file changed, 1 insertion(+) diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index 586b1a835e..c413028a25 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -765,6 +765,7 @@ FW_VERSIONS = { b'\x03312N6100\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203402\x00\x00\x00\x00', b'\x03312N6200\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203202\x00\x00\x00\x00', b'\x03312N6200\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203302\x00\x00\x00\x00', + b'\x03312N6200\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203402\x00\x00\x00\x00', b'\x02312K4000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00', b'\x02312U5000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00', ], From 2e36d07106c7e4e8e12afe0c33762abbf217a416 Mon Sep 17 00:00:00 2001 From: Vivek Aithal Date: Fri, 13 Jan 2023 13:23:06 -0800 Subject: [PATCH 079/484] paramsd: Remove one active condition (#26922) * remove one active condition * update refs * remove unused vars * update refs * update refs --- selfdrive/locationd/paramsd.py | 4 +--- selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/selfdrive/locationd/paramsd.py b/selfdrive/locationd/paramsd.py index 9bd4ed0837..7e7c2b091f 100755 --- a/selfdrive/locationd/paramsd.py +++ b/selfdrive/locationd/paramsd.py @@ -36,7 +36,6 @@ class ParamsLearner: self.yaw_rate = 0.0 self.yaw_rate_std = 0.0 self.roll = 0.0 - self.steering_pressed = False self.steering_angle = 0.0 self.valid = True @@ -88,10 +87,9 @@ class ParamsLearner: elif which == 'carState': self.steering_angle = msg.steeringAngleDeg - self.steering_pressed = msg.steeringPressed self.speed = msg.vEgo - in_linear_region = abs(self.steering_angle) < 45 or not self.steering_pressed + in_linear_region = abs(self.steering_angle) < 45 self.active = self.speed > 1 and in_linear_region if self.active: diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 58b6139d3e..ea45fb4957 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -e41c3dd90cf61d0a630ba53c7e866cb2be3fc1a8 \ No newline at end of file +c714ab010923f2bce732bd22e903ccc4454136fd \ No newline at end of file From 50f62a7adc011c753aa846807a8eabfdbdf93996 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 13 Jan 2023 13:46:01 -0800 Subject: [PATCH 080/484] revert timeout increase from #26850 --- .github/workflows/tools_tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tools_tests.yaml b/.github/workflows/tools_tests.yaml index 7ad0ce35b1..94cc3c2580 100644 --- a/.github/workflows/tools_tests.yaml +++ b/.github/workflows/tools_tests.yaml @@ -38,7 +38,7 @@ jobs: - name: Build Docker image run: eval "$BUILD" - name: Unit test - timeout-minutes: 6 + timeout-minutes: 2 run: | ${{ env.RUN }} "scons -j$(nproc) --directory=/tmp/openpilot/cereal && \ apt-get update && \ From 23bd5d6f5d554b4117671605f2a99f6e37903789 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 13 Jan 2023 14:49:01 -0800 Subject: [PATCH 081/484] clutil: use logging functions (#26950) Co-authored-by: Comma Device --- common/clutil.cc | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/common/clutil.cc b/common/clutil.cc index 9d3447d807..3cfc8a8c8c 100644 --- a/common/clutil.cc +++ b/common/clutil.cc @@ -5,6 +5,7 @@ #include #include "common/util.h" +#include "common/swaglog.h" namespace { // helper functions @@ -31,14 +32,14 @@ void cl_print_info(cl_platform_id platform, cl_device_id device) { case CL_DEVICE_TYPE_ACCELERATOR: type_str = "CL_DEVICE_TYPE_ACCELERATOR"; break; } - std::cout << "vendor: " << get_platform_info(platform, CL_PLATFORM_VENDOR) << std::endl - << "platform version: " << get_platform_info(platform, CL_PLATFORM_VERSION) << std::endl - << "profile: " << get_platform_info(platform, CL_PLATFORM_PROFILE) << std::endl - << "extensions: " << get_platform_info(platform, CL_PLATFORM_EXTENSIONS) << std::endl - << "name :" << get_device_info(device, CL_DEVICE_NAME) << std::endl - << "device version :" << get_device_info(device, CL_DEVICE_VERSION) << std::endl - << "max work group size :" << work_group_size << std::endl - << "type = " << device_type << " = " << type_str << std::endl; + LOGD("vendor: %s", get_platform_info(platform, CL_PLATFORM_VENDOR).c_str()); + LOGD("platform version: %s", get_platform_info(platform, CL_PLATFORM_VERSION).c_str()); + LOGD("profile: %s", get_platform_info(platform, CL_PLATFORM_PROFILE).c_str()); + LOGD("extensions: %s", get_platform_info(platform, CL_PLATFORM_EXTENSIONS).c_str()); + LOGD("name: %s", get_device_info(device, CL_DEVICE_NAME).c_str()); + LOGD("device version: %s", get_device_info(device, CL_DEVICE_VERSION).c_str()); + LOGD("max work group size: %d", work_group_size); + LOGD("type = %d = ", device_type, type_str); } void cl_print_build_errors(cl_program program, cl_device_id device) { @@ -49,7 +50,7 @@ void cl_print_build_errors(cl_program program, cl_device_id device) { std::string log(log_size, '\0'); clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, log_size, &log[0], NULL); - std::cout << "build failed; status=" << status << ", log:" << std::endl << log << std::endl; + LOGE("build failed; status=%d, log: %s", status, log.c_str()); } } // namespace @@ -61,14 +62,15 @@ cl_device_id cl_get_device_id(cl_device_type device_type) { CL_CHECK(clGetPlatformIDs(num_platforms, &platform_ids[0], NULL)); for (size_t i = 0; i < num_platforms; ++i) { - std::cout << "platform[" << i << "] CL_PLATFORM_NAME: " << get_platform_info(platform_ids[i], CL_PLATFORM_NAME) << std::endl; + LOGD("platform[%d] CL_PLATFORM_NAME: %s", i, get_platform_info(platform_ids[i], CL_PLATFORM_NAME).c_str()); + // Get first device if (cl_device_id device_id = NULL; clGetDeviceIDs(platform_ids[i], device_type, 1, &device_id, NULL) == 0 && device_id) { cl_print_info(platform_ids[i], device_id); return device_id; } } - std::cout << "No valid openCL platform found" << std::endl; + LOGE("No valid openCL platform found"); assert(0); return nullptr; } From 3136985b950e79cf2943dafa2cea4db5ad0f528b Mon Sep 17 00:00:00 2001 From: Robbe Derks Date: Fri, 13 Jan 2023 15:01:55 -0800 Subject: [PATCH 082/484] No more magic for the can chunks (#26861) * remove magic and add checksum * add comms reset * bump submodule --- panda | 2 +- selfdrive/boardd/panda.cc | 71 ++++++++++++------- selfdrive/boardd/panda.h | 8 ++- .../boardd/tests/test_boardd_usbprotocol.cc | 37 +++++++--- 4 files changed, 80 insertions(+), 38 deletions(-) diff --git a/panda b/panda index 0b3b906036..11d90f9e78 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 0b3b9060365739eeeddb1528cfe4018fec4efe7a +Subproject commit 11d90f9e78b1c070e44e02d5d8c2b18790617324 diff --git a/selfdrive/boardd/panda.cc b/selfdrive/boardd/panda.cc index 3e447c830e..d4d53aa230 100644 --- a/selfdrive/boardd/panda.cc +++ b/selfdrive/boardd/panda.cc @@ -26,6 +26,8 @@ Panda::Panda(std::string serial, uint32_t bus_offset) : bus_offset(bus_offset) { (hw_type == cereal::PandaState::PandaType::DOS) || (hw_type == cereal::PandaState::PandaType::TRES); + can_reset_communications(); + return; } @@ -174,10 +176,6 @@ void Panda::pack_can_buffer(const capnp::List::Reader &can_data int32_t pos = 0; uint8_t send_buf[2 * USB_TX_SOFT_LIMIT]; - uint32_t magic = CAN_TRANSACTION_MAGIC; - memcpy(&send_buf[0], &magic, sizeof(uint32_t)); - pos += sizeof(uint32_t); - for (auto cmsg : can_data_list) { // check if the message is intended for this panda uint8_t bus = cmsg.getSrc(); @@ -194,20 +192,25 @@ void Panda::pack_can_buffer(const capnp::List::Reader &can_data header.extended = (cmsg.getAddress() >= 0x800) ? 1 : 0; header.data_len_code = data_len_code; header.bus = bus - bus_offset; + header.checksum = 0; memcpy(&send_buf[pos], (uint8_t *)&header, sizeof(can_header)); - pos += sizeof(can_header); - memcpy(&send_buf[pos], (uint8_t *)can_data.begin(), can_data.size()); - pos += can_data.size(); + memcpy(&send_buf[pos + sizeof(can_header)], (uint8_t *)can_data.begin(), can_data.size()); + uint32_t msg_size = sizeof(can_header) + can_data.size(); + + // set checksum + ((can_header *) &send_buf[pos])->checksum = calculate_checksum(&send_buf[pos], msg_size); + + pos += msg_size; if (pos >= USB_TX_SOFT_LIMIT) { write_func(send_buf, pos); - pos = sizeof(uint32_t); + pos = 0; } } // send remaining packets - if (pos > sizeof(uint32_t)) write_func(send_buf, pos); + if (pos > 0) write_func(send_buf, pos); } void Panda::can_send(capnp::List::Reader can_data_list) { @@ -217,36 +220,35 @@ void Panda::can_send(capnp::List::Reader can_data_list) { } bool Panda::can_receive(std::vector& out_vec) { - uint8_t data[RECV_SIZE]; - int recv = handle->bulk_read(0x81, (uint8_t*)data, RECV_SIZE); + int recv = handle->bulk_read(0x81, &receive_buffer[receive_buffer_size], RECV_SIZE); if (!comms_healthy()) { return false; } if (recv == RECV_SIZE) { LOGW("Panda receive buffer full"); } + receive_buffer_size += recv; - return (recv <= 0) ? true : unpack_can_buffer(data, recv, out_vec); + return (recv <= 0) ? true : unpack_can_buffer(receive_buffer, receive_buffer_size, out_vec); } -bool Panda::unpack_can_buffer(uint8_t *data, int size, std::vector &out_vec) { - if (size < sizeof(uint32_t)) { - return true; - } +void Panda::can_reset_communications() { + handle->control_write(0xc0, 0, 0); +} - uint32_t magic; - memcpy(&magic, &data[0], sizeof(uint32_t)); - if (magic != CAN_TRANSACTION_MAGIC) { - LOGE("CAN recv: buffer didn't start with magic"); - handle->comms_healthy = false; - return false; - } +bool Panda::unpack_can_buffer(uint8_t *data, uint32_t &size, std::vector &out_vec) { + int pos = 0; - int pos = sizeof(uint32_t); - while (pos < size) { + while (pos <= size - sizeof(can_header)) { can_header header; memcpy(&header, &data[pos], sizeof(can_header)); + const uint8_t data_len = dlc_to_len[header.data_len_code]; + if (pos + sizeof(can_header) + data_len > size) { + // we don't have all the data for this message yet + break; + } + can_frame &canData = out_vec.emplace_back(); canData.busTime = 0; canData.address = header.addr; @@ -258,10 +260,27 @@ bool Panda::unpack_can_buffer(uint8_t *data, int size, std::vector &o canData.src += CAN_RETURNED_BUS_OFFSET; } - const uint8_t data_len = dlc_to_len[header.data_len_code]; + if (calculate_checksum(&data[pos], sizeof(can_header) + data_len) != 0) { + LOGE("Panda CAN checksum failed"); + return false; + } + canData.dat.assign((char *)&data[pos + sizeof(can_header)], data_len); pos += sizeof(can_header) + data_len; } + + // move the overflowing data to the beginning of the buffer for the next round + memmove(data, &data[pos], size - pos); + size -= pos; + return true; } + +uint8_t Panda::calculate_checksum(uint8_t *data, uint32_t len) { + uint8_t checksum = 0U; + for (uint32_t i = 0U; i < len; i++) { + checksum ^= data[i]; + } + return checksum; +} diff --git a/selfdrive/boardd/panda.h b/selfdrive/boardd/panda.h index 75fec57a3e..156efe7ef3 100644 --- a/selfdrive/boardd/panda.h +++ b/selfdrive/boardd/panda.h @@ -30,6 +30,7 @@ struct __attribute__((packed)) can_header { uint8_t returned : 1; uint8_t extended : 1; uint32_t addr : 29; + uint8_t checksum : 8; }; struct can_frame { @@ -80,11 +81,16 @@ public: void set_canfd_non_iso(uint16_t bus, bool non_iso); void can_send(capnp::List::Reader can_data_list); bool can_receive(std::vector& out_vec); + void can_reset_communications(); protected: // for unit tests + uint8_t receive_buffer[RECV_SIZE + sizeof(can_header) + 64]; + uint32_t receive_buffer_size = 0; + Panda(uint32_t bus_offset) : bus_offset(bus_offset) {} void pack_can_buffer(const capnp::List::Reader &can_data_list, std::function write_func); - bool unpack_can_buffer(uint8_t *data, int size, std::vector &out_vec); + bool unpack_can_buffer(uint8_t *data, uint32_t &size, std::vector &out_vec); + uint8_t calculate_checksum(uint8_t *data, uint32_t len); }; diff --git a/selfdrive/boardd/tests/test_boardd_usbprotocol.cc b/selfdrive/boardd/tests/test_boardd_usbprotocol.cc index cc3b4bce9a..3b78eb7bd2 100644 --- a/selfdrive/boardd/tests/test_boardd_usbprotocol.cc +++ b/selfdrive/boardd/tests/test_boardd_usbprotocol.cc @@ -16,7 +16,8 @@ int random_int(int min, int max) { struct PandaTest : public Panda { PandaTest(uint32_t bus_offset, int can_list_size, cereal::PandaState::PandaType hw_type); void test_can_send(); - void test_can_recv(); + void test_can_recv(uint32_t chunk_size = 0); + void test_chunked_can_recv(); std::map test_data; int can_list_size = 0; @@ -58,11 +59,7 @@ PandaTest::PandaTest(uint32_t bus_offset_, int can_list_size, cereal::PandaState void PandaTest::test_can_send() { std::vector unpacked_data; this->pack_can_buffer(can_data_list, [&](uint8_t *chunk, size_t size) { - uint32_t magic; - memcpy(&magic, &chunk[0], sizeof(uint32_t)); - - REQUIRE(magic == CAN_TRANSACTION_MAGIC); - unpacked_data.insert(unpacked_data.end(), &chunk[sizeof(uint32_t)], &chunk[size]); + unpacked_data.insert(unpacked_data.end(), chunk, &chunk[size]); }); REQUIRE(unpacked_data.size() == total_pakets_size); @@ -77,16 +74,30 @@ void PandaTest::test_can_send() { REQUIRE(header.addr == cnt); REQUIRE(test_data.find(data_len) != test_data.end()); const std::string &dat = test_data[data_len]; - REQUIRE(memcmp(dat.data(), &unpacked_data[pos + 5], dat.size()) == 0); + REQUIRE(memcmp(dat.data(), &unpacked_data[pos + sizeof(can_header)], dat.size()) == 0); ++cnt; } REQUIRE(cnt == can_list_size); } -void PandaTest::test_can_recv() { +void PandaTest::test_can_recv(uint32_t rx_chunk_size) { std::vector frames; - this->pack_can_buffer(can_data_list, [&](uint8_t *data, size_t size) { - this->unpack_can_buffer(data, size, frames); + this->pack_can_buffer(can_data_list, [&](uint8_t *data, uint32_t size) { + if (rx_chunk_size == 0) { + REQUIRE(this->unpack_can_buffer(data, size, frames)); + } else { + this->receive_buffer_size = 0; + uint32_t pos = 0; + + while(pos < size) { + uint32_t chunk_size = std::min(rx_chunk_size, size - pos); + memcpy(&this->receive_buffer[this->receive_buffer_size], &data[pos], chunk_size); + this->receive_buffer_size += chunk_size; + pos += chunk_size; + + REQUIRE(this->unpack_can_buffer(this->receive_buffer, this->receive_buffer_size, frames)); + } + } }); REQUIRE(frames.size() == can_list_size); @@ -109,6 +120,9 @@ TEST_CASE("send/recv CAN 2.0 packets") { SECTION("can_receive") { test.test_can_recv(); } + SECTION("chunked_can_receive") { + test.test_can_recv(0x40); + } } TEST_CASE("send/recv CAN FD packets") { @@ -122,4 +136,7 @@ TEST_CASE("send/recv CAN FD packets") { SECTION("can_receive") { test.test_can_recv(); } + SECTION("chunked_can_receive") { + test.test_can_recv(0x40); + } } From c91679c2d433e69b3a5cfc1a2bd275b757d2eff3 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 13 Jan 2023 15:23:58 -0800 Subject: [PATCH 083/484] remove dead libdiag.h (#26951) --- release/files_common | 1 - selfdrive/sensord/libdiag.h | 37 ------------------------------------- 2 files changed, 38 deletions(-) delete mode 100644 selfdrive/sensord/libdiag.h diff --git a/release/files_common b/release/files_common index d0afa30dea..3d07924a91 100644 --- a/release/files_common +++ b/release/files_common @@ -280,7 +280,6 @@ selfdrive/loggerd/deleter.py selfdrive/loggerd/xattr_cache.py selfdrive/sensord/SConscript -selfdrive/sensord/libdiag.h selfdrive/sensord/sensors_qcom2.cc selfdrive/sensord/sensors/*.cc selfdrive/sensord/sensors/*.h diff --git a/selfdrive/sensord/libdiag.h b/selfdrive/sensord/libdiag.h deleted file mode 100644 index 03a59464ed..0000000000 --- a/selfdrive/sensord/libdiag.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define DIAG_MAX_RX_PKT_SIZ 4096 - -bool Diag_LSM_Init(uint8_t* pIEnv); -bool Diag_LSM_DeInit(void); - -// DCI - -#define DIAG_CON_APSS 0x001 -#define DIAG_CON_MPSS 0x002 -#define DIAG_CON_LPASS 0x004 -#define DIAG_CON_WCNSS 0x008 - -enum { - DIAG_DCI_NO_ERROR = 1001, -} diag_dci_error_type; - -int diag_register_dci_client(int*, uint16_t*, int, void*); -int diag_log_stream_config(int client_id, int set_mask, uint16_t log_codes_array[], int num_codes); -int diag_register_dci_stream(void (*func_ptr_logs)(unsigned char *ptr, int len), void (*func_ptr_events)(unsigned char *ptr, int len)); -int diag_release_dci_client(int*); - -int diag_send_dci_async_req(int client_id, unsigned char buf[], int bytes, unsigned char *rsp_ptr, int rsp_len, - void (*func_ptr)(unsigned char *ptr, int len, void *data_ptr), void *data_ptr); - - -#ifdef __cplusplus -} -#endif From 406d12b7c17ff0d3d4fb713a90b178978c36d042 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 13 Jan 2023 17:20:45 -0800 Subject: [PATCH 084/484] Fix car docs diff bot (#26955) fix --- selfdrive/debug/print_docs_diff.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/selfdrive/debug/print_docs_diff.py b/selfdrive/debug/print_docs_diff.py index b804286458..103dd52dd8 100755 --- a/selfdrive/debug/print_docs_diff.py +++ b/selfdrive/debug/print_docs_diff.py @@ -9,6 +9,7 @@ from selfdrive.car.docs_definitions import Column FOOTNOTE_TAG = "{}" STAR_ICON = '' +VIDEO_ICON = '' COLUMNS = "|" + "|".join([column.value for column in Column]) + "|" COLUMN_HEADER = "|---|---|---|{}|".format("|".join([":---:"] * (len(Column) - 3))) ARROW_SYMBOL = "➡️" @@ -39,8 +40,8 @@ def match_cars(base_cars, new_cars): def build_column_diff(base_car, new_car): row_builder = [] for column in Column: - base_column = base_car.get_column(column, STAR_ICON, FOOTNOTE_TAG) - new_column = new_car.get_column(column, STAR_ICON, FOOTNOTE_TAG) + base_column = base_car.get_column(column, STAR_ICON, VIDEO_ICON, FOOTNOTE_TAG) + new_column = new_car.get_column(column, STAR_ICON, VIDEO_ICON, FOOTNOTE_TAG) if base_column != new_column: row_builder.append(f"{base_column} {ARROW_SYMBOL} {new_column}") @@ -74,11 +75,11 @@ def print_car_info_diff(path): # Removals for car_info in car_removals: - changes["removals"].append(format_row([car_info.get_column(column, STAR_ICON, FOOTNOTE_TAG) for column in Column])) + changes["removals"].append(format_row([car_info.get_column(column, STAR_ICON, VIDEO_ICON, FOOTNOTE_TAG) for column in Column])) # Additions for car_info in car_additions: - changes["additions"].append(format_row([car_info.get_column(column, STAR_ICON, FOOTNOTE_TAG) for column in Column])) + changes["additions"].append(format_row([car_info.get_column(column, STAR_ICON, VIDEO_ICON, FOOTNOTE_TAG) for column in Column])) for new_car, base_car in car_changes: # Column changes From 6527f2c825191860317317f2810fe320b277b69c Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 13 Jan 2023 17:52:24 -0800 Subject: [PATCH 085/484] Car docs: link to car harness on shop (#26957) * harness links * test * fix for no years cars * match order of docs --- docs/CARS.md | 444 +++++++++++++++--------------- selfdrive/car/docs_definitions.py | 30 +- 2 files changed, 241 insertions(+), 233 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index 48145d78a9..0d4c53ac01 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -8,229 +8,229 @@ A supported vehicle is one that just works when you install a comma three. All s |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness|Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| -|Acura|ILX 2016-19|AcuraWatch Plus|openpilot|25 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| -|Acura|RDX 2016-18|AcuraWatch Plus|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| -|Acura|RDX 2019-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| -|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Cadillac|Escalade ESV 2016[3](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|| -|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|| -|Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|| -|Chevrolet|Silverado 1500 2020-21|Safety Package II|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|| -|Chevrolet|Volt 2017-18[3](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|| -|Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|| -|Chrysler|Pacifica 2019-20|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|| -|Chrysler|Pacifica 2021|All|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|| -|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|| -|Chrysler|Pacifica Hybrid 2019-22|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|| +|Acura|ILX 2016-19|AcuraWatch Plus|openpilot|25 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| +|Acura|RDX 2016-18|AcuraWatch Plus|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| +|Acura|RDX 2019-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| +|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Cadillac|Escalade ESV 2016[3](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|| +|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|| +|Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|| +|Chevrolet|Silverado 1500 2020-21|Safety Package II|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|| +|Chevrolet|Volt 2017-18[3](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|| +|Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|| +|Chrysler|Pacifica 2019-20|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|| +|Chrysler|Pacifica 2021|All|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|| +|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|| +|Chrysler|Pacifica Hybrid 2019-22|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|| |comma|body|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|None|| -|Genesis|G70 2018-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F|| -|Genesis|G70 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F|| -|Genesis|G80 2017-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| -|Genesis|G90 2017-18|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| -|Genesis|GV60 2023[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| -|Genesis|GV70 2022-23[5](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| -|GMC|Acadia 2018[3](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|| -|GMC|Sierra 1500 2020-21|Driver Alert Package II|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|| -|Honda|Accord 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| -|Honda|Accord Hybrid 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| -|Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| -|Honda|Civic 2019-21|All|openpilot available[1](#footnotes)|0 mph|2 mph[4](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| -|Honda|Civic 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch B|| -|Honda|Civic Hatchback 2017-21|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| -|Honda|Civic Hatchback 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch B|| -|Honda|CR-V 2015-16|Touring Trim|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| -|Honda|CR-V 2017-22|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| -|Honda|CR-V Hybrid 2017-19|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| -|Honda|e 2020|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| -|Honda|Fit 2018-20|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| -|Honda|Freed 2020|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| -|Honda|HR-V 2019-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| -|Honda|Insight 2019-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| -|Honda|Inspire 2018|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| -|Honda|Odyssey 2018-20|Honda Sensing|openpilot|25 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| -|Honda|Passport 2019-21|All|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| -|Honda|Pilot 2016-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| -|Honda|Ridgeline 2017-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| -|Hyundai|Elantra 2017-19|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai B|| -|Hyundai|Elantra 2021-22|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| -|Hyundai|Elantra GT 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|| -|Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| -|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|Stock|19 mph|37 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai J|| -|Hyundai|i30 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|| -|Hyundai|Ioniq 5 (Southeast Asia only) 2022-23[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai Q|| -|Hyundai|Ioniq 5 (with HDA II) 2022-23[5](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai Q|| -|Hyundai|Ioniq 5 (without HDA II) 2022-23[5](#footnotes)|Highway Driving Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| -|Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| -|Hyundai|Ioniq Electric 2020|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| -|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| -|Hyundai|Ioniq Hybrid 2020-22|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| -|Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| -|Hyundai|Ioniq Plug-in Hybrid 2020-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| -|Hyundai|Kona 2020|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai B|| -|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai G|| -|Hyundai|Kona Electric 2022|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai O|| -|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai I|| -|Hyundai|Palisade 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| -|Hyundai|Santa Cruz 2021-22[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|| -|Hyundai|Santa Fe 2019-20|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai D|| -|Hyundai|Santa Fe 2021-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| -|Hyundai|Santa Fe Hybrid 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| -|Hyundai|Santa Fe Plug-in Hybrid 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| -|Hyundai|Sonata 2018-19|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|| -|Hyundai|Sonata 2020-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|| -|Hyundai|Sonata Hybrid 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|| -|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| -|Hyundai|Tucson 2022[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|| -|Hyundai|Tucson 2023[5](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|| -|Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| -|Hyundai|Tucson Hybrid 2022[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|| -|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|| -|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|| -|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|| -|Kia|Ceed 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|| -|Kia|EV6 (Southeast Asia only) 2022-23[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai P|| -|Kia|EV6 (with HDA II) 2022[5](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai P|| -|Kia|EV6 (without HDA II) 2022[5](#footnotes)|Highway Driving Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| -|Kia|Forte 2019-21|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai G|| -|Kia|K5 2021-22|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|| -|Kia|Niro EV 2019|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| -|Kia|Niro EV 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F|| -|Kia|Niro EV 2021|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| -|Kia|Niro EV 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| -|Kia|Niro Hybrid 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F|| -|Kia|Niro Hybrid 2022|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| -|Kia|Niro Plug-in Hybrid 2018-19|All|openpilot available[1](#footnotes)|10 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| -|Kia|Optima 2017|Advanced Smart Cruise Control|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai B|| -|Kia|Optima 2019-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai G|| -|Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|| -|Kia|Sorento 2018|Advanced Smart Cruise Control|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| -|Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|| -|Kia|Sorento 2022-23[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| -|Kia|Sorento Plug-in Hybrid 2022-23[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|| -|Kia|Sportage 2023[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|| -|Kia|Sportage Hybrid 2023[5](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|| -|Kia|Stinger 2018-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| -|Kia|Stinger 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| -|Kia|Telluride 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| -|Lexus|CT Hybrid 2017-18|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Lexus|ES 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Lexus|ES Hybrid 2017-18|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Lexus|ES Hybrid 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Lexus|IS 2017-19|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Lexus|NX 2018-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Lexus|NX 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Lexus|NX Hybrid 2018-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Lexus|NX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Lexus|RC 2017-20|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Lexus|RX 2016|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Lexus|RX 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Lexus|RX 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Lexus|RX Hybrid 2016|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Lexus|RX Hybrid 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Lexus|RX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Lexus|UX Hybrid 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Mazda|CX-5 2022-23|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Mazda|| -|Mazda|CX-9 2021-23|All|Stock|0 mph|28 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Mazda|| -|Nissan|Altima 2019-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Nissan B|| -|Nissan|Leaf 2018-22|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Nissan A|| -|Nissan|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Nissan A|| -|Nissan|X-Trail 2017|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Nissan A|| -|Ram|1500 2019-22|Adaptive Cruise Control (ACC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Ram|| -|SEAT|Ateca 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Subaru|Ascent 2019-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|| -|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|| -|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|| -|Subaru|Forester 2019-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|| -|Subaru|Impreza 2017-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|| -|Subaru|Impreza 2020-22|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|| -|Subaru|Legacy 2020-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru B|| -|Subaru|Outback 2020-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru B|| -|Subaru|XV 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|| -|Subaru|XV 2020-21|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|| -|Škoda|Kamiq 2021[7](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[10](#footnotes)|| -|Škoda|Karoq 2019-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Škoda|Kodiaq 2018-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Škoda|Octavia 2015, 2018-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Škoda|Octavia RS 2016|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Škoda|Scala 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[10](#footnotes)|| -|Škoda|Superb 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Toyota|Avalon 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Toyota|Avalon 2017-18|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Toyota|Avalon 2019-21|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Toyota|Avalon 2022|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Toyota|Avalon Hybrid 2019-21|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Toyota|Avalon Hybrid 2022|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Toyota|C-HR 2017-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Toyota|C-HR Hybrid 2017-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Toyota|Camry 2018-20|All|Stock|0 mph[6](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Toyota|Camry 2021-22|All|openpilot|0 mph[6](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Toyota|Camry Hybrid 2021-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Toyota|Corolla 2017-19|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Toyota|Corolla 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Toyota|Corolla Cross (Non-US only) 2020-23|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Toyota|Corolla Cross Hybrid (Non-US only) 2020-22|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Toyota|Corolla Hatchback 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Toyota|Corolla Hybrid 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Toyota|Highlander 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Toyota|Highlander 2020-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Toyota|Highlander Hybrid 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Toyota|Highlander Hybrid 2020-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Toyota|Mirai 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Toyota|Prius 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Toyota|Prius 2017-20|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Toyota|Prius 2021-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Toyota|Prius Prime 2017-20|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Toyota|Prius Prime 2021-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Toyota|Prius v 2017|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Toyota|RAV4 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Toyota|RAV4 2017-18|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Toyota|RAV4 2019-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Toyota|RAV4 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Toyota|RAV4 Hybrid 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Toyota|RAV4 Hybrid 2017-18|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Toyota|RAV4 Hybrid 2019-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Toyota|RAV4 Hybrid 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Toyota|Sienna 2018-20|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Volkswagen|Arteon 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|Arteon eHybrid 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|Arteon R 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|Atlas Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|California 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|Jetta 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|Jetta GLI 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|Passat 2015-22[8](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|Passat Alltrack 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|Passat GTE 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|Polo 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[10](#footnotes)|| -|Volkswagen|Polo GTI 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[10](#footnotes)|| -|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[10](#footnotes)|| -|Volkswagen|T-Roc 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[10](#footnotes)|| -|Volkswagen|Taos 2022|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|Tiguan 2019-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|Touran 2017|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Genesis|G70 2018-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F|| +|Genesis|G70 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F|| +|Genesis|G80 2017-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| +|Genesis|G90 2017-18|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| +|Genesis|GV60 2023[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| +|Genesis|GV70 2022-23[5](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| +|GMC|Acadia 2018[3](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|| +|GMC|Sierra 1500 2020-21|Driver Alert Package II|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|| +|Honda|Accord 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| +|Honda|Accord Hybrid 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| +|Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| +|Honda|Civic 2019-21|All|openpilot available[1](#footnotes)|0 mph|2 mph[4](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| +|Honda|Civic 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch B|| +|Honda|Civic Hatchback 2017-21|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| +|Honda|Civic Hatchback 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch B|| +|Honda|CR-V 2015-16|Touring Trim|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| +|Honda|CR-V 2017-22|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| +|Honda|CR-V Hybrid 2017-19|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| +|Honda|e 2020|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| +|Honda|Fit 2018-20|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| +|Honda|Freed 2020|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| +|Honda|HR-V 2019-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| +|Honda|Insight 2019-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| +|Honda|Inspire 2018|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| +|Honda|Odyssey 2018-20|Honda Sensing|openpilot|25 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| +|Honda|Passport 2019-21|All|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| +|Honda|Pilot 2016-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| +|Honda|Ridgeline 2017-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| +|Hyundai|Elantra 2017-19|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai B|| +|Hyundai|Elantra 2021-22|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| +|Hyundai|Elantra GT 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|| +|Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| +|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|Stock|19 mph|37 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai J|| +|Hyundai|i30 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|| +|Hyundai|Ioniq 5 (Southeast Asia only) 2022-23[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai Q|| +|Hyundai|Ioniq 5 (with HDA II) 2022-23[5](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai Q|| +|Hyundai|Ioniq 5 (without HDA II) 2022-23[5](#footnotes)|Highway Driving Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| +|Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| +|Hyundai|Ioniq Electric 2020|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| +|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| +|Hyundai|Ioniq Hybrid 2020-22|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| +|Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| +|Hyundai|Ioniq Plug-in Hybrid 2020-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| +|Hyundai|Kona 2020|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai B|| +|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai G|| +|Hyundai|Kona Electric 2022|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai O|| +|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai I|| +|Hyundai|Palisade 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| +|Hyundai|Santa Cruz 2021-22[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|| +|Hyundai|Santa Fe 2019-20|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai D|| +|Hyundai|Santa Fe 2021-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| +|Hyundai|Santa Fe Hybrid 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| +|Hyundai|Santa Fe Plug-in Hybrid 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| +|Hyundai|Sonata 2018-19|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|| +|Hyundai|Sonata 2020-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|| +|Hyundai|Sonata Hybrid 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|| +|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| +|Hyundai|Tucson 2022[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|| +|Hyundai|Tucson 2023[5](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|| +|Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| +|Hyundai|Tucson Hybrid 2022[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|| +|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|| +|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|| +|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|| +|Kia|Ceed 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|| +|Kia|EV6 (Southeast Asia only) 2022-23[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai P|| +|Kia|EV6 (with HDA II) 2022[5](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai P|| +|Kia|EV6 (without HDA II) 2022[5](#footnotes)|Highway Driving Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| +|Kia|Forte 2019-21|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai G|| +|Kia|K5 2021-22|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|| +|Kia|Niro EV 2019|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| +|Kia|Niro EV 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F|| +|Kia|Niro EV 2021|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| +|Kia|Niro EV 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| +|Kia|Niro Hybrid 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F|| +|Kia|Niro Hybrid 2022|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| +|Kia|Niro Plug-in Hybrid 2018-19|All|openpilot available[1](#footnotes)|10 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| +|Kia|Optima 2017|Advanced Smart Cruise Control|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai B|| +|Kia|Optima 2019-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai G|| +|Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|| +|Kia|Sorento 2018|Advanced Smart Cruise Control|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| +|Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|| +|Kia|Sorento 2022-23[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| +|Kia|Sorento Plug-in Hybrid 2022-23[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|| +|Kia|Sportage 2023[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|| +|Kia|Sportage Hybrid 2023[5](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|| +|Kia|Stinger 2018-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| +|Kia|Stinger 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| +|Kia|Telluride 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| +|Lexus|CT Hybrid 2017-18|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Lexus|ES 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Lexus|ES Hybrid 2017-18|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Lexus|ES Hybrid 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Lexus|IS 2017-19|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Lexus|NX 2018-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Lexus|NX 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Lexus|NX Hybrid 2018-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Lexus|NX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Lexus|RC 2017-20|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Lexus|RX 2016|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Lexus|RX 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Lexus|RX 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Lexus|RX Hybrid 2016|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Lexus|RX Hybrid 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Lexus|RX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Lexus|UX Hybrid 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Mazda|CX-5 2022-23|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Mazda|| +|Mazda|CX-9 2021-23|All|Stock|0 mph|28 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Mazda|| +|Nissan|Altima 2019-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Nissan B|| +|Nissan|Leaf 2018-22|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Nissan A|| +|Nissan|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Nissan A|| +|Nissan|X-Trail 2017|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Nissan A|| +|Ram|1500 2019-22|Adaptive Cruise Control (ACC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Ram|| +|SEAT|Ateca 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Subaru|Ascent 2019-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|| +|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|| +|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|| +|Subaru|Forester 2019-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|| +|Subaru|Impreza 2017-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|| +|Subaru|Impreza 2020-22|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|| +|Subaru|Legacy 2020-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru B|| +|Subaru|Outback 2020-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru B|| +|Subaru|XV 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|| +|Subaru|XV 2020-21|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|| +|Škoda|Kamiq 2021[7](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[10](#footnotes)|| +|Škoda|Karoq 2019-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Škoda|Kodiaq 2018-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Škoda|Octavia 2015, 2018-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Škoda|Octavia RS 2016|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Škoda|Scala 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[10](#footnotes)|| +|Škoda|Superb 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Avalon 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|Avalon 2017-18|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|Avalon 2019-21|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|Avalon 2022|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Avalon Hybrid 2019-21|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|Avalon Hybrid 2022|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|C-HR 2017-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|C-HR Hybrid 2017-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|Camry 2018-20|All|Stock|0 mph[6](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|Camry 2021-22|All|openpilot|0 mph[6](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|Camry Hybrid 2021-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Corolla 2017-19|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|Corolla 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Corolla Cross (Non-US only) 2020-23|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Corolla Cross Hybrid (Non-US only) 2020-22|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Corolla Hatchback 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Corolla Hybrid 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Highlander 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Highlander 2020-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Highlander Hybrid 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Highlander Hybrid 2020-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Mirai 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Prius 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|Prius 2017-20|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|Prius 2021-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Prius Prime 2017-20|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|Prius Prime 2021-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Prius v 2017|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|RAV4 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|RAV4 2017-18|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|RAV4 2019-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|RAV4 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|RAV4 Hybrid 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|RAV4 Hybrid 2017-18|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|RAV4 Hybrid 2019-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|RAV4 Hybrid 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|Sienna 2018-20|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Volkswagen|Arteon 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Arteon eHybrid 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Arteon R 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Atlas Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|California 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Jetta 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Jetta GLI 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Passat 2015-22[8](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Passat Alltrack 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Passat GTE 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Polo 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[10](#footnotes)|| +|Volkswagen|Polo GTI 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[10](#footnotes)|| +|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[10](#footnotes)|| +|Volkswagen|T-Roc 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[10](#footnotes)|| +|Volkswagen|Taos 2022|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Tiguan 2019-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Touran 2017|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| 1Experimental openpilot longitudinal control is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `master-ci`.
diff --git a/selfdrive/car/docs_definitions.py b/selfdrive/car/docs_definitions.py index c3063bc47c..e047b33e76 100644 --- a/selfdrive/car/docs_definitions.py +++ b/selfdrive/car/docs_definitions.py @@ -126,20 +126,11 @@ class CarInfo: harness: Enum = Harness.none def init(self, CP: car.CarParams, all_footnotes: Dict[Enum, int]): - # TODO: set all the min steer speeds in carParams and remove this - if self.min_steer_speed is not None: - assert CP.minSteerSpeed == 0, f"{CP.carFingerprint}: Minimum steer speed set in both CarInfo and CarParams" - else: - self.min_steer_speed = CP.minSteerSpeed - - # TODO: set all the min enable speeds in carParams correctly and remove this - if self.min_enable_speed is None: - self.min_enable_speed = CP.minEnableSpeed - self.car_name = CP.carName self.car_fingerprint = CP.carFingerprint self.make, self.model, self.years = split_name(self.name) + # longitudinal column op_long = "Stock" if CP.openpilotLongitudinalControl and not CP.enableDsu: op_long = "openpilot" @@ -150,6 +141,23 @@ class CarInfo: else: self.footnotes.append(CommonFootnote.EXP_LONG_AVAIL) + # min steer & enable speed columns + # TODO: set all the min steer speeds in carParams and remove this + if self.min_steer_speed is not None: + assert CP.minSteerSpeed == 0, f"{CP.carFingerprint}: Minimum steer speed set in both CarInfo and CarParams" + else: + self.min_steer_speed = CP.minSteerSpeed + + # TODO: set all the min enable speeds in carParams correctly and remove this + if self.min_enable_speed is None: + self.min_enable_speed = CP.minEnableSpeed + + # harness column + harness_col = self.harness.value + if self.harness is not Harness.none: + model_years = self.model + (' ' + self.years if self.years else '') + harness_col = f'{harness_col}' + self.row = { Column.MAKE: self.make, Column.MODEL: self.model, @@ -159,7 +167,7 @@ class CarInfo: Column.FSR_STEERING: f"{max(self.min_steer_speed * CV.MS_TO_MPH, 0):.0f} mph", Column.STEERING_TORQUE: Star.EMPTY, Column.AUTO_RESUME: Star.FULL if CP.autoResumeSng else Star.EMPTY, - Column.HARNESS: self.harness.value, + Column.HARNESS: harness_col, Column.VIDEO: self.video_link if self.video_link is not None else "", # replaced with an image and link from template in get_column } From 028527423c86800e5f260377533c1f8024658f67 Mon Sep 17 00:00:00 2001 From: ZwX1616 Date: Fri, 13 Jan 2023 21:32:15 -0800 Subject: [PATCH 086/484] camerad: rework ox exposure score (#26948) * wip * make separate score func * n4ot g * clean up * remove * simplify * b2b * balance costs * clean up * no if tici * box view * new baselines * Revert "new baselines" This reverts commit f7a5d059eda70fa4d49e3024923da30e8821f42b. * Revert "box view" This reverts commit a57924be36e7d6abc2585aca9575c74cd583ef86. Co-authored-by: Comma Device --- system/camerad/cameras/camera_qcom2.cc | 66 +++++++++++++++----------- system/camerad/cameras/camera_qcom2.h | 4 ++ system/camerad/test/test_exposure.py | 11 ++--- 3 files changed, 44 insertions(+), 37 deletions(-) diff --git a/system/camerad/cameras/camera_qcom2.cc b/system/camerad/cameras/camera_qcom2.cc index 091b0d91d9..8e2ad7bd91 100644 --- a/system/camerad/cameras/camera_qcom2.cc +++ b/system/camerad/cameras/camera_qcom2.cc @@ -104,8 +104,8 @@ const int ANALOG_GAIN_MIN_IDX_OX03C10 = 0x0; const int ANALOG_GAIN_REC_IDX_OX03C10 = 0x11; // 2.5x const int ANALOG_GAIN_MAX_IDX_OX03C10 = 0x36; const int ANALOG_GAIN_COST_DELTA_OX03C10 = -1; -const float ANALOG_GAIN_COST_LOW_OX03C10 = 0.05; -const float ANALOG_GAIN_COST_HIGH_OX03C10 = 0.8; +const float ANALOG_GAIN_COST_LOW_OX03C10 = 0.4; +const float ANALOG_GAIN_COST_HIGH_OX03C10 = 6.4; const int EXPOSURE_TIME_MIN_AR0231 = 2; // with HDR, fastest ss const int EXPOSURE_TIME_MAX_AR0231 = 0x0855; // with HDR, slowest ss, 40ms @@ -1041,6 +1041,30 @@ void CameraState::handle_camera_event(void *evdat) { } } +void CameraState::update_exposure_score(float desired_ev, int exp_t, int exp_g_idx, float exp_gain) { + float score = 1e6; + if (camera_id == CAMERA_ID_AR0231) { + // Cost of ev diff + score = std::abs(desired_ev - (exp_t * exp_gain)) * 10; + // Cost of absolute gain + float m = exp_g_idx > analog_gain_rec_idx ? analog_gain_cost_high : analog_gain_cost_low; + score += std::abs(exp_g_idx - (int)analog_gain_rec_idx) * m; + // Cost of changing gain + score += std::abs(exp_g_idx - gain_idx) * (score + 1.0) / 10.0; + } else if (camera_id == CAMERA_ID_OX03C10) { + score = std::abs(desired_ev - (exp_t * exp_gain)); + float m = exp_g_idx > analog_gain_rec_idx ? analog_gain_cost_high : analog_gain_cost_low; + score += std::abs(exp_g_idx - (int)analog_gain_rec_idx) * m; + score += ((1 - analog_gain_cost_delta) + analog_gain_cost_delta * (exp_g_idx - analog_gain_min_idx) / (analog_gain_max_idx - analog_gain_min_idx)) * std::abs(exp_g_idx - gain_idx) * 5.0; + } + + if (score < best_ev_score) { + new_exp_t = exp_t; + new_exp_g = exp_g_idx; + best_ev_score = score; + } +} + void CameraState::set_camera_exposure(float grey_frac) { if (!enabled) return; const float dt = 0.05; @@ -1066,9 +1090,9 @@ void CameraState::set_camera_exposure(float grey_frac) { float k = (1.0 - k_ev) / 3.0; desired_ev = (k * cur_ev[0]) + (k * cur_ev[1]) + (k * cur_ev[2]) + (k_ev * desired_ev); - float best_ev_score = 1e6; - int new_g = 0; - int new_t = 0; + best_ev_score = 1e6; + new_exp_g = 0; + new_exp_t = 0; // Hysteresis around high conversion gain // We usually want this on since it results in lower noise, but turn off in very bright day scenes @@ -1095,8 +1119,8 @@ void CameraState::set_camera_exposure(float grey_frac) { gain_idx = std::stoi(gain_bytes); exposure_time = std::stoi(time_bytes); - new_g = gain_idx; - new_t = exposure_time; + new_exp_g = gain_idx; + new_exp_t = exposure_time; enable_dc_gain = false; } else { // Simple brute force optimizer to choose sensor parameters @@ -1112,23 +1136,7 @@ void CameraState::set_camera_exposure(float grey_frac) { continue; } - // Compute error to desired ev - float score = std::abs(desired_ev - (t * gain)) * 10; - - // Going below recommended gain needs lower penalty to not overexpose - float m = g > analog_gain_rec_idx ? analog_gain_cost_high : analog_gain_cost_low; - score += std::abs(g - (int)analog_gain_rec_idx) * m; - - // LOGE("cam: %d - gain: %d, t: %d (%.2f), score %.2f, score + gain %.2f, %.3f, %.3f", camera_num, g, t, desired_ev / gain, score, score + std::abs(g - gain_idx) * (score + 1.0) / 10.0, desired_ev, min_ev); - - // Small penalty on changing gain - score += ((1 - analog_gain_cost_delta) + analog_gain_cost_delta * (g - analog_gain_min_idx) / (analog_gain_max_idx - analog_gain_min_idx)) * std::abs(g - gain_idx) * (score + 1.0) / 10.0; - - if (score < best_ev_score) { - new_t = t; - new_g = g; - best_ev_score = score; - } + update_exposure_score(desired_ev, t, g, gain); } } @@ -1137,9 +1145,9 @@ void CameraState::set_camera_exposure(float grey_frac) { measured_grey_fraction = grey_frac; target_grey_fraction = target_grey; - analog_gain_frac = sensor_analog_gains[new_g]; - gain_idx = new_g; - exposure_time = new_t; + analog_gain_frac = sensor_analog_gains[new_exp_g]; + gain_idx = new_exp_g; + exposure_time = new_exp_t; dc_gain_enabled = enable_dc_gain; float gain = analog_gain_frac * (1 + dc_gain_weight * (dc_gain_factor-1) / dc_gain_max_weight); @@ -1156,7 +1164,7 @@ void CameraState::set_camera_exposure(float grey_frac) { // LOGE("ae - camera %d, cur_t %.5f, sof %.5f, dt %.5f", camera_num, 1e-9 * nanos_since_boot(), 1e-9 * buf.cur_frame_data.timestamp_sof, 1e-9 * (nanos_since_boot() - buf.cur_frame_data.timestamp_sof)); if (camera_id == CAMERA_ID_AR0231) { - uint16_t analog_gain_reg = 0xFF00 | (new_g << 4) | new_g; + uint16_t analog_gain_reg = 0xFF00 | (new_exp_g << 4) | new_exp_g; struct i2c_random_wr_payload exp_reg_array[] = { {0x3366, analog_gain_reg}, {0x3362, (uint16_t)(dc_gain_enabled ? 0x1 : 0x0)}, @@ -1171,7 +1179,7 @@ void CameraState::set_camera_exposure(float grey_frac) { uint32_t vs_time = std::min(std::max((uint32_t)exposure_time / 128, VS_TIME_MIN_OX03C10), VS_TIME_MAX_OX03C10); uint32_t spd_time = vs_time; - uint32_t real_gain = ox03c10_analog_gains_reg[new_g]; + uint32_t real_gain = ox03c10_analog_gains_reg[new_exp_g]; uint32_t min_gain = ox03c10_analog_gains_reg[0]; struct i2c_random_wr_payload exp_reg_array[] = { {0x3501, hcg_time>>8}, {0x3502, hcg_time&0xFF}, diff --git a/system/camerad/cameras/camera_qcom2.h b/system/camerad/cameras/camera_qcom2.h index 9f0c3743f1..9e0109ab20 100644 --- a/system/camerad/cameras/camera_qcom2.h +++ b/system/camerad/cameras/camera_qcom2.h @@ -47,6 +47,9 @@ public: float cur_ev[3]; float min_ev, max_ev; + float best_ev_score; + int new_exp_g; + int new_exp_t; float measured_grey_fraction; float target_grey_fraction; @@ -58,6 +61,7 @@ public: int camera_num; void handle_camera_event(void *evdat); + void update_exposure_score(float desired_ev, int exp_t, int exp_g_idx, float exp_gain); void set_camera_exposure(float grey_frac); void sensors_start(); diff --git a/system/camerad/test/test_exposure.py b/system/camerad/test/test_exposure.py index 8cce7e7ffa..201b205c4f 100755 --- a/system/camerad/test/test_exposure.py +++ b/system/camerad/test/test_exposure.py @@ -6,16 +6,13 @@ import numpy as np from selfdrive.test.helpers import with_processes from system.camerad.snapshot.snapshot import get_snapshots -from system.hardware import TICI - TEST_TIME = 45 REPEAT = 5 class TestCamerad(unittest.TestCase): @classmethod def setUpClass(cls): - if not TICI: - raise unittest.SkipTest + pass def _numpy_rgb2gray(self, im): ret = np.clip(im[:,:,2] * 0.114 + im[:,:,1] * 0.587 + im[:,:,0] * 0.299, 0, 255).astype(np.uint8) @@ -37,13 +34,11 @@ class TestCamerad(unittest.TestCase): start = time.time() while time.time() - start < TEST_TIME and passed < REPEAT: rpic, dpic = get_snapshots(frame="roadCameraState", front_frame="driverCameraState") + wpic, _ = get_snapshots(frame="wideRoadCameraState") res = self._is_exposure_okay(rpic) res = res and self._is_exposure_okay(dpic) - - if TICI: - wpic, _ = get_snapshots(frame="wideRoadCameraState") - res = res and self._is_exposure_okay(wpic) + res = res and self._is_exposure_okay(wpic) if passed > 0 and not res: passed = -passed # fails test if any failure after first sus From 1e8e4f7ede35d9a95c97ccfbb0bf8d7c5df46e39 Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Fri, 13 Jan 2023 22:24:16 -0800 Subject: [PATCH 087/484] temporary revert laikad as main gps --- selfdrive/locationd/locationd.cc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/selfdrive/locationd/locationd.cc b/selfdrive/locationd/locationd.cc index 928e19081e..70ccf3bb4e 100755 --- a/selfdrive/locationd/locationd.cc +++ b/selfdrive/locationd/locationd.cc @@ -354,6 +354,7 @@ void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::R this->reset_kalman(NAN, initial_pose_ecef_quat, ecef_pos, ecef_vel, ecef_pos_R, ecef_vel_R); } + this->last_gps_msg = sensor_time; this->kf->predict_and_observe(sensor_time, OBSERVATION_ECEF_POS, { ecef_pos }, { ecef_pos_R }); this->kf->predict_and_observe(sensor_time, OBSERVATION_ECEF_VEL, { ecef_vel }, { ecef_vel_R }); } @@ -588,12 +589,12 @@ void Localizer::handle_msg(const cereal::Event::Reader& log) { this->handle_sensor(t, log.getAccelerometer()); } else if (log.isGyroscope()) { this->handle_sensor(t, log.getGyroscope()); - //} else if (log.isGpsLocation()) { - // this->handle_gps(t, log.getGpsLocation(), GPS_QUECTEL_SENSOR_TIME_OFFSET); - //} else if (log.isGpsLocationExternal()) { - // this->handle_gps(t, log.getGpsLocationExternal(), GPS_UBLOX_SENSOR_TIME_OFFSET); - } else if (log.isGnssMeasurements()) { - this->handle_gnss(t, log.getGnssMeasurements()); + } else if (log.isGpsLocation()) { + this->handle_gps(t, log.getGpsLocation(), GPS_QUECTEL_SENSOR_TIME_OFFSET); + } else if (log.isGpsLocationExternal()) { + this->handle_gps(t, log.getGpsLocationExternal(), GPS_UBLOX_SENSOR_TIME_OFFSET); + //} else if (log.isGnssMeasurements()) { + // this->handle_gnss(t, log.getGnssMeasurements()); } else if (log.isCarState()) { this->handle_car_state(t, log.getCarState()); } else if (log.isCameraOdometry()) { From c44b6225592c070d87d2b3e77018a623d344f054 Mon Sep 17 00:00:00 2001 From: Bruce Wayne Date: Sat, 14 Jan 2023 10:34:59 -0800 Subject: [PATCH 088/484] Revert "temporary revert laikad as main gps" This reverts commit 1e8e4f7ede35d9a95c97ccfbb0bf8d7c5df46e39. --- selfdrive/locationd/locationd.cc | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/selfdrive/locationd/locationd.cc b/selfdrive/locationd/locationd.cc index 70ccf3bb4e..928e19081e 100755 --- a/selfdrive/locationd/locationd.cc +++ b/selfdrive/locationd/locationd.cc @@ -354,7 +354,6 @@ void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::R this->reset_kalman(NAN, initial_pose_ecef_quat, ecef_pos, ecef_vel, ecef_pos_R, ecef_vel_R); } - this->last_gps_msg = sensor_time; this->kf->predict_and_observe(sensor_time, OBSERVATION_ECEF_POS, { ecef_pos }, { ecef_pos_R }); this->kf->predict_and_observe(sensor_time, OBSERVATION_ECEF_VEL, { ecef_vel }, { ecef_vel_R }); } @@ -589,12 +588,12 @@ void Localizer::handle_msg(const cereal::Event::Reader& log) { this->handle_sensor(t, log.getAccelerometer()); } else if (log.isGyroscope()) { this->handle_sensor(t, log.getGyroscope()); - } else if (log.isGpsLocation()) { - this->handle_gps(t, log.getGpsLocation(), GPS_QUECTEL_SENSOR_TIME_OFFSET); - } else if (log.isGpsLocationExternal()) { - this->handle_gps(t, log.getGpsLocationExternal(), GPS_UBLOX_SENSOR_TIME_OFFSET); - //} else if (log.isGnssMeasurements()) { - // this->handle_gnss(t, log.getGnssMeasurements()); + //} else if (log.isGpsLocation()) { + // this->handle_gps(t, log.getGpsLocation(), GPS_QUECTEL_SENSOR_TIME_OFFSET); + //} else if (log.isGpsLocationExternal()) { + // this->handle_gps(t, log.getGpsLocationExternal(), GPS_UBLOX_SENSOR_TIME_OFFSET); + } else if (log.isGnssMeasurements()) { + this->handle_gnss(t, log.getGnssMeasurements()); } else if (log.isCarState()) { this->handle_car_state(t, log.getCarState()); } else if (log.isCameraOdometry()) { From d064a32b00c375f21afbbe32171231122281c1ea Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sat, 14 Jan 2023 16:28:17 -0800 Subject: [PATCH 089/484] docs: add experimental mode to compatibility description (#26954) * docs: add experimental mode to compatibility description * lower case --- selfdrive/car/docs_definitions.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/selfdrive/car/docs_definitions.py b/selfdrive/car/docs_definitions.py index e047b33e76..c0fb4420df 100644 --- a/selfdrive/car/docs_definitions.py +++ b/selfdrive/car/docs_definitions.py @@ -204,6 +204,13 @@ class CarInfo: if self.row[Column.STEERING_TORQUE] != Star.FULL: sentence_builder += " This car may not be able to take tight turns on its own." + # experimental mode + exp_link = "Experimental mode" + if CP.openpilotLongitudinalControl or CP.experimentalLongitudinalAvailable: + sentence_builder += f" Traffic light and stop sign handling is also available in {exp_link}." + else: + sentence_builder += f" {exp_link}, with traffic light and stop sign handling, is not currently available for this car, but may be added in a future software update." + return sentence_builder.format(car_model=f"{self.make} {self.model}", alc=alc, acc=acc) else: From 4c33d94d3f088c6f1fc88a0f40f3a828968c1c52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Sat, 14 Jan 2023 16:56:14 -0800 Subject: [PATCH 090/484] Revert locationd to gpsLocation (#26963) * revert locationd laika change * switch msg * change proc replay * back to gpslocation * update ref * no np floats in msg * Ignore empty laikad messages in mapos * more tolerance * c++ doesnt work like that --- selfdrive/locationd/locationd.cc | 23 +++++++----- selfdrive/locationd/test/test_locationd.py | 35 ++++++++++++------- .../test/process_replay/process_replay.py | 2 +- selfdrive/test/process_replay/ref_commit | 2 +- selfdrive/ui/qt/maps/map.cc | 3 +- 5 files changed, 42 insertions(+), 23 deletions(-) diff --git a/selfdrive/locationd/locationd.cc b/selfdrive/locationd/locationd.cc index 928e19081e..8941b50248 100755 --- a/selfdrive/locationd/locationd.cc +++ b/selfdrive/locationd/locationd.cc @@ -354,6 +354,7 @@ void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::R this->reset_kalman(NAN, initial_pose_ecef_quat, ecef_pos, ecef_vel, ecef_pos_R, ecef_vel_R); } + this->last_gps_msg = sensor_time; this->kf->predict_and_observe(sensor_time, OBSERVATION_ECEF_POS, { ecef_pos }, { ecef_pos_R }); this->kf->predict_and_observe(sensor_time, OBSERVATION_ECEF_VEL, { ecef_vel }, { ecef_vel_R }); } @@ -588,12 +589,12 @@ void Localizer::handle_msg(const cereal::Event::Reader& log) { this->handle_sensor(t, log.getAccelerometer()); } else if (log.isGyroscope()) { this->handle_sensor(t, log.getGyroscope()); - //} else if (log.isGpsLocation()) { - // this->handle_gps(t, log.getGpsLocation(), GPS_QUECTEL_SENSOR_TIME_OFFSET); - //} else if (log.isGpsLocationExternal()) { - // this->handle_gps(t, log.getGpsLocationExternal(), GPS_UBLOX_SENSOR_TIME_OFFSET); - } else if (log.isGnssMeasurements()) { - this->handle_gnss(t, log.getGnssMeasurements()); + } else if (log.isGpsLocation()) { + this->handle_gps(t, log.getGpsLocation(), GPS_QUECTEL_SENSOR_TIME_OFFSET); + } else if (log.isGpsLocationExternal()) { + this->handle_gps(t, log.getGpsLocationExternal(), GPS_UBLOX_SENSOR_TIME_OFFSET); + //} else if (log.isGnssMeasurements()) { + // this->handle_gnss(t, log.getGnssMeasurements()); } else if (log.isCarState()) { this->handle_car_state(t, log.getCarState()); } else if (log.isCameraOdometry()) { @@ -657,11 +658,17 @@ void Localizer::determine_gps_mode(double current_time) { int Localizer::locationd_thread() { ublox_available = Params().getBool("UbloxAvailable", true); - const std::initializer_list service_list = {"gnssMeasurements", "cameraOdometry", "liveCalibration", + const char* gps_location_socket; + if (ublox_available) { + gps_location_socket = "gpsLocationExternal"; + } else { + gps_location_socket = "gpsLocation"; + } + const std::initializer_list service_list = {gps_location_socket, "cameraOdometry", "liveCalibration", "carState", "carParams", "accelerometer", "gyroscope"}; // TODO: remove carParams once we're always sending at 100Hz - SubMaster sm(service_list, {}, nullptr, {"gnssMeasurements", "carParams"}); + SubMaster sm(service_list, {}, nullptr, {gps_location_socket, "carParams"}); PubMaster pm({"liveLocationKalman"}); uint64_t cnt = 0; diff --git a/selfdrive/locationd/test/test_locationd.py b/selfdrive/locationd/test/test_locationd.py index 6e65acaaf9..9f643e2b8f 100755 --- a/selfdrive/locationd/test/test_locationd.py +++ b/selfdrive/locationd/test/test_locationd.py @@ -15,7 +15,7 @@ from selfdrive.manager.process_config import managed_processes class TestLocationdProc(unittest.TestCase): MAX_WAITS = 1000 - LLD_MSGS = ['gnssMeasurements', 'cameraOdometry', 'carState', 'liveCalibration', + LLD_MSGS = ['gpsLocationExternal', 'cameraOdometry', 'carState', 'liveCalibration', 'accelerometer', 'gyroscope', 'magnetometer'] def setUp(self): @@ -46,14 +46,25 @@ class TestLocationdProc(unittest.TestCase): except capnp.lib.capnp.KjException: msg = messaging.new_message(name, 0) - if name == "gnssMeasurements": - msg.gnssMeasurements.measTime = t - msg.gnssMeasurements.positionECEF.value = [self.x , self.y, self.z] - msg.gnssMeasurements.positionECEF.std = [0,0,0] - msg.gnssMeasurements.positionECEF.valid = True - msg.gnssMeasurements.velocityECEF.value = [] - msg.gnssMeasurements.velocityECEF.std = [0,0,0] - msg.gnssMeasurements.velocityECEF.valid = True + + if name == "gpsLocationExternal": + msg.gpsLocationExternal.flags = 1 + msg.gpsLocationExternal.verticalAccuracy = 1.0 + msg.gpsLocationExternal.speedAccuracy = 1.0 + msg.gpsLocationExternal.bearingAccuracyDeg = 1.0 + msg.gpsLocationExternal.vNED = [0.0, 0.0, 0.0] + msg.gpsLocationExternal.latitude = float(self.lat) + msg.gpsLocationExternal.longitude = float(self.lon) + msg.gpsLocationExternal.unixTimestampMillis = t * 1e6 + msg.gpsLocationExternal.altitude = float(self.alt) + #if name == "gnssMeasurements": + # msg.gnssMeasurements.measTime = t + # msg.gnssMeasurements.positionECEF.value = [self.x , self.y, self.z] + # msg.gnssMeasurements.positionECEF.std = [0,0,0] + # msg.gnssMeasurements.positionECEF.valid = True + # msg.gnssMeasurements.velocityECEF.value = [] + # msg.gnssMeasurements.velocityECEF.std = [0,0,0] + # msg.gnssMeasurements.velocityECEF.valid = True elif name == 'cameraOdometry': msg.cameraOdometry.rot = [0.0, 0.0, 0.0] msg.cameraOdometry.rotStd = [0.0, 0.0, 0.0] @@ -84,9 +95,9 @@ class TestLocationdProc(unittest.TestCase): time.sleep(1) # wait for async params write lastGPS = json.loads(Params().get('LastGPSPosition')) - self.assertAlmostEqual(lastGPS['latitude'], self.lat, places=4) - self.assertAlmostEqual(lastGPS['longitude'], self.lon, places=4) - self.assertAlmostEqual(lastGPS['altitude'], self.alt, places=4) + self.assertAlmostEqual(lastGPS['latitude'], self.lat, places=3) + self.assertAlmostEqual(lastGPS['longitude'], self.lon, places=3) + self.assertAlmostEqual(lastGPS['altitude'], self.alt, places=3) if __name__ == "__main__": diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 24ee87e471..22ec099285 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -331,7 +331,7 @@ CONFIGS = [ pub_sub={ "cameraOdometry": ["liveLocationKalman"], "accelerometer": [], "gyroscope": [], - "gnssMeasurements": [], "liveCalibration": [], "carState": [], + "gpsLocationExternal": [], "liveCalibration": [], "carState": [], }, ignore=["logMonoTime", "valid"], init_callback=get_car_params, diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index ea45fb4957..a173cf5205 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -c714ab010923f2bce732bd22e903ccc4454136fd \ No newline at end of file +18a70665bdb6b6aee4a224e826417049415e8290 diff --git a/selfdrive/ui/qt/maps/map.cc b/selfdrive/ui/qt/maps/map.cc index c625564f1d..dd30b5feff 100644 --- a/selfdrive/ui/qt/maps/map.cc +++ b/selfdrive/ui/qt/maps/map.cc @@ -125,7 +125,8 @@ void MapWindow::updateState(const UIState &s) { } } - if (sm.updated("gnssMeasurements")) { + // TODO should check a valid/status flag + if (sm.updated("gnssMeasurements") && sm["gnssMeasurements"].getGnssMeasurements().getGpsWeek() > 0){ auto laikad_location = sm["gnssMeasurements"].getGnssMeasurements(); auto laikad_pos = laikad_location.getPositionECEF(); auto laikad_pos_ecef = laikad_pos.getValue(); From 6861c9999195a9791bdb31675a4ed07dbd562b9c Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sat, 14 Jan 2023 23:06:09 -0800 Subject: [PATCH 091/484] don't run ublox procs without ublox (#26965) * pigeond: don't run if ublox missing * same for ubloxd Co-authored-by: Comma Device --- selfdrive/manager/process_config.py | 9 +++++++-- selfdrive/sensord/pigeond.py | 3 +-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/selfdrive/manager/process_config.py b/selfdrive/manager/process_config.py index c03e995497..8fc4d94e55 100644 --- a/selfdrive/manager/process_config.py +++ b/selfdrive/manager/process_config.py @@ -17,6 +17,11 @@ def logging(started, params, CP: car.CarParams) -> bool: run = (not CP.notCar) or not params.get_bool("DisableLogging") return started and run +def ublox(started, params, CP: car.CarParams) -> bool: + use_ublox = os.path.exists('/dev/ttyHS0') and not os.path.exists('/persist/comma/use-quectel-gps') + params.put_bool("UbloxAvailable", use_ublox) + return started and use_ublox + procs = [ # due to qualcomm kernel bugs SIGKILLing camerad sometimes causes page table corruption NativeProcess("camerad", "system/camerad", ["./camerad"], unkillable=True, callback=driverview), @@ -35,7 +40,6 @@ procs = [ NativeProcess("mapsd", "selfdrive/navd", ["./map_renderer"], enabled=False), NativeProcess("navmodeld", "selfdrive/modeld", ["./navmodeld"], enabled=False), NativeProcess("sensord", "selfdrive/sensord", ["./sensord"], enabled=not PC), - NativeProcess("ubloxd", "selfdrive/locationd", ["./ubloxd"], enabled=(not PC or WEBCAM)), NativeProcess("ui", "selfdrive/ui", ["./ui"], offroad=True, watchdog_max_dt=(5 if not PC else None)), NativeProcess("soundd", "selfdrive/ui/soundd", ["./soundd"], offroad=True), NativeProcess("locationd", "selfdrive/locationd", ["./locationd"]), @@ -50,7 +54,8 @@ procs = [ PythonProcess("navd", "selfdrive.navd.navd"), PythonProcess("pandad", "selfdrive.boardd.pandad", offroad=True), PythonProcess("paramsd", "selfdrive.locationd.paramsd"), - PythonProcess("pigeond", "selfdrive.sensord.pigeond", enabled=TICI), + NativeProcess("ubloxd", "selfdrive/locationd", ["./ubloxd"], enabled=TICI, onroad=False, callback=ublox), + PythonProcess("pigeond", "selfdrive.sensord.pigeond", enabled=TICI, onroad=False, callback=ublox), PythonProcess("plannerd", "selfdrive.controls.plannerd"), PythonProcess("radard", "selfdrive.controls.radard"), PythonProcess("thermald", "selfdrive.thermald.thermald", offroad=True), diff --git a/selfdrive/sensord/pigeond.py b/selfdrive/sensord/pigeond.py index f56af1c705..bb53dd1bd4 100755 --- a/selfdrive/sensord/pigeond.py +++ b/selfdrive/sensord/pigeond.py @@ -303,8 +303,7 @@ def main(): pigeon, pm = create_pigeon() init_baudrate(pigeon) - r = initialize_pigeon(pigeon) - Params().put_bool("UbloxAvailable", r) + initialize_pigeon(pigeon) # start receiving data run_receiving(pigeon, pm) From ae6ad15df5e9047f841e990812d26f0409545dc2 Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Sun, 15 Jan 2023 21:50:29 +0100 Subject: [PATCH 092/484] MacOS: bump body to fix build (#26972) --- body | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/body b/body index dc780f858c..e1805f65ee 160000 --- a/body +++ b/body @@ -1 +1 @@ -Subproject commit dc780f858c1ef641471d09b72569e199e3e10acb +Subproject commit e1805f65ee75fab4454c21eda8b42b49d4bdc48f From 58bd02408979f19999acd8c755ed30785b9ef6d7 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 16 Jan 2023 04:52:11 +0800 Subject: [PATCH 093/484] cabana: support drag and drop to merge charts (#26968) --- tools/cabana/chartswidget.cc | 67 +++++++++++++++++++++++++++++++----- tools/cabana/chartswidget.h | 6 ++++ 2 files changed, 65 insertions(+), 8 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 952ed2d83f..c8208b2f14 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -331,6 +332,17 @@ void ChartView::msgRemoved(uint32_t address) { } } +void ChartView::addSeries(const QList &series_list) { + for (auto &s : series_list) { + if (auto m = dbc()->msg(s[0])) { + auto it = m->sigs.find(s[2]); + if (it != m->sigs.end() && !hasSeries(s[0], &(it->second))) { + addSeries(s[0], &(it->second)); + } + } + } +} + void ChartView::manageSeries() { SeriesSelector dlg(this); for (auto &s : sigs) { @@ -343,14 +355,7 @@ void ChartView::manageSeries() { if (series_list.isEmpty()) { emit remove(); } else { - for (auto &s : series_list) { - if (auto m = dbc()->msg(s[0])) { - auto it = m->sigs.find(s[2]); - if (it != m->sigs.end() && !hasSeries(s[0], &(it->second))) { - addSeries(s[0], &(it->second)); - } - } - } + addSeries(series_list); for (auto it = sigs.begin(); it != sigs.end(); /**/) { bool exists = std::any_of(series_list.cbegin(), series_list.cend(), [&](auto &s) { return s[0] == it->msg_id && s[2] == it->sig->name.c_str(); @@ -495,6 +500,23 @@ void ChartView::leaveEvent(QEvent *event) { QChartView::leaveEvent(event); } +void ChartView::mousePressEvent(QMouseEvent *event) { + if (event->button() == Qt::LeftButton && !chart()->plotArea().contains(event->pos()) && + !manage_btn_proxy->widget()->underMouse() && !close_btn_proxy->widget()->underMouse()) { + QMimeData *mimeData = new QMimeData; + mimeData->setData(mime_type, QByteArray::number((qulonglong)this)); + QDrag *drag = new QDrag(this); + drag->setMimeData(mimeData); + drag->setPixmap(grab()); + drag->setHotSpot(event->pos()); + Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::MoveAction); + if (dropAction == Qt::MoveAction) { + return; + } + } + QChartView::mousePressEvent(event); +} + void ChartView::mouseReleaseEvent(QMouseEvent *event) { auto rubber = findChild(); if (event->button() == Qt::LeftButton && rubber && rubber->isVisible()) { @@ -550,6 +572,35 @@ void ChartView::mouseMoveEvent(QMouseEvent *ev) { QChartView::mouseMoveEvent(ev); } +void ChartView::dragMoveEvent(QDragMoveEvent *event) { + if (event->mimeData()->hasFormat(mime_type)) { + event->setDropAction(event->source() == this ? Qt::MoveAction : Qt::CopyAction); + event->accept(); + } else { + event->ignore(); + } +} + +void ChartView::dropEvent(QDropEvent *event) { + if (event->mimeData()->hasFormat(mime_type)) { + if (event->source() == this) { + event->setDropAction(Qt::MoveAction); + event->accept(); + } else { + ChartView *source_chart = (ChartView *)event->source(); + QList series; + for (auto &s : source_chart->sigs) { + series.push_back({s.msg_id, msgName(s.msg_id), QString::fromStdString(s.sig->name)}); + } + addSeries(series); + emit source_chart->remove(); + event->acceptProposedAction(); + } + } else { + event->ignore(); + } +} + void ChartView::drawForeground(QPainter *painter, const QRectF &rect) { qreal x = chart()->mapToPosition(QPointF{can->currentSec(), 0}).x(); qreal y1 = chart()->plotArea().top() - 2; diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index 819432920b..ebce8ceda6 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -58,8 +59,11 @@ private slots: private: QList::iterator removeSeries(const QList::iterator &it); + void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *ev) override; + void dragMoveEvent(QDragMoveEvent *event) override; + void dropEvent(QDropEvent *event) override; void leaveEvent(QEvent *event) override; void resizeEvent(QResizeEvent *event) override; void updateAxisY(); @@ -68,6 +72,7 @@ private: void drawForeground(QPainter *painter, const QRectF &rect) override; void applyNiceNumbers(qreal min, qreal max); qreal niceNumber(qreal x, bool ceiling); + void addSeries(const QList &series_list); QValueAxis *axis_x; QValueAxis *axis_y; @@ -76,6 +81,7 @@ private: QGraphicsProxyWidget *manage_btn_proxy; std::pair events_range = {0, 0}; QList sigs; + const QString mime_type = "application/x-cabanachartview"; }; class ChartsWidget : public QWidget { From f6dc12a735b4856d2ac6225ec9a2f2aa36d2c2de Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Sun, 15 Jan 2023 16:13:50 -0500 Subject: [PATCH 094/484] VW MQB: Add diagnostic firewall note to config tool (#26975) --- selfdrive/debug/vw_mqb_config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/selfdrive/debug/vw_mqb_config.py b/selfdrive/debug/vw_mqb_config.py index 8c4dbc55ee..8952405b8e 100755 --- a/selfdrive/debug/vw_mqb_config.py +++ b/selfdrive/debug/vw_mqb_config.py @@ -126,6 +126,7 @@ if __name__ == "__main__": uds_client.security_access(ACCESS_TYPE_LEVEL_1.SEND_KEY, struct.pack("!I", key)) # type: ignore except (NegativeResponseError, MessageTimeoutError): print("Security access failed!") + print("Open the hood and retry (disables the \"diagnostic firewall\" on newer vehicles)") quit() try: From 11da83d2b58d26e876fe65e6bb2ec1013b7a5316 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 15 Jan 2023 13:52:16 -0800 Subject: [PATCH 095/484] boardd: lock spi device (#26937) * lock spi * bump panda Co-authored-by: Comma Device --- panda | 2 +- selfdrive/boardd/spi.cc | 25 ++++++++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/panda b/panda index 11d90f9e78..e83b2189c1 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 11d90f9e78b1c070e44e02d5d8c2b18790617324 +Subproject commit e83b2189c148499b8ba430433886791217f3389a diff --git a/selfdrive/boardd/spi.cc b/selfdrive/boardd/spi.cc index 717b6ce820..8a6bf8be7f 100644 --- a/selfdrive/boardd/spi.cc +++ b/selfdrive/boardd/spi.cc @@ -1,3 +1,4 @@ +#include #include #include @@ -28,6 +29,23 @@ struct __attribute__((packed)) spi_header { const int SPI_MAX_RETRIES = 5; const int SPI_ACK_TIMEOUT = 50; // milliseconds +class LockEx { +public: + LockEx(int fd, std::recursive_mutex &m) : fd(fd), m(m) { + m.lock(); + flock(fd, LOCK_EX); + }; + + ~LockEx() { + m.unlock(); + flock(fd, LOCK_UN); + } + +private: + int fd; + std::recursive_mutex &m; +}; + PandaSpiHandle::PandaSpiHandle(std::string serial) : PandaCommsHandle(serial) { LOGD("opening SPI panda: %s", serial.c_str()); @@ -84,6 +102,7 @@ void PandaSpiHandle::cleanup() { int PandaSpiHandle::control_write(uint8_t request, uint16_t param1, uint16_t param2, unsigned int timeout) { + LockEx lock(spi_fd, hw_lock); ControlPacket_t packet = { .request = request, .param1 = param1, @@ -94,6 +113,7 @@ int PandaSpiHandle::control_write(uint8_t request, uint16_t param1, uint16_t par } int PandaSpiHandle::control_read(uint8_t request, uint16_t param1, uint16_t param2, unsigned char *data, uint16_t length, unsigned int timeout) { + LockEx lock(spi_fd, hw_lock); ControlPacket_t packet = { .request = request, .param1 = param1, @@ -104,15 +124,15 @@ int PandaSpiHandle::control_read(uint8_t request, uint16_t param1, uint16_t para } int PandaSpiHandle::bulk_write(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout) { + LockEx lock(spi_fd, hw_lock); return bulk_transfer(endpoint, data, length, NULL, 0); } int PandaSpiHandle::bulk_read(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout) { + LockEx lock(spi_fd, hw_lock); return bulk_transfer(endpoint, NULL, 0, data, length); } int PandaSpiHandle::bulk_transfer(uint8_t endpoint, uint8_t *tx_data, uint16_t tx_len, uint8_t *rx_data, uint16_t rx_len) { - std::lock_guard lk(hw_lock); - const int xfer_size = 0x40 * 15; int ret = 0; @@ -167,7 +187,6 @@ int PandaSpiHandle::spi_transfer_retry(uint8_t endpoint, uint8_t *tx_data, uint1 int ret; int count = SPI_MAX_RETRIES; - std::lock_guard lk(hw_lock); do { // TODO: handle error ret = spi_transfer(endpoint, tx_data, tx_len, rx_data, max_rx_len); From 9c58e5fedd7cee154eec44857c6df83dcc1a8ed0 Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Sun, 15 Jan 2023 20:16:45 -0500 Subject: [PATCH 096/484] =?UTF-8?q?VW=20MQB:=20Add=20FW=20for=202020=20?= =?UTF-8?q?=C5=A0koda=20Scala=20(#26976)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- selfdrive/car/volkswagen/values.py | 1 + 1 file changed, 1 insertion(+) diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py index f627e517be..6ae4969cc0 100755 --- a/selfdrive/car/volkswagen/values.py +++ b/selfdrive/car/volkswagen/values.py @@ -1079,6 +1079,7 @@ FW_VERSIONS = { b'\xf1\x870CW300050 \xf1\x891709', ], (Ecu.srs, 0x715, None): [ + b'\xf1\x872Q0959655AJ\xf1\x890250\xf1\x82\x1211110411110411--04040404131111112H14', b'\xf1\x872Q0959655AM\xf1\x890351\xf1\x82\022111104111104112104040404111111112H14', ], (Ecu.eps, 0x712, None): [ From 5cc192be4d994d26e385f03a1945d810d465eb00 Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Mon, 16 Jan 2023 07:13:16 +0100 Subject: [PATCH 097/484] cabana: MacOS fixes (#26973) * make qtcharts a framework * cabana: dfisable openpilot prefix on macos * cabana: disable opengl on charts * fix linux build * no newline * add comments --- tools/cabana/SConscript | 5 ++++- tools/cabana/cabana.cc | 4 ++++ tools/cabana/chartswidget.cc | 5 +++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/tools/cabana/SConscript b/tools/cabana/SConscript index 718e2e50af..507b98ae88 100644 --- a/tools/cabana/SConscript +++ b/tools/cabana/SConscript @@ -8,10 +8,13 @@ base_libs = [common, messaging, cereal, visionipc, transformations, 'zmq', if arch == "Darwin": base_frameworks.append('OpenCL') + base_frameworks.append('QtCharts') else: base_libs.append('OpenCL') + base_libs.append('Qt5Charts') + +qt_libs = ['qt_util'] + base_libs -qt_libs = ['qt_util', 'Qt5Charts'] + base_libs cabana_libs = [widgets, cereal, messaging, visionipc, replay_lib, opendbc,'avutil', 'avcodec', 'avformat', 'bz2', 'curl', 'yuv'] + qt_libs cabana_env = qt_env.Clone() diff --git a/tools/cabana/cabana.cc b/tools/cabana/cabana.cc index 4f037ba595..6a9db120e9 100644 --- a/tools/cabana/cabana.cc +++ b/tools/cabana/cabana.cc @@ -33,7 +33,11 @@ int main(int argc, char *argv[]) { replay_flags |= REPLAY_FLAG_QCAMERA; } + // TODO: Remove when OpenpilotPrefix supports ZMQ +#ifndef __APPLE__ OpenpilotPrefix op_prefix; +#endif + CANMessages p(&app); int ret = 0; if (p.loadRoute(route, cmd_parser.value("data_dir"), replay_flags)) { diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index c8208b2f14..b8ec0af346 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -268,7 +268,12 @@ void ChartView::setPlotAreaLeftPosition(int pos) { void ChartView::addSeries(const QString &msg_id, const Signal *sig) { QLineSeries *series = new QLineSeries(this); + + // TODO: Due to a bug in CameraWidget the camera frames + // are drawn instead of the graphs on MacOS. Re-enable OpenGL when fixed +#ifndef __APPLE__ series->setUseOpenGL(true); +#endif chart()->addSeries(series); series->attachAxis(axis_x); series->attachAxis(axis_y); From f25239f7c516b19782926423754db7c31a29bd75 Mon Sep 17 00:00:00 2001 From: apache2046 Date: Tue, 17 Jan 2023 02:22:09 +0800 Subject: [PATCH 098/484] Radard: pdf not cdf (#26978) Update radard.py according the the equation, it is the probability density function (pdf) , not the cumulative distribution function (cdf) --- selfdrive/controls/radard.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/selfdrive/controls/radard.py b/selfdrive/controls/radard.py index db87604e98..6a0902e510 100755 --- a/selfdrive/controls/radard.py +++ b/selfdrive/controls/radard.py @@ -35,7 +35,7 @@ class KalmanParams(): self.K = [[interp(dt, dts, K0)], [interp(dt, dts, K1)]] -def laplacian_cdf(x, mu, b): +def laplacian_pdf(x, mu, b): b = max(b, 1e-4) return math.exp(-abs(x-mu)/b) @@ -45,9 +45,9 @@ def match_vision_to_cluster(v_ego, lead, clusters): offset_vision_dist = lead.x[0] - RADAR_TO_CAMERA def prob(c): - prob_d = laplacian_cdf(c.dRel, offset_vision_dist, lead.xStd[0]) - prob_y = laplacian_cdf(c.yRel, -lead.y[0], lead.yStd[0]) - prob_v = laplacian_cdf(c.vRel + v_ego, lead.v[0], lead.vStd[0]) + prob_d = laplacian_pdf(c.dRel, offset_vision_dist, lead.xStd[0]) + prob_y = laplacian_pdf(c.yRel, -lead.y[0], lead.yStd[0]) + prob_v = laplacian_pdf(c.vRel + v_ego, lead.v[0], lead.vStd[0]) # This is isn't exactly right, but good heuristic return prob_d * prob_y * prob_v From 5cf1dd7f1377c65172a5dfaaade1396c17eb9e5a Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Mon, 16 Jan 2023 19:36:06 +0100 Subject: [PATCH 099/484] MacOS: mock gpio functions (#26971) --- common/gpio.cc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/common/gpio.cc b/common/gpio.cc index 9f5c211a4b..aa65c0bd9d 100644 --- a/common/gpio.cc +++ b/common/gpio.cc @@ -1,5 +1,20 @@ #include "common/gpio.h" +#ifdef __APPLE__ +int gpio_init(int pin_nr, bool output) { + return 0; +} + +int gpio_set(int pin_nr, bool high) { + return 0; +} + +int gpiochip_get_ro_value_fd(const char* consumer_label, int gpiochiop_id, int pin_nr) { + return 0; +} + +#else + #include #include @@ -63,3 +78,5 @@ int gpiochip_get_ro_value_fd(const char* consumer_label, int gpiochiop_id, int p close(fd); return rq.fd; } + +#endif From 657f1c13379f9b7f5b820ab17772fd6a37cd14d2 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Mon, 16 Jan 2023 11:20:20 -0800 Subject: [PATCH 100/484] add bootstrap icons (#26980) * add bootstrap icons * pin --- third_party/bootstrap/.gitignore | 1 + third_party/bootstrap/bootstrap-icons.svg | 1 + third_party/bootstrap/pull.sh | 14 ++++++++++++++ 3 files changed, 16 insertions(+) create mode 100644 third_party/bootstrap/.gitignore create mode 100644 third_party/bootstrap/bootstrap-icons.svg create mode 100755 third_party/bootstrap/pull.sh diff --git a/third_party/bootstrap/.gitignore b/third_party/bootstrap/.gitignore new file mode 100644 index 0000000000..ac06c0cf85 --- /dev/null +++ b/third_party/bootstrap/.gitignore @@ -0,0 +1 @@ +/icons/ diff --git a/third_party/bootstrap/bootstrap-icons.svg b/third_party/bootstrap/bootstrap-icons.svg new file mode 100644 index 0000000000..61f2720db4 --- /dev/null +++ b/third_party/bootstrap/bootstrap-icons.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/third_party/bootstrap/pull.sh b/third_party/bootstrap/pull.sh new file mode 100755 index 0000000000..0b03b4db9e --- /dev/null +++ b/third_party/bootstrap/pull.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -e + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" +cd $DIR + +if [ ! -d icons/ ]; then + git clone https://github.com/twbs/icons/ +fi + +cd icons +git fetch --all +git checkout d5aa187483a1b0b186f87adcfa8576350d970d98 +cp bootstrap-icons.svg ../ From 164880524a705fee3ba0507e70c7c0069b1f7ef0 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 17 Jan 2023 03:21:15 +0800 Subject: [PATCH 101/484] cabana: add a combobox to switch between signal and raw hex value (#26974) * switch between hex&signal value * sync display mode between view & model --- tools/cabana/historylog.cc | 36 +++++++++++++++++++++++++++++------- tools/cabana/historylog.h | 14 ++++++++++++-- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/tools/cabana/historylog.cc b/tools/cabana/historylog.cc index 485a21cc1b..c6ee7d9086 100644 --- a/tools/cabana/historylog.cc +++ b/tools/cabana/historylog.cc @@ -7,16 +7,24 @@ // HistoryLogModel +void HistoryLogModel::setDisplayType(HistoryLogModel::DisplayType type) { + if (display_type != type) { + display_type = type; + refresh(); + } +} + QVariant HistoryLogModel::data(const QModelIndex &index, int role) const { + const bool display_signals = display_type == HistoryLogModel::Signals; if (role == Qt::DisplayRole) { const auto &m = messages[index.row()]; if (index.column() == 0) { return QString::number((m.mono_time / (double)1e9) - can->routeStartTime(), 'f', 2); } - return !sigs.empty() ? QString::number(m.sig_values[index.column() - 1]) : m.data; - } else if (role == Qt::FontRole && index.column() == 1 && sigs.empty()) { + return display_signals ? QString::number(m.sig_values[index.column() - 1]) : m.data; + } else if (role == Qt::FontRole && index.column() == 1 && !display_signals) { return QFontDatabase::systemFont(QFontDatabase::FixedFont); - } else if (role == Qt::ToolTipRole && index.column() > 0 && !sigs.empty()) { + } else if (role == Qt::ToolTipRole && index.column() > 0 && display_signals) { return tr("double click to open the chart"); } return {}; @@ -28,6 +36,7 @@ void HistoryLogModel::setMessage(const QString &message_id) { if (auto dbc_msg = dbc()->msg(msg_id)) { sigs = dbc_msg->getSignals(); } + display_type = !sigs.empty() ? HistoryLogModel::Signals : HistoryLogModel::Hex; filter_cmp = nullptr; refresh(); } @@ -41,15 +50,16 @@ void HistoryLogModel::refresh() { } QVariant HistoryLogModel::headerData(int section, Qt::Orientation orientation, int role) const { + const bool display_signals = display_type == HistoryLogModel::Signals && !sigs.empty(); if (orientation == Qt::Horizontal) { if (role == Qt::DisplayRole || role == Qt::ToolTipRole) { if (section == 0) { return "Time"; } - return !sigs.empty() ? QString::fromStdString(sigs[section - 1]->name).replace('_', ' ') : "Data"; - } else if (role == Qt::BackgroundRole && section > 0 && !sigs.empty()) { + return display_signals ? QString::fromStdString(sigs[section - 1]->name).replace('_', ' ') : "Data"; + } else if (role == Qt::BackgroundRole && section > 0 && display_signals) { return QBrush(QColor(getColor(section - 1))); - } else if (role == Qt::ForegroundRole && section > 0 && !sigs.empty()) { + } else if (role == Qt::ForegroundRole && section > 0 && display_signals) { return QBrush(Qt::black); } } @@ -182,6 +192,11 @@ LogsWidget::LogsWidget(QWidget *parent) : QWidget(parent) { QVBoxLayout *main_layout = new QVBoxLayout(this); QHBoxLayout *h = new QHBoxLayout(); + + display_type_cb = new QComboBox(); + display_type_cb->addItems({"Signal value", "Hex value"}); + h->addWidget(display_type_cb); + signals_cb = new QComboBox(this); signals_cb->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); h->addWidget(signals_cb); @@ -202,6 +217,7 @@ LogsWidget::LogsWidget(QWidget *parent) : QWidget(parent) { main_layout->addWidget(logs); QObject::connect(logs, &QTableView::doubleClicked, this, &LogsWidget::doubleClicked); + QObject::connect(display_type_cb, SIGNAL(activated(int)), this, SLOT(displayTypeChanged())); QObject::connect(signals_cb, SIGNAL(activated(int)), this, SLOT(setFilter())); QObject::connect(comp_box, SIGNAL(activated(int)), this, SLOT(setFilter())); QObject::connect(value_edit, &QLineEdit::textChanged, this, &LogsWidget::setFilter); @@ -222,6 +238,8 @@ void LogsWidget::setMessage(const QString &message_id) { signals_cb->addItem(s->name.c_str()); } } + display_type_cb->setCurrentIndex(has_signals ? 0 : 1); + display_type_cb->setVisible(has_signals); comp_box->setVisible(has_signals); value_edit->setVisible(has_signals); signals_cb->setVisible(has_signals); @@ -245,6 +263,10 @@ void LogsWidget::setFilter() { cur_filter_text = value_edit->text(); } +void LogsWidget::displayTypeChanged() { + model->setDisplayType(display_type_cb->currentIndex() == 0 ? HistoryLogModel::Signals : HistoryLogModel::Hex); +} + void LogsWidget::showEvent(QShowEvent *event) { if (dynamic_mode->isChecked()) { model->refresh(); @@ -259,7 +281,7 @@ void LogsWidget::updateState() { void LogsWidget::doubleClicked(const QModelIndex &index) { if (index.isValid()) { - if (model->sigs.size() > 0 && index.column() > 0) { + if (model->display_type == HistoryLogModel::Signals && model->sigs.size() > 0 && index.column() > 0) { emit openChart(model->msg_id, model->sigs[index.column()-1]); } can->seekTo(model->messages[index.row()].mono_time / (double)1e9 - can->routeStartTime()); diff --git a/tools/cabana/historylog.h b/tools/cabana/historylog.h index f20f51637a..1eea7e5eba 100644 --- a/tools/cabana/historylog.h +++ b/tools/cabana/historylog.h @@ -21,16 +21,24 @@ class HistoryLogModel : public QAbstractTableModel { Q_OBJECT public: + enum DisplayType { + Signals, + Hex + }; + HistoryLogModel(QObject *parent) : QAbstractTableModel(parent) {} void setMessage(const QString &message_id); void updateState(); void setFilter(int sig_idx, const QString &value, std::function cmp); + void setDisplayType(DisplayType type); QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; void fetchMore(const QModelIndex &parent) override; inline bool canFetchMore(const QModelIndex &parent) const override { return has_more_data; } int rowCount(const QModelIndex &parent = QModelIndex()) const override { return messages.size(); } - int columnCount(const QModelIndex &parent = QModelIndex()) const override { return std::max(1ul, sigs.size()) + 1; } + int columnCount(const QModelIndex &parent = QModelIndex()) const override { + return display_type == HistoryLogModel::Hex ? 2 : std::max(1ul, sigs.size()) + 1; + } void setDynamicMode(int state); void segmentsMerged(); void refresh(); @@ -55,6 +63,7 @@ public: std::deque messages; std::vector sigs; bool dynamic_mode = false; + DisplayType display_type = HistoryLogModel::Signals; }; class HistoryLog : public QTableView { @@ -76,6 +85,7 @@ signals: private slots: void setFilter(); + void displayTypeChanged(); private: void doubleClicked(const QModelIndex &index); @@ -84,7 +94,7 @@ private: HistoryLog *logs; HistoryLogModel *model; QCheckBox *dynamic_mode; - QComboBox *signals_cb, *comp_box; + QComboBox *signals_cb, *comp_box, *display_type_cb; QLineEdit *value_edit; QString cur_filter_text; }; From af97a96080428976abf2f65aa7deec109a015032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Mon, 16 Jan 2023 19:43:34 -0800 Subject: [PATCH 102/484] Add lane ekf (#26986) * change lane_kf pos * add lane back here --- SConstruct | 2 +- selfdrive/locationd/models/lane_kf.py | 105 ++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) create mode 100755 selfdrive/locationd/models/lane_kf.py diff --git a/SConstruct b/SConstruct index b148a9116a..3051f4298d 100644 --- a/SConstruct +++ b/SConstruct @@ -387,10 +387,10 @@ rednose_config = { if arch != "larch64": rednose_config['to_build'].update({ 'loc_4': ('#selfdrive/locationd/models/loc_kf.py', True, [], rednose_deps), + 'lane': ('#selfdrive/locationd/models/lane_kf.py', True, [], rednose_deps), 'pos_computer_4': ('#rednose/helpers/lst_sq_computer.py', False, [], []), 'pos_computer_5': ('#rednose/helpers/lst_sq_computer.py', False, [], []), 'feature_handler_5': ('#rednose/helpers/feature_handler.py', False, [], []), - 'lane': ('#xx/pipeline/lib/ekf/lane_kf.py', True, [], rednose_deps), }) Export('rednose_config') diff --git a/selfdrive/locationd/models/lane_kf.py b/selfdrive/locationd/models/lane_kf.py new file mode 100755 index 0000000000..4d38fa8e09 --- /dev/null +++ b/selfdrive/locationd/models/lane_kf.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +import sys +import numpy as np +import sympy as sp + +from selfdrive.locationd.models.constants import ObservationKind +from rednose.helpers.ekf_sym import gen_code, EKF_sym + + +class LaneKalman(): + name = 'lane' + + @staticmethod + def generate_code(generated_dir): + # make functions and jacobians with sympy + # state variables + dim = 6 + state = sp.MatrixSymbol('state', dim, 1) + + dd = sp.Symbol('dd') # WARNING: NOT TIME + + # Time derivative of the state as a function of state + state_dot = sp.Matrix(np.zeros((dim, 1))) + state_dot[:3,0] = sp.Matrix(state[3:6,0]) + + # Basic descretization, 1st order intergrator + # Can be pretty bad if dt is big + f_sym = sp.Matrix(state) + dd*state_dot + + # + # Observation functions + # + h_lane_sym = sp.Matrix(state[:3,0]) + obs_eqs = [[h_lane_sym, ObservationKind.LANE_PT, None]] + gen_code(generated_dir, LaneKalman.name, f_sym, dd, state, obs_eqs, dim, dim) + + def __init__(self, generated_dir, pt_std=5): + # state + # left and right lane centers in ecef + # WARNING: this is not a temporal model + # the 'time' in this kalman filter is + # the distance traveled by the vehicle, + # which should approximately be the + # distance along the lane path + # a more logical parametrization + # states 0-2 are ecef coordinates distance d + # states 3-5 is the 3d "velocity" of the + # lane in ecef (m/m). + x_initial = np.array([0,0,0, + 0,0,0]) + + # state covariance + P_initial = np.diag([1e16, 1e16, 1e16, + 1**2, 1**2, 1**2]) + + # process noise + Q = np.diag([0.1**2, 0.1**2, 0.1**2, + 0.1**2, 0.1**2, 0.1*2]) + + self.dim_state = len(x_initial) + + # init filter + self.filter = EKF_sym(generated_dir, self.name, Q, x_initial, P_initial, x_initial.shape[0], P_initial.shape[0]) + self.obs_noise = {ObservationKind.LANE_PT: np.diag([pt_std**2]*3)} + + @property + def x(self): + return self.filter.state() + + @property + def P(self): + return self.filter.covs() + + def predict(self, t): + return self.filter.predict(t) + + def rts_smooth(self, estimates): + return self.filter.rts_smooth(estimates, norm_quats=False) + + + def init_state(self, state, covs_diag=None, covs=None, filter_time=None): + if covs_diag is not None: + P = np.diag(covs_diag) + elif covs is not None: + P = covs + else: + P = self.filter.covs() + self.filter.init_state(state, P, filter_time) + + def predict_and_observe(self, t, kind, data): + data = np.atleast_2d(data) + return self.filter.predict_and_update_batch(t, kind, data, self.get_R(kind, len(data))) + + def get_R(self, kind, n): + obs_noise = self.obs_noise[kind] + dim = obs_noise.shape[0] + R = np.zeros((n, dim, dim)) + for i in range(n): + R[i,:,:] = obs_noise + return R + + +if __name__ == "__main__": + generated_dir = sys.argv[2] + LaneKalman.generate_code(generated_dir) From a43a243b6e3852798390e0adc85f8f688f43b4d3 Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Mon, 16 Jan 2023 21:02:30 -0700 Subject: [PATCH 103/484] Improve GPSbox tests (#26983) * add altitude testing * fix proc check Co-authored-by: Kurt Nistelberger --- tools/gpstest/README.md | 7 +- tools/gpstest/fuzzy_testing.py | 204 +++++++++++---------------- tools/gpstest/helper.py | 53 +++++++ tools/gpstest/remote_checker.py | 54 ------- tools/gpstest/rpc_server.py | 185 ++++++++++++++++++++++++ tools/gpstest/simulate_gps_signal.py | 25 ++-- 6 files changed, 341 insertions(+), 187 deletions(-) create mode 100644 tools/gpstest/helper.py delete mode 100644 tools/gpstest/remote_checker.py create mode 100644 tools/gpstest/rpc_server.py diff --git a/tools/gpstest/README.md b/tools/gpstest/README.md index 5aff0ee3d7..01f44df0ce 100644 --- a/tools/gpstest/README.md +++ b/tools/gpstest/README.md @@ -3,12 +3,9 @@ Testing the GPS receiver using GPS spoofing. At the moment only static location relpay is supported. # Usage -``` -# on host, start gps signal simulation -./run_static_lime.py -``` +on C3 run `rpc_server.py`, on host PC run `fuzzy_testing.py` -`run_static_lime.py` downloads the latest ephemeris file from +`simulate_gps_signal.py` downloads the latest ephemeris file from https://cddis.nasa.gov/archive/gnss/data/daily/20xx/brdc/. diff --git a/tools/gpstest/fuzzy_testing.py b/tools/gpstest/fuzzy_testing.py index bd204e7ae7..a2e130342c 100755 --- a/tools/gpstest/fuzzy_testing.py +++ b/tools/gpstest/fuzzy_testing.py @@ -1,147 +1,115 @@ #!/usr/bin/env python3 -import sys -import time -import random -import datetime as dt -import subprocess as sp +import argparse import multiprocessing -import threading -from typing import Tuple, Any +import rpyc # pylint: disable=import-error +from collections import defaultdict -from laika.downloader import download_nav -from laika.gps_time import GPSTime -from laika.helpers import ConstellationId +from helper import download_rinex, exec_LimeGPS_bin +from helper import get_random_coords, get_continuous_coords -cache_dir = '/tmp/gpstest/' +#------------------------------------------------------------------------------ +# this script is supposed to run on HOST PC +# limeSDR is unreliable via c3 USB +#------------------------------------------------------------------------------ -def download_rinex(): - # TODO: check if there is a better way to get the full brdc file for LimeGPS - gps_time = GPSTime.from_datetime(dt.datetime.utcnow()) - utc_time = dt.datetime.utcnow() - dt.timedelta(1) - gps_time = GPSTime.from_datetime(dt.datetime(utc_time.year, utc_time.month, utc_time.day)) - return download_nav(gps_time, cache_dir, ConstellationId.GPS) - - -def exec_LimeGPS_bin(rinex_file: str, location: str, duration: int): - # this functions should never return, cause return means, timeout is - # reached or it crashed - try: - cmd = ["LimeGPS/LimeGPS", "-e", rinex_file, "-l", location] - sp.check_output(cmd, timeout=duration) - except sp.TimeoutExpired: - print("LimeGPS timeout reached!") - except Exception as e: - print(f"LimeGPS crashed: {str(e)}") - - -def run_lime_gps(rinex_file: str, location: str, duration: int): - print(f"LimeGPS {location} {duration}") - +def run_lime_gps(rinex_file: str, location: str, timeout: int): + # needs to run longer than the checker + timeout += 10 + print(f"LimeGPS {location} {timeout}") p = multiprocessing.Process(target=exec_LimeGPS_bin, - args=(rinex_file, location, duration)) + args=(rinex_file, location, timeout)) p.start() return p +con = None +def run_remote_checker(lat, lon, alt, duration, ip_addr): + global con + try: + con = rpyc.connect(ip_addr, 18861) + con._config['sync_request_timeout'] = duration+20 + except ConnectionRefusedError: + print("could not run remote checker is 'rpc_server.py' running???") + return False, None, None -def get_random_coords(lat, lon) -> Tuple[int, int]: - # jump around the world - # max values, lat: -90 to 90, lon: -180 to 180 - - lat_add = random.random()*20 + 10 - lon_add = random.random()*20 + 20 - - lat = ((lat + lat_add + 90) % 180) - 90 - lon = ((lon + lon_add + 180) % 360) - 180 - return round(lat, 5), round(lon, 5) - -def get_continuous_coords(lat, lon) -> Tuple[int, int]: - # continuously move around the world - - lat_add = random.random()*0.01 - lon_add = random.random()*0.01 - - lat = ((lat + lat_add + 90) % 180) - 90 - lon = ((lon + lon_add + 180) % 360) - 180 - return round(lat, 5), round(lon, 5) - -rc_p: Any = None -def exec_remote_checker(lat, lon, duration, ip_addr): - global rc_p - # TODO: good enough for testing - remote_cmd = "export PYTHONPATH=/data/pythonpath && " - remote_cmd += "cd /data/openpilot && " - remote_cmd += f"timeout {duration} /usr/local/pyenv/shims/python tools/gpstest/remote_checker.py " - remote_cmd += f"{lat} {lon}" - - ssh_cmd = ["ssh", "-i", "/home/batman/openpilot/xx/phone/key/id_rsa", - f"comma@{ip_addr}"] - ssh_cmd += [remote_cmd] - - rc_p = sp.Popen(ssh_cmd, stdout=sp.PIPE) - rc_p.wait() - rc_output = rc_p.stdout.read() - print(f"Checker Result: {rc_output.strip().decode('utf-8')}") + matched, log, info = con.root.exposed_run_checker(lat, lon, alt, + timeout=duration, + use_laikad=True) + con.close() # TODO: might wanna fetch more logs here + con = None + print(f"Remote Checker: {log} {info}") + return matched, log, info -def run_remote_checker(spoof_proc, lat, lon, duration, ip_addr) -> bool: - checker_thread = threading.Thread(target=exec_remote_checker, - args=(lat, lon, duration, ip_addr)) - checker_thread.start() - tcnt = 0 - while True: - if not checker_thread.is_alive(): - # assume this only happens when the signal got matched - return True +stats = defaultdict(int) # type: ignore +keys = ['success', 'failed', 'ublox_fail', 'laikad_fail', 'proc_crash', 'checker_crash'] - # the spoofing process has a timeout, kill checker if reached - if not spoof_proc.is_alive(): - rc_p.kill() - # spoofing process died, assume timeout - print("Spoofing process timeout") - return False +def print_report(): + print("\nFuzzy testing report summary:") + for k in keys: + print(f" {k}: {stats[k]}") - print(f"Time elapsed: {tcnt}[s]", end = "\r") - time.sleep(1) - tcnt += 1 +def update_stats(matched, log, info): + if matched: + stats['success'] += 1 + return -def main(): - if len(sys.argv) < 2: - print(f"usage: {sys.argv[0]} [-c]") - ip_addr = sys.argv[1] + stats['failed'] += 1 + if log == "PROC CRASH": + stats['proc_crash'] += 1 + if log == "CHECKER CRASHED": + stats['checker_crash'] += 1 + if log == "TIMEOUT": + if "LAIKAD" in info: + stats['laikad_fail'] += 1 + else: # "UBLOX" in info + stats['ublox_fail'] += 1 - continuous_mode = False - if len(sys.argv) == 3 and sys.argv[2] == '-c': - print("Continuous Mode!") - continuous_mode = True +def main(ip_addr, continuous_mode, timeout, pos): rinex_file = download_rinex() - duration = 60*3 # max runtime in seconds - lat, lon = get_random_coords(47.2020, 15.7403) + lat, lon, alt = pos + if lat == 0 and lon == 0 and alt == 0: + lat, lon, alt = get_random_coords(47.2020, 15.7403) + + try: + while True: + # spoof random location + spoof_proc = run_lime_gps(rinex_file, f"{lat},{lon},{alt}", timeout) - while True: - # spoof random location - spoof_proc = run_lime_gps(rinex_file, f"{lat},{lon},100", duration) - start_time = time.monotonic() + # remote checker execs blocking + matched, log, info = run_remote_checker(lat, lon, alt, timeout, ip_addr) + update_stats(matched, log, info) + spoof_proc.terminate() + spoof_proc = None - # remote checker runs blocking - if not run_remote_checker(spoof_proc, lat, lon, duration, ip_addr): - # location could not be matched by ublox module - pass + if continuous_mode: + lat, lon, alt = get_continuous_coords(lat, lon, alt) + else: + lat, lon, alt = get_random_coords(lat, lon) + except KeyboardInterrupt: + if spoof_proc is not None: + spoof_proc.terminate() - end_time = time.monotonic() - spoof_proc.terminate() + if con is not None and not con.closed: + con.root.exposed_kill_procs() + con.close() - # -1 to count process startup - print(f"Time to get Signal: {round(end_time - start_time - 1, 4)}") + print_report() - if continuous_mode: - lat, lon = get_continuous_coords(lat, lon) - else: - lat, lon = get_random_coords(lat, lon) if __name__ == "__main__": - main() + parser = argparse.ArgumentParser(description="Fuzzy test GPS stack with random locations.") + parser.add_argument("ip_addr", type=str) + parser.add_argument("-c", "--contin", type=bool, nargs='?', default=False, help='Continous location change') + parser.add_argument("-t", "--timeout", type=int, nargs='?', default=180, help='Timeout to get location') + + # for replaying a location + parser.add_argument("lat", type=float, nargs='?', default=0) + parser.add_argument("lon", type=float, nargs='?', default=0) + parser.add_argument("alt", type=float, nargs='?', default=0) + args = parser.parse_args() + main(args.ip_addr, args.contin, args.timeout, (args.lat, args.lon, args.alt)) diff --git a/tools/gpstest/helper.py b/tools/gpstest/helper.py new file mode 100644 index 0000000000..4f62e60db0 --- /dev/null +++ b/tools/gpstest/helper.py @@ -0,0 +1,53 @@ +import random +import datetime as dt +import subprocess as sp +from typing import Tuple + +from laika.downloader import download_nav +from laika.gps_time import GPSTime +from laika.helpers import ConstellationId + + +def download_rinex(): + # TODO: check if there is a better way to get the full brdc file for LimeGPS + gps_time = GPSTime.from_datetime(dt.datetime.utcnow()) + utc_time = dt.datetime.utcnow() - dt.timedelta(1) + gps_time = GPSTime.from_datetime(dt.datetime(utc_time.year, utc_time.month, utc_time.day)) + return download_nav(gps_time, '/tmp/gpstest/', ConstellationId.GPS) + + +def exec_LimeGPS_bin(rinex_file: str, location: str, duration: int): + # this functions should never return, cause return means, timeout is + # reached or it crashed + try: + cmd = ["LimeGPS/LimeGPS", "-e", rinex_file, "-l", location] + sp.check_output(cmd, timeout=duration) + except sp.TimeoutExpired: + print("LimeGPS timeout reached!") + except Exception as e: + print(f"LimeGPS crashed: {str(e)}") + + +def get_random_coords(lat, lon) -> Tuple[float, float, int]: + # jump around the world + # max values, lat: -90 to 90, lon: -180 to 180 + + lat_add = random.random()*20 + 10 + lon_add = random.random()*20 + 20 + alt = random.randint(-10**3, 4*10**3) + + lat = ((lat + lat_add + 90) % 180) - 90 + lon = ((lon + lon_add + 180) % 360) - 180 + return round(lat, 5), round(lon, 5), alt + + +def get_continuous_coords(lat, lon, alt) -> Tuple[float, float, int]: + # continuously move around the world + lat_add = random.random()*0.01 + lon_add = random.random()*0.01 + alt_add = random.randint(-100, 100) + + lat = ((lat + lat_add + 90) % 180) - 90 + lon = ((lon + lon_add + 180) % 360) - 180 + alt += alt_add + return round(lat, 5), round(lon, 5), alt diff --git a/tools/gpstest/remote_checker.py b/tools/gpstest/remote_checker.py deleted file mode 100644 index a649a105c3..0000000000 --- a/tools/gpstest/remote_checker.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python3 -import sys -import time -from typing import List - -from common.params import Params -import cereal.messaging as messaging -from selfdrive.manager.process_config import managed_processes - -DELTA = 0.001 -# assume running openpilot for now -procs: List[str] = []#"ubloxd", "pigeond"] - - -def main(): - if len(sys.argv) != 4: - print("args: ") - return - - quectel_mod = Params().get_bool("UbloxAvailable") - sol_lat = float(sys.argv[2]) - sol_lon = float(sys.argv[3]) - - for p in procs: - managed_processes[p].start() - time.sleep(0.5) # give time to startup - - socket = 'gpsLocation' if quectel_mod else 'gpsLocationExternal' - gps_sock = messaging.sub_sock(socket, timeout=0.1) - - # analyze until the location changed - while True: - events = messaging.drain_sock(gps_sock) - for e in events: - loc = e.gpsLocation if quectel_mod else e.gpsLocationExternal - lat = loc.latitude - lon = loc.longitude - - if abs(lat - sol_lat) < DELTA and abs(lon - sol_lon) < DELTA: - print("MATCH") - return - - time.sleep(0.1) - - for p in procs: - if not managed_processes[p].proc.is_alive(): - print(f"ERROR: '{p}' died") - return - - -if __name__ == "__main__": - main() - for p in procs: - managed_processes[p].stop() diff --git a/tools/gpstest/rpc_server.py b/tools/gpstest/rpc_server.py new file mode 100644 index 0000000000..b35c66d02d --- /dev/null +++ b/tools/gpstest/rpc_server.py @@ -0,0 +1,185 @@ +import os +import time +import shutil +from datetime import datetime +from collections import defaultdict + +import rpyc # pylint: disable=import-error +from rpyc.utils.server import ThreadedServer # pylint: disable=import-error + +#from common.params import Params +import cereal.messaging as messaging +from selfdrive.manager.process_config import managed_processes +from laika.lib.coordinates import ecef2geodetic + +DELTA = 0.001 +ALT_DELTA = 30 +MATCH_NUM = 10 +REPORT_STATS = 10 + +EPHEM_CACHE = "/data/params/d/LaikadEphemeris" +DOWNLOAD_CACHE = "/tmp/comma_download_cache" + +SERVER_LOG_FILE = "/tmp/fuzzy_server.log" +server_log = open(SERVER_LOG_FILE, "w+") + +def slog(msg): + server_log.write(f"{datetime.now().strftime('%H:%M:%S.%f')} | {msg}\n") + server_log.flush() + +def handle_laikad(msg): + if not hasattr(msg, 'correctedMeasurements'): + return None + + num_corr = len(msg.correctedMeasurements) + pos_ecef = msg.positionECEF.value + pos_geo = [] + if len(pos_ecef) > 0: + pos_geo = ecef2geodetic(pos_ecef) + + pos_std = msg.positionECEF.std + pos_valid = msg.positionECEF.valid + + slog(f"{num_corr} {pos_geo} {pos_ecef} {pos_std} {pos_valid}") + return pos_geo, (num_corr, pos_geo, list(pos_ecef), list(msg.positionECEF.std)) + +hw_msgs = 0 +ephem_msgs: dict = defaultdict(int) +def handle_ublox(msg): + global hw_msgs + + d = msg.to_dict() + + if 'hwStatus2' in d: + hw_msgs += 1 + + if 'ephemeris' in d: + ephem_msgs[msg.ephemeris.svId] += 1 + + num_meas = None + if 'measurementReport' in d: + num_meas = msg.measurementReport.numMeas + + return [hw_msgs, ephem_msgs, num_meas] + + +def start_procs(procs): + for p in procs: + managed_processes[p].start() + time.sleep(1) + +def kill_procs(procs, no_retry=False): + for p in procs: + managed_processes[p].stop() + time.sleep(1) + + if not no_retry: + for p in procs: + mp = managed_processes[p].proc + if mp is not None and mp.is_alive(): + managed_processes[p].stop() + time.sleep(3) + +def check_alive_procs(procs): + for p in procs: + mp = managed_processes[p].proc + if mp is None or not mp.is_alive(): + return False, p + return True, None + + +class RemoteCheckerService(rpyc.Service): + def on_connect(self, conn): + pass + + def on_disconnect(self, conn): + #kill_procs(self.procs, no_retry=False) + # this execution is delayed, it will kill the next run of laikad + # TODO: add polling to wait for everything is killed + pass + + def run_checker(self, slat, slon, salt, sockets, procs, timeout): + global hw_msgs, ephem_msgs + hw_msgs = 0 + ephem_msgs = defaultdict(int) + + slog(f"Run test: {slat} {slon} {salt}") + + # quectel_mod = Params().get_bool("UbloxAvailable") + + match_cnt = 0 + msg_cnt = 0 + stats_laikad = [] + stats_ublox = [] + + self.procs = procs + start_procs(procs) + sm = messaging.SubMaster(sockets) + + start_time = time.monotonic() + while True: + sm.update() + + if sm.updated['ubloxGnss']: + stats_ublox.append(handle_ublox(sm['ubloxGnss'])) + + if sm.updated['gnssMeasurements']: + pos_geo, stats = handle_laikad(sm['gnssMeasurements']) + if pos_geo is None or len(pos_geo) == 0: + continue + + match = all(abs(g-s) < DELTA for g,s in zip(pos_geo[:2], [slat, slon])) + match &= abs(pos_geo[2] - salt) < ALT_DELTA + if match: + match_cnt += 1 + if match_cnt >= MATCH_NUM: + return True, "MATCH", f"After: {round(time.monotonic() - start_time, 4)}" + + # keep some stats for error reporting + stats_laikad.append(stats) + + if (msg_cnt % 10) == 0: + a, p = check_alive_procs(procs) + if not a: + return False, "PROC CRASH", f"{p}" + msg_cnt += 1 + + if (time.monotonic() - start_time) > timeout: + h = f"LAIKAD: {stats_laikad[-REPORT_STATS:]}" + if len(h) == 0: + h = f"UBLOX: {stats_ublox[-REPORT_STATS:]}" + return False, "TIMEOUT", h + + + def exposed_run_checker(self, slat, slon, salt, timeout=180, use_laikad=True): + try: + procs = [] + sockets = [] + + if use_laikad: + procs.append("laikad") # pigeond, ubloxd # might wanna keep them running + sockets += ['ubloxGnss', 'gnssMeasurements'] + + if os.path.exists(EPHEM_CACHE): + os.remove(EPHEM_CACHE) + shutil.rmtree(DOWNLOAD_CACHE, ignore_errors=True) + + ret = self.run_checker(slat, slon, salt, sockets, procs, timeout) + kill_procs(procs) + return ret + + except Exception as e: + # always make sure processes get killed + kill_procs(procs) + return False, "CHECKER CRASHED", f"{str(e)}" + + + def exposed_kill_procs(self): + kill_procs(self.procs, no_retry=True) + + +if __name__ == "__main__": + print(f"Sever Log written to: {SERVER_LOG_FILE}") + t = ThreadedServer(RemoteCheckerService, port=18861) + t.start() + diff --git a/tools/gpstest/simulate_gps_signal.py b/tools/gpstest/simulate_gps_signal.py index a6aca1c404..da0f64eaca 100755 --- a/tools/gpstest/simulate_gps_signal.py +++ b/tools/gpstest/simulate_gps_signal.py @@ -16,7 +16,7 @@ cache_dir = '/tmp/gpstest/' def download_rinex(): # TODO: check if there is a better way to get the full brdc file for LimeGPS gps_time = GPSTime.from_datetime(dt.datetime.utcnow()) - utc_time = dt.datetime.utcnow() - dt.timedelta(1) + utc_time = dt.datetime.utcnow()# - dt.timedelta(1) gps_time = GPSTime.from_datetime(dt.datetime(utc_time.year, utc_time.month, utc_time.day)) return download_nav(gps_time, cache_dir, ConstellationId.GPS) @@ -36,11 +36,15 @@ def get_random_coords(lat, lon) -> Tuple[int, int]: # jump around the world return get_coords(lat, lon, 20, 20, 10, 20) -def run_limeSDR_loop(lat, lon, contin_sim, rinex_file, timeout): +def run_limeSDR_loop(lat, lon, alt, contin_sim, rinex_file, timeout): while True: try: - print(f"starting LimeGPS, Location: {lat},{lon}") - cmd = ["LimeGPS/LimeGPS", "-e", rinex_file, "-l", f"{lat},{lon},100"] + # TODO: add starttime setting and altitude + # -t 2023/01/15,00:00:00 -T 2023/01/15,00:00:00 + # this needs to match the date of the navigation file + print(f"starting LimeGPS, Location: {lat} {lon} {alt}") + cmd = ["LimeGPS/LimeGPS", "-e", rinex_file, "-l", f"{lat},{lon},{alt}"] + print(f"CMD: {cmd}") sp.check_output(cmd, stderr=sp.PIPE, timeout=timeout) except KeyboardInterrupt: print("stopping LimeGPS") @@ -71,7 +75,7 @@ def run_hackRF_loop(lat, lon, rinex_file, timeout): try: print(f"starting gps-sdr-sim, Location: {lat},{lon}") # create 30second file and replay with hackrf endless - cmd = ["gps-sdr-sim/gps-sdr-sim", "-e", rinex_file, "-l", f"{lat},{lon},100", "-d", "30"] + cmd = ["gps-sdr-sim/gps-sdr-sim", "-e", rinex_file, "-l", f"{lat},{lon},-200", "-d", "30"] sp.check_output(cmd, stderr=sp.PIPE, timeout=timeout) # created in current working directory except Exception: @@ -90,7 +94,7 @@ def run_hackRF_loop(lat, lon, rinex_file, timeout): print(f"hackrf_transfer crashed:{str(e)}") -def main(lat, lon, jump_sim, contin_sim, hackrf_mode): +def main(lat, lon, alt, jump_sim, contin_sim, hackrf_mode): if hackrf_mode: if not os.path.exists('hackrf'): @@ -130,17 +134,18 @@ def main(lat, lon, jump_sim, contin_sim, hackrf_mode): if jump_sim: timeout = 30 - if not hackrf_mode: - run_limeSDR_loop(lat, lon, contin_sim, rinex_file, timeout) - else: + if hackrf_mode: run_hackRF_loop(lat, lon, rinex_file, timeout) + else: + run_limeSDR_loop(lat, lon, alt, contin_sim, rinex_file, timeout) if __name__ == "__main__": parser = argparse.ArgumentParser(description="Simulate static [or random jumping] GPS signal.") parser.add_argument("lat", type=float, nargs='?', default=0) parser.add_argument("lon", type=float, nargs='?', default=0) + parser.add_argument("alt", type=float, nargs='?', default=0) parser.add_argument("--jump", action="store_true", help="signal that jumps around the world") parser.add_argument("--contin", action="store_true", help="continuously/slowly moving around the world") parser.add_argument("--hackrf", action="store_true", help="hackrf mode (DEFAULT: LimeSDR)") args = parser.parse_args() - main(args.lat, args.lon, args.jump, args.contin, args.hackrf) + main(args.lat, args.lon, args.alt, args.jump, args.contin, args.hackrf) From 2ef127ebc05c609dffff506a64fa3c600d7b194b Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 18 Jan 2023 05:09:18 +0800 Subject: [PATCH 104/484] cabana: more compact form (#26985) --- tools/cabana/signaledit.cc | 52 ++++++++++++++++++++++---------------- tools/cabana/signaledit.h | 2 +- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/tools/cabana/signaledit.cc b/tools/cabana/signaledit.cc index 3845e72be1..2a92e03461 100644 --- a/tools/cabana/signaledit.cc +++ b/tools/cabana/signaledit.cc @@ -9,50 +9,56 @@ // SignalForm SignalForm::SignalForm(QWidget *parent) : QWidget(parent) { - QFormLayout *form_layout = new QFormLayout(this); + auto double_validator = new QDoubleValidator(this); + QVBoxLayout *main_layout = new QVBoxLayout(this); + QFormLayout *form_layout = new QFormLayout(); + main_layout->addLayout(form_layout); name = new QLineEdit(); name->setValidator(new QRegExpValidator(QRegExp("^(\\w+)"), name)); form_layout->addRow(tr("Name"), name); + QHBoxLayout *hl = new QHBoxLayout(this); size = new QSpinBox(); size->setMinimum(1); - form_layout->addRow(tr("Size"), size); - + hl->addWidget(size); endianness = new QComboBox(); - endianness->addItems({"Little", "Big"}); - form_layout->addRow(tr("Endianness"), endianness); - - form_layout->addRow(tr("lsb"), lsb = new QLabel()); - form_layout->addRow(tr("msb"), msb = new QLabel()); - + endianness->addItems({"Little Endianness", "Big Endianness"}); + hl->addWidget(endianness); sign = new QComboBox(); sign->addItems({"Signed", "Unsigned"}); - form_layout->addRow(tr("sign"), sign); - - auto double_validator = new QDoubleValidator(this); + hl->addWidget(sign); + form_layout->addRow(tr("Size"), hl); + offset = new QLineEdit(); + offset->setValidator(double_validator); + form_layout->addRow(tr("Offset"), offset); factor = new QLineEdit(); factor->setValidator(double_validator); form_layout->addRow(tr("Factor"), factor); - offset = new QLineEdit(); - offset->setValidator(double_validator); - form_layout->addRow(tr("Offset"), offset); + expand_btn = new QToolButton(this); + expand_btn->setText(tr("more...")); + main_layout->addWidget(expand_btn, 0, Qt::AlignRight); // TODO: parse the following parameters in opendbc + QWidget *extra_container = new QWidget(this); + QFormLayout *extra_layout = new QFormLayout(extra_container); unit = new QLineEdit(); - form_layout->addRow(tr("Unit"), unit); + extra_layout->addRow(tr("Unit"), unit); comment = new QLineEdit(); - form_layout->addRow(tr("Comment"), comment); + extra_layout->addRow(tr("Comment"), comment); min_val = new QLineEdit(); min_val->setValidator(double_validator); - form_layout->addRow(tr("Minimum value"), min_val); + extra_layout->addRow(tr("Minimum value"), min_val); max_val = new QLineEdit(); max_val->setValidator(double_validator); - form_layout->addRow(tr("Maximum value"), max_val); + extra_layout->addRow(tr("Maximum value"), max_val); val_desc = new QLineEdit(); - form_layout->addRow(tr("Value descriptions"), val_desc); + extra_layout->addRow(tr("Value descriptions"), val_desc); + + main_layout->addWidget(extra_container); + extra_container->setVisible(false); QObject::connect(name, &QLineEdit::editingFinished, this, &SignalForm::textBoxEditingFinished); QObject::connect(factor, &QLineEdit::editingFinished, this, &SignalForm::textBoxEditingFinished); @@ -60,6 +66,10 @@ SignalForm::SignalForm(QWidget *parent) : QWidget(parent) { QObject::connect(size, &QSpinBox::editingFinished, this, &SignalForm::changed); QObject::connect(sign, SIGNAL(activated(int)), SIGNAL(changed())); QObject::connect(endianness, SIGNAL(activated(int)), SIGNAL(changed())); + QObject::connect(expand_btn, &QToolButton::clicked, [=]() { + extra_container->setVisible(!extra_container->isVisible()); + expand_btn->setText(extra_container->isVisible() ? tr("less...") : tr("more...")); + }); } void SignalForm::textBoxEditingFinished() { @@ -180,8 +190,6 @@ void SignalEdit::updateForm(bool visible) { form->sign->setCurrentIndex(sig->is_signed ? 0 : 1); form->factor->setText(QString::number(sig->factor)); form->offset->setText(QString::number(sig->offset)); - form->msb->setText(QString::number(sig->msb)); - form->lsb->setText(QString::number(sig->lsb)); form->size->setValue(sig->size); } form->setVisible(visible); diff --git a/tools/cabana/signaledit.h b/tools/cabana/signaledit.h index d7b9084e7f..a18f1d34f0 100644 --- a/tools/cabana/signaledit.h +++ b/tools/cabana/signaledit.h @@ -17,9 +17,9 @@ public: void textBoxEditingFinished(); QLineEdit *name, *unit, *comment, *val_desc, *offset, *factor, *min_val, *max_val; - QLabel *lsb, *msb; QSpinBox *size; QComboBox *sign, *endianness; + QToolButton *expand_btn; signals: void changed(); From bc8e2032f7aaafb172e4b2505e7cdc5da1970eb9 Mon Sep 17 00:00:00 2001 From: ZwX1616 Date: Tue, 17 Jan 2023 14:16:28 -0800 Subject: [PATCH 105/484] camerad: reduce ox LFM noise (#26949) * wip * make separate score func * n4ot g * clean up * remove * simplify * b2b * more than 0 * this is fine * led * balance costs * clean up * no if tici * box view * new baselines * Revert "new baselines" This reverts commit f7a5d059eda70fa4d49e3024923da30e8821f42b. * Revert "box view" This reverts commit a57924be36e7d6abc2585aca9575c74cd583ef86. * maximize dcg Co-authored-by: Comma Dev1ce --- system/camerad/cameras/camera_qcom2.cc | 25 +++++++++++++------------ system/camerad/cameras/sensor2_i2c.h | 8 ++++---- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/system/camerad/cameras/camera_qcom2.cc b/system/camerad/cameras/camera_qcom2.cc index 8e2ad7bd91..92b3bde413 100644 --- a/system/camerad/cameras/camera_qcom2.cc +++ b/system/camerad/cameras/camera_qcom2.cc @@ -60,15 +60,15 @@ CameraInfo cameras_supported[CAMERA_ID_MAX] = { const float DC_GAIN_AR0231 = 2.5; const float DC_GAIN_OX03C10 = 7.32; -const float DC_GAIN_ON_GREY_AR0231= 0.2; +const float DC_GAIN_ON_GREY_AR0231 = 0.2; const float DC_GAIN_OFF_GREY_AR0231 = 0.3; -const float DC_GAIN_ON_GREY_OX03C10= 0.25; -const float DC_GAIN_OFF_GREY_OX03C10 = 0.35; +const float DC_GAIN_ON_GREY_OX03C10 = 0.9; +const float DC_GAIN_OFF_GREY_OX03C10 = 1.0; const int DC_GAIN_MIN_WEIGHT_AR0231 = 0; const int DC_GAIN_MAX_WEIGHT_AR0231 = 1; -const int DC_GAIN_MIN_WEIGHT_OX03C10 = 16; -const int DC_GAIN_MAX_WEIGHT_OX03C10 = 32; +const int DC_GAIN_MIN_WEIGHT_OX03C10 = 1; // always on is fine +const int DC_GAIN_MAX_WEIGHT_OX03C10 = 1; const float TARGET_GREY_FACTOR_AR0231 = 1.0; const float TARGET_GREY_FACTOR_OX03C10 = 0.02; @@ -1172,15 +1172,16 @@ void CameraState::set_camera_exposure(float grey_frac) { }; sensors_i2c(exp_reg_array, sizeof(exp_reg_array)/sizeof(struct i2c_random_wr_payload), CAM_SENSOR_PACKET_OPCODE_SENSOR_CONFIG, true); } else if (camera_id == CAMERA_ID_OX03C10) { - // t_HCG + t_LCG + t_VS on LPD, t_SPD on SPD - uint32_t hcg_time = std::max((dc_gain_weight * exposure_time / dc_gain_max_weight), 0); - uint32_t lcg_time = std::max(((dc_gain_max_weight - dc_gain_weight) * exposure_time / dc_gain_max_weight), 0); - // uint32_t spd_time = std::max(hcg_time / 16, (uint32_t)exposure_time_min); - uint32_t vs_time = std::min(std::max((uint32_t)exposure_time / 128, VS_TIME_MIN_OX03C10), VS_TIME_MAX_OX03C10); - uint32_t spd_time = vs_time; + // t_HCG&t_LCG + t_VS on LPD, t_SPD on SPD + uint32_t hcg_time = exposure_time; + uint32_t lcg_time = hcg_time; + uint32_t spd_time = exposure_time_max + VS_TIME_MAX_OX03C10; + uint32_t vs_time = std::min(std::max((uint32_t)exposure_time / 40, VS_TIME_MIN_OX03C10), VS_TIME_MAX_OX03C10); uint32_t real_gain = ox03c10_analog_gains_reg[new_exp_g]; uint32_t min_gain = ox03c10_analog_gains_reg[0]; + uint32_t spd_gain = 0xF00; + struct i2c_random_wr_payload exp_reg_array[] = { {0x3501, hcg_time>>8}, {0x3502, hcg_time&0xFF}, {0x3581, lcg_time>>8}, {0x3582, lcg_time&0xFF}, @@ -1189,7 +1190,7 @@ void CameraState::set_camera_exposure(float grey_frac) { {0x3508, real_gain>>8}, {0x3509, real_gain&0xFF}, {0x3588, min_gain>>8}, {0x3589, min_gain&0xFF}, - {0x3548, min_gain>>8}, {0x3549, min_gain&0xFF}, + {0x3548, spd_gain>>8}, {0x3549, spd_gain&0xFF}, {0x35c8, min_gain>>8}, {0x35c9, min_gain&0xFF}, }; sensors_i2c(exp_reg_array, sizeof(exp_reg_array)/sizeof(struct i2c_random_wr_payload), CAM_SENSOR_PACKET_OPCODE_SENSOR_CONFIG, false); diff --git a/system/camerad/cameras/sensor2_i2c.h b/system/camerad/cameras/sensor2_i2c.h index ab51059d9a..83fcb8f7a9 100644 --- a/system/camerad/cameras/sensor2_i2c.h +++ b/system/camerad/cameras/sensor2_i2c.h @@ -126,7 +126,7 @@ struct i2c_random_wr_payload init_array_ox03c10[] = { {0x3219, 0x08}, {0x3506, 0x20}, {0x3507, 0x00}, // hcg fine exposure - {0x350a, 0x04}, {0x350b, 0x00}, {0x350c, 0x00}, // hcg digital gain + {0x350a, 0x01}, {0x350b, 0x00}, {0x350c, 0x00}, // hcg digital gain {0x3586, 0x40}, {0x3587, 0x00}, // lcg fine exposure {0x358a, 0x01}, {0x358b, 0x00}, {0x358c, 0x00}, // lcg digital gain @@ -711,11 +711,11 @@ struct i2c_random_wr_payload init_array_ox03c10[] = { {0x4221, 0x03}, // this is changed from 1 -> 3 // DCG exposure coarse - {0x3501, 0x01}, {0x3502, 0xc8}, + // {0x3501, 0x01}, {0x3502, 0xc8}, // SPD exposure coarse - {0x3541, 0x01}, {0x3542, 0xc8}, + // {0x3541, 0x01}, {0x3542, 0xc8}, // VS exposure coarse - {0x35c1, 0x00}, {0x35c2, 0x01}, + // {0x35c1, 0x00}, {0x35c2, 0x01}, // crc reference {0x420e, 0x66}, {0x420f, 0x5d}, {0x4210, 0xa8}, {0x4211, 0x55}, From fe9fadaa55760d3940d7ccabd1e7edbf3bed1b1a Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 17 Jan 2023 14:22:47 -0800 Subject: [PATCH 106/484] Ioniq 2019 EV: add FW versions (#26988) * add FW from 26e73a0e32642dc4|2023-01-16--13-05-28 * duplicate --- selfdrive/car/hyundai/values.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index c4fda42b63..ec7605657b 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -448,11 +448,13 @@ FW_VERSIONS = { b'\xf1\x00AE MDPS C 1.00 1.04 56310/G7501 4AEEC104', b'\xf1\x00AE MDPS C 1.00 1.03 56310/G7300 4AEEC103', b'\xf1\x00AE MDPS C 1.00 1.03 56310G7300\x00 4AEEC103', + b'\xf1\x00AE MDPS C 1.00 1.04 56310/G7301 4AEEC104', ], (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00AEE MFC AT EUR LHD 1.00 1.00 95740-G7200 160418', b'\xf1\x00AEE MFC AT USA LHD 1.00 1.00 95740-G2400 180222', b'\xf1\x00AEE MFC AT EUR LHD 1.00 1.00 95740-G2300 170703', + b'\xf1\x00AEE MFC AT EUR LHD 1.00 1.00 95740-G2400 180222', ], }, CAR.IONIQ_HEV_2022: { From e5931ed7624208e1413cb06e1522eb704948e1c0 Mon Sep 17 00:00:00 2001 From: ZwX1616 Date: Tue, 17 Jan 2023 17:17:10 -0800 Subject: [PATCH 107/484] boardd: retune LED curve for new autoexposure (#26991) * use il only * works on both Co-authored-by: Comma Dev1ce --- selfdrive/boardd/boardd.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/selfdrive/boardd/boardd.cc b/selfdrive/boardd/boardd.cc index 1f1249194d..e626882191 100644 --- a/selfdrive/boardd/boardd.cc +++ b/selfdrive/boardd/boardd.cc @@ -48,8 +48,8 @@ #define MAX_IR_POWER 0.5f #define MIN_IR_POWER 0.0f -#define CUTOFF_IL 200 -#define SATURATE_IL 1600 +#define CUTOFF_IL 400 +#define SATURATE_IL 1000 #define NIBBLE_TO_HEX(n) ((n) < 10 ? (n) + '0' : ((n) - 10) + 'a') using namespace std::chrono_literals; @@ -540,9 +540,8 @@ void peripheral_control_thread(Panda *panda, bool no_fan_control) { if (sm.updated("driverCameraState")) { auto event = sm["driverCameraState"]; int cur_integ_lines = event.getDriverCameraState().getIntegLines(); - float cur_gain = event.getDriverCameraState().getGain(); - cur_integ_lines = integ_lines_filter.update(cur_integ_lines * cur_gain); + cur_integ_lines = integ_lines_filter.update(cur_integ_lines); last_front_frame_t = event.getLogMonoTime(); if (cur_integ_lines <= CUTOFF_IL) { From ef42652368bb56a93e75f172062ac9f6aca29563 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 17 Jan 2023 19:52:06 -0800 Subject: [PATCH 108/484] fix typo AR0321 -> AR0231 (#26993) --- Jenkinsfile | 2 +- cereal | 2 +- selfdrive/ui/ui.cc | 2 +- system/camerad/cameras/camera_common.cc | 2 +- system/camerad/test/test_camerad.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 3b16b3f112..bd138bed76 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -152,7 +152,7 @@ pipeline { stage('camerad-ar') { agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } } steps { - phone_steps("tici-ar0321", [ + phone_steps("tici-ar0231", [ ["build", "cd selfdrive/manager && ./build.py"], ["test camerad", "python system/camerad/test/test_camerad.py"], ["test exposure", "python system/camerad/test/test_exposure.py"], diff --git a/cereal b/cereal index c0d9abf6f7..6b91a52d3d 160000 --- a/cereal +++ b/cereal @@ -1 +1 @@ -Subproject commit c0d9abf6f7c7de140c41af10e322e226d900ef99 +Subproject commit 6b91a52d3d7bd62507f26ac1321338d4cfd2dc03 diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index c62d737481..4df963167a 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -163,7 +163,7 @@ static void update_state(UIState *s) { scene.longitudinal_control = sm["carParams"].getCarParams().getOpenpilotLongitudinalControl(); } if (sm.updated("wideRoadCameraState")) { - float scale = (sm["wideRoadCameraState"].getWideRoadCameraState().getSensor() == cereal::FrameData::ImageSensor::AR0321) ? 6.0f : 1.0f; + float scale = (sm["wideRoadCameraState"].getWideRoadCameraState().getSensor() == cereal::FrameData::ImageSensor::AR0231) ? 6.0f : 1.0f; scene.light_sensor = std::max(100.0f - scale * sm["wideRoadCameraState"].getWideRoadCameraState().getExposureValPercent(), 0.0f); } scene.started = sm["deviceState"].getDeviceState().getStarted() && scene.ignition; diff --git a/system/camerad/cameras/camera_common.cc b/system/camerad/cameras/camera_common.cc index 30e2810ec4..7ee3738057 100644 --- a/system/camerad/cameras/camera_common.cc +++ b/system/camerad/cameras/camera_common.cc @@ -167,7 +167,7 @@ void fill_frame_data(cereal::FrameData::Builder &framed, const FrameMetadata &fr framed.setExposureValPercent(perc); if (c->camera_id == CAMERA_ID_AR0231) { - framed.setSensor(cereal::FrameData::ImageSensor::AR0321); + framed.setSensor(cereal::FrameData::ImageSensor::AR0231); } else if (c->camera_id == CAMERA_ID_OX03C10) { framed.setSensor(cereal::FrameData::ImageSensor::OX03C10); } diff --git a/system/camerad/test/test_camerad.py b/system/camerad/test/test_camerad.py index 6c2ef1c7bc..34411e4ef1 100755 --- a/system/camerad/test/test_camerad.py +++ b/system/camerad/test/test_camerad.py @@ -10,7 +10,7 @@ from selfdrive.manager.process_config import managed_processes from system.hardware import TICI TEST_TIMESPAN = 30 -LAG_FRAME_TOLERANCE = {log.FrameData.ImageSensor.ar0321: 0.5, # ARs use synced pulses for frame starts +LAG_FRAME_TOLERANCE = {log.FrameData.ImageSensor.ar0231: 0.5, # ARs use synced pulses for frame starts log.FrameData.ImageSensor.ox03c10: 1.0} # OXs react to out-of-sync at next frame CAMERAS = ('roadCameraState', 'driverCameraState', 'wideRoadCameraState') From 2fd48e26a3580a52d76b43dada7fc04eb2cbe669 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 17 Jan 2023 22:00:01 -0800 Subject: [PATCH 109/484] Deprecate canMonoTimes (#26992) * not used anywhere * bump cereal * also here --- cereal | 2 +- selfdrive/controls/controlsd.py | 1 - selfdrive/controls/radard.py | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/cereal b/cereal index 6b91a52d3d..b27131e72f 160000 --- a/cereal +++ b/cereal @@ -1 +1 @@ -Subproject commit 6b91a52d3d7bd62507f26ac1321338d4cfd2dc03 +Subproject commit b27131e72f987f3bd0fcd28a42f686b583709e1f diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 1d92cd4fb2..ac36df7aa2 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -757,7 +757,6 @@ class Controls: controlsState.alertType = current_alert.alert_type controlsState.alertSound = current_alert.audible_alert - controlsState.canMonoTimes = list(CS.canMonoTimes) controlsState.longitudinalPlanMonoTime = self.sm.logMonoTime['longitudinalPlan'] controlsState.lateralPlanMonoTime = self.sm.logMonoTime['lateralPlan'] controlsState.enabled = self.enabled diff --git a/selfdrive/controls/radard.py b/selfdrive/controls/radard.py index 6a0902e510..34f0f274fe 100755 --- a/selfdrive/controls/radard.py +++ b/selfdrive/controls/radard.py @@ -165,7 +165,6 @@ class RadarD(): dat.valid = sm.all_checks() and len(rr.errors) == 0 radarState = dat.radarState radarState.mdMonoTime = sm.logMonoTime['modelV2'] - radarState.canMonoTimes = list(rr.canMonoTimes) radarState.radarErrors = list(rr.errors) radarState.carStateMonoTime = sm.logMonoTime['carState'] From ef89ec3eb095df8604d8c66009413444b634b1e6 Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Wed, 18 Jan 2023 19:31:40 +0100 Subject: [PATCH 110/484] cabana: fix segfault on descending sort (#26995) --- tools/cabana/messageswidget.cc | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index fd0bc68514..ffbca1b1df 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -96,8 +96,9 @@ void MessageListModel::sortMessages() { beginResetModel(); if (sort_column == 0) { std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) { - bool ret = std::pair{msgName(l), l} < std::pair{msgName(r), r}; - return sort_order == Qt::AscendingOrder ? ret : !ret; + auto ll = std::pair{msgName(l), l}; + auto rr = std::pair{msgName(r), r}; + return sort_order == Qt::AscendingOrder ? ll < rr : ll > rr; }); } else if (sort_column == 1) { std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) { @@ -105,13 +106,15 @@ void MessageListModel::sortMessages() { }); } else if (sort_column == 2) { std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) { - bool ret = std::pair{can->lastMessage(l).freq, l} < std::pair{can->lastMessage(r).freq, r}; - return sort_order == Qt::AscendingOrder ? ret : !ret; + auto ll = std::pair{can->lastMessage(l).freq, l}; + auto rr = std::pair{can->lastMessage(r).freq, r}; + return sort_order == Qt::AscendingOrder ? ll < rr : ll > rr; }); } else if (sort_column == 3) { std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) { - bool ret = std::pair{can->lastMessage(l).count, l} < std::pair{can->lastMessage(r).count, r}; - return sort_order == Qt::AscendingOrder ? ret : !ret; + auto ll = std::pair{can->lastMessage(l).count, l}; + auto rr = std::pair{can->lastMessage(r).count, r}; + return sort_order == Qt::AscendingOrder ? ll < rr : ll > rr; }); } endResetModel(); From e69e4f4052927b1053145d06cc8144f209f84f11 Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Wed, 18 Jan 2023 19:32:04 +0100 Subject: [PATCH 111/484] cabana: sort bus:id numerically instead of alphabetically (#26996) * cabana: sort bus:id numerically instead of alphabetically * Update tools/cabana/canmessages.h --- tools/cabana/canmessages.cc | 2 ++ tools/cabana/canmessages.h | 2 ++ tools/cabana/messageswidget.cc | 4 +++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/cabana/canmessages.cc b/tools/cabana/canmessages.cc index 6e19eb440f..a473313f90 100644 --- a/tools/cabana/canmessages.cc +++ b/tools/cabana/canmessages.cc @@ -64,6 +64,8 @@ bool CANMessages::eventFilter(const Event *event) { QString id = QString("%1:%2").arg(c.getSrc()).arg(c.getAddress(), 1, 16); CanData &data = (*new_msgs)[id]; data.ts = current_sec; + data.src = c.getSrc(); + data.address = c.getAddress(); data.dat = QByteArray((char *)c.getDat().begin(), c.getDat().size()); data.count = ++counters[id]; if (double delta = (current_sec - counters_begin_sec); delta > 0) { diff --git a/tools/cabana/canmessages.h b/tools/cabana/canmessages.h index ea43933565..2451793742 100644 --- a/tools/cabana/canmessages.h +++ b/tools/cabana/canmessages.h @@ -11,6 +11,8 @@ struct CanData { double ts = 0.; + uint8_t src = 0; + uint32_t address = 0; uint32_t count = 0; uint32_t freq = 0; QByteArray dat; diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index ffbca1b1df..3477abe37b 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -102,7 +102,9 @@ void MessageListModel::sortMessages() { }); } else if (sort_column == 1) { std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) { - return sort_order == Qt::AscendingOrder ? l < r : l > r; + auto ll = std::tuple{can->lastMessage(l).src, can->lastMessage(l).address, l}; + auto rr = std::tuple{can->lastMessage(r).src, can->lastMessage(r).address, r}; + return sort_order == Qt::AscendingOrder ? ll < rr : ll > rr; }); } else if (sort_column == 2) { std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) { From 351d97ab5a72f3dfb22de2e7e321c4cc447a44c9 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 19 Jan 2023 03:40:44 +0800 Subject: [PATCH 112/484] cabana: make video resizable (#26998) --- tools/cabana/chartswidget.cc | 1 - tools/cabana/mainwin.cc | 19 ++++++++++++------- tools/cabana/mainwin.h | 4 +++- tools/cabana/settings.cc | 2 ++ tools/cabana/settings.h | 4 +++- tools/cabana/videowidget.cc | 30 +++++++++++++++--------------- tools/cabana/videowidget.h | 2 +- 7 files changed, 36 insertions(+), 26 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index b8ec0af346..33e133d140 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -16,7 +16,6 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(0, 0, 0, 0); // toolbar QToolBar *toolbar = new QToolBar(tr("Charts"), this); diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index 9df5894b90..af89d26f4f 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -116,19 +116,23 @@ void MainWindow::createDockWindows() { addDockWidget(Qt::LeftDockWidgetArea, dock); // right panel - QWidget *right_container = new QWidget(this); - r_layout = new QVBoxLayout(right_container); charts_widget = new ChartsWidget(this); + QWidget *charts_container = new QWidget(this); + charts_layout = new QVBoxLayout(charts_container); + charts_layout->setContentsMargins(0, 0, 0, 0); + charts_layout->addWidget(charts_widget); + video_widget = new VideoWidget(this); - r_layout->addWidget(video_widget, 0, Qt::AlignTop); - r_layout->addWidget(charts_widget, 1); - r_layout->addStretch(0); + video_splitter = new QSplitter(Qt::Vertical,this); + video_splitter->addWidget(video_widget); + video_splitter->addWidget(charts_container); + video_splitter->restoreState(settings.video_splitter_state); video_dock = new QDockWidget(can->routeName(), this); video_dock->setObjectName(tr("VideoPanel")); video_dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); video_dock->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); - video_dock->setWidget(right_container); + video_dock->setWidget(video_splitter); addDockWidget(Qt::RightDockWidgetArea, video_dock); } @@ -239,7 +243,7 @@ void MainWindow::updateDownloadProgress(uint64_t cur, uint64_t total, bool succe void MainWindow::dockCharts(bool dock) { if (dock && floating_window) { floating_window->removeEventFilter(charts_widget); - r_layout->insertWidget(2, charts_widget, 1); + charts_layout->insertWidget(0, charts_widget, 1); floating_window->deleteLater(); floating_window = nullptr; } else if (!dock && !floating_window) { @@ -270,6 +274,7 @@ void MainWindow::closeEvent(QCloseEvent *event) { settings.geometry = saveGeometry(); settings.window_state = saveState(); + settings.video_splitter_state = video_splitter->saveState(); settings.save(); QWidget::closeEvent(event); } diff --git a/tools/cabana/mainwin.h b/tools/cabana/mainwin.h index f8b5f92349..3e80ffa1aa 100644 --- a/tools/cabana/mainwin.h +++ b/tools/cabana/mainwin.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "tools/cabana/chartswidget.h" @@ -51,8 +52,9 @@ protected: DetailWidget *detail_widget; ChartsWidget *charts_widget; QWidget *floating_window = nullptr; - QVBoxLayout *r_layout; + QVBoxLayout *charts_layout; QProgressBar *progress_bar; QJsonDocument fingerprint_to_dbc; QComboBox *dbc_combo; + QSplitter *video_splitter;; }; diff --git a/tools/cabana/settings.cc b/tools/cabana/settings.cc index 5e7f833317..a5b490e9fb 100644 --- a/tools/cabana/settings.cc +++ b/tools/cabana/settings.cc @@ -21,6 +21,7 @@ void Settings::save() { s.setValue("last_dir", last_dir); s.setValue("window_state", window_state); s.setValue("geometry", geometry); + s.setValue("video_splitter_state", video_splitter_state); } void Settings::load() { @@ -32,6 +33,7 @@ void Settings::load() { last_dir = s.value("last_dir", QDir::homePath()).toString(); window_state = s.value("window_state").toByteArray(); geometry = s.value("geometry").toByteArray(); + video_splitter_state = s.value("video_splitter_state").toByteArray(); } // SettingsDlg diff --git a/tools/cabana/settings.h b/tools/cabana/settings.h index d231a3a53a..76be2f1487 100644 --- a/tools/cabana/settings.h +++ b/tools/cabana/settings.h @@ -18,7 +18,9 @@ public: int chart_height = 200; int max_chart_x_range = 3 * 60; // 3 minutes QString last_dir; - QByteArray window_state, geometry; + QByteArray geometry; + QByteArray video_splitter_state; + QByteArray window_state; signals: void changed(); diff --git a/tools/cabana/videowidget.cc b/tools/cabana/videowidget.cc index ed4354ce65..99d82daccf 100644 --- a/tools/cabana/videowidget.cc +++ b/tools/cabana/videowidget.cc @@ -17,20 +17,16 @@ inline QString formatTime(int seconds) { return QDateTime::fromTime_t(seconds).toString(seconds > 60 * 60 ? "hh:mm:ss" : "mm:ss"); } -VideoWidget::VideoWidget(QWidget *parent) : QFrame(parent) { - setFrameShape(QFrame::StyledPanel); - setFrameShadow(QFrame::Sunken); - QHBoxLayout *containter_layout = new QHBoxLayout(this); - QVBoxLayout *main_layout = new QVBoxLayout(); - main_layout->setContentsMargins(0, 0, 0, 0); +VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent) { + QVBoxLayout *main_layout = new QVBoxLayout(this); + QFrame *frame = new QFrame(this); + frame->setFrameShape(QFrame::StyledPanel); + frame->setFrameShadow(QFrame::Sunken); + main_layout->addWidget(frame); - containter_layout->addStretch(1); - containter_layout->addLayout(main_layout); - containter_layout->addStretch(1); - - cam_widget = new CameraWidget("camerad", can->visionStreamType(), false, this); - cam_widget->setFixedSize(parent->width(), parent->width() / 1.596); - main_layout->addWidget(cam_widget); + QVBoxLayout *frame_layout = new QVBoxLayout(frame); + cam_widget = new CameraWidget("camerad", can->visionStreamType(), false); + frame_layout->addWidget(cam_widget); // slider controls QHBoxLayout *slider_layout = new QHBoxLayout(); @@ -43,7 +39,7 @@ VideoWidget::VideoWidget(QWidget *parent) : QFrame(parent) { end_time_label = new QLabel(this); slider_layout->addWidget(end_time_label); - main_layout->addLayout(slider_layout); + frame_layout->addLayout(slider_layout); // btn controls QHBoxLayout *control_layout = new QHBoxLayout(); @@ -61,7 +57,11 @@ VideoWidget::VideoWidget(QWidget *parent) : QFrame(parent) { group->addButton(btn); if (speed == 1.0) btn->setChecked(true); } - main_layout->addLayout(control_layout); + frame_layout->addLayout(control_layout); + + cam_widget->setMinimumHeight(100); + cam_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); QObject::connect(slider, &QSlider::sliderReleased, [this]() { can->seekTo(slider->value() / 1000.0); }); QObject::connect(slider, &QSlider::valueChanged, [=](int value) { time_label->setText(formatTime(value / 1000)); }); diff --git a/tools/cabana/videowidget.h b/tools/cabana/videowidget.h index ec8bc4bec4..515dab6c87 100644 --- a/tools/cabana/videowidget.h +++ b/tools/cabana/videowidget.h @@ -35,7 +35,7 @@ private: QSize thumbnail_size = {}; }; -class VideoWidget : public QFrame { +class VideoWidget : public QWidget { Q_OBJECT public: From c21d9408a191ba8e3303f2459d1f7476fefd35ba Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 19 Jan 2023 05:19:23 +0800 Subject: [PATCH 113/484] cabana: use bootstrap icons (#26981) * use bootstrap icons * typo * build into asset_obj * add to files_common --- SConstruct | 2 +- release/files_common | 1 + selfdrive/assets/assets.qrc | 1 + selfdrive/ui/SConscript | 1 + selfdrive/ui/qt/util.cc | 37 ++++++++++++++++++++++++++++++++++++ selfdrive/ui/qt/util.h | 1 + tools/cabana/SConscript | 4 ++-- tools/cabana/chartswidget.cc | 13 ++++++++----- tools/cabana/detailwidget.cc | 5 +++-- tools/cabana/signaledit.cc | 6 ++++-- tools/cabana/videowidget.cc | 15 +++++++++++---- tools/cabana/videowidget.h | 1 + 12 files changed, 71 insertions(+), 16 deletions(-) diff --git a/SConstruct b/SConstruct index 3051f4298d..8864164f15 100644 --- a/SConstruct +++ b/SConstruct @@ -282,7 +282,7 @@ Export('envCython') # Qt build environment qt_env = env.Clone() -qt_modules = ["Widgets", "Gui", "Core", "Network", "Concurrent", "Multimedia", "Quick", "Qml", "QuickWidgets", "Location", "Positioning", "DBus"] +qt_modules = ["Widgets", "Gui", "Core", "Network", "Concurrent", "Multimedia", "Quick", "Qml", "QuickWidgets", "Location", "Positioning", "DBus", "Xml"] qt_libs = [] if arch == "Darwin": diff --git a/release/files_common b/release/files_common index 3d07924a91..0dc0e96226 100644 --- a/release/files_common +++ b/release/files_common @@ -436,6 +436,7 @@ third_party/acados/larch64/** third_party/acados/include/** third_party/acados/acados_template/** +third_party/bootstrap/** third_party/qt5/larch64/bin/** scripts/update_now.sh diff --git a/selfdrive/assets/assets.qrc b/selfdrive/assets/assets.qrc index 39be41aa65..79a1a1e272 100644 --- a/selfdrive/assets/assets.qrc +++ b/selfdrive/assets/assets.qrc @@ -1,5 +1,6 @@ + ../../third_party/bootstrap/bootstrap-icons.svg img_continue_triangle.svg img_circled_check.svg img_circled_slash.svg diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 669c214746..3ce7a0505d 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -41,6 +41,7 @@ assets_src = "#selfdrive/assets/assets.qrc" qt_env.Command(assets, assets_src, f"rcc $SOURCES -o $TARGET") qt_env.Depends(assets, Glob('#selfdrive/assets/*', exclude=[assets, assets_src, "#selfdrive/assets/assets.o"])) asset_obj = qt_env.Object("assets", assets) +Export('asset_obj') # build soundd qt_env.Program("soundd/_soundd", ["soundd/main.cc", "soundd/sound.cc"], LIBS=qt_libs) diff --git a/selfdrive/ui/qt/util.cc b/selfdrive/ui/qt/util.cc index 59903e3376..4f52310649 100644 --- a/selfdrive/ui/qt/util.cc +++ b/selfdrive/ui/qt/util.cc @@ -2,11 +2,14 @@ #include #include +#include #include #include #include #include #include +#include +#include #include "common/params.h" #include "common/swaglog.h" @@ -218,3 +221,37 @@ QColor interpColor(float xv, std::vector xp, std::vector fp) { ); } } + +static QHash load_bootstrap_icons() { + QHash icons; + + QFile f(":/bootstrap-icons.svg"); + if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { + QDomDocument xml; + xml.setContent(&f); + QDomNode n = xml.documentElement().firstChild(); + while (!n.isNull()) { + QDomElement e = n.toElement(); + if (!e.isNull() && e.hasAttribute("id")) { + QString svg_str; + QTextStream stream(&svg_str); + n.save(stream, 0); + svg_str.replace("", ""); + icons[e.attribute("id")] = svg_str.toUtf8(); + } + n = n.nextSibling(); + } + } + return icons; +} + +QPixmap bootstrapPixmap(const QString &id) { + static QHash icons = load_bootstrap_icons(); + + QPixmap pixmap; + if (auto it = icons.find(id); it != icons.end()) { + pixmap.loadFromData(it.value(), "svg"); + } + return pixmap; +} diff --git a/selfdrive/ui/qt/util.h b/selfdrive/ui/qt/util.h index 61a27a8669..3188f3f9b9 100644 --- a/selfdrive/ui/qt/util.h +++ b/selfdrive/ui/qt/util.h @@ -22,6 +22,7 @@ void swagLogMessageHandler(QtMsgType type, const QMessageLogContext &context, co void initApp(int argc, char *argv[]); QWidget* topWidget (QWidget* widget); QPixmap loadPixmap(const QString &fileName, const QSize &size = {}, Qt::AspectRatioMode aspectRatioMode = Qt::KeepAspectRatio); +QPixmap bootstrapPixmap(const QString &id); QRect getTextRect(QPainter &p, int flags, const QString &text); void drawRoundedRect(QPainter &painter, const QRectF &rect, qreal xRadiusTop, qreal yRadiusTop, qreal xRadiusBottom, qreal yRadiusBottom); diff --git a/tools/cabana/SConscript b/tools/cabana/SConscript index 507b98ae88..6726a042ad 100644 --- a/tools/cabana/SConscript +++ b/tools/cabana/SConscript @@ -1,6 +1,6 @@ import os Import('env', 'qt_env', 'arch', 'common', 'messaging', 'visionipc', 'replay_lib', - 'cereal', 'transformations', 'widgets', 'opendbc') + 'cereal', 'transformations', 'widgets', 'opendbc', 'asset_obj') base_frameworks = qt_env['FRAMEWORKS'] base_libs = [common, messaging, cereal, visionipc, transformations, 'zmq', @@ -22,7 +22,7 @@ prev_moc_path = cabana_env['QT_MOCHPREFIX'] cabana_env['QT_MOCHPREFIX'] = os.path.dirname(prev_moc_path) + '/cabana/moc_' cabana_lib = cabana_env.Library("cabana_lib", ['mainwin.cc', 'binaryview.cc', 'chartswidget.cc', 'historylog.cc', 'videowidget.cc', 'signaledit.cc', 'dbcmanager.cc', 'canmessages.cc', 'commands.cc', 'messageswidget.cc', 'settings.cc', 'detailwidget.cc', 'tools/findsimilarbits.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) -cabana_env.Program('_cabana', ['cabana.cc', cabana_lib], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) +cabana_env.Program('_cabana', ['cabana.cc', cabana_lib, asset_obj], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) if GetOption('test'): cabana_env.Program('tests/_test_cabana', ['tests/test_runner.cc', 'tests/test_cabana.cc', cabana_lib], LIBS=[cabana_libs]) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 33e133d140..69229baa64 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -12,6 +12,8 @@ #include #include +#include "selfdrive/ui/qt/util.h" + // ChartsWidget ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { @@ -19,14 +21,15 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { // toolbar QToolBar *toolbar = new QToolBar(tr("Charts"), this); + toolbar->setIconSize({16, 16}); title_label = new QLabel(); title_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); toolbar->addWidget(title_label); show_all_values_btn = toolbar->addAction(""); toolbar->addWidget(range_label = new QLabel()); - reset_zoom_btn = toolbar->addAction("⟲"); + reset_zoom_btn = toolbar->addAction(bootstrapPixmap("arrow-counterclockwise"), ""); reset_zoom_btn->setToolTip(tr("Reset zoom (drag on chart to zoom X-Axis)")); - remove_all_btn = toolbar->addAction("✖"); + remove_all_btn = toolbar->addAction(bootstrapPixmap("x"), ""); remove_all_btn->setToolTip(tr("Remove all charts")); dock_btn = toolbar->addAction(""); main_layout->addWidget(toolbar); @@ -137,7 +140,7 @@ void ChartsWidget::updateToolBar() { reset_zoom_btn->setEnabled(is_zoomed); range_label->setText(is_zoomed ? tr("%1 - %2").arg(zoomed_range.first, 0, 'f', 2).arg(zoomed_range.second, 0, 'f', 2) : ""); title_label->setText(charts.size() > 0 ? tr("Charts (%1)").arg(charts.size()) : tr("Charts")); - dock_btn->setText(docking ? "⬈" : "⬋"); + dock_btn->setIcon(bootstrapPixmap(docking ? "arrow-up-right" : "arrow-down-left")); dock_btn->setToolTip(docking ? tr("Undock charts") : tr("Dock charts")); } @@ -223,7 +226,7 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { chart->layout()->setContentsMargins(0, 0, 0, 0); QToolButton *remove_btn = new QToolButton(); - remove_btn->setText("X"); + remove_btn->setIcon(bootstrapPixmap("x")); remove_btn->setAutoRaise(true); remove_btn->setToolTip(tr("Remove Chart")); close_btn_proxy = new QGraphicsProxyWidget(chart); @@ -231,7 +234,7 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { close_btn_proxy->setZValue(chart->zValue() + 11); QToolButton *manage_btn = new QToolButton(); - manage_btn->setText("🔧"); + manage_btn->setIcon(bootstrapPixmap("gear")); manage_btn->setAutoRaise(true); manage_btn->setToolTip(tr("Manage series")); manage_btn_proxy = new QGraphicsProxyWidget(chart); diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index 06377616da..247111aaf4 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -36,6 +36,7 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart // message title toolbar = new QToolBar(this); + toolbar->setIconSize({16, 16}); toolbar->addWidget(new QLabel("time:")); time_label = new QLabel(this); time_label->setStyleSheet("font-weight:bold"); @@ -45,8 +46,8 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart name_label->setAlignment(Qt::AlignCenter); name_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); toolbar->addWidget(name_label); - toolbar->addAction("🖍", this, &DetailWidget::editMsg)->setToolTip(tr("Edit Message")); - remove_msg_act = toolbar->addAction("X", this, &DetailWidget::removeMsg); + toolbar->addAction(bootstrapPixmap("pencil"), "", this, &DetailWidget::editMsg)->setToolTip(tr("Edit Message")); + remove_msg_act = toolbar->addAction(bootstrapPixmap("x-lg"), "", this, &DetailWidget::removeMsg); remove_msg_act->setToolTip(tr("Remove Message")); toolbar->setVisible(false); frame_layout->addWidget(toolbar); diff --git a/tools/cabana/signaledit.cc b/tools/cabana/signaledit.cc index 2a92e03461..e2025dcc82 100644 --- a/tools/cabana/signaledit.cc +++ b/tools/cabana/signaledit.cc @@ -6,6 +6,8 @@ #include #include +#include "selfdrive/ui/qt/util.h" + // SignalForm SignalForm::SignalForm(QWidget *parent) : QWidget(parent) { @@ -104,13 +106,13 @@ SignalEdit::SignalEdit(int index, QWidget *parent) : form_idx(index), QWidget(pa title_layout->addWidget(title); plot_btn = new QToolButton(this); - plot_btn->setText("📈"); + plot_btn->setIcon(bootstrapPixmap("graph-up")); plot_btn->setCheckable(true); plot_btn->setAutoRaise(true); title_layout->addWidget(plot_btn); auto remove_btn = new QToolButton(this); remove_btn->setAutoRaise(true); - remove_btn->setText("x"); + remove_btn->setIcon(bootstrapPixmap("x")); remove_btn->setToolTip(tr("Remove signal")); title_layout->addWidget(remove_btn); main_layout->addWidget(title_bar); diff --git a/tools/cabana/videowidget.cc b/tools/cabana/videowidget.cc index 99d82daccf..400cc5108a 100644 --- a/tools/cabana/videowidget.cc +++ b/tools/cabana/videowidget.cc @@ -13,6 +13,8 @@ #include #include +#include "selfdrive/ui/qt/util.h" + inline QString formatTime(int seconds) { return QDateTime::fromTime_t(seconds).toString(seconds > 60 * 60 ? "hh:mm:ss" : "mm:ss"); } @@ -43,8 +45,7 @@ VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent) { // btn controls QHBoxLayout *control_layout = new QHBoxLayout(); - play_btn = new QPushButton("⏸"); - play_btn->setStyleSheet("font-weight:bold; height:16px"); + play_btn = new QPushButton(); control_layout->addWidget(play_btn); QButtonGroup *group = new QButtonGroup(this); @@ -68,12 +69,13 @@ VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent) { QObject::connect(cam_widget, &CameraWidget::clicked, []() { can->pause(!can->isPaused()); }); QObject::connect(play_btn, &QPushButton::clicked, []() { can->pause(!can->isPaused()); }); QObject::connect(can, &CANMessages::updated, this, &VideoWidget::updateState); - QObject::connect(can, &CANMessages::paused, [this]() { play_btn->setText("▶"); }); - QObject::connect(can, &CANMessages::resume, [this]() { play_btn->setText("⏸"); }); + QObject::connect(can, &CANMessages::paused, this, &VideoWidget::updatePlayBtnState); + QObject::connect(can, &CANMessages::resume, this, &VideoWidget::updatePlayBtnState); QObject::connect(can, &CANMessages::streamStarted, [this]() { end_time_label->setText(formatTime(can->totalSeconds())); slider->setRange(0, can->totalSeconds() * 1000); }); + updatePlayBtnState(); } void VideoWidget::rangeChanged(double min, double max, bool is_zoomed) { @@ -90,6 +92,11 @@ void VideoWidget::updateState() { slider->setValue(can->currentSec() * 1000); } +void VideoWidget::updatePlayBtnState() { + play_btn->setIcon(bootstrapPixmap(can->isPaused() ? "play" : "pause")); + play_btn->setToolTip(can->isPaused() ? tr("Play") : tr("Pause")); +} + // Slider Slider::Slider(QWidget *parent) : QSlider(Qt::Horizontal, parent) { QTimer *timer = new QTimer(this); diff --git a/tools/cabana/videowidget.h b/tools/cabana/videowidget.h index 515dab6c87..424a5b08fe 100644 --- a/tools/cabana/videowidget.h +++ b/tools/cabana/videowidget.h @@ -44,6 +44,7 @@ public: protected: void updateState(); + void updatePlayBtnState(); CameraWidget *cam_widget; QLabel *end_time_label; From 669becadde77d79443d933364234212c10d56151 Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Wed, 18 Jan 2023 19:57:54 -0700 Subject: [PATCH 114/484] Ublox tow continuity check (#27001) * add continuity check * simplify * remove pair * update refs * bump laika update refs Co-authored-by: Kurt Nistelberger --- laika_repo | 2 +- selfdrive/locationd/ublox_msg.cc | 13 +++++++++++-- selfdrive/locationd/ublox_msg.h | 1 + selfdrive/test/process_replay/ref_commit | 2 +- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/laika_repo b/laika_repo index 73bf110ae0..2e5c7b8a85 160000 --- a/laika_repo +++ b/laika_repo @@ -1 +1 @@ -Subproject commit 73bf110ae0093ad86755bf5eb6a03e46ff5c239d +Subproject commit 2e5c7b8a85cbaebda7fa715fe53d5e6ecbd62b0a diff --git a/selfdrive/locationd/ublox_msg.cc b/selfdrive/locationd/ublox_msg.cc index 6431e9d48b..8bd9985170 100644 --- a/selfdrive/locationd/ublox_msg.cc +++ b/selfdrive/locationd/ublox_msg.cc @@ -170,9 +170,18 @@ kj::Array UbloxMsgParser::gen_rxm_sfrbx(ubx_t::rxm_sfrbx_t *msg) { kaitai::kstream stream(subframe_data); gps_t subframe(&stream); int subframe_id = subframe.how()->subframe_id(); + int sv_id = msg->sv_id(); + uint64_t tow_counter = subframe.how()->tow_count(); - if (subframe_id == 1) gps_subframes[msg->sv_id()].clear(); - gps_subframes[msg->sv_id()][subframe_id] = subframe_data; + bool clear_buffer = subframe_id == 1; + if (gps_sat_tow_count.count(sv_id) != 0) { + int64_t counter_diff = tow_counter - gps_sat_tow_count[sv_id]; + clear_buffer |= counter_diff != 1 && counter_diff != -100798; + } + if (clear_buffer) gps_subframes[sv_id].clear(); + + gps_subframes[sv_id][subframe_id] = subframe_data; + gps_sat_tow_count[sv_id] = tow_counter; } if (gps_subframes[msg->sv_id()].size() == 5) { diff --git a/selfdrive/locationd/ublox_msg.h b/selfdrive/locationd/ublox_msg.h index 542e72816b..919e9963f1 100644 --- a/selfdrive/locationd/ublox_msg.h +++ b/selfdrive/locationd/ublox_msg.h @@ -103,6 +103,7 @@ class UbloxMsgParser { inline bool valid_so_far(); std::unordered_map> gps_subframes; + std::unordered_map gps_sat_tow_count; size_t bytes_in_parse_buf = 0; uint8_t msg_parse_buf[ublox::UBLOX_HEADER_SIZE + ublox::UBLOX_MAX_MSG_SIZE]; diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index a173cf5205..1aa3aa6f1e 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -18a70665bdb6b6aee4a224e826417049415e8290 +acc5655633af6ade096ad41f680fd8a28c2e3790 \ No newline at end of file From b7ce77b3aa48c3605554f63068a13860023eff86 Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Wed, 18 Jan 2023 23:29:12 -0700 Subject: [PATCH 115/484] Add low Gnss laikad test (#26987) * add low gnss test * enable laikad * temp fix for cache * update replay * save LaikadEphemeris for testing * rem * update refs * add comment Co-authored-by: Kurt Nistelberger --- selfdrive/locationd/laikad.py | 6 +++- selfdrive/locationd/locationd.cc | 23 +++++------- selfdrive/locationd/test/test_laikad.py | 36 +++++++++++++++++-- selfdrive/locationd/test/test_locationd.py | 29 +++++---------- .../test/process_replay/process_replay.py | 2 +- selfdrive/test/process_replay/ref_commit | 2 +- 6 files changed, 59 insertions(+), 39 deletions(-) diff --git a/selfdrive/locationd/laikad.py b/selfdrive/locationd/laikad.py index b8d86c63fd..ac8fac01f0 100755 --- a/selfdrive/locationd/laikad.py +++ b/selfdrive/locationd/laikad.py @@ -391,13 +391,17 @@ def process_msg(laikad, gnss_msg, mono_time, block=False): def clear_tmp_cache(): + # TODO: remove this function once laika downloader is changed to not write to disk + if "CI" in os.environ or "REPLAY" in os.environ: + return + if os.path.exists(DOWNLOADS_CACHE_FOLDER): shutil.rmtree(DOWNLOADS_CACHE_FOLDER) os.mkdir(DOWNLOADS_CACHE_FOLDER) def main(sm=None, pm=None, qc=None): - #clear_tmp_cache() + clear_tmp_cache() use_qcom = not Params().get_bool("UbloxAvailable", block=True) if use_qcom or (qc is not None and qc): diff --git a/selfdrive/locationd/locationd.cc b/selfdrive/locationd/locationd.cc index 8941b50248..e78b3d3903 100755 --- a/selfdrive/locationd/locationd.cc +++ b/selfdrive/locationd/locationd.cc @@ -589,12 +589,12 @@ void Localizer::handle_msg(const cereal::Event::Reader& log) { this->handle_sensor(t, log.getAccelerometer()); } else if (log.isGyroscope()) { this->handle_sensor(t, log.getGyroscope()); - } else if (log.isGpsLocation()) { - this->handle_gps(t, log.getGpsLocation(), GPS_QUECTEL_SENSOR_TIME_OFFSET); - } else if (log.isGpsLocationExternal()) { - this->handle_gps(t, log.getGpsLocationExternal(), GPS_UBLOX_SENSOR_TIME_OFFSET); - //} else if (log.isGnssMeasurements()) { - // this->handle_gnss(t, log.getGnssMeasurements()); + //} else if (log.isGpsLocation()) { + // this->handle_gps(t, log.getGpsLocation(), GPS_QUECTEL_SENSOR_TIME_OFFSET); + //} else if (log.isGpsLocationExternal()) { + // this->handle_gps(t, log.getGpsLocationExternal(), GPS_UBLOX_SENSOR_TIME_OFFSET); + } else if (log.isGnssMeasurements()) { + this->handle_gnss(t, log.getGnssMeasurements()); } else if (log.isCarState()) { this->handle_car_state(t, log.getCarState()); } else if (log.isCameraOdometry()) { @@ -658,17 +658,12 @@ void Localizer::determine_gps_mode(double current_time) { int Localizer::locationd_thread() { ublox_available = Params().getBool("UbloxAvailable", true); - const char* gps_location_socket; - if (ublox_available) { - gps_location_socket = "gpsLocationExternal"; - } else { - gps_location_socket = "gpsLocation"; - } - const std::initializer_list service_list = {gps_location_socket, "cameraOdometry", "liveCalibration", + + const std::initializer_list service_list = {"gnssMeasurements", "cameraOdometry", "liveCalibration", "carState", "carParams", "accelerometer", "gyroscope"}; // TODO: remove carParams once we're always sending at 100Hz - SubMaster sm(service_list, {}, nullptr, {gps_location_socket, "carParams"}); + SubMaster sm(service_list, {}, nullptr, {"gnssMeasurements", "carParams"}); PubMaster pm({"liveLocationKalman"}); uint64_t cnt = 0; diff --git a/selfdrive/locationd/test/test_laikad.py b/selfdrive/locationd/test/test_laikad.py index 6059eab68f..8cd84d91c2 100755 --- a/selfdrive/locationd/test/test_laikad.py +++ b/selfdrive/locationd/test/test_laikad.py @@ -22,7 +22,26 @@ def get_log(segs=range(0)): logs = [] for i in segs: logs.extend(LogReader(get_url("4cf7a6ad03080c90|2021-09-29--13-46-36", i))) - return [m for m in logs if m.which() == 'ubloxGnss'] + all_logs = [m for m in logs if m.which() == 'ubloxGnss'] + + low_gnss = [] + for m in all_logs: + if m.ubloxGnss.which() != 'measurementReport': + continue + if m.ubloxGnss.measurementReport.numMeas == 0: + continue + + MAX_MEAS = 7 + if m.ubloxGnss.measurementReport.numMeas > MAX_MEAS: + mb = m.as_builder() + mb.ubloxGnss.measurementReport.numMeas = MAX_MEAS + mb.ubloxGnss.measurementReport.measurements = list(m.ubloxGnss.measurementReport.measurements)[:MAX_MEAS] + mb.ubloxGnss.measurementReport.measurements[0].pseudorange += 1000 + low_gnss.append(mb.as_reader()) + else: + low_gnss.append(m) + + return all_logs, low_gnss def verify_messages(lr, laikad, return_one_success=False): @@ -59,8 +78,9 @@ class TestLaikad(unittest.TestCase): @classmethod def setUpClass(cls): - logs = get_log(range(1)) + logs, low_gnss = get_log(range(1)) cls.logs = logs + cls.low_gnss = low_gnss first_gps_time = get_first_gps_time(logs) cls.first_gps_time = first_gps_time @@ -254,6 +274,18 @@ class TestLaikad(unittest.TestCase): # Verify orbit data is not downloaded mock_method.assert_not_called() + def test_low_gnss_meas(self): + cnt = 0 + laikad = Laikad() + for m in self.low_gnss: + msg = laikad.process_gnss_msg(m.ubloxGnss, m.logMonoTime, block=True) + if msg is None: + continue + gm = msg.gnssMeasurements + if len(gm.correctedMeasurements) != 0 and gm.positionECEF.valid: + cnt += 1 + self.assertEqual(cnt, 554) + def dict_has_values(self, dct): self.assertGreater(len(dct), 0) self.assertGreater(min([len(v) for v in dct.values()]), 0) diff --git a/selfdrive/locationd/test/test_locationd.py b/selfdrive/locationd/test/test_locationd.py index 9f643e2b8f..943969a49e 100755 --- a/selfdrive/locationd/test/test_locationd.py +++ b/selfdrive/locationd/test/test_locationd.py @@ -15,7 +15,7 @@ from selfdrive.manager.process_config import managed_processes class TestLocationdProc(unittest.TestCase): MAX_WAITS = 1000 - LLD_MSGS = ['gpsLocationExternal', 'cameraOdometry', 'carState', 'liveCalibration', + LLD_MSGS = ['gnssMeasurements', 'cameraOdometry', 'carState', 'liveCalibration', 'accelerometer', 'gyroscope', 'magnetometer'] def setUp(self): @@ -46,25 +46,14 @@ class TestLocationdProc(unittest.TestCase): except capnp.lib.capnp.KjException: msg = messaging.new_message(name, 0) - - if name == "gpsLocationExternal": - msg.gpsLocationExternal.flags = 1 - msg.gpsLocationExternal.verticalAccuracy = 1.0 - msg.gpsLocationExternal.speedAccuracy = 1.0 - msg.gpsLocationExternal.bearingAccuracyDeg = 1.0 - msg.gpsLocationExternal.vNED = [0.0, 0.0, 0.0] - msg.gpsLocationExternal.latitude = float(self.lat) - msg.gpsLocationExternal.longitude = float(self.lon) - msg.gpsLocationExternal.unixTimestampMillis = t * 1e6 - msg.gpsLocationExternal.altitude = float(self.alt) - #if name == "gnssMeasurements": - # msg.gnssMeasurements.measTime = t - # msg.gnssMeasurements.positionECEF.value = [self.x , self.y, self.z] - # msg.gnssMeasurements.positionECEF.std = [0,0,0] - # msg.gnssMeasurements.positionECEF.valid = True - # msg.gnssMeasurements.velocityECEF.value = [] - # msg.gnssMeasurements.velocityECEF.std = [0,0,0] - # msg.gnssMeasurements.velocityECEF.valid = True + if name == "gnssMeasurements": + msg.gnssMeasurements.measTime = t + msg.gnssMeasurements.positionECEF.value = [self.x , self.y, self.z] + msg.gnssMeasurements.positionECEF.std = [0,0,0] + msg.gnssMeasurements.positionECEF.valid = True + msg.gnssMeasurements.velocityECEF.value = [] + msg.gnssMeasurements.velocityECEF.std = [0,0,0] + msg.gnssMeasurements.velocityECEF.valid = True elif name == 'cameraOdometry': msg.cameraOdometry.rot = [0.0, 0.0, 0.0] msg.cameraOdometry.rotStd = [0.0, 0.0, 0.0] diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 22ec099285..24ee87e471 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -331,7 +331,7 @@ CONFIGS = [ pub_sub={ "cameraOdometry": ["liveLocationKalman"], "accelerometer": [], "gyroscope": [], - "gpsLocationExternal": [], "liveCalibration": [], "carState": [], + "gnssMeasurements": [], "liveCalibration": [], "carState": [], }, ignore=["logMonoTime", "valid"], init_callback=get_car_params, diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 1aa3aa6f1e..629a9a2f51 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -acc5655633af6ade096ad41f680fd8a28c2e3790 \ No newline at end of file +660149849230afe36363100ab0f5935038d8191e \ No newline at end of file From cebee69f304357c5966dec9b316a4d15e68c571b Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 20 Jan 2023 02:32:48 +0800 Subject: [PATCH 116/484] cabana: click time label to seek to a specified time (#27006) --- tools/cabana/canmessages.h | 1 + tools/cabana/videowidget.cc | 28 +++++++++++++++++++++++++--- tools/cabana/videowidget.h | 5 +++++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/tools/cabana/canmessages.h b/tools/cabana/canmessages.h index 2451793742..2cdc010d8c 100644 --- a/tools/cabana/canmessages.h +++ b/tools/cabana/canmessages.h @@ -34,6 +34,7 @@ public: inline double totalSeconds() const { return replay->totalSeconds(); } inline double routeStartTime() const { return replay->routeStartTime() / (double)1e9; } inline double currentSec() const { return replay->currentSeconds(); } + inline QDateTime currentDateTime() const { return replay->currentDateTime(); } inline const CanData &lastMessage(const QString &id) { return can_msgs[id]; } inline const Route* route() const { return replay->route(); } diff --git a/tools/cabana/videowidget.cc b/tools/cabana/videowidget.cc index 400cc5108a..2f6aab249a 100644 --- a/tools/cabana/videowidget.cc +++ b/tools/cabana/videowidget.cc @@ -3,11 +3,11 @@ #include #include #include -#include #include #include #include #include +#include #include #include #include @@ -31,8 +31,9 @@ VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent) { frame_layout->addWidget(cam_widget); // slider controls - QHBoxLayout *slider_layout = new QHBoxLayout(); - QLabel *time_label = new QLabel("00:00"); + slider_layout = new QHBoxLayout(); + time_label = new ElidedLabel("00:00"); + time_label->setToolTip(tr("Click to set current time")); slider_layout->addWidget(time_label); slider = new Slider(this); @@ -64,6 +65,7 @@ VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent) { cam_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); + QObject::connect(time_label, &ElidedLabel::clicked, this, &VideoWidget::timeLabelClicked); QObject::connect(slider, &QSlider::sliderReleased, [this]() { can->seekTo(slider->value() / 1000.0); }); QObject::connect(slider, &QSlider::valueChanged, [=](int value) { time_label->setText(formatTime(value / 1000)); }); QObject::connect(cam_widget, &CameraWidget::clicked, []() { can->pause(!can->isPaused()); }); @@ -78,6 +80,26 @@ VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent) { updatePlayBtnState(); } +void VideoWidget::timeLabelClicked() { + auto time_edit = new QTimeEdit(this); + auto init_date_time = can->currentDateTime(); + time_edit->setDateTime(init_date_time); + time_edit->setDisplayFormat("hh:mm:ss"); + time_label->setVisible(false); + slider_layout->insertWidget(0, time_edit); + QTimer::singleShot(0, [=]() { time_edit->setFocus(); }); + + QObject::connect(time_edit, &QTimeEdit::editingFinished, [=]() { + if (time_edit->dateTime() != init_date_time) { + int seconds = can->route()->datetime().secsTo(time_edit->dateTime()); + can->seekTo(seconds); + } + time_edit->setVisible(false); + time_label->setVisible(true); + time_edit->deleteLater(); + }); +} + void VideoWidget::rangeChanged(double min, double max, bool is_zoomed) { if (!is_zoomed) { min = 0; diff --git a/tools/cabana/videowidget.h b/tools/cabana/videowidget.h index 424a5b08fe..e9899abbbf 100644 --- a/tools/cabana/videowidget.h +++ b/tools/cabana/videowidget.h @@ -3,12 +3,14 @@ #include #include +#include #include #include #include #include #include "selfdrive/ui/qt/widgets/cameraview.h" +#include "selfdrive/ui/qt/widgets/controls.h" #include "tools/cabana/canmessages.h" class Slider : public QSlider { @@ -45,9 +47,12 @@ public: protected: void updateState(); void updatePlayBtnState(); + void timeLabelClicked(); CameraWidget *cam_widget; QLabel *end_time_label; + ElidedLabel *time_label; + QHBoxLayout *slider_layout; QPushButton *play_btn; Slider *slider; }; From 5b8d124be7d1277d816ff337fd69600da0e891e2 Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Thu, 19 Jan 2023 19:35:18 +0100 Subject: [PATCH 117/484] cabana: color bytes based on activity (#26970) * cabana: color bytes based on activity * newlines * fix text color when selected * fix indent * add colors to binary view * no need to check contains * whitespace * Apply suggestions from code review Co-authored-by: Adeeb Shihadeh --- tools/cabana/binaryview.cc | 8 +++-- tools/cabana/canmessages.cc | 60 ++++++++++++++++++++++++++++++++++ tools/cabana/canmessages.h | 2 ++ tools/cabana/messageswidget.cc | 53 ++++++++++++++++++++++++++++-- tools/cabana/messageswidget.h | 8 +++++ 5 files changed, 127 insertions(+), 4 deletions(-) diff --git a/tools/cabana/binaryview.cc b/tools/cabana/binaryview.cc index b98c75d452..b6a5fa75aa 100644 --- a/tools/cabana/binaryview.cc +++ b/tools/cabana/binaryview.cc @@ -177,7 +177,9 @@ void BinaryViewModel::setMessage(const QString &message_id) { void BinaryViewModel::updateState() { auto prev_items = items; - const auto &binary = can->lastMessage(msg_id).dat; + const auto &last_msg = can->lastMessage(msg_id); + const auto &binary = last_msg.dat; + // data size may changed. if (binary.size() > row_count) { beginInsertRows({}, row_count, binary.size() - 1); @@ -193,6 +195,7 @@ void BinaryViewModel::updateState() { hex[0] = toHex(binary[i] >> 4); hex[1] = toHex(binary[i] & 0xf); items[i * column_count + 8].val = hex; + items[i * column_count + 8].bg_color = last_msg.colors[i]; } for (int i = binary.size(); i < row_count; ++i) { for (int j = 0; j < column_count; ++j) { @@ -201,7 +204,7 @@ void BinaryViewModel::updateState() { } for (int i = 0; i < row_count * column_count; ++i) { - if (i >= prev_items.size() || prev_items[i].val != items[i].val) { + if (i >= prev_items.size() || prev_items[i].val != items[i].val || prev_items[i].bg_color != items[i].bg_color) { auto idx = index(i / column_count, i % column_count); emit dataChanged(idx, idx); } @@ -234,6 +237,7 @@ void BinaryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op if (index.column() == 8) { painter->setFont(hex_font); + painter->fillRect(option.rect, item->bg_color); } else if (option.state & QStyle::State_Selected) { painter->fillRect(option.rect, selection_color); painter->setPen(option.palette.color(QPalette::BrightText)); diff --git a/tools/cabana/canmessages.cc b/tools/cabana/canmessages.cc index a473313f90..9b5f41c60a 100644 --- a/tools/cabana/canmessages.cc +++ b/tools/cabana/canmessages.cc @@ -18,6 +18,10 @@ static bool event_filter(const Event *e, void *opaque) { return c->eventFilter(e); } +static QColor blend(QColor a, QColor b) { + return QColor((a.red() + b.red()) / 2, (a.green() + b.green()) / 2, (a.blue() + b.blue()) / 2, (a.alpha() + b.alpha()) / 2); +} + bool CANMessages::loadRoute(const QString &route, const QString &data_dir, uint32_t replay_flags) { replay = new Replay(route, {"can", "roadEncodeIdx", "wideRoadEncodeIdx", "carParams"}, {}, nullptr, replay_flags, data_dir, this); replay->setSegmentCacheLimit(settings.cached_segment_limit); @@ -49,6 +53,9 @@ void CANMessages::process(QHash *messages) { bool CANMessages::eventFilter(const Event *event) { static std::unique_ptr new_msgs = std::make_unique>(); + static QHash prev_dat; + static QHash> colors; + static QHash> last_change_t; static double prev_update_ts = 0; if (event->which == cereal::Event::Which::CAN) { @@ -71,6 +78,59 @@ bool CANMessages::eventFilter(const Event *event) { if (double delta = (current_sec - counters_begin_sec); delta > 0) { data.freq = data.count / delta; } + + // Init colors + if (colors[id].size() != data.dat.size()) { + colors[id].clear(); + for (int i = 0; i < data.dat.size(); i++){ + colors[id].append(QColor(0, 0, 0, 0)); + } + } + + // Init last_change_t + if (last_change_t[id].size() != data.dat.size()) { + last_change_t[id].clear(); + for (int i = 0; i < data.dat.size(); i++){ + last_change_t[id].append(data.ts); + } + } + + // Compute background color for the bytes based on changes + if (prev_dat[id].size() == data.dat.size()) { + for (int i = 0; i < data.dat.size(); i++){ + uint8_t last = prev_dat[id][i]; + uint8_t cur = data.dat[i]; + + const int periodic_threshold = 10; + const int start_alpha = 128; + const float fade_time = 2.0; + + if (last != cur) { + double delta_t = data.ts - last_change_t[id][i]; + + if (delta_t * data.freq > periodic_threshold) { + // Last change was while ago, choose color based on delta up or down + if (cur > last) { + colors[id][i] = QColor(0, 187, 255, start_alpha); // Cyan + } else { + colors[id][i] = QColor(255, 0, 0, start_alpha); // Red + } + } else { + // Periodic changes + colors[id][i] = blend(colors[id][i], QColor(102, 86, 169, start_alpha / 2)); // Greyish/Blue + } + + last_change_t[id][i] = data.ts; + } else { + // Fade out + float alpha_delta = 1.0 / (data.freq + 1) / fade_time; + colors[id][i].setAlphaF(std::max(0.0, colors[id][i].alphaF() - alpha_delta)); + } + } + } + + data.colors = colors[id]; + prev_dat[id] = data.dat; } double ts = millis_since_boot(); diff --git a/tools/cabana/canmessages.h b/tools/cabana/canmessages.h index 2cdc010d8c..c30361af41 100644 --- a/tools/cabana/canmessages.h +++ b/tools/cabana/canmessages.h @@ -4,6 +4,7 @@ #include #include +#include #include "opendbc/can/common_dbc.h" #include "tools/cabana/settings.h" @@ -16,6 +17,7 @@ struct CanData { uint32_t count = 0; uint32_t freq = 0; QByteArray dat; + QList colors; }; class CANMessages : public QObject { diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index 3477abe37b..10a9c2e849 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include "tools/cabana/dbcmanager.h" @@ -21,6 +23,7 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { table_widget = new QTableView(this); model = new MessageListModel(this); table_widget->setModel(model); + table_widget->setItemDelegateForColumn(4, new MessageBytesDelegate(table_widget)); table_widget->setSelectionBehavior(QAbstractItemView::SelectRows); table_widget->setSelectionMode(QAbstractItemView::SingleSelection); table_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); @@ -31,6 +34,7 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { table_widget->setColumnWidth(2, 80); table_widget->horizontalHeader()->setStretchLastSection(true); table_widget->verticalHeader()->hide(); + main_layout->addWidget(table_widget); // signals/slots @@ -65,9 +69,10 @@ QVariant MessageListModel::headerData(int section, Qt::Orientation orientation, } QVariant MessageListModel::data(const QModelIndex &index, int role) const { + const auto &id = msgs[index.row()]; + auto &can_data = can->lastMessage(id); + if (role == Qt::DisplayRole) { - const auto &id = msgs[index.row()]; - auto &can_data = can->lastMessage(id); switch (index.column()) { case 0: return msgName(id); case 1: return id; @@ -77,6 +82,13 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const { } } else if (role == Qt::FontRole && index.column() == columnCount() - 1) { return QFontDatabase::systemFont(QFontDatabase::FixedFont); + } else if (role == Qt::UserRole && index.column() == 4) { + + QList colors; + for (int i = 0; i < can_data.dat.size(); i++){ + colors.append(can_data.colors[i]); + } + return colors; } return {}; } @@ -146,3 +158,40 @@ void MessageListModel::sort(int column, Qt::SortOrder order) { sortMessages(); } } + + +MessageBytesDelegate::MessageBytesDelegate(QObject *parent) : QStyledItemDelegate(parent) { +} + +void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { + QList colors = index.data(Qt::UserRole).toList(); + + QStyleOptionViewItemV4 opt = option; + initStyleOption(&opt, index); + + const QFont font = index.data(Qt::FontRole).value(); + painter->setFont(font); + + QRect rect = opt.rect; + QString bytes = QString(opt.text); + + QRect pos = rect; + QRect space = painter->boundingRect(pos, opt.displayAlignment, " "); + pos.setX(pos.x() + space.width()); + + if ((option.state & QStyle::State_Selected) && (option.state & QStyle::State_Active)) { + painter->setPen(option.palette.color(QPalette::HighlightedText)); + } else { + painter->setPen(option.palette.color(QPalette::Text)); + } + + int i = 0; + for (auto &byte : bytes.split(" ")) { + QRect sz = painter->boundingRect(pos, opt.displayAlignment, byte); + const int m = space.width() / 2; + painter->fillRect(sz.marginsAdded(QMargins(m + 1, m, m, m)), colors[i].value()); + painter->drawText(pos, opt.displayAlignment, byte); + pos.setX(pos.x() + sz.width() + space.width()); + i++; + } +} diff --git a/tools/cabana/messageswidget.h b/tools/cabana/messageswidget.h index 62aa23c02e..8b74f1427e 100644 --- a/tools/cabana/messageswidget.h +++ b/tools/cabana/messageswidget.h @@ -2,6 +2,7 @@ #include #include +#include #include "tools/cabana/canmessages.h" @@ -41,3 +42,10 @@ protected: QString current_msg_id; MessageListModel *model; }; + +class MessageBytesDelegate : public QStyledItemDelegate { + Q_OBJECT +public: + MessageBytesDelegate(QObject *parent); + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; +}; From d365d99cb005b99ce7385d169e696847b0726315 Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Thu, 19 Jan 2023 19:36:17 +0100 Subject: [PATCH 118/484] cabana: improve open/save functions (#27007) --- tools/cabana/mainwin.cc | 23 +++++++++++++++-------- tools/cabana/mainwin.h | 2 ++ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index af89d26f4f..89876f4267 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -68,13 +68,14 @@ MainWindow::MainWindow() : QMainWindow() { void MainWindow::createActions() { QMenu *file_menu = menuBar()->addMenu(tr("&File")); - file_menu->addAction(tr("Open DBC File..."), this, &MainWindow::loadDBCFromFile); + file_menu->addAction(tr("Open DBC File..."), this, &MainWindow::loadDBCFromFile)->setShortcuts(QKeySequence::Open); file_menu->addAction(tr("Load DBC From Clipboard"), this, &MainWindow::loadDBCFromClipboard); file_menu->addSeparator(); - file_menu->addAction(tr("Save DBC As..."), this, &MainWindow::saveDBCToFile); + file_menu->addAction(tr("Save DBC..."), this, &MainWindow::saveDBCToFile)->setShortcuts(QKeySequence::Save); + file_menu->addAction(tr("Save DBC As..."), this, &MainWindow::saveAsDBCToFile)->setShortcuts(QKeySequence::SaveAs); file_menu->addAction(tr("Copy DBC To Clipboard"), this, &MainWindow::saveDBCToClipboard); file_menu->addSeparator(); - file_menu->addAction(tr("Settings..."), this, &MainWindow::setOption); + file_menu->addAction(tr("Settings..."), this, &MainWindow::setOption)->setShortcuts(QKeySequence::Preferences); QMenu *edit_menu = menuBar()->addMenu(tr("&Edit")); auto undo_act = detail_widget->undo_stack->createUndoAction(this, tr("&Undo")); @@ -182,7 +183,7 @@ void MainWindow::loadDBCFromName(const QString &name) { } void MainWindow::loadDBCFromFile() { - QString file_name = QFileDialog::getOpenFileName(this, tr("Open File"), settings.last_dir, "DBC (*.dbc)"); + file_name = QFileDialog::getOpenFileName(this, tr("Open File"), settings.last_dir, "DBC (*.dbc)"); if (!file_name.isEmpty()) { settings.last_dir = QFileInfo(file_name).absolutePath(); QFile file(file_name); @@ -213,18 +214,24 @@ void MainWindow::loadDBCFromFingerprint() { } void MainWindow::saveDBCToFile() { - QString file_name = QFileDialog::getSaveFileName(this, tr("Save File"), - QDir::cleanPath(settings.last_dir + "/untitled.dbc"), tr("DBC (*.dbc)")); - if (!file_name.isEmpty()) { + if (file_name.isEmpty()) { + saveAsDBCToFile(); + } else { settings.last_dir = QFileInfo(file_name).absolutePath(); QFile file(file_name); if (file.open(QIODevice::WriteOnly)) { file.write(dbc()->generateDBC().toUtf8()); - detail_widget->undo_stack->clear(); } } } +void MainWindow::saveAsDBCToFile() { + file_name = QFileDialog::getSaveFileName(this, tr("Save File"), QDir::cleanPath(settings.last_dir + "/untitled.dbc"), tr("DBC (*.dbc)")); + if (!file_name.isEmpty()) { + saveDBCToFile(); + } +} + void MainWindow::saveDBCToClipboard() { QGuiApplication::clipboard()->setText(dbc()->generateDBC()); QMessageBox::information(this, tr("Copy To Clipboard"), tr("DBC Successfully copied!")); diff --git a/tools/cabana/mainwin.h b/tools/cabana/mainwin.h index 3e80ffa1aa..c1a5908a53 100644 --- a/tools/cabana/mainwin.h +++ b/tools/cabana/mainwin.h @@ -28,6 +28,7 @@ public slots: void loadDBCFromFile(); void loadDBCFromClipboard(); void saveDBCToFile(); + void saveAsDBCToFile(); void saveDBCToClipboard(); signals: @@ -57,4 +58,5 @@ protected: QJsonDocument fingerprint_to_dbc; QComboBox *dbc_combo; QSplitter *video_splitter;; + QString file_name = ""; }; From 35b83579634f2cdee4d6ef4652995b1844773b6e Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Thu, 19 Jan 2023 19:37:27 +0100 Subject: [PATCH 119/484] cabana: search by signal name (#26944) --- tools/cabana/messageswidget.cc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index 10a9c2e849..5deac031a1 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -97,7 +97,24 @@ void MessageListModel::setFilterString(const QString &string) { filter_str = string; msgs.clear(); for (auto it = can->can_msgs.begin(); it != can->can_msgs.end(); ++it) { + bool found = false; + + // Search by message id or name if (it.key().contains(filter_str, Qt::CaseInsensitive) || msgName(it.key()).contains(filter_str, Qt::CaseInsensitive)) { + found = true; + } + + // Search by signal name + const DBCMsg *msg = dbc()->msg(it.key()); + if (msg != nullptr) { + for (auto &signal: msg->getSignals()) { + if (QString::fromStdString(signal->name).contains(filter_str, Qt::CaseInsensitive)) { + found = true; + } + } + } + + if (found) { msgs.push_back(it.key()); } } From 9131da9103718d6ae113ad7b3519037e2b4ffa70 Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Thu, 19 Jan 2023 11:46:22 -0800 Subject: [PATCH 120/484] Revert "Add low Gnss laikad test (#26987)" This reverts commit b7ce77b3aa48c3605554f63068a13860023eff86. --- selfdrive/locationd/laikad.py | 6 +--- selfdrive/locationd/locationd.cc | 23 +++++++----- selfdrive/locationd/test/test_laikad.py | 36 ++----------------- selfdrive/locationd/test/test_locationd.py | 29 ++++++++++----- .../test/process_replay/process_replay.py | 2 +- selfdrive/test/process_replay/ref_commit | 2 +- 6 files changed, 39 insertions(+), 59 deletions(-) diff --git a/selfdrive/locationd/laikad.py b/selfdrive/locationd/laikad.py index ac8fac01f0..b8d86c63fd 100755 --- a/selfdrive/locationd/laikad.py +++ b/selfdrive/locationd/laikad.py @@ -391,17 +391,13 @@ def process_msg(laikad, gnss_msg, mono_time, block=False): def clear_tmp_cache(): - # TODO: remove this function once laika downloader is changed to not write to disk - if "CI" in os.environ or "REPLAY" in os.environ: - return - if os.path.exists(DOWNLOADS_CACHE_FOLDER): shutil.rmtree(DOWNLOADS_CACHE_FOLDER) os.mkdir(DOWNLOADS_CACHE_FOLDER) def main(sm=None, pm=None, qc=None): - clear_tmp_cache() + #clear_tmp_cache() use_qcom = not Params().get_bool("UbloxAvailable", block=True) if use_qcom or (qc is not None and qc): diff --git a/selfdrive/locationd/locationd.cc b/selfdrive/locationd/locationd.cc index e78b3d3903..8941b50248 100755 --- a/selfdrive/locationd/locationd.cc +++ b/selfdrive/locationd/locationd.cc @@ -589,12 +589,12 @@ void Localizer::handle_msg(const cereal::Event::Reader& log) { this->handle_sensor(t, log.getAccelerometer()); } else if (log.isGyroscope()) { this->handle_sensor(t, log.getGyroscope()); - //} else if (log.isGpsLocation()) { - // this->handle_gps(t, log.getGpsLocation(), GPS_QUECTEL_SENSOR_TIME_OFFSET); - //} else if (log.isGpsLocationExternal()) { - // this->handle_gps(t, log.getGpsLocationExternal(), GPS_UBLOX_SENSOR_TIME_OFFSET); - } else if (log.isGnssMeasurements()) { - this->handle_gnss(t, log.getGnssMeasurements()); + } else if (log.isGpsLocation()) { + this->handle_gps(t, log.getGpsLocation(), GPS_QUECTEL_SENSOR_TIME_OFFSET); + } else if (log.isGpsLocationExternal()) { + this->handle_gps(t, log.getGpsLocationExternal(), GPS_UBLOX_SENSOR_TIME_OFFSET); + //} else if (log.isGnssMeasurements()) { + // this->handle_gnss(t, log.getGnssMeasurements()); } else if (log.isCarState()) { this->handle_car_state(t, log.getCarState()); } else if (log.isCameraOdometry()) { @@ -658,12 +658,17 @@ void Localizer::determine_gps_mode(double current_time) { int Localizer::locationd_thread() { ublox_available = Params().getBool("UbloxAvailable", true); - - const std::initializer_list service_list = {"gnssMeasurements", "cameraOdometry", "liveCalibration", + const char* gps_location_socket; + if (ublox_available) { + gps_location_socket = "gpsLocationExternal"; + } else { + gps_location_socket = "gpsLocation"; + } + const std::initializer_list service_list = {gps_location_socket, "cameraOdometry", "liveCalibration", "carState", "carParams", "accelerometer", "gyroscope"}; // TODO: remove carParams once we're always sending at 100Hz - SubMaster sm(service_list, {}, nullptr, {"gnssMeasurements", "carParams"}); + SubMaster sm(service_list, {}, nullptr, {gps_location_socket, "carParams"}); PubMaster pm({"liveLocationKalman"}); uint64_t cnt = 0; diff --git a/selfdrive/locationd/test/test_laikad.py b/selfdrive/locationd/test/test_laikad.py index 8cd84d91c2..6059eab68f 100755 --- a/selfdrive/locationd/test/test_laikad.py +++ b/selfdrive/locationd/test/test_laikad.py @@ -22,26 +22,7 @@ def get_log(segs=range(0)): logs = [] for i in segs: logs.extend(LogReader(get_url("4cf7a6ad03080c90|2021-09-29--13-46-36", i))) - all_logs = [m for m in logs if m.which() == 'ubloxGnss'] - - low_gnss = [] - for m in all_logs: - if m.ubloxGnss.which() != 'measurementReport': - continue - if m.ubloxGnss.measurementReport.numMeas == 0: - continue - - MAX_MEAS = 7 - if m.ubloxGnss.measurementReport.numMeas > MAX_MEAS: - mb = m.as_builder() - mb.ubloxGnss.measurementReport.numMeas = MAX_MEAS - mb.ubloxGnss.measurementReport.measurements = list(m.ubloxGnss.measurementReport.measurements)[:MAX_MEAS] - mb.ubloxGnss.measurementReport.measurements[0].pseudorange += 1000 - low_gnss.append(mb.as_reader()) - else: - low_gnss.append(m) - - return all_logs, low_gnss + return [m for m in logs if m.which() == 'ubloxGnss'] def verify_messages(lr, laikad, return_one_success=False): @@ -78,9 +59,8 @@ class TestLaikad(unittest.TestCase): @classmethod def setUpClass(cls): - logs, low_gnss = get_log(range(1)) + logs = get_log(range(1)) cls.logs = logs - cls.low_gnss = low_gnss first_gps_time = get_first_gps_time(logs) cls.first_gps_time = first_gps_time @@ -274,18 +254,6 @@ class TestLaikad(unittest.TestCase): # Verify orbit data is not downloaded mock_method.assert_not_called() - def test_low_gnss_meas(self): - cnt = 0 - laikad = Laikad() - for m in self.low_gnss: - msg = laikad.process_gnss_msg(m.ubloxGnss, m.logMonoTime, block=True) - if msg is None: - continue - gm = msg.gnssMeasurements - if len(gm.correctedMeasurements) != 0 and gm.positionECEF.valid: - cnt += 1 - self.assertEqual(cnt, 554) - def dict_has_values(self, dct): self.assertGreater(len(dct), 0) self.assertGreater(min([len(v) for v in dct.values()]), 0) diff --git a/selfdrive/locationd/test/test_locationd.py b/selfdrive/locationd/test/test_locationd.py index 943969a49e..9f643e2b8f 100755 --- a/selfdrive/locationd/test/test_locationd.py +++ b/selfdrive/locationd/test/test_locationd.py @@ -15,7 +15,7 @@ from selfdrive.manager.process_config import managed_processes class TestLocationdProc(unittest.TestCase): MAX_WAITS = 1000 - LLD_MSGS = ['gnssMeasurements', 'cameraOdometry', 'carState', 'liveCalibration', + LLD_MSGS = ['gpsLocationExternal', 'cameraOdometry', 'carState', 'liveCalibration', 'accelerometer', 'gyroscope', 'magnetometer'] def setUp(self): @@ -46,14 +46,25 @@ class TestLocationdProc(unittest.TestCase): except capnp.lib.capnp.KjException: msg = messaging.new_message(name, 0) - if name == "gnssMeasurements": - msg.gnssMeasurements.measTime = t - msg.gnssMeasurements.positionECEF.value = [self.x , self.y, self.z] - msg.gnssMeasurements.positionECEF.std = [0,0,0] - msg.gnssMeasurements.positionECEF.valid = True - msg.gnssMeasurements.velocityECEF.value = [] - msg.gnssMeasurements.velocityECEF.std = [0,0,0] - msg.gnssMeasurements.velocityECEF.valid = True + + if name == "gpsLocationExternal": + msg.gpsLocationExternal.flags = 1 + msg.gpsLocationExternal.verticalAccuracy = 1.0 + msg.gpsLocationExternal.speedAccuracy = 1.0 + msg.gpsLocationExternal.bearingAccuracyDeg = 1.0 + msg.gpsLocationExternal.vNED = [0.0, 0.0, 0.0] + msg.gpsLocationExternal.latitude = float(self.lat) + msg.gpsLocationExternal.longitude = float(self.lon) + msg.gpsLocationExternal.unixTimestampMillis = t * 1e6 + msg.gpsLocationExternal.altitude = float(self.alt) + #if name == "gnssMeasurements": + # msg.gnssMeasurements.measTime = t + # msg.gnssMeasurements.positionECEF.value = [self.x , self.y, self.z] + # msg.gnssMeasurements.positionECEF.std = [0,0,0] + # msg.gnssMeasurements.positionECEF.valid = True + # msg.gnssMeasurements.velocityECEF.value = [] + # msg.gnssMeasurements.velocityECEF.std = [0,0,0] + # msg.gnssMeasurements.velocityECEF.valid = True elif name == 'cameraOdometry': msg.cameraOdometry.rot = [0.0, 0.0, 0.0] msg.cameraOdometry.rotStd = [0.0, 0.0, 0.0] diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 24ee87e471..22ec099285 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -331,7 +331,7 @@ CONFIGS = [ pub_sub={ "cameraOdometry": ["liveLocationKalman"], "accelerometer": [], "gyroscope": [], - "gnssMeasurements": [], "liveCalibration": [], "carState": [], + "gpsLocationExternal": [], "liveCalibration": [], "carState": [], }, ignore=["logMonoTime", "valid"], init_callback=get_car_params, diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 629a9a2f51..1aa3aa6f1e 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -660149849230afe36363100ab0f5935038d8191e \ No newline at end of file +acc5655633af6ade096ad41f680fd8a28c2e3790 \ No newline at end of file From fd4dc109e1ab01b2d1ea6aaf3e99126dbaddc63b Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 20 Jan 2023 05:51:55 +0800 Subject: [PATCH 121/484] cabana: add support for multiple columns charts (#27000) --- tools/cabana/chartswidget.cc | 68 +++++++++++++++++++++++++++++------- tools/cabana/chartswidget.h | 14 ++++++-- tools/cabana/settings.cc | 2 ++ tools/cabana/settings.h | 1 + 4 files changed, 70 insertions(+), 15 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 69229baa64..412365a15b 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -22,9 +22,19 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { // toolbar QToolBar *toolbar = new QToolBar(tr("Charts"), this); toolbar->setIconSize({16, 16}); - title_label = new QLabel(); - title_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - toolbar->addWidget(title_label); + + toolbar->addWidget(title_label = new QLabel()); + title_label->setContentsMargins(0 ,0, 12, 0); + columns_cb = new QComboBox(this); + columns_cb->addItems({"1", "2", "3", "4"}); + columns_cb->setCurrentIndex(std::clamp(settings.chart_column_count - 1, 0, 3)); + toolbar->addWidget(new QLabel(tr("Columns:"))); + toolbar->addWidget(columns_cb); + + QLabel *stretch_label = new QLabel(this); + stretch_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + toolbar->addWidget(stretch_label); + show_all_values_btn = toolbar->addAction(""); toolbar->addWidget(range_label = new QLabel()); reset_zoom_btn = toolbar->addAction(bootstrapPixmap("arrow-counterclockwise"), ""); @@ -36,8 +46,11 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { // charts QWidget *charts_container = new QWidget(this); - charts_layout = new QVBoxLayout(charts_container); - charts_layout->addStretch(); + QVBoxLayout *charts_main_layout = new QVBoxLayout(charts_container); + charts_main_layout->setContentsMargins(0, 0, 0, 0); + charts_layout = new QGridLayout(charts_container); + charts_main_layout->addLayout(charts_layout); + charts_main_layout->addStretch(0); QScrollArea *charts_scroll = new QScrollArea(this); charts_scroll->setWidgetResizable(true); @@ -53,6 +66,7 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { align_charts_timer = new QTimer(this); align_charts_timer->setSingleShot(true); align_charts_timer->callOnTimeout(this, &ChartsWidget::alignCharts); + column_count = settings.chart_column_count; QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &ChartsWidget::removeAll); QObject::connect(can, &CANMessages::eventsMerged, this, &ChartsWidget::eventsMerged); @@ -60,6 +74,8 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { QObject::connect(show_all_values_btn, &QAction::triggered, this, &ChartsWidget::showAllData); QObject::connect(remove_all_btn, &QAction::triggered, this, &ChartsWidget::removeAll); QObject::connect(reset_zoom_btn, &QAction::triggered, this, &ChartsWidget::zoomReset); + QObject::connect(columns_cb, SIGNAL(activated(int)), SLOT(setColumnCount(int))); + QObject::connect(&settings, &Settings::changed, this, &ChartsWidget::settingChanged); QObject::connect(dock_btn, &QAction::triggered, [this]() { emit dock(!docking); docking = !docking; @@ -144,6 +160,14 @@ void ChartsWidget::updateToolBar() { dock_btn->setToolTip(docking ? tr("Undock charts") : tr("Dock charts")); } +void ChartsWidget::settingChanged() { + for (auto c : charts) { + c->setFixedHeight(settings.chart_height); + columns_cb->setCurrentIndex(std::clamp(settings.chart_column_count - 1, 0, columns_cb->count() - 1)); + setColumnCount(settings.chart_column_count); + } +} + ChartView *ChartsWidget::findChart(const QString &id, const Signal *sig) { for (auto c : charts) if (c->hasSeries(id, sig)) return c; @@ -157,6 +181,9 @@ void ChartsWidget::showChart(const QString &id, const Signal *sig, bool show, bo chart = merge && charts.size() > 0 ? charts.back() : nullptr; if (!chart) { chart = new ChartView(this); + chart->setFixedHeight(settings.chart_height); + chart->setMinimumWidth(CHART_MIN_WIDTH); + chart->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); chart->chart()->setTheme(use_dark_theme ? QChart::QChart::ChartThemeDark : QChart::ChartThemeLight); chart->setEventsRange(display_range); auto range = is_zoomed ? zoomed_range : display_range; @@ -167,8 +194,8 @@ void ChartsWidget::showChart(const QString &id, const Signal *sig, bool show, bo QObject::connect(chart, &ChartView::seriesRemoved, this, &ChartsWidget::seriesChanged); QObject::connect(chart, &ChartView::seriesAdded, this, &ChartsWidget::seriesChanged); QObject::connect(chart, &ChartView::axisYUpdated, [this]() { align_charts_timer->start(100); }); - charts_layout->insertWidget(0, chart); charts.push_back(chart); + updateLayout(); } chart->addSeries(id, sig); } else if (!show && chart) { @@ -178,6 +205,29 @@ void ChartsWidget::showChart(const QString &id, const Signal *sig, bool show, bo setUpdatesEnabled(true); } +void ChartsWidget::setColumnCount(int n) { + n = std::clamp(n + 1, 1, columns_cb->count()); + if (column_count != n) { + column_count = n; + updateLayout(); + } +} + +void ChartsWidget::updateLayout() { + int n = column_count; + for (; n >= 1; --n) { + if ((n * (CHART_MIN_WIDTH + charts_layout->spacing())) < rect().width()) break; + } + for (int i = 0; i < charts.size(); ++i) { + charts_layout->addWidget(charts[i], i / n, i % n); + } +} + +void ChartsWidget::resizeEvent(QResizeEvent *event) { + QWidget::resizeEvent(event); + updateLayout(); +} + void ChartsWidget::removeChart(ChartView *chart) { charts.removeOne(chart); chart->deleteLater(); @@ -244,13 +294,11 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { setChart(chart); setRenderHint(QPainter::Antialiasing); setRubberBand(QChartView::HorizontalRubberBand); - updateFromSettings(); QObject::connect(dbc(), &DBCManager::signalRemoved, this, &ChartView::signalRemoved); QObject::connect(dbc(), &DBCManager::signalUpdated, this, &ChartView::signalUpdated); QObject::connect(dbc(), &DBCManager::msgRemoved, this, &ChartView::msgRemoved); QObject::connect(dbc(), &DBCManager::msgUpdated, this, &ChartView::msgUpdated); - QObject::connect(&settings, &Settings::changed, this, &ChartView::updateFromSettings); QObject::connect(remove_btn, &QToolButton::clicked, this, &ChartView::remove); QObject::connect(manage_btn, &QToolButton::clicked, this, &ChartView::manageSeries); } @@ -386,10 +434,6 @@ void ChartView::updateTitle() { } } -void ChartView::updateFromSettings() { - setFixedHeight(settings.chart_height); -} - void ChartView::setEventsRange(const std::pair &range) { if (range != events_range) { events_range = range; diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index ebce8ceda6..54c84ad2db 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -3,11 +3,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include @@ -68,7 +68,6 @@ private: void resizeEvent(QResizeEvent *event) override; void updateAxisY(); void updateTitle(); - void updateFromSettings(); void drawForeground(QPainter *painter, const QRectF &rect) override; void applyNiceNumbers(qreal min, qreal max); qreal niceNumber(qreal x, bool ceiling); @@ -92,12 +91,16 @@ public: void showChart(const QString &id, const Signal *sig, bool show, bool merge); inline bool hasSignal(const QString &id, const Signal *sig) { return findChart(id, sig) != nullptr; } +public slots: + void setColumnCount(int n); + signals: void dock(bool floating); void rangeChanged(double min, double max, bool is_zommed); void seriesChanged(); private: + void resizeEvent(QResizeEvent *event) override; void alignCharts(); void removeChart(ChartView *chart); void eventsMerged(); @@ -108,6 +111,8 @@ private: void updateToolBar(); void removeAll(); void showAllData(); + void updateLayout(); + void settingChanged(); bool eventFilter(QObject *obj, QEvent *event) override; ChartView *findChart(const QString &id, const Signal *sig); @@ -119,7 +124,7 @@ private: QAction *reset_zoom_btn; QAction *remove_all_btn; QTimer *align_charts_timer; - QVBoxLayout *charts_layout; + QGridLayout *charts_layout; QList charts; uint32_t max_chart_range = 0; bool is_zoomed = false; @@ -127,6 +132,9 @@ private: std::pair display_range; std::pair zoomed_range; bool use_dark_theme = false; + QComboBox *columns_cb; + int column_count = 1; + const int CHART_MIN_WIDTH = 300; }; class SeriesSelector : public QDialog { diff --git a/tools/cabana/settings.cc b/tools/cabana/settings.cc index a5b490e9fb..0b0610edc9 100644 --- a/tools/cabana/settings.cc +++ b/tools/cabana/settings.cc @@ -18,6 +18,7 @@ void Settings::save() { s.setValue("cached_segment", cached_segment_limit); s.setValue("chart_height", chart_height); s.setValue("max_chart_x_range", max_chart_x_range); + s.setValue("chart_column_count", chart_column_count); s.setValue("last_dir", last_dir); s.setValue("window_state", window_state); s.setValue("geometry", geometry); @@ -30,6 +31,7 @@ void Settings::load() { cached_segment_limit = s.value("cached_segment", 3).toInt(); chart_height = s.value("chart_height", 200).toInt(); max_chart_x_range = s.value("max_chart_x_range", 3 * 60).toInt(); + chart_column_count = s.value("chart_column_count", 1).toInt(); last_dir = s.value("last_dir", QDir::homePath()).toString(); window_state = s.value("window_state").toByteArray(); geometry = s.value("geometry").toByteArray(); diff --git a/tools/cabana/settings.h b/tools/cabana/settings.h index 76be2f1487..924021eb94 100644 --- a/tools/cabana/settings.h +++ b/tools/cabana/settings.h @@ -16,6 +16,7 @@ public: int fps = 10; int cached_segment_limit = 3; int chart_height = 200; + int chart_column_count = 1; int max_chart_x_range = 3 * 60; // 3 minutes QString last_dir; QByteArray geometry; From f9490739ab37ae680aef272d51eb3cf2b36b4103 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 20 Jan 2023 07:26:00 +0800 Subject: [PATCH 122/484] Cabana: support live streaming (#26946) * support live streaming * update live stream's time * cleanup stream classes * disable video control in live streaming mode * emit streamStarted() in LiveStream::streamThread * disable some features in live streaming mode * refactor charts to support live streaming mode * disable dynamic mode checkbox in live streaming mode * updateDispalyRange * thread safe events * TODO: add support for ZMQ * atomic time stamp * only keep settings.cached_segment_limit*60 seconds data in liveStream * make charts work better in live mode * cleanup ChartView * fix toolbar * cleanup cleanup * disable openpilotPrefix and useOpenGL on macos * add comment * exit gracefully * support ZMQ * use ::operator new/delete * cleanup streams * cleanup * align stream buffers * check looping back * check if series is empty * cleanup * add TODO: write stream to log file to replay it * upper_bound * remove class member event_range * change default settings value * cleanup updateDisplayrange * fix merge error --- tools/cabana/SConscript | 4 +- tools/cabana/binaryview.cc | 2 +- tools/cabana/cabana.cc | 47 +++++---- tools/cabana/canmessages.h | 86 ----------------- tools/cabana/chartswidget.cc | 95 +++++++++++-------- tools/cabana/chartswidget.h | 6 +- tools/cabana/commands.h | 1 - tools/cabana/dbcmanager.h | 2 - tools/cabana/detailwidget.cc | 4 +- tools/cabana/historylog.cc | 9 +- tools/cabana/historylog.h | 2 +- tools/cabana/mainwin.cc | 11 ++- tools/cabana/mainwin.h | 2 +- tools/cabana/messageswidget.cc | 3 +- tools/cabana/messageswidget.h | 2 +- tools/cabana/settings.cc | 6 +- tools/cabana/settings.h | 2 +- tools/cabana/signaledit.h | 2 +- .../abstractstream.cc} | 69 +++----------- tools/cabana/streams/abstractstream.h | 76 +++++++++++++++ tools/cabana/streams/livestream.cc | 71 ++++++++++++++ tools/cabana/streams/livestream.h | 31 ++++++ tools/cabana/streams/replaystream.cc | 54 +++++++++++ tools/cabana/streams/replaystream.h | 32 +++++++ tools/cabana/tools/findsimilarbits.cc | 2 +- tools/cabana/videowidget.cc | 10 +- tools/cabana/videowidget.h | 2 +- 27 files changed, 395 insertions(+), 238 deletions(-) delete mode 100644 tools/cabana/canmessages.h rename tools/cabana/{canmessages.cc => streams/abstractstream.cc} (64%) create mode 100644 tools/cabana/streams/abstractstream.h create mode 100644 tools/cabana/streams/livestream.cc create mode 100644 tools/cabana/streams/livestream.h create mode 100644 tools/cabana/streams/replaystream.cc create mode 100644 tools/cabana/streams/replaystream.h diff --git a/tools/cabana/SConscript b/tools/cabana/SConscript index 6726a042ad..9b172a544f 100644 --- a/tools/cabana/SConscript +++ b/tools/cabana/SConscript @@ -20,8 +20,8 @@ cabana_env = qt_env.Clone() prev_moc_path = cabana_env['QT_MOCHPREFIX'] cabana_env['QT_MOCHPREFIX'] = os.path.dirname(prev_moc_path) + '/cabana/moc_' -cabana_lib = cabana_env.Library("cabana_lib", ['mainwin.cc', 'binaryview.cc', 'chartswidget.cc', 'historylog.cc', 'videowidget.cc', 'signaledit.cc', 'dbcmanager.cc', - 'canmessages.cc', 'commands.cc', 'messageswidget.cc', 'settings.cc', 'detailwidget.cc', 'tools/findsimilarbits.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) +cabana_lib = cabana_env.Library("cabana_lib", ['mainwin.cc', 'streams/livestream.cc', 'streams/abstractstream.cc', 'streams/replaystream.cc', 'binaryview.cc', 'chartswidget.cc', 'historylog.cc', 'videowidget.cc', 'signaledit.cc', 'dbcmanager.cc', + 'commands.cc', 'messageswidget.cc', 'settings.cc', 'detailwidget.cc', 'tools/findsimilarbits.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) cabana_env.Program('_cabana', ['cabana.cc', cabana_lib, asset_obj], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) if GetOption('test'): diff --git a/tools/cabana/binaryview.cc b/tools/cabana/binaryview.cc index b6a5fa75aa..8d28080790 100644 --- a/tools/cabana/binaryview.cc +++ b/tools/cabana/binaryview.cc @@ -6,7 +6,7 @@ #include #include -#include "tools/cabana/canmessages.h" +#include "tools/cabana/streams/abstractstream.h" // BinaryView diff --git a/tools/cabana/cabana.cc b/tools/cabana/cabana.cc index 6a9db120e9..ce0cb443cb 100644 --- a/tools/cabana/cabana.cc +++ b/tools/cabana/cabana.cc @@ -4,6 +4,8 @@ #include "common/prefix.h" #include "selfdrive/ui/qt/util.h" #include "tools/cabana/mainwin.h" +#include "tools/cabana/streams/livestream.h" +#include "tools/cabana/streams/replaystream.h" int main(int argc, char *argv[]) { QCoreApplication::setApplicationName("Cabana"); @@ -17,33 +19,40 @@ int main(int argc, char *argv[]) { cmd_parser.addOption({"demo", "use a demo route instead of providing your own"}); cmd_parser.addOption({"qcam", "load qcamera"}); cmd_parser.addOption({"ecam", "load wide road camera"}); + cmd_parser.addOption({"stream", "read can messages from live streaming"}); + cmd_parser.addOption({"zmq", "the ip address on which to receive zmq messages", "zmq"}); cmd_parser.addOption({"data_dir", "local directory with routes", "data_dir"}); cmd_parser.process(app); const QStringList args = cmd_parser.positionalArguments(); - if (args.empty() && !cmd_parser.isSet("demo")) { + if (args.empty() && !cmd_parser.isSet("demo") && !cmd_parser.isSet("stream")) { cmd_parser.showHelp(); } + std::unique_ptr op_prefix; + std::unique_ptr stream; - const QString route = args.empty() ? DEMO_ROUTE : args.first(); - uint32_t replay_flags = REPLAY_FLAG_NONE; - if (cmd_parser.isSet("ecam")) { - replay_flags |= REPLAY_FLAG_ECAM; - } else if (cmd_parser.isSet("qcam")) { - replay_flags |= REPLAY_FLAG_QCAMERA; - } - - // TODO: Remove when OpenpilotPrefix supports ZMQ + if (cmd_parser.isSet("stream")) { + stream.reset(new LiveStream(&app, cmd_parser.value("zmq"))); + } else { + // TODO: Remove when OpenpilotPrefix supports ZMQ #ifndef __APPLE__ - OpenpilotPrefix op_prefix; + op_prefix.reset(new OpenpilotPrefix()); #endif - - CANMessages p(&app); - int ret = 0; - if (p.loadRoute(route, cmd_parser.value("data_dir"), replay_flags)) { - MainWindow w; - w.show(); - ret = app.exec(); + const QString route = args.empty() ? DEMO_ROUTE : args.first(); + uint32_t replay_flags = REPLAY_FLAG_NONE; + if (cmd_parser.isSet("ecam")) { + replay_flags |= REPLAY_FLAG_ECAM; + } else if (cmd_parser.isSet("qcam")) { + replay_flags |= REPLAY_FLAG_QCAMERA; + } + auto replay_stream = new ReplayStream(&app); + stream.reset(replay_stream); + if (!replay_stream->loadRoute(route, cmd_parser.value("data_dir"), replay_flags)) { + return 0; + } } - return ret; + + MainWindow w; + w.show(); + return app.exec(); } diff --git a/tools/cabana/canmessages.h b/tools/cabana/canmessages.h deleted file mode 100644 index c30361af41..0000000000 --- a/tools/cabana/canmessages.h +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -#include "opendbc/can/common_dbc.h" -#include "tools/cabana/settings.h" -#include "tools/replay/replay.h" - -struct CanData { - double ts = 0.; - uint8_t src = 0; - uint32_t address = 0; - uint32_t count = 0; - uint32_t freq = 0; - QByteArray dat; - QList colors; -}; - -class CANMessages : public QObject { - Q_OBJECT - -public: - CANMessages(QObject *parent); - ~CANMessages(); - bool loadRoute(const QString &route, const QString &data_dir, uint32_t replay_flags = REPLAY_FLAG_NONE); - void seekTo(double ts); - bool eventFilter(const Event *event); - - inline QString routeName() const { return replay->route()->name(); } - inline QString carFingerprint() const { return replay->carFingerprint().c_str(); } - inline VisionStreamType visionStreamType() const { return replay->hasFlag(REPLAY_FLAG_ECAM) ? VISION_STREAM_WIDE_ROAD : VISION_STREAM_ROAD; } - inline double totalSeconds() const { return replay->totalSeconds(); } - inline double routeStartTime() const { return replay->routeStartTime() / (double)1e9; } - inline double currentSec() const { return replay->currentSeconds(); } - inline QDateTime currentDateTime() const { return replay->currentDateTime(); } - inline const CanData &lastMessage(const QString &id) { return can_msgs[id]; } - - inline const Route* route() const { return replay->route(); } - inline const std::vector *events() const { return replay->events(); } - inline void setSpeed(float speed) { replay->setSpeed(speed); } - inline bool isPaused() const { return replay->isPaused(); } - void pause(bool pause); - inline const std::vector> getTimeline() { return replay->getTimeline(); } - -signals: - void paused(); - void resume(); - void seekedTo(double sec); - void streamStarted(); - void eventsMerged(); - void updated(); - void msgsReceived(const QHash *); - void received(QHash *); - -public: - QMap can_msgs; - -protected: - void process(QHash *); - void settingChanged(); - - Replay *replay = nullptr; - std::atomic counters_begin_sec = 0; - std::atomic processing = false; - QHash counters; -}; - -inline QString toHex(const QByteArray &dat) { - return dat.toHex(' ').toUpper(); -} -inline char toHex(uint value) { - return "0123456789ABCDEF"[value & 0xF]; -} - -inline const QString &getColor(int i) { - // TODO: add more colors - static const QString SIGNAL_COLORS[] = {"#9FE2BF", "#40E0D0", "#6495ED", "#CCCCFF", "#FF7F50", "#FFBF00"}; - return SIGNAL_COLORS[i % std::size(SIGNAL_COLORS)]; -} - -// A global pointer referring to the unique CANMessages object -extern CANMessages *can; diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 412365a15b..0a855b851b 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -69,8 +69,8 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { column_count = settings.chart_column_count; QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &ChartsWidget::removeAll); - QObject::connect(can, &CANMessages::eventsMerged, this, &ChartsWidget::eventsMerged); - QObject::connect(can, &CANMessages::updated, this, &ChartsWidget::updateState); + QObject::connect(can, &AbstractStream::eventsMerged, this, &ChartsWidget::updateState); + QObject::connect(can, &AbstractStream::updated, this, &ChartsWidget::updateState); QObject::connect(show_all_values_btn, &QAction::triggered, this, &ChartsWidget::showAllData); QObject::connect(remove_all_btn, &QAction::triggered, this, &ChartsWidget::removeAll); QObject::connect(reset_zoom_btn, &QAction::triggered, this, &ChartsWidget::zoomReset); @@ -83,27 +83,33 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { }); } -void ChartsWidget::eventsMerged() { - if (auto events = can->events(); events && !events->empty()) { - event_range.first = (events->front()->mono_time / (double)1e9) - can->routeStartTime(); - event_range.second = (events->back()->mono_time / (double)1e9) - can->routeStartTime(); - updateState(); - } -} - void ChartsWidget::updateDisplayRange() { - auto prev_range = display_range; - double current_sec = can->currentSec(); - if (current_sec < display_range.first || current_sec >= (display_range.second - 5)) { - // reached the end, or seeked to a timestamp out of range. - display_range.first = current_sec - 5; - } - display_range.first = std::floor(std::max(display_range.first, event_range.first) * 10.0) / 10.0; - display_range.second = std::floor(std::min(display_range.first + max_chart_range, event_range.second) * 10.0) / 10.0; - if (prev_range != display_range) { - QFutureSynchronizer future_synchronizer; - for (auto c : charts) - future_synchronizer.addFuture(QtConcurrent::run(c, &ChartView::setEventsRange, display_range)); + auto events = can->events(); + double min_event_sec = (events->front()->mono_time / (double)1e9) - can->routeStartTime(); + double max_event_sec = (events->back()->mono_time / (double)1e9) - can->routeStartTime(); + const double cur_sec = can->currentSec(); + if (!can->liveStreaming()) { + auto prev_range = display_range; + if (cur_sec < display_range.first || cur_sec >= (display_range.second - 5)) { + // reached the end, or seeked to a timestamp out of range. + display_range.first = cur_sec - 5; + } + display_range.first = std::floor(std::max(min_event_sec, display_range.first)); + display_range.second = std::floor(std::min(display_range.first + max_chart_range, max_event_sec)); + if (prev_range != display_range) { + QFutureSynchronizer future_synchronizer; + for (auto c : charts) { + future_synchronizer.addFuture(QtConcurrent::run(c, &ChartView::setEventsRange, display_range)); + } + } + } else { + if (cur_sec >= (display_range.second - 5)) { + display_range.first = std::floor(std::max(min_event_sec, cur_sec - settings.max_chart_x_range / 2.0)); + } + display_range.second = std::floor(display_range.first + settings.max_chart_x_range); + for (auto c : charts) { + c->updateSeries(nullptr, events, false); + } } } @@ -151,7 +157,7 @@ void ChartsWidget::updateToolBar() { bool displaying_all = max_chart_range != min_range; show_all_values_btn->setText(tr("%1 minutes").arg(max_chart_range / 60)); show_all_values_btn->setToolTip(tr("Click to display %1 data").arg(displaying_all ? tr("%1 minutes").arg(min_range / 60) : tr("ALL cached"))); - show_all_values_btn->setVisible(!is_zoomed); + show_all_values_btn->setVisible(!is_zoomed && !can->liveStreaming()); remove_all_btn->setEnabled(!charts.isEmpty()); reset_zoom_btn->setEnabled(is_zoomed); range_label->setText(is_zoomed ? tr("%1 - %2").arg(zoomed_range.first, 0, 'f', 2).arg(zoomed_range.second, 0, 'f', 2) : ""); @@ -274,6 +280,7 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { chart->addAxis(axis_y, Qt::AlignLeft); chart->legend()->setShowToolTips(true); chart->layout()->setContentsMargins(0, 0, 0, 0); + chart->setMargins({20, 11, 11, 11}); QToolButton *remove_btn = new QToolButton(); remove_btn->setIcon(bootstrapPixmap("x")); @@ -293,7 +300,8 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { setChart(chart); setRenderHint(QPainter::Antialiasing); - setRubberBand(QChartView::HorizontalRubberBand); + // TODO: enable zoomIn/seekTo in live streaming mode. + setRubberBand(can->liveStreaming() ? QChartView::NoRubberBand : QChartView::HorizontalRubberBand); QObject::connect(dbc(), &DBCManager::signalRemoved, this, &ChartView::signalRemoved); QObject::connect(dbc(), &DBCManager::signalUpdated, this, &ChartView::signalUpdated); @@ -331,7 +339,6 @@ void ChartView::addSeries(const QString &msg_id, const Signal *sig) { sigs.push_back({.msg_id = msg_id, .address = address, .source = source, .sig = sig, .series = series}); updateTitle(); updateSeries(sig); - updateAxisY(); emit seriesAdded(msg_id, sig); } @@ -366,7 +373,6 @@ void ChartView::signalUpdated(const Signal *sig) { updateTitle(); // TODO: don't update series if only name changed. updateSeries(sig); - updateAxisY(); } } @@ -448,22 +454,21 @@ void ChartView::setDisplayRange(double min, double max) { } } -void ChartView::updateSeries(const Signal *sig) { - auto events = can->events(); +void ChartView::updateSeries(const Signal *sig, const std::vector *events, bool clear) { + if (!events) events = can->events(); if (!events || sigs.isEmpty()) return; for (auto &s : sigs) { if (!sig || s.sig == sig) { - s.vals.clear(); - s.vals.reserve((events_range.second - events_range.first) * 1000); // [n]seconds * 1000hz - s.min_y = std::numeric_limits::max(); - s.max_y = std::numeric_limits::lowest(); - + if (clear) { + s.vals.clear(); + s.last_value_mono_time = 0; + } double route_start_time = can->routeStartTime(); - Event begin_event(cereal::Event::Which::INIT_DATA, (route_start_time + events_range.first) * 1e9); - auto begin = std::lower_bound(events->begin(), events->end(), &begin_event, Event::lessThan()); - double end_ns = (route_start_time + events_range.second) * 1e9; - + uint64_t begin_ts = can->liveStreaming() ? s.last_value_mono_time : (route_start_time + events_range.first) * 1e9; + Event begin_event(cereal::Event::Which::INIT_DATA, begin_ts); + auto begin = std::upper_bound(events->begin(), events->end(), &begin_event, Event::lessThan()); + uint64_t end_ns = can->liveStreaming() ? events->back()->mono_time : (route_start_time + events_range.second) * 1e9; for (auto it = begin; it != events->end() && (*it)->mono_time <= end_ns; ++it) { if ((*it)->which == cereal::Event::Which::CAN) { for (const auto &c : (*it)->event.getCan()) { @@ -472,14 +477,20 @@ void ChartView::updateSeries(const Signal *sig) { double value = get_raw_value((uint8_t *)dat.begin(), dat.size(), *s.sig); double ts = ((*it)->mono_time / (double)1e9) - route_start_time; // seconds s.vals.push_back({ts, value}); - - if (value < s.min_y) s.min_y = value; - if (value > s.max_y) s.max_y = value; } } } } + if (!s.vals.isEmpty()) { + auto [min_v, max_v] = std::minmax_element(s.vals.begin(), s.vals.end(), [](auto &l, auto &r) { return l.y() < r.y(); }); + s.min_y = min_v->y(); + s.max_y = max_v->y(); + s.last_value_mono_time = events->back()->mono_time; + } else { + s.min_y = s.max_y = 0; + } s.series->replace(s.vals); + updateAxisY(); } } } @@ -490,7 +501,7 @@ void ChartView::updateAxisY() { double min_y = std::numeric_limits::max(); double max_y = std::numeric_limits::lowest(); - if (events_range == std::pair{axis_x->min(), axis_x->max()}) { + if (can->liveStreaming() || events_range == std::pair{axis_x->min(), axis_x->max()}) { for (auto &s : sigs) { if (s.min_y < min_y) min_y = s.min_y; if (s.max_y > max_y) max_y = s.max_y; @@ -583,7 +594,7 @@ void ChartView::mouseReleaseEvent(QMouseEvent *event) { emit zoomIn(min, max); } event->accept(); - } else if (event->button() == Qt::RightButton) { + } else if (!can->liveStreaming() && event->button() == Qt::RightButton) { emit zoomReset(); event->accept(); } else { diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index 54c84ad2db..9ba5ce5a30 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -12,8 +12,8 @@ #include #include -#include "tools/cabana/canmessages.h" #include "tools/cabana/dbcmanager.h" +#include "tools/cabana/streams/abstractstream.h" using namespace QtCharts; @@ -25,7 +25,7 @@ public: void addSeries(const QString &msg_id, const Signal *sig); void removeSeries(const QString &msg_id, const Signal *sig); bool hasSeries(const QString &msg_id, const Signal *sig) const; - void updateSeries(const Signal *sig = nullptr); + void updateSeries(const Signal *sig = nullptr, const std::vector *events = nullptr, bool clear = true); void setEventsRange(const std::pair &range); void setDisplayRange(double min, double max); void setPlotAreaLeftPosition(int pos); @@ -40,6 +40,7 @@ public: double min_y = 0; double max_y = 0; QVector vals; + uint64_t last_value_mono_time = 0; }; signals: @@ -128,7 +129,6 @@ private: QList charts; uint32_t max_chart_range = 0; bool is_zoomed = false; - std::pair event_range; std::pair display_range; std::pair zoomed_range; bool use_dark_theme = false; diff --git a/tools/cabana/commands.h b/tools/cabana/commands.h index 7ea1f66653..e46223d630 100644 --- a/tools/cabana/commands.h +++ b/tools/cabana/commands.h @@ -2,7 +2,6 @@ #include -#include "tools/cabana/canmessages.h" #include "tools/cabana/dbcmanager.h" class EditMsgCommand : public QUndoCommand { diff --git a/tools/cabana/dbcmanager.h b/tools/cabana/dbcmanager.h index c7675121bb..5db6737be8 100644 --- a/tools/cabana/dbcmanager.h +++ b/tools/cabana/dbcmanager.h @@ -28,9 +28,7 @@ public: static std::pair parseId(const QString &id); inline static std::vector allDBCNames() { return get_dbc_names(); } - inline std::map &allMsgs() { return msgs; } inline QString name() const { return dbc ? dbc->name.c_str() : ""; } - void updateMsg(const QString &id, const QString &name, uint32_t size); void removeMsg(const QString &id); inline const std::map &messages() const { return msgs; } diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index 247111aaf4..78e5a6a6f6 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -8,9 +8,9 @@ #include #include "selfdrive/ui/qt/util.h" -#include "tools/cabana/canmessages.h" #include "tools/cabana/commands.h" #include "tools/cabana/dbcmanager.h" +#include "tools/cabana/streams/abstractstream.h" // DetailWidget @@ -99,7 +99,7 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart QObject::connect(binary_view, &BinaryView::resizeSignal, this, &DetailWidget::resizeSignal); QObject::connect(binary_view, &BinaryView::addSignal, this, &DetailWidget::addSignal); QObject::connect(tab_widget, &QTabWidget::currentChanged, [this]() { updateState(); }); - QObject::connect(can, &CANMessages::msgsReceived, this, &DetailWidget::updateState); + QObject::connect(can, &AbstractStream::msgsReceived, this, &DetailWidget::updateState); QObject::connect(dbc(), &DBCManager::DBCFileChanged, [this]() { dbcMsgChanged(); }); QObject::connect(tabbar, &QTabBar::customContextMenuRequested, this, &DetailWidget::showTabBarContextMenu); QObject::connect(tabbar, &QTabBar::currentChanged, [this](int index) { diff --git a/tools/cabana/historylog.cc b/tools/cabana/historylog.cc index c6ee7d9086..f0f40697b8 100644 --- a/tools/cabana/historylog.cc +++ b/tools/cabana/historylog.cc @@ -222,8 +222,13 @@ LogsWidget::LogsWidget(QWidget *parent) : QWidget(parent) { QObject::connect(comp_box, SIGNAL(activated(int)), this, SLOT(setFilter())); QObject::connect(value_edit, &QLineEdit::textChanged, this, &LogsWidget::setFilter); QObject::connect(dynamic_mode, &QCheckBox::stateChanged, model, &HistoryLogModel::setDynamicMode); - QObject::connect(can, &CANMessages::seekedTo, model, &HistoryLogModel::refresh); - QObject::connect(can, &CANMessages::eventsMerged, model, &HistoryLogModel::segmentsMerged); + QObject::connect(can, &AbstractStream::seekedTo, model, &HistoryLogModel::refresh); + QObject::connect(can, &AbstractStream::eventsMerged, model, &HistoryLogModel::segmentsMerged); + + if (can->liveStreaming()) { + dynamic_mode->setChecked(true); + dynamic_mode->setEnabled(false); + } } void LogsWidget::setMessage(const QString &message_id) { diff --git a/tools/cabana/historylog.h b/tools/cabana/historylog.h index 1eea7e5eba..2ab204af15 100644 --- a/tools/cabana/historylog.h +++ b/tools/cabana/historylog.h @@ -7,8 +7,8 @@ #include #include -#include "tools/cabana/canmessages.h" #include "tools/cabana/dbcmanager.h" +#include "tools/cabana/streams/abstractstream.h" class HeaderView : public QHeaderView { public: diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index 89876f4267..48c9ad3a56 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -58,8 +58,7 @@ MainWindow::MainWindow() : QMainWindow() { QObject::connect(this, &MainWindow::updateProgressBar, this, &MainWindow::updateDownloadProgress); QObject::connect(messages_widget, &MessagesWidget::msgSelectionChanged, detail_widget, &DetailWidget::setMessage); QObject::connect(charts_widget, &ChartsWidget::dock, this, &MainWindow::dockCharts); - QObject::connect(charts_widget, &ChartsWidget::rangeChanged, video_widget, &VideoWidget::rangeChanged); - QObject::connect(can, &CANMessages::streamStarted, this, &MainWindow::loadDBCFromFingerprint); + QObject::connect(can, &AbstractStream::streamStarted, this, &MainWindow::loadDBCFromFingerprint); QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &MainWindow::DBCFileChanged); QObject::connect(detail_widget->undo_stack, &QUndoStack::indexChanged, [this](int index) { setWindowTitle(tr("%1%2 - Cabana").arg(index > 0 ? "* " : "").arg(dbc()->name())); @@ -123,9 +122,13 @@ void MainWindow::createDockWindows() { charts_layout->setContentsMargins(0, 0, 0, 0); charts_layout->addWidget(charts_widget); - video_widget = new VideoWidget(this); video_splitter = new QSplitter(Qt::Vertical,this); - video_splitter->addWidget(video_widget); + + if (!can->liveStreaming()) { + video_widget = new VideoWidget(this); + video_splitter->addWidget(video_widget); + QObject::connect(charts_widget, &ChartsWidget::rangeChanged, video_widget, &VideoWidget::rangeChanged); + } video_splitter->addWidget(charts_container); video_splitter->restoreState(settings.video_splitter_state); diff --git a/tools/cabana/mainwin.h b/tools/cabana/mainwin.h index c1a5908a53..a38834b997 100644 --- a/tools/cabana/mainwin.h +++ b/tools/cabana/mainwin.h @@ -47,7 +47,7 @@ protected: void setOption(); void findSimilarBits(); - VideoWidget *video_widget; + VideoWidget *video_widget = nullptr; QDockWidget *video_dock; MessagesWidget *messages_widget; DetailWidget *detail_widget; diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index 5deac031a1..0d06604fdc 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -26,7 +26,6 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { table_widget->setItemDelegateForColumn(4, new MessageBytesDelegate(table_widget)); table_widget->setSelectionBehavior(QAbstractItemView::SelectRows); table_widget->setSelectionMode(QAbstractItemView::SingleSelection); - table_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); table_widget->setSortingEnabled(true); table_widget->sortByColumn(0, Qt::AscendingOrder); table_widget->setColumnWidth(0, 250); @@ -39,7 +38,7 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { // signals/slots QObject::connect(filter, &QLineEdit::textChanged, model, &MessageListModel::setFilterString); - QObject::connect(can, &CANMessages::msgsReceived, model, &MessageListModel::msgsReceived); + QObject::connect(can, &AbstractStream::msgsReceived, model, &MessageListModel::msgsReceived); QObject::connect(dbc(), &DBCManager::DBCFileChanged, model, &MessageListModel::sortMessages); QObject::connect(dbc(), &DBCManager::msgUpdated, model, &MessageListModel::sortMessages); QObject::connect(dbc(), &DBCManager::msgRemoved, model, &MessageListModel::sortMessages); diff --git a/tools/cabana/messageswidget.h b/tools/cabana/messageswidget.h index 8b74f1427e..187e4715b9 100644 --- a/tools/cabana/messageswidget.h +++ b/tools/cabana/messageswidget.h @@ -4,7 +4,7 @@ #include #include -#include "tools/cabana/canmessages.h" +#include "tools/cabana/streams/abstractstream.h" class MessageListModel : public QAbstractTableModel { Q_OBJECT diff --git a/tools/cabana/settings.cc b/tools/cabana/settings.cc index 0b0610edc9..7a25449c81 100644 --- a/tools/cabana/settings.cc +++ b/tools/cabana/settings.cc @@ -28,7 +28,7 @@ void Settings::save() { void Settings::load() { QSettings s("settings", QSettings::IniFormat); fps = s.value("fps", 10).toInt(); - cached_segment_limit = s.value("cached_segment", 3).toInt(); + cached_segment_limit = s.value("cached_segment", 5).toInt(); chart_height = s.value("chart_height", 200).toInt(); max_chart_x_range = s.value("max_chart_x_range", 3 * 60).toInt(); chart_column_count = s.value("chart_column_count", 1).toInt(); @@ -51,13 +51,13 @@ SettingsDlg::SettingsDlg(QWidget *parent) : QDialog(parent) { form_layout->addRow("FPS", fps); cached_segment = new QSpinBox(this); - cached_segment->setRange(3, 60); + cached_segment->setRange(5, 60); cached_segment->setSingleStep(1); cached_segment->setValue(settings.cached_segment_limit); form_layout->addRow(tr("Cached segments limit"), cached_segment); max_chart_x_range = new QSpinBox(this); - max_chart_x_range->setRange(1, 60); + max_chart_x_range->setRange(3, 60); max_chart_x_range->setSingleStep(1); max_chart_x_range->setValue(settings.max_chart_x_range / 60); form_layout->addRow(tr("Chart range (minutes)"), max_chart_x_range); diff --git a/tools/cabana/settings.h b/tools/cabana/settings.h index 924021eb94..ed0ba3a549 100644 --- a/tools/cabana/settings.h +++ b/tools/cabana/settings.h @@ -14,7 +14,7 @@ public: void load(); int fps = 10; - int cached_segment_limit = 3; + int cached_segment_limit = 5; int chart_height = 200; int chart_column_count = 1; int max_chart_x_range = 3 * 60; // 3 minutes diff --git a/tools/cabana/signaledit.h b/tools/cabana/signaledit.h index a18f1d34f0..fcc27baeb1 100644 --- a/tools/cabana/signaledit.h +++ b/tools/cabana/signaledit.h @@ -7,8 +7,8 @@ #include #include "selfdrive/ui/qt/widgets/controls.h" -#include "tools/cabana/canmessages.h" #include "tools/cabana/dbcmanager.h" +#include "tools/cabana/streams/abstractstream.h" class SignalForm : public QWidget { Q_OBJECT diff --git a/tools/cabana/canmessages.cc b/tools/cabana/streams/abstractstream.cc similarity index 64% rename from tools/cabana/canmessages.cc rename to tools/cabana/streams/abstractstream.cc index 9b5f41c60a..6ae1900184 100644 --- a/tools/cabana/canmessages.cc +++ b/tools/cabana/streams/abstractstream.cc @@ -1,47 +1,13 @@ -#include "tools/cabana/canmessages.h" -#include "tools/cabana/dbcmanager.h" +#include "tools/cabana/streams/abstractstream.h" -CANMessages *can = nullptr; +AbstractStream *can = nullptr; -CANMessages::CANMessages(QObject *parent) : QObject(parent) { +AbstractStream::AbstractStream(QObject *parent, bool is_live_streaming) : is_live_streaming(is_live_streaming), QObject(parent) { can = this; - QObject::connect(this, &CANMessages::received, this, &CANMessages::process, Qt::QueuedConnection); - QObject::connect(&settings, &Settings::changed, this, &CANMessages::settingChanged); + QObject::connect(this, &AbstractStream::received, this, &AbstractStream::process, Qt::QueuedConnection); } -CANMessages::~CANMessages() { - replay->stop(); -} - -static bool event_filter(const Event *e, void *opaque) { - CANMessages *c = (CANMessages *)opaque; - return c->eventFilter(e); -} - -static QColor blend(QColor a, QColor b) { - return QColor((a.red() + b.red()) / 2, (a.green() + b.green()) / 2, (a.blue() + b.blue()) / 2, (a.alpha() + b.alpha()) / 2); -} - -bool CANMessages::loadRoute(const QString &route, const QString &data_dir, uint32_t replay_flags) { - replay = new Replay(route, {"can", "roadEncodeIdx", "wideRoadEncodeIdx", "carParams"}, {}, nullptr, replay_flags, data_dir, this); - replay->setSegmentCacheLimit(settings.cached_segment_limit); - replay->installEventFilter(event_filter, this); - QObject::connect(replay, &Replay::seekedTo, this, &CANMessages::seekedTo); - QObject::connect(replay, &Replay::segmentsMerged, this, &CANMessages::eventsMerged); - QObject::connect(replay, &Replay::streamStarted, this, &CANMessages::streamStarted); - if (replay->load()) { - const auto &segments = replay->route()->segments(); - if (std::none_of(segments.begin(), segments.end(), [](auto &s) { return s.second.rlog.length() > 0; })) { - qWarning() << "no rlogs in route" << route; - return false; - } - replay->start(); - return true; - } - return false; -} - -void CANMessages::process(QHash *messages) { +void AbstractStream::process(QHash *messages) { for (auto it = messages->begin(); it != messages->end(); ++it) { can_msgs[it.key()] = it.value(); } @@ -51,7 +17,11 @@ void CANMessages::process(QHash *messages) { processing = false; } -bool CANMessages::eventFilter(const Event *event) { +static QColor blend(QColor a, QColor b) { + return QColor((a.red() + b.red()) / 2, (a.green() + b.green()) / 2, (a.blue() + b.blue()) / 2, (a.alpha() + b.alpha()) / 2); +} + +bool AbstractStream::updateEvent(const Event *event) { static std::unique_ptr new_msgs = std::make_unique>(); static QHash prev_dat; static QHash> colors; @@ -59,7 +29,7 @@ bool CANMessages::eventFilter(const Event *event) { static double prev_update_ts = 0; if (event->which == cereal::Event::Which::CAN) { - double current_sec = replay->currentSeconds(); + double current_sec = currentSec(); if (counters_begin_sec == 0 || counters_begin_sec >= current_sec) { new_msgs->clear(); counters.clear(); @@ -79,7 +49,7 @@ bool CANMessages::eventFilter(const Event *event) { data.freq = data.count / delta; } - // Init colors + // Init colors if (colors[id].size() != data.dat.size()) { colors[id].clear(); for (int i = 0; i < data.dat.size(); i++){ @@ -146,18 +116,3 @@ bool CANMessages::eventFilter(const Event *event) { } return true; } - -void CANMessages::seekTo(double ts) { - replay->seekTo(std::max(double(0), ts), false); - counters_begin_sec = 0; - emit updated(); -} - -void CANMessages::pause(bool pause) { - replay->pause(pause); - emit (pause ? paused() : resume()); -} - -void CANMessages::settingChanged() { - replay->setSegmentCacheLimit(settings.cached_segment_limit); -} diff --git a/tools/cabana/streams/abstractstream.h b/tools/cabana/streams/abstractstream.h new file mode 100644 index 0000000000..1fe90c8595 --- /dev/null +++ b/tools/cabana/streams/abstractstream.h @@ -0,0 +1,76 @@ +#pragma once + +#include + +#include +#include + +#include "tools/cabana/settings.h" +#include "tools/replay/replay.h" + +struct CanData { + double ts = 0.; + uint8_t src = 0; + uint32_t address = 0; + uint32_t count = 0; + uint32_t freq = 0; + QByteArray dat; + QList colors; +}; + +class AbstractStream : public QObject { + Q_OBJECT + +public: + AbstractStream(QObject *parent, bool is_live_streaming); + virtual ~AbstractStream() {}; + inline bool liveStreaming() const { return is_live_streaming; } + virtual void seekTo(double ts) {} + virtual QString routeName() const = 0; + virtual QString carFingerprint() const { return ""; } + virtual double totalSeconds() const { return 0; } + virtual double routeStartTime() const { return 0; } + virtual double currentSec() const = 0; + virtual QDateTime currentDateTime() const { return {}; } + virtual const CanData &lastMessage(const QString &id) { return can_msgs[id]; } + virtual VisionStreamType visionStreamType() const { return VISION_STREAM_ROAD; } + virtual const Route *route() const { return nullptr; } + virtual const std::vector *events() const = 0; + virtual void setSpeed(float speed) {} + virtual bool isPaused() const { return false; } + virtual void pause(bool pause) {} + virtual const std::vector> getTimeline() { return {}; } + +signals: + void paused(); + void resume(); + void seekedTo(double sec); + void streamStarted(); + void eventsMerged(); + void updated(); + void msgsReceived(const QHash *); + void received(QHash *); + +public: + QMap can_msgs; + +protected: + void process(QHash *); + bool updateEvent(const Event *event); + + bool is_live_streaming = false; + std::atomic counters_begin_sec = 0; + std::atomic processing = false; + QHash counters; +}; + +inline QString toHex(const QByteArray &dat) { return dat.toHex(' ').toUpper(); } +inline char toHex(uint value) { return "0123456789ABCDEF"[value & 0xF]; } +inline const QString &getColor(int i) { + // TODO: add more colors + static const QString SIGNAL_COLORS[] = {"#9FE2BF", "#40E0D0", "#6495ED", "#CCCCFF", "#FF7F50", "#FFBF00"}; + return SIGNAL_COLORS[i % std::size(SIGNAL_COLORS)]; +} + +// A global pointer referring to the unique AbstractStream object +extern AbstractStream *can; diff --git a/tools/cabana/streams/livestream.cc b/tools/cabana/streams/livestream.cc new file mode 100644 index 0000000000..bc5b811f44 --- /dev/null +++ b/tools/cabana/streams/livestream.cc @@ -0,0 +1,71 @@ +#include "tools/cabana/streams/livestream.h" + +LiveStream::LiveStream(QObject *parent, QString address) : zmq_address(address), AbstractStream(parent, true) { + if (!zmq_address.isEmpty()) { + setenv("ZMQ", "1", 1); + } + updateCachedNS(); + QObject::connect(&settings, &Settings::changed, this, &LiveStream::updateCachedNS); + stream_thread = new QThread(this); + QObject::connect(stream_thread, &QThread::started, [=]() { streamThread(); }); + QObject::connect(stream_thread, &QThread::finished, stream_thread, &QThread::deleteLater); + stream_thread->start(); +} + +LiveStream::~LiveStream() { + stream_thread->requestInterruption(); + stream_thread->quit(); + stream_thread->wait(); + for (Event *e : can_events) ::delete e; + for (auto m : messages) delete m; +} + +void LiveStream::streamThread() { + std::unique_ptr context(Context::create()); + std::string address = zmq_address.isEmpty() ? "127.0.0.1" : zmq_address.toStdString(); + std::unique_ptr sock(SubSocket::create(context.get(), "can", address)); + assert(sock != NULL); + sock->setTimeout(50); + + // run as fast as messages come in + while (!QThread::currentThread()->isInterruptionRequested()) { + Message *msg = sock->receive(true); + if (!msg) { + QThread::msleep(50); + continue; + } + AlignedBuffer *buf = messages.emplace_back(new AlignedBuffer()); + Event *evt = ::new Event(buf->align(msg)); + delete msg; + + { + std::lock_guard lk(lock); + can_events.push_back(evt); + if ((evt->mono_time - can_events.front()->mono_time) > cache_ns) { + ::delete can_events.front(); + delete messages.front(); + can_events.pop_front(); + messages.pop_front(); + } + } + if (start_ts == 0) { + start_ts = evt->mono_time; + emit streamStarted(); + } + current_ts = evt->mono_time; + if (start_ts > current_ts) { + qDebug() << "stream is looping back to old time stamp"; + start_ts = current_ts.load(); + } + updateEvent(evt); + // TODO: write stream to log file to replay it with cabana --data_dir flag. + } +} + +const std::vector *LiveStream::events() const { + std::lock_guard lk(lock); + events_vector.clear(); + events_vector.reserve(can_events.size()); + std::copy(can_events.begin(), can_events.end(), std::back_inserter(events_vector)); + return &events_vector; +} diff --git a/tools/cabana/streams/livestream.h b/tools/cabana/streams/livestream.h new file mode 100644 index 0000000000..17301d48e7 --- /dev/null +++ b/tools/cabana/streams/livestream.h @@ -0,0 +1,31 @@ +#pragma once + +#include "tools/cabana/streams/abstractstream.h" + +class LiveStream : public AbstractStream { + Q_OBJECT + +public: + LiveStream(QObject *parent, QString address = {}); + ~LiveStream(); + inline QString routeName() const override { + return QString("Live Streaming From %1").arg(zmq_address.isEmpty() ? "127.0.0.1" : zmq_address); + } + inline double routeStartTime() const override { return start_ts / (double)1e9; } + inline double currentSec() const override { return (current_ts - start_ts) / (double)1e9; } + const std::vector *events() const override; + +protected: + void streamThread(); + void updateCachedNS() { cache_ns = (settings.cached_segment_limit * 60) * 1e9; } + + mutable std::mutex lock; + mutable std::vector events_vector; + std::deque can_events; + std::deque messages; + std::atomic start_ts = 0; + std::atomic current_ts = 0; + std::atomic cache_ns = 0; + const QString zmq_address; + QThread *stream_thread; +}; diff --git a/tools/cabana/streams/replaystream.cc b/tools/cabana/streams/replaystream.cc new file mode 100644 index 0000000000..97a3911adf --- /dev/null +++ b/tools/cabana/streams/replaystream.cc @@ -0,0 +1,54 @@ +#include "tools/cabana/streams/replaystream.h" + +#include "tools/cabana/dbcmanager.h" + +ReplayStream::ReplayStream(QObject *parent) : AbstractStream(parent, false) { + QObject::connect(&settings, &Settings::changed, [this]() { + if (replay) replay->setSegmentCacheLimit(settings.cached_segment_limit); + }); +} + +ReplayStream::~ReplayStream() { + if (replay) replay->stop(); +} + +static bool event_filter(const Event *e, void *opaque) { + return ((ReplayStream *)opaque)->eventFilter(e); +} + +bool ReplayStream::loadRoute(const QString &route, const QString &data_dir, uint32_t replay_flags) { + replay = new Replay(route, {"can", "roadEncodeIdx", "wideRoadEncodeIdx", "carParams"}, {}, nullptr, replay_flags, data_dir, this); + replay->setSegmentCacheLimit(settings.cached_segment_limit); + replay->installEventFilter(event_filter, this); + QObject::connect(replay, &Replay::seekedTo, this, &AbstractStream::seekedTo); + QObject::connect(replay, &Replay::segmentsMerged, this, &AbstractStream::eventsMerged); + QObject::connect(replay, &Replay::streamStarted, this, &AbstractStream::streamStarted); + if (replay->load()) { + const auto &segments = replay->route()->segments(); + if (std::none_of(segments.begin(), segments.end(), [](auto &s) { return s.second.rlog.length() > 0; })) { + qWarning() << "no rlogs in route" << route; + return false; + } + replay->start(); + return true; + } + return false; +} + +bool ReplayStream::eventFilter(const Event *event) { + if (event->which == cereal::Event::Which::CAN) { + updateEvent(event); + } + return true; +} + +void ReplayStream::seekTo(double ts) { + replay->seekTo(std::max(double(0), ts), false); + counters_begin_sec = 0; + emit updated(); +} + +void ReplayStream::pause(bool pause) { + replay->pause(pause); + emit(pause ? paused() : resume()); +} diff --git a/tools/cabana/streams/replaystream.h b/tools/cabana/streams/replaystream.h new file mode 100644 index 0000000000..1688915212 --- /dev/null +++ b/tools/cabana/streams/replaystream.h @@ -0,0 +1,32 @@ +#pragma once + +#include "opendbc/can/common_dbc.h" +#include "tools/cabana/streams/abstractstream.h" +#include "tools/cabana/settings.h" + +class ReplayStream : public AbstractStream { + Q_OBJECT + +public: + ReplayStream(QObject *parent); + ~ReplayStream(); + bool loadRoute(const QString &route, const QString &data_dir, uint32_t replay_flags = REPLAY_FLAG_NONE); + bool eventFilter(const Event *event); + void seekTo(double ts) override; + inline QString routeName() const override { return replay->route()->name(); } + inline QString carFingerprint() const override { return replay->carFingerprint().c_str(); } + inline VisionStreamType visionStreamType() const override { return replay->hasFlag(REPLAY_FLAG_ECAM) ? VISION_STREAM_WIDE_ROAD : VISION_STREAM_ROAD; } + inline double totalSeconds() const override { return replay->totalSeconds(); } + inline double routeStartTime() const override { return replay->routeStartTime() / (double)1e9; } + inline double currentSec() const override { return replay->currentSeconds(); } + inline QDateTime currentDateTime() const override { return replay->currentDateTime(); } + inline const Route *route() const override { return replay->route(); } + inline const std::vector *events() const override { return replay->events(); } + inline void setSpeed(float speed) override { replay->setSpeed(speed); } + inline bool isPaused() const override { return replay->isPaused(); } + void pause(bool pause) override; + inline const std::vector> getTimeline() override { return replay->getTimeline(); } + +private: + Replay *replay = nullptr; +}; diff --git a/tools/cabana/tools/findsimilarbits.cc b/tools/cabana/tools/findsimilarbits.cc index 8a05e3e236..63d01b152d 100644 --- a/tools/cabana/tools/findsimilarbits.cc +++ b/tools/cabana/tools/findsimilarbits.cc @@ -7,8 +7,8 @@ #include #include -#include "tools/cabana/canmessages.h" #include "tools/cabana/dbcmanager.h" +#include "tools/cabana/streams/abstractstream.h" FindSimilarBitsDlg::FindSimilarBitsDlg(QWidget *parent) : QDialog(parent, Qt::WindowFlags() | Qt::Window) { setWindowTitle(tr("Find similar bits")); diff --git a/tools/cabana/videowidget.cc b/tools/cabana/videowidget.cc index 2f6aab249a..7d46769a47 100644 --- a/tools/cabana/videowidget.cc +++ b/tools/cabana/videowidget.cc @@ -70,10 +70,10 @@ VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent) { QObject::connect(slider, &QSlider::valueChanged, [=](int value) { time_label->setText(formatTime(value / 1000)); }); QObject::connect(cam_widget, &CameraWidget::clicked, []() { can->pause(!can->isPaused()); }); QObject::connect(play_btn, &QPushButton::clicked, []() { can->pause(!can->isPaused()); }); - QObject::connect(can, &CANMessages::updated, this, &VideoWidget::updateState); - QObject::connect(can, &CANMessages::paused, this, &VideoWidget::updatePlayBtnState); - QObject::connect(can, &CANMessages::resume, this, &VideoWidget::updatePlayBtnState); - QObject::connect(can, &CANMessages::streamStarted, [this]() { + QObject::connect(can, &AbstractStream::updated, this, &VideoWidget::updateState); + QObject::connect(can, &AbstractStream::paused, this, &VideoWidget::updatePlayBtnState); + QObject::connect(can, &AbstractStream::resume, this, &VideoWidget::updatePlayBtnState); + QObject::connect(can, &AbstractStream::streamStarted, [this]() { end_time_label->setText(formatTime(can->totalSeconds())); slider->setRange(0, can->totalSeconds() * 1000); }); @@ -130,7 +130,7 @@ Slider::Slider(QWidget *parent) : QSlider(Qt::Horizontal, parent) { setMouseTracking(true); QObject::connect(can, SIGNAL(streamStarted()), timer, SLOT(start())); - QObject::connect(can, &CANMessages::streamStarted, this, &Slider::streamStarted); + QObject::connect(can, &AbstractStream::streamStarted, this, &Slider::streamStarted); } void Slider::streamStarted() { diff --git a/tools/cabana/videowidget.h b/tools/cabana/videowidget.h index e9899abbbf..6b4c02c573 100644 --- a/tools/cabana/videowidget.h +++ b/tools/cabana/videowidget.h @@ -11,7 +11,7 @@ #include "selfdrive/ui/qt/widgets/cameraview.h" #include "selfdrive/ui/qt/widgets/controls.h" -#include "tools/cabana/canmessages.h" +#include "tools/cabana/streams/abstractstream.h" class Slider : public QSlider { Q_OBJECT From c1fcd63bba1797133309d94b360d98e147b69548 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 19 Jan 2023 18:42:34 -0800 Subject: [PATCH 123/484] cameraview: fix spaces in debug print --- selfdrive/ui/qt/widgets/cameraview.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/qt/widgets/cameraview.cc b/selfdrive/ui/qt/widgets/cameraview.cc index 16aad13437..4a7874b160 100644 --- a/selfdrive/ui/qt/widgets/cameraview.cc +++ b/selfdrive/ui/qt/widgets/cameraview.cc @@ -358,7 +358,7 @@ void CameraWidget::vipcThread() { while (!QThread::currentThread()->isInterruptionRequested()) { if (!vipc_client || cur_stream != requested_stream_type) { clearFrames(); - qDebug() << "connecting to stream " << requested_stream_type << ", was connected to " << cur_stream; + qDebug().nospace() << "connecting to stream " << requested_stream_type << ", was connected to " << cur_stream; cur_stream = requested_stream_type; vipc_client.reset(new VisionIpcClient(stream_name, cur_stream, false)); } From 1c128db8b07a1abf10b9a2f2cc6a7af22c9ae065 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 19 Jan 2023 20:54:50 -0800 Subject: [PATCH 124/484] Cabana: darken signal backgrounds on hover (#27010) * darker signal background when hovering * remove import * little better --- tools/cabana/binaryview.cc | 5 +++-- tools/cabana/signaledit.cc | 9 ++++++--- tools/cabana/signaledit.h | 1 + 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/tools/cabana/binaryview.cc b/tools/cabana/binaryview.cc index 8d28080790..84c2dd2a52 100644 --- a/tools/cabana/binaryview.cc +++ b/tools/cabana/binaryview.cc @@ -242,8 +242,9 @@ void BinaryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op painter->fillRect(option.rect, selection_color); painter->setPen(option.palette.color(QPalette::BrightText)); } else if (!item->sigs.isEmpty() && (!bin_view->selectionModel()->hasSelection() || !item->sigs.contains(bin_view->resize_sig))) { - painter->fillRect(option.rect, item->bg_color); - painter->setPen(item->sigs.contains(bin_view->hovered_sig) ? option.palette.color(QPalette::BrightText) : Qt::black); + bool sig_hovered = item->sigs.contains(bin_view->hovered_sig); + painter->fillRect(option.rect, sig_hovered ? item->bg_color.darker(125) : item->bg_color); // 4/5x brightness + painter->setPen(sig_hovered ? option.palette.color(QPalette::BrightText) : Qt::black); } painter->drawText(option.rect, Qt::AlignCenter, item->val); diff --git a/tools/cabana/signaledit.cc b/tools/cabana/signaledit.cc index e2025dcc82..cc57cf0f3e 100644 --- a/tools/cabana/signaledit.cc +++ b/tools/cabana/signaledit.cc @@ -89,6 +89,8 @@ SignalEdit::SignalEdit(int index, QWidget *parent) : form_idx(index), QWidget(pa main_layout->setContentsMargins(0, 0, 0, 0); main_layout->setSpacing(0); + bg_color = QColor(getColor(form_idx)); + // title bar auto title_bar = new QWidget(this); title_bar->setFixedHeight(32); @@ -142,7 +144,7 @@ void SignalEdit::setSignal(const QString &message_id, const Signal *signal) { updateForm(msg_id == message_id && form->isVisible()); msg_id = message_id; color_label->setText(QString::number(form_idx + 1)); - color_label->setStyleSheet(QString("color:black; background-color:%2").arg(getColor(form_idx))); + color_label->setStyleSheet(QString("color:black; background-color:%2").arg(bg_color.name())); title->setText(sig->name.c_str()); show(); } @@ -199,8 +201,9 @@ void SignalEdit::updateForm(bool visible) { } void SignalEdit::signalHovered(const Signal *s) { - auto color = sig == s ? "white" : "black"; - color_label->setStyleSheet(QString("color:%1; background-color:%2").arg(color).arg(getColor(form_idx))); + auto text_color = sig == s ? "white" : "black"; + auto _bg_color = sig == s ? bg_color.darker(125) : bg_color; // 4/5x brightness + color_label->setStyleSheet(QString("color:%1; background-color:%2").arg(text_color).arg(_bg_color.name())); } void SignalEdit::enterEvent(QEvent *event) { diff --git a/tools/cabana/signaledit.h b/tools/cabana/signaledit.h index fcc27baeb1..90df3fd3f4 100644 --- a/tools/cabana/signaledit.h +++ b/tools/cabana/signaledit.h @@ -54,5 +54,6 @@ protected: QLabel *color_label; QLabel *icon; int form_idx = 0; + QColor bg_color; QToolButton *plot_btn; }; From c408b7e30609ca64971a4278eab16643fbb123b3 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 19 Jan 2023 23:06:18 -0800 Subject: [PATCH 125/484] UI: show experimental long available to release users (#26910) * UI: show experimental long available to release users * more description * cleanup * hide toggle * fix process replay * clear param * actually fix --- common/params.cc | 3 ++- selfdrive/car/car_helpers.py | 6 ++--- selfdrive/controls/controlsd.py | 8 +++---- selfdrive/manager/manager.py | 3 ++- .../test/process_replay/process_replay.py | 2 +- selfdrive/ui/qt/offroad/settings.cc | 20 ++++++++++++---- selfdrive/ui/translations/main_de.ts | 16 +++++++++---- selfdrive/ui/translations/main_ja.ts | 24 ++++++++++++------- selfdrive/ui/translations/main_ko.ts | 24 ++++++++++++------- selfdrive/ui/translations/main_pt-BR.ts | 24 ++++++++++++------- selfdrive/ui/translations/main_zh-CHS.ts | 24 ++++++++++++------- selfdrive/ui/translations/main_zh-CHT.ts | 24 ++++++++++++------- 12 files changed, 117 insertions(+), 61 deletions(-) diff --git a/common/params.cc b/common/params.cc index 8f6532bc79..35ceecfdc3 100644 --- a/common/params.cc +++ b/common/params.cc @@ -104,7 +104,7 @@ std::unordered_map keys = { {"DisablePowerDown", PERSISTENT}, {"ExperimentalMode", PERSISTENT}, {"ExperimentalModeConfirmed", PERSISTENT}, - {"ExperimentalLongitudinalEnabled", PERSISTENT}, // WARNING: THIS MAY DISABLE AEB + {"ExperimentalLongitudinalEnabled", PERSISTENT}, {"DisableUpdates", PERSISTENT}, {"DisengageOnAccelerator", PERSISTENT}, {"DongleId", PERSISTENT}, @@ -134,6 +134,7 @@ std::unordered_map keys = { {"IsRhdDetected", PERSISTENT}, {"IsTakingSnapshot", CLEAR_ON_MANAGER_START}, {"IsTestedBranch", CLEAR_ON_MANAGER_START}, + {"IsReleaseBranch", CLEAR_ON_MANAGER_START}, {"IsUpdateAvailable", CLEAR_ON_MANAGER_START}, {"JoystickDebugMode", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF}, {"LaikadEphemeris", PERSISTENT | DONT_LOG}, diff --git a/selfdrive/car/car_helpers.py b/selfdrive/car/car_helpers.py index 61e7a3d55d..88e8c72153 100644 --- a/selfdrive/car/car_helpers.py +++ b/selfdrive/car/car_helpers.py @@ -173,17 +173,15 @@ def fingerprint(logcan, sendcan, num_pandas): return car_fingerprint, finger, vin, car_fw, source, exact_match -def get_car(logcan, sendcan, num_pandas=1): +def get_car(logcan, sendcan, experimental_long_allowed, num_pandas=1): candidate, fingerprints, vin, car_fw, source, exact_match = fingerprint(logcan, sendcan, num_pandas) if candidate is None: cloudlog.warning("car doesn't match any fingerprints: %r", fingerprints) candidate = "mock" - experimental_long = Params().get_bool("ExperimentalLongitudinalEnabled") - CarInterface, CarController, CarState = interfaces[candidate] - CP = CarInterface.get_params(candidate, fingerprints, car_fw, experimental_long) + CP = CarInterface.get_params(candidate, fingerprints, car_fw, experimental_long_allowed) CP.carVin = vin CP.carFw = car_fw CP.fingerprintSource = source diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index ac36df7aa2..c0b01bfc99 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -99,7 +99,8 @@ class Controls: get_one_can(self.can_sock) num_pandas = len(messaging.recv_one_retry(self.sm.sock['pandaStates']).pandaStates) - self.CI, self.CP = get_car(self.can_sock, self.pm.sock['sendcan'], num_pandas) + experimental_long_allowed = self.params.get_bool("ExperimentalLongitudinalEnabled") and not is_release_branch() + self.CI, self.CP = get_car(self.can_sock, self.pm.sock['sendcan'], experimental_long_allowed, num_pandas) else: self.CI, self.CP = CI, CI.CP @@ -132,9 +133,6 @@ class Controls: safety_config.safetyModel = car.CarParams.SafetyModel.noOutput self.CP.safetyConfigs = [safety_config] - if is_release_branch(): - self.CP.experimentalLongitudinalAvailable = False - # Write CarParams for radard cp_bytes = self.CP.to_bytes() self.params.put("CarParams", cp_bytes) @@ -142,7 +140,7 @@ class Controls: put_nonblocking("CarParamsPersistent", cp_bytes) # cleanup old params - if not self.CP.experimentalLongitudinalAvailable: + if not self.CP.experimentalLongitudinalAvailable or is_release_branch(): self.params.remove("ExperimentalLongitudinalEnabled") if not self.CP.openpilotLongitudinalControl: self.params.remove("ExperimentalMode") diff --git a/selfdrive/manager/manager.py b/selfdrive/manager/manager.py index 369c529626..865966d6c5 100755 --- a/selfdrive/manager/manager.py +++ b/selfdrive/manager/manager.py @@ -20,7 +20,7 @@ from selfdrive.manager.process_config import managed_processes from selfdrive.athena.registration import register, UNREGISTERED_DONGLE_ID from system.swaglog import cloudlog, add_file_handler from system.version import is_dirty, get_commit, get_version, get_origin, get_short_branch, \ - terms_version, training_version, is_tested_branch + terms_version, training_version, is_tested_branch, is_release_branch @@ -76,6 +76,7 @@ def manager_init() -> None: params.put("GitBranch", get_short_branch(default="")) params.put("GitRemote", get_origin(default="")) params.put_bool("IsTestedBranch", is_tested_branch()) + params.put_bool("IsReleaseBranch", is_release_branch()) # set dongle id reg_res = register(show_spinner=True) diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 22ec099285..a7d53bf9da 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -188,7 +188,7 @@ def get_car_params(msgs, fsm, can_sock, fingerprint): canmsgs = [msg for msg in msgs if msg.which() == 'can'] for m in canmsgs[:300]: can.send(m.as_builder().to_bytes()) - _, CP = get_car(can, sendcan) + _, CP = get_car(can, sendcan, Params().get_bool("ExperimentalLongitudinalEnabled")) Params().put("CarParams", CP.to_bytes()) diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index bde8628dc4..63b87149d4 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -132,16 +132,17 @@ void TogglesPanel::updateToggles() { .arg(tr("New Driving Visualization")) .arg(tr("The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner.")); + const bool is_release = params.getBool("IsReleaseBranch"); auto cp_bytes = params.get("CarParamsPersistent"); if (!cp_bytes.empty()) { AlignedBuffer aligned_buf; capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size())); cereal::CarParams::Reader CP = cmsg.getRoot(); - if (!CP.getExperimentalLongitudinalAvailable()) { + if (!CP.getExperimentalLongitudinalAvailable() || is_release) { params.remove("ExperimentalLongitudinalEnabled"); } - op_long_toggle->setVisible(CP.getExperimentalLongitudinalAvailable()); + op_long_toggle->setVisible(CP.getExperimentalLongitudinalAvailable() && !is_release); const bool op_long = CP.getOpenpilotLongitudinalControl() && !CP.getExperimentalLongitudinalAvailable(); const bool exp_long_enabled = CP.getExperimentalLongitudinalAvailable() && params.getBool("ExperimentalLongitudinalEnabled"); @@ -154,9 +155,18 @@ void TogglesPanel::updateToggles() { e2e_toggle->setEnabled(false); params.remove("ExperimentalMode"); - const QString no_long = tr("Experimental mode is currently unavailable on this car, since the car's stock ACC is used for longitudinal control."); - const QString exp_long = tr("Enable experimental longitudinal control to allow experimental mode."); - e2e_toggle->setDescription("" + (CP.getExperimentalLongitudinalAvailable() ? exp_long : no_long) + "

" + e2e_description); + const QString unavailable = tr("Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control."); + + QString long_desc = unavailable + " " + \ + tr("openpilot longitudinal control may come in a future update."); + if (CP.getExperimentalLongitudinalAvailable()) { + if (is_release) { + long_desc = unavailable + " " + tr("An experimental version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches."); + } else { + long_desc = tr("Enable experimental longitudinal control to allow Experimental mode."); + } + } + e2e_toggle->setDescription("" + long_desc + "

" + e2e_description); } e2e_toggle->refresh(); diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index ecb44557c9..987202a03f 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -1044,12 +1044,20 @@ location set Die Fahrvisualisierung wechselt bei niedrigen Geschwindigkeiten zur Straßengewandten Weitwinkelkamera, um manche Kurven besser zu zeigen. Außerdem wird das Experimenteller Modus logo oben rechts angezeigt.
- Experimental mode is currently unavailable on this car, since the car's stock ACC is used for longitudinal control. - Der experimentelle Modus ist momentan für dieses Auto nicht verfügbar, da es den eingebauten adaptiven Tempomaten des Autos benutzt. + Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. + - Enable experimental longitudinal control to allow experimental mode. - Aktiviere den experimentellen Openpilot Tempomaten für experimentelle Funktionen. + openpilot longitudinal control may come in a future update. + + + + An experimental version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable experimental longitudinal control to allow Experimental mode. + diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index 8226dd59f9..aeeac6104b 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -1017,14 +1017,6 @@ location set On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when using experimental openpilot longitudinal control. openpilotはこの車の場合、車に内蔵されているACCを標準で利用します。この機能を有効にすることで実験段階のopenpilotによるアクセル制御を利用できます。実験モードと合わせて利用することをお勧めします。
- - Experimental mode is currently unavailable on this car, since the car's stock ACC is used for longitudinal control. - この車のACCがアクセル制御を行うため、実験モードを利用することができません。 - - - Enable experimental longitudinal control to allow experimental mode. - 実験段階のopenpilotによるアクセル制御を有効にしてください。 - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: openpilotは標準ではゆっくりとくつろげる運転を提供します。この実験モードを有効にすると、以下のくつろげる段階ではない開発中の機能を利用する事ができます。 @@ -1045,6 +1037,22 @@ location set The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. 新しい運転画面では、低速時に広角カメラの映像を表示することで、曲がる際の道路の視覚を向上します。実験段階を表すマークが右上に表示されます。 + + Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. + + + + openpilot longitudinal control may come in a future update. + + + + An experimental version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable experimental longitudinal control to allow Experimental mode. + + Updater diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index bda64c53ab..80cf61c2c3 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -1017,14 +1017,6 @@ location set On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when using experimental openpilot longitudinal control. 이 차량은 openpilot 롱컨트롤 대신 차량의 내장 ACC로 기본 설정됩니다. openpilot 롱컨트롤을 사용하려면 이 옵션을 활성화하세요. 실험적 openpilot 롱컨트롤을 사용하는 경우 실험적 모드를 활성화 하세요.
- - Experimental mode is currently unavailable on this car, since the car's stock ACC is used for longitudinal control. - 차량의 기본 ACC가 롱컨트롤에 사용되기 때문에 현재 이 차량에서는 실험적 모드를 사용할수 없습니다. - - - Enable experimental longitudinal control to allow experimental mode. - 실험적 롱컨트롤을 사용하려면 실험적 모드를 활성화 하세요. - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: openpilot은 기본적으로 <b>안정적 모드</b>로 주행합니다. 실험적 모드는 안정적 모드에 준비되지 않은 <b>알파 수준 기능</b>을 활성화 합니다. 실험 모드의 특징은 아래에 나열되어 있습니다 @@ -1045,6 +1037,22 @@ location set The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. 주행 시각화는 저속에서 도로를 향하는 광각 카메라로 전환되어 일부 회전을 더 잘 보여줍니다. 실험적 모드 로고도 우측상단에 표시됩니다. + + Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. + + + + openpilot longitudinal control may come in a future update. + + + + An experimental version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable experimental longitudinal control to allow Experimental mode. + + Updater diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index 105d6f77f0..3356372496 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -1021,14 +1021,6 @@ trabalho definido On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when using experimental openpilot longitudinal control. Neste carro o penpilot por padrão utiliza o ACC nativo do veículo ao invés de controlar longitudinalmente. Ative isto para mudar para o controle longitudinal do openpilot. Ativar o Modo Experimental é recomendado quando em uso do controle longitudinal experimental do openpilot.
- - Experimental mode is currently unavailable on this car, since the car's stock ACC is used for longitudinal control. - O Modo Experimental está atualmente indisponível para este carro, já que o ACC original do carro é usado para controle longitudinal. - - - Enable experimental longitudinal control to allow experimental mode. - Ative o controle longitudinal experimental para permitir o modo experimental. - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: openpilot por padrão funciona em <b>modo chill</b>. modo Experimental ativa <b>recursos de nível-alfa</b> que não estão prontos para o modo chill. Recursos experimentais estão listados abaixo: @@ -1049,6 +1041,22 @@ trabalho definido The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. A visualização da direção fará a transição para a câmera grande angular voltada para a estrada em baixas velocidades para mostrar melhor algumas curvas. O logotipo do modo Experimental também será exibido no canto superior direito. + + Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. + + + + openpilot longitudinal control may come in a future update. + + + + An experimental version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable experimental longitudinal control to allow Experimental mode. + + Updater diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index 31202e45f2..c0838e18f3 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -1015,14 +1015,6 @@ location set On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when using experimental openpilot longitudinal control. 针对此车辆,openpilot默认使用车辆自带的ACC,而非openpilot的纵向控制。启用此选项将切换到openpilot纵向控制。当使用试验性的openpilot纵向控制时,建议同时启用试验模式。 - - Experimental mode is currently unavailable on this car, since the car's stock ACC is used for longitudinal control. - 由于此车辆使用自带的ACC纵向控制,当前无法使用试验模式。 - - - Enable experimental longitudinal control to allow experimental mode. - 启用试验性的纵向控制,以便允许使用试验模式。 - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: openpilot 默认 <b>轻松模式</b>驾驶车辆。试验模式启用一些轻松模式之外的 <b>试验性功能</b>。试验性功能包括: @@ -1043,6 +1035,22 @@ location set The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. 当低速行驶时,驾驶视角将切换到前向广角摄像头,便于更完整地显示转向路径。右上角将显示试验模式图标。 + + Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. + + + + openpilot longitudinal control may come in a future update. + + + + An experimental version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable experimental longitudinal control to allow Experimental mode. + + Updater diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index 0379e926c4..82e6cf0fc1 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -1017,14 +1017,6 @@ location set On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when using experimental openpilot longitudinal control. 在本車輛中,openpilot預設將使用原車內建的ACC系統,而非openpilot縱向控制。開啟此開關來啟用openpilot縱向控制,使用此選項時建議一併啟用實驗模式。 - - Experimental mode is currently unavailable on this car, since the car's stock ACC is used for longitudinal control. - 因車輛使用內建ACC系統,無法在本車輛上啟動實驗模式。 - - - Enable experimental longitudinal control to allow experimental mode. - 啟用實驗性縱向控制以使用實驗模式。 - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: openpilot 預設以 <b>輕鬆模式</b> 駕駛。 實驗模式啟用了尚未準備好進入輕鬆模式的 <b>alpha 級功能</b>。實驗功能如下: @@ -1045,6 +1037,22 @@ location set The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. 低速行駛時,將會切換成路側廣角鏡頭,以完整顯示轉彎路徑,右上角將出現實驗模式圖案。 + + Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. + + + + openpilot longitudinal control may come in a future update. + + + + An experimental version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable experimental longitudinal control to allow Experimental mode. + + Updater From 42b0ecc891cf34fc6a82af587889096b7ee3ce3c Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 19 Jan 2023 23:32:16 -0800 Subject: [PATCH 126/484] Genesis: GV60 2023 Advanced: add missing camera FW (#27011) add new camera FW from c104d893f7c211bb|2023-01-19--20-13-10 --- selfdrive/car/hyundai/values.py | 1 + 1 file changed, 1 insertion(+) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index ec7605657b..31cbe68d13 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -1544,6 +1544,7 @@ FW_VERSIONS = { CAR.GENESIS_GV60_EV_1ST_GEN: { (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00JW1 MFC AT USA LHD 1.00 1.02 99211-CU100 211215', + b'\xf1\x00JW1 MFC AT USA LHD 1.00 1.02 99211-CU000 211215', ], (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00JW1_ RDR ----- 1.00 1.00 99110-CU000 ', From 40cc43c076df7d0998b61a0a473b8127637fd07f Mon Sep 17 00:00:00 2001 From: Lee Jong Mun <43285072+crwusiz@users.noreply.github.com> Date: Sat, 21 Jan 2023 03:43:14 +0900 Subject: [PATCH 127/484] Multilang: kor translation update (#27013) --- selfdrive/ui/translations/main_ko.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index 80cf61c2c3..f50d2d9b3f 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -501,7 +501,7 @@ location set Become a comma prime member at connect.comma.ai - connect.comma.ai에서 comma prime에 가입합니다 + connect.comma.ai 접속 comma prime 가입 PRIME FEATURES: @@ -1019,7 +1019,7 @@ location set openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: - openpilot은 기본적으로 <b>안정적 모드</b>로 주행합니다. 실험적 모드는 안정적 모드에 준비되지 않은 <b>알파 수준 기능</b>을 활성화 합니다. 실험 모드의 특징은 아래에 나열되어 있습니다 + openpilot은 기본적으로 <b>안정적 모드</b>로 주행합니다. 실험적 모드는 안정적 모드에 준비되지 않은 <b>알파 수준 기능</b>을 활성화 합니다. 실험적 모드의 특징은 아래에 나열되어 있습니다 🌮 End-to-End Longitudinal Control 🌮 @@ -1035,23 +1035,23 @@ location set The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. - 주행 시각화는 저속에서 도로를 향하는 광각 카메라로 전환되어 일부 회전을 더 잘 보여줍니다. 실험적 모드 로고도 우측상단에 표시됩니다. + 주행 시각화는 저속에서 도로를 향하는 광각 카메라로 전환되어 일부 회전을 더 잘 보여줍니다. 실험적 모드의 로고도 우측상단에 표시됩니다. Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. - + 차량에 장착된 ACC가 롱컨트롤에 사용되기 때문에 현재 이 차량은 실험적 모드를 사용할 수 없습니다. openpilot longitudinal control may come in a future update. - + 오픈파일럿 롱컨트롤은 향후 업데이트에서 제공될 수 있습니다. An experimental version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - + 오픈파일럿 롱컨트롤의 실험 버전은 실험적 모드와 함께 릴리즈 되지 않은 브랜치에서 테스트할 수 있습니다. Enable experimental longitudinal control to allow Experimental mode. - + 실험적 롱컨트롤을 사용하려면 실험적 모드를 활성화 하세요. From 21af68e3c36eef4a84b076a41305caabf76bd4a9 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 21 Jan 2023 02:43:30 +0800 Subject: [PATCH 128/484] cabana: improve video splitter (#27012) --- tools/cabana/mainwin.cc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index 48c9ad3a56..d2e4ff9ca7 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -124,13 +124,20 @@ void MainWindow::createDockWindows() { video_splitter = new QSplitter(Qt::Vertical,this); + // splitter between video and charts + video_splitter = new QSplitter(Qt::Vertical, this); if (!can->liveStreaming()) { video_widget = new VideoWidget(this); video_splitter->addWidget(video_widget); QObject::connect(charts_widget, &ChartsWidget::rangeChanged, video_widget, &VideoWidget::rangeChanged); } video_splitter->addWidget(charts_container); + video_splitter->setStretchFactor(1, 1); video_splitter->restoreState(settings.video_splitter_state); + if (!can->liveStreaming() && video_splitter->sizes()[0] == 0) { + // display video at minimum size. + video_splitter->setSizes({1, 1}); + } video_dock = new QDockWidget(can->routeName(), this); video_dock->setObjectName(tr("VideoPanel")); @@ -284,7 +291,9 @@ void MainWindow::closeEvent(QCloseEvent *event) { settings.geometry = saveGeometry(); settings.window_state = saveState(); - settings.video_splitter_state = video_splitter->saveState(); + if (!can->liveStreaming()) { + settings.video_splitter_state = video_splitter->saveState(); + } settings.save(); QWidget::closeEvent(event); } From da621245b2cc84dd972b0582bec077a008c8e871 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 20 Jan 2023 10:46:45 -0800 Subject: [PATCH 129/484] UI: update translations during scons build (#27009) * UI: update translation during scons build * all in scons * debug * precious * debug * add missing * Update release/check-dirty.sh Co-authored-by: Shane Smiskol --- release/files_common | 6 ++++++ selfdrive/ui/SConscript | 16 ++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/release/files_common b/release/files_common index 0dc0e96226..edb37bc0de 100644 --- a/release/files_common +++ b/release/files_common @@ -320,6 +320,12 @@ selfdrive/ui/qt/widgets/*.cc selfdrive/ui/qt/widgets/*.h selfdrive/ui/qt/maps/*.cc selfdrive/ui/qt/maps/*.h +selfdrive/ui/qt/setup/*.cc +selfdrive/ui/qt/setup/*.h + +selfdrive/ui/installer/*.cc +selfdrive/ui/installer/*.h +selfdrive/ui/installer/*.cc system/camerad/SConscript system/camerad/main.cc diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 3ce7a0505d..0f28f5ccc3 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -1,4 +1,5 @@ import os +import json Import('qt_env', 'arch', 'common', 'messaging', 'visionipc', 'cereal', 'transformations') @@ -67,11 +68,18 @@ if GetOption('test'): # build translation files -translation_sources = Glob("#selfdrive/ui/translations/*.ts", strings=True) +with open(File("translations/languages.json").abspath) as f: + languages = json.loads(f.read()) +translation_sources = [f"#selfdrive/ui/translations/{l}.ts" for l in languages.values()] translation_targets = [src.replace(".ts", ".qm") for src in translation_sources] -lrelease = 'third_party/qt5/larch64/bin/lrelease' if arch == 'larch64' else 'lrelease' -qt_env.Command(translation_targets, translation_sources, f"{lrelease} $SOURCES") - +lrelease_bin = 'third_party/qt5/larch64/bin/lrelease' if arch == 'larch64' else 'lrelease' + +lupdate = qt_env.Command(translation_sources, qt_src, "selfdrive/ui/update_translations.py") +lrelease = qt_env.Command(translation_targets, translation_sources, f"{lrelease_bin} $SOURCES") +qt_env.Depends(lrelease, lupdate) +qt_env.NoClean(translation_sources) +qt_env.Precious(translation_sources) +qt_env.NoCache(lupdate) # setup and factory resetter if GetOption('extras'): From 986ec99f827f98e01290461e8e35ebb5b68dd0f3 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 20 Jan 2023 11:29:10 -0800 Subject: [PATCH 130/484] Multilang: add back some translations (#27019) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add back translations * 🤔 * actually shouldn't be a space --- selfdrive/ui/translations/main_de.ts | 4 ++-- selfdrive/ui/translations/main_ja.ts | 4 ++-- selfdrive/ui/translations/main_pt-BR.ts | 6 +++--- selfdrive/ui/translations/main_zh-CHS.ts | 4 ++-- selfdrive/ui/translations/main_zh-CHT.ts | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index 987202a03f..2606df5efe 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -1045,7 +1045,7 @@ location set Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. - + Der experimentelle Modus ist momentan für dieses Auto nicht verfügbar da es den eingebauten adaptiven Tempomaten des Autos benutzt. openpilot longitudinal control may come in a future update. @@ -1057,7 +1057,7 @@ location set Enable experimental longitudinal control to allow Experimental mode. - + Aktiviere den experimentellen Openpilot Tempomaten für experimentelle Funktionen. diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index aeeac6104b..16f35dffaa 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -1039,7 +1039,7 @@ location set Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. - + この車のACCがアクセル制御を行うため実験モードを利用することができません。 openpilot longitudinal control may come in a future update. @@ -1051,7 +1051,7 @@ location set Enable experimental longitudinal control to allow Experimental mode. - + 実験段階のopenpilotによるアクセル制御を有効にしてください。 diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index 3356372496..2bbc1abace 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -388,7 +388,7 @@ Get turn-by-turn directions displayed and more with a comma prime subscription. Sign up now: https://connect.comma.ai - Obtenha instruções passo a passo exibidas e muito mais com + Obtenha instruções passo a passo exibidas e muito mais com uma assinatura prime Inscreva-se agora: https://connect.comma.ai @@ -1043,7 +1043,7 @@ trabalho definido Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. - + O modo Experimental está atualmente indisponível para este carro já que o ACC original do carro é usado para controle longitudinal. openpilot longitudinal control may come in a future update. @@ -1055,7 +1055,7 @@ trabalho definido Enable experimental longitudinal control to allow Experimental mode. - + Ative o controle longitudinal experimental para permitir o modo Experimental. diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index c0838e18f3..792a918770 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -1037,7 +1037,7 @@ location set Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. - + 由于此车辆使用自带的ACC纵向控制,当前无法使用试验模式。 openpilot longitudinal control may come in a future update. @@ -1049,7 +1049,7 @@ location set Enable experimental longitudinal control to allow Experimental mode. - + 启用试验性的纵向控制,以便允许使用试验模式。 diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index 82e6cf0fc1..e8e2ab6bd3 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -1039,7 +1039,7 @@ location set Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. - + 因車輛使用內建ACC系統,無法在本車輛上啟動實驗模式。 openpilot longitudinal control may come in a future update. @@ -1051,7 +1051,7 @@ location set Enable experimental longitudinal control to allow Experimental mode. - + 啟用實驗性縱向控制以使用實驗模式。 From 2c5a952a1c84a799b2951867943c4748618721d2 Mon Sep 17 00:00:00 2001 From: AlexandreSato <66435071+AlexandreSato@users.noreply.github.com> Date: Fri, 20 Jan 2023 16:33:05 -0300 Subject: [PATCH 131/484] Multilang: improve pt-BR translations (#26962) Co-authored-by: Shane Smiskol --- selfdrive/ui/translations/main_pt-BR.ts | 28 ++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index 2bbc1abace..92bb552d3d 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -132,19 +132,19 @@ Driver Camera - Câmera voltada para o Motorista + Câmera do Motorista PREVIEW - PREVISUAL + VER Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off) - Pré-visualizar a câmera voltada para o motorista para garantir que monitor tem uma boa visibilidade (veículo precisa estar desligado) + Pré-visualizar a câmera voltada para o motorista para garantir que o monitoramento do sistema tenha uma boa visibilidade (veículo precisa estar desligado) Reset Calibration - Resetar Calibragem + Reinicializar Calibragem RESET @@ -200,7 +200,7 @@ openpilot requires the device to be mounted within 4° left or right and within 5° up or 8° down. openpilot is continuously calibrating, resetting is rarely required. - o openpilot requer que o dispositivo seja montado dentro de 4° esquerda ou direita e dentro de 5° para cima ou 8° para baixo. o openpilot está continuamente calibrando, resetar raramente é necessário. + O openpilot requer que o dispositivo seja montado dentro de 4° esquerda ou direita e dentro de 5° para cima ou 8° para baixo. O openpilot está continuamente calibrando, resetar raramente é necessário. Your device is pointed %1° %2 and %3° %4. @@ -389,7 +389,7 @@ Get turn-by-turn directions displayed and more with a comma prime subscription. Sign up now: https://connect.comma.ai Obtenha instruções passo a passo exibidas e muito mais com -uma assinatura prime Inscreva-se agora: https://connect.comma.ai +uma assinatura prime. Inscreva-se agora: https://connect.comma.ai No home @@ -502,7 +502,7 @@ trabalho definido Become a comma prime member at connect.comma.ai - Torne-se um membro comma prime em connect.comma.ai + Seja um membro comma prime em connect.comma.ai PRIME FEATURES: @@ -514,11 +514,11 @@ trabalho definido 1 year of storage - 1 ano de armazenamento + 1 ano na nuvem Developer perks - Benefícios para desenvolvedor + Benefícios para devs @@ -835,7 +835,7 @@ trabalho definido Current Version - Versao Atual + Versão Atual Download @@ -863,11 +863,11 @@ trabalho definido UNINSTALL - DESINSTAL + REMOVER Uninstall %1 - Desintalar o %1 + Desinstalar o %1 Are you sure you want to uninstall? @@ -987,7 +987,7 @@ trabalho definido Disengage on Accelerator Pedal - Desacionar Com Pedal Do Acelerador + Desacionar com Pedal do Acelerador When enabled, pressing the accelerator pedal will disengage openpilot. @@ -995,7 +995,7 @@ trabalho definido Show ETA in 24h Format - Mostrar ETA em formato 24h + Mostrar ETA em Formato 24h Use 24h format instead of am/pm From 3efb22e82bb77601fc86db8ba02eafd9cc7ead51 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 20 Jan 2023 11:46:02 -0800 Subject: [PATCH 132/484] release pre-commit: test translations (#27017) we should be able to run on the stripped dir now --- .github/workflows/selfdrive_tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index 7cc45d23b9..a4f7c68573 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -61,7 +61,7 @@ jobs: cp .pylintrc $STRIPPED_DIR cp mypy.ini $STRIPPED_DIR cd $STRIPPED_DIR - ${{ env.RUN }} "SKIP=test_translations pre-commit run --all" + ${{ env.RUN }} "pre-commit run --all" build_all: name: build all From f0d22f88e183c381897a3d48c34bfac63feb63a3 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 21 Jan 2023 03:47:29 +0800 Subject: [PATCH 133/484] cabana: add a slider to adjust chart range from 1 sec to max cached seconds. (#27005) * add slider to set chart range * set minumu to 1 sec * make chart work in any range * cleanup * rename to max_cached_minitues * more * dont draw out of plotarea * cleanup * updateLayout after chart removed * cleanup * fix clamp * usea same cur_sec to update all charts --- tools/cabana/chartswidget.cc | 187 ++++++++++++--------------- tools/cabana/chartswidget.h | 17 +-- tools/cabana/settings.cc | 29 ++--- tools/cabana/settings.h | 7 +- tools/cabana/streams/livestream.h | 2 +- tools/cabana/streams/replaystream.cc | 4 +- 6 files changed, 110 insertions(+), 136 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 0a855b851b..edca0e4d6a 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -2,10 +2,11 @@ #include #include +#include #include -#include #include #include +#include #include #include #include @@ -24,10 +25,9 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { toolbar->setIconSize({16, 16}); toolbar->addWidget(title_label = new QLabel()); - title_label->setContentsMargins(0 ,0, 12, 0); + title_label->setContentsMargins(0, 0, 12, 0); columns_cb = new QComboBox(this); columns_cb->addItems({"1", "2", "3", "4"}); - columns_cb->setCurrentIndex(std::clamp(settings.chart_column_count - 1, 0, 3)); toolbar->addWidget(new QLabel(tr("Columns:"))); toolbar->addWidget(columns_cb); @@ -35,8 +35,16 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { stretch_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); toolbar->addWidget(stretch_label); - show_all_values_btn = toolbar->addAction(""); - toolbar->addWidget(range_label = new QLabel()); + toolbar->addWidget(new QLabel(tr("Range:"))); + toolbar->addWidget(range_lb = new QLabel(this)); + range_slider = new QSlider(Qt::Horizontal, this); + range_slider->setToolTip(tr("Set the chart range")); + range_slider->setRange(1, settings.max_cached_minutes * 60); + range_slider->setSingleStep(1); + range_slider->setPageStep(60); // 1 min + toolbar->addWidget(range_slider); + + toolbar->addWidget(zoom_range_lb = new QLabel()); reset_zoom_btn = toolbar->addAction(bootstrapPixmap("arrow-counterclockwise"), ""); reset_zoom_btn->setToolTip(tr("Reset zoom (drag on chart to zoom X-Axis)")); remove_all_btn = toolbar->addAction(bootstrapPixmap("x"), ""); @@ -48,8 +56,7 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { QWidget *charts_container = new QWidget(this); QVBoxLayout *charts_main_layout = new QVBoxLayout(charts_container); charts_main_layout->setContentsMargins(0, 0, 0, 0); - charts_layout = new QGridLayout(charts_container); - charts_main_layout->addLayout(charts_layout); + charts_main_layout->addLayout(charts_layout = new QGridLayout); charts_main_layout->addStretch(0); QScrollArea *charts_scroll = new QScrollArea(this); @@ -58,20 +65,24 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { charts_scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); main_layout->addWidget(charts_scroll); - max_chart_range = settings.max_chart_x_range; - use_dark_theme = QApplication::style()->standardPalette().color(QPalette::WindowText).value() > - QApplication::style()->standardPalette().color(QPalette::Background).value(); - updateToolBar(); - align_charts_timer = new QTimer(this); align_charts_timer->setSingleShot(true); align_charts_timer->callOnTimeout(this, &ChartsWidget::alignCharts); - column_count = settings.chart_column_count; + + // init settings + use_dark_theme = QApplication::style()->standardPalette().color(QPalette::WindowText).value() > + QApplication::style()->standardPalette().color(QPalette::Background).value(); + column_count = std::clamp(settings.chart_column_count, 1, columns_cb->count()); + max_chart_range = std::clamp(settings.chart_range, 1, settings.max_cached_minutes * 60); + display_range = {0, max_chart_range}; + columns_cb->setCurrentIndex(column_count); + range_slider->setValue(max_chart_range); + updateToolBar(); QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &ChartsWidget::removeAll); - QObject::connect(can, &AbstractStream::eventsMerged, this, &ChartsWidget::updateState); + QObject::connect(can, &AbstractStream::eventsMerged, this, &ChartsWidget::eventsMerged); QObject::connect(can, &AbstractStream::updated, this, &ChartsWidget::updateState); - QObject::connect(show_all_values_btn, &QAction::triggered, this, &ChartsWidget::showAllData); + QObject::connect(range_slider, &QSlider::valueChanged, this, &ChartsWidget::setMaxChartRange); QObject::connect(remove_all_btn, &QAction::triggered, this, &ChartsWidget::removeAll); QObject::connect(reset_zoom_btn, &QAction::triggered, this, &ChartsWidget::zoomReset); QObject::connect(columns_cb, SIGNAL(activated(int)), SLOT(setColumnCount(int))); @@ -83,34 +94,16 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { }); } -void ChartsWidget::updateDisplayRange() { - auto events = can->events(); - double min_event_sec = (events->front()->mono_time / (double)1e9) - can->routeStartTime(); - double max_event_sec = (events->back()->mono_time / (double)1e9) - can->routeStartTime(); - const double cur_sec = can->currentSec(); - if (!can->liveStreaming()) { - auto prev_range = display_range; - if (cur_sec < display_range.first || cur_sec >= (display_range.second - 5)) { - // reached the end, or seeked to a timestamp out of range. - display_range.first = cur_sec - 5; - } - display_range.first = std::floor(std::max(min_event_sec, display_range.first)); - display_range.second = std::floor(std::min(display_range.first + max_chart_range, max_event_sec)); - if (prev_range != display_range) { - QFutureSynchronizer future_synchronizer; - for (auto c : charts) { - future_synchronizer.addFuture(QtConcurrent::run(c, &ChartView::setEventsRange, display_range)); - } - } - } else { - if (cur_sec >= (display_range.second - 5)) { - display_range.first = std::floor(std::max(min_event_sec, cur_sec - settings.max_chart_x_range / 2.0)); - } - display_range.second = std::floor(display_range.first + settings.max_chart_x_range); +void ChartsWidget::eventsMerged() { + { + assert(!can->liveStreaming()); + QFutureSynchronizer future_synchronizer; + const auto events = can->events(); for (auto c : charts) { - c->updateSeries(nullptr, events, false); + future_synchronizer.addFuture(QtConcurrent::run(c, &ChartView::updateSeries, nullptr, events, true)); } } + updateState(); } void ChartsWidget::zoomIn(double min, double max) { @@ -128,49 +121,61 @@ void ChartsWidget::zoomReset() { void ChartsWidget::updateState() { if (charts.isEmpty()) return; + if (can->liveStreaming()) { + // appends incoming events to the end of series + const auto events = can->events(); + for (auto c : charts) { + c->updateSeries(nullptr, events, false); + } + } + + const double cur_sec = can->currentSec(); if (!is_zoomed) { - updateDisplayRange(); - } else if (can->currentSec() < zoomed_range.first || can->currentSec() >= zoomed_range.second) { + double pos = (cur_sec - display_range.first) / max_chart_range; + if (pos > 0.8) { + const double min_event_sec = (can->events()->front()->mono_time / (double)1e9) - can->routeStartTime(); + display_range.first = std::floor(std::max(min_event_sec, cur_sec - max_chart_range * 0.2)); + } + display_range.second = std::floor(display_range.first + max_chart_range); + } else if (cur_sec < zoomed_range.first || cur_sec >= zoomed_range.second) { + // loop in zoommed range can->seekTo(zoomed_range.first); } - const auto &range = is_zoomed ? zoomed_range : display_range; setUpdatesEnabled(false); + const auto &range = is_zoomed ? zoomed_range : display_range; for (auto c : charts) { - c->setDisplayRange(range.first, range.second); - c->scene()->invalidate({}, QGraphicsScene::ForegroundLayer); + c->updatePlot(cur_sec, range.first, range.second); } setUpdatesEnabled(true); } -void ChartsWidget::showAllData() { - bool switch_to_show_all = max_chart_range == settings.max_chart_x_range; - max_chart_range = switch_to_show_all ? settings.cached_segment_limit * 60 - : settings.max_chart_x_range; - max_chart_range = std::min(max_chart_range, (uint32_t)can->totalSeconds()); +void ChartsWidget::setMaxChartRange(int value) { + max_chart_range = settings.chart_range = value; + double current_sec = can->currentSec(); + const double min_event_sec = (can->events()->front()->mono_time / (double)1e9) - can->routeStartTime(); + // keep current_sec's pos + double pos = (current_sec - display_range.first) / (display_range.second - display_range.first); + display_range.first = std::floor(std::max(min_event_sec, current_sec - max_chart_range * (1.0 - pos))); + display_range.second = std::floor(display_range.first + max_chart_range); updateToolBar(); updateState(); } void ChartsWidget::updateToolBar() { - int min_range = std::min(settings.max_chart_x_range, (int)can->totalSeconds()); - bool displaying_all = max_chart_range != min_range; - show_all_values_btn->setText(tr("%1 minutes").arg(max_chart_range / 60)); - show_all_values_btn->setToolTip(tr("Click to display %1 data").arg(displaying_all ? tr("%1 minutes").arg(min_range / 60) : tr("ALL cached"))); - show_all_values_btn->setVisible(!is_zoomed && !can->liveStreaming()); - remove_all_btn->setEnabled(!charts.isEmpty()); - reset_zoom_btn->setEnabled(is_zoomed); - range_label->setText(is_zoomed ? tr("%1 - %2").arg(zoomed_range.first, 0, 'f', 2).arg(zoomed_range.second, 0, 'f', 2) : ""); - title_label->setText(charts.size() > 0 ? tr("Charts (%1)").arg(charts.size()) : tr("Charts")); + range_lb->setText(QString(" %1:%2 ").arg(max_chart_range / 60, 2, 10, QLatin1Char('0')).arg(max_chart_range % 60, 2, 10, QLatin1Char('0'))); + zoom_range_lb->setText(is_zoomed ? tr("Zooming: %1 - %2").arg(zoomed_range.first, 0, 'f', 2).arg(zoomed_range.second, 0, 'f', 2) : ""); + title_label->setText(tr("Charts: %1").arg(charts.size())); dock_btn->setIcon(bootstrapPixmap(docking ? "arrow-up-right" : "arrow-down-left")); dock_btn->setToolTip(docking ? tr("Undock charts") : tr("Dock charts")); + remove_all_btn->setEnabled(!charts.isEmpty()); + reset_zoom_btn->setEnabled(is_zoomed); } void ChartsWidget::settingChanged() { + range_slider->setRange(1, settings.max_cached_minutes * 60); for (auto c : charts) { c->setFixedHeight(settings.chart_height); - columns_cb->setCurrentIndex(std::clamp(settings.chart_column_count - 1, 0, columns_cb->count() - 1)); - setColumnCount(settings.chart_column_count); } } @@ -189,11 +194,8 @@ void ChartsWidget::showChart(const QString &id, const Signal *sig, bool show, bo chart = new ChartView(this); chart->setFixedHeight(settings.chart_height); chart->setMinimumWidth(CHART_MIN_WIDTH); - chart->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); + chart->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); chart->chart()->setTheme(use_dark_theme ? QChart::QChart::ChartThemeDark : QChart::ChartThemeLight); - chart->setEventsRange(display_range); - auto range = is_zoomed ? zoomed_range : display_range; - chart->setDisplayRange(range.first, range.second); QObject::connect(chart, &ChartView::remove, [=]() { removeChart(chart); }); QObject::connect(chart, &ChartView::zoomIn, this, &ChartsWidget::zoomIn); QObject::connect(chart, &ChartView::zoomReset, this, &ChartsWidget::zoomReset); @@ -204,6 +206,7 @@ void ChartsWidget::showChart(const QString &id, const Signal *sig, bool show, bo updateLayout(); } chart->addSeries(id, sig); + updateState(); } else if (!show && chart) { chart->removeSeries(id, sig); } @@ -214,7 +217,7 @@ void ChartsWidget::showChart(const QString &id, const Signal *sig, bool show, bo void ChartsWidget::setColumnCount(int n) { n = std::clamp(n + 1, 1, columns_cb->count()); if (column_count != n) { - column_count = n; + column_count = settings.chart_column_count = n; updateLayout(); } } @@ -239,6 +242,7 @@ void ChartsWidget::removeChart(ChartView *chart) { chart->deleteLater(); updateToolBar(); alignCharts(); + updateLayout(); emit seriesChanged(); } @@ -440,36 +444,28 @@ void ChartView::updateTitle() { } } -void ChartView::setEventsRange(const std::pair &range) { - if (range != events_range) { - events_range = range; - updateSeries(); - } -} - -void ChartView::setDisplayRange(double min, double max) { +void ChartView::updatePlot(double cur, double min, double max) { + cur_sec = cur; if (min != axis_x->min() || max != axis_x->max()) { axis_x->setRange(min, max); updateAxisY(); } + scene()->invalidate({}, QGraphicsScene::ForegroundLayer); } -void ChartView::updateSeries(const Signal *sig, const std::vector *events, bool clear) { - if (!events) events = can->events(); - if (!events || sigs.isEmpty()) return; - +void ChartView::updateSeries(const Signal *sig, const std::vector *events, bool clear) { + events = events ? events : can->events(); for (auto &s : sigs) { if (!sig || s.sig == sig) { if (clear) { s.vals.clear(); + s.vals.reserve(settings.max_cached_minutes * 60 * 100); // [n]seconds * 100hz s.last_value_mono_time = 0; } double route_start_time = can->routeStartTime(); - uint64_t begin_ts = can->liveStreaming() ? s.last_value_mono_time : (route_start_time + events_range.first) * 1e9; - Event begin_event(cereal::Event::Which::INIT_DATA, begin_ts); + Event begin_event(cereal::Event::Which::INIT_DATA, s.last_value_mono_time); auto begin = std::upper_bound(events->begin(), events->end(), &begin_event, Event::lessThan()); - uint64_t end_ns = can->liveStreaming() ? events->back()->mono_time : (route_start_time + events_range.second) * 1e9; - for (auto it = begin; it != events->end() && (*it)->mono_time <= end_ns; ++it) { + for (auto it = begin; it != events->end(); ++it) { if ((*it)->which == cereal::Event::Which::CAN) { for (const auto &c : (*it)->event.getCan()) { if (s.source == c.getSrc() && s.address == c.getAddress()) { @@ -481,13 +477,8 @@ void ChartView::updateSeries(const Signal *sig, const std::vector *event } } } - if (!s.vals.isEmpty()) { - auto [min_v, max_v] = std::minmax_element(s.vals.begin(), s.vals.end(), [](auto &l, auto &r) { return l.y() < r.y(); }); - s.min_y = min_v->y(); - s.max_y = max_v->y(); + if (events->size()) { s.last_value_mono_time = events->back()->mono_time; - } else { - s.min_y = s.max_y = 0; } s.series->replace(s.vals); updateAxisY(); @@ -501,18 +492,11 @@ void ChartView::updateAxisY() { double min_y = std::numeric_limits::max(); double max_y = std::numeric_limits::lowest(); - if (can->liveStreaming() || events_range == std::pair{axis_x->min(), axis_x->max()}) { - for (auto &s : sigs) { - if (s.min_y < min_y) min_y = s.min_y; - if (s.max_y > max_y) max_y = s.max_y; - } - } else { - for (auto &s : sigs) { - auto begin = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->min(), [](auto &p, double x) { return p.x() < x; }); - for (auto it = begin; it != s.vals.end() && it->x() <= axis_x->max(); ++it) { - if (it->y() < min_y) min_y = it->y(); - if (it->y() > max_y) max_y = it->y(); - } + for (auto &s : sigs) { + auto begin = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->min(), [](auto &p, double x) { return p.x() < x; }); + for (auto it = begin; it != s.vals.end() && it->x() <= axis_x->max(); ++it) { + if (it->y() < min_y) min_y = it->y(); + if (it->y() > max_y) max_y = it->y(); } } @@ -664,7 +648,8 @@ void ChartView::dropEvent(QDropEvent *event) { } void ChartView::drawForeground(QPainter *painter, const QRectF &rect) { - qreal x = chart()->mapToPosition(QPointF{can->currentSec(), 0}).x(); + qreal x = chart()->mapToPosition(QPointF{cur_sec, 0}).x(); + x = std::clamp(x, chart()->plotArea().left(), chart()->plotArea().right()); qreal y1 = chart()->plotArea().top() - 2; qreal y2 = chart()->plotArea().bottom() + 2; painter->setPen(QPen(chart()->titleBrush().color(), 2)); diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index 9ba5ce5a30..94218918df 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -1,12 +1,12 @@ #pragma once #include -#include #include #include #include #include #include +#include #include #include #include @@ -26,8 +26,7 @@ public: void removeSeries(const QString &msg_id, const Signal *sig); bool hasSeries(const QString &msg_id, const Signal *sig) const; void updateSeries(const Signal *sig = nullptr, const std::vector *events = nullptr, bool clear = true); - void setEventsRange(const std::pair &range); - void setDisplayRange(double min, double max); + void updatePlot(double cur, double min, double max); void setPlotAreaLeftPosition(int pos); qreal getYAsixLabelWidth() const; @@ -37,8 +36,6 @@ public: uint32_t address = 0; const Signal *sig = nullptr; QLineSeries *series = nullptr; - double min_y = 0; - double max_y = 0; QVector vals; uint64_t last_value_mono_time = 0; }; @@ -79,8 +76,8 @@ private: QPointF track_pt; QGraphicsProxyWidget *close_btn_proxy; QGraphicsProxyWidget *manage_btn_proxy; - std::pair events_range = {0, 0}; QList sigs; + double cur_sec = 0; const QString mime_type = "application/x-cabanachartview"; }; @@ -106,21 +103,21 @@ private: void removeChart(ChartView *chart); void eventsMerged(); void updateState(); - void updateDisplayRange(); void zoomIn(double min, double max); void zoomReset(); void updateToolBar(); void removeAll(); - void showAllData(); + void setMaxChartRange(int value); void updateLayout(); void settingChanged(); bool eventFilter(QObject *obj, QEvent *event) override; ChartView *findChart(const QString &id, const Signal *sig); QLabel *title_label; - QLabel *range_label; + QLabel *zoom_range_lb; + QLabel *range_lb; + QSlider *range_slider; bool docking = true; - QAction *show_all_values_btn; QAction *dock_btn; QAction *reset_zoom_btn; QAction *remove_all_btn; diff --git a/tools/cabana/settings.cc b/tools/cabana/settings.cc index 7a25449c81..8c26b265e0 100644 --- a/tools/cabana/settings.cc +++ b/tools/cabana/settings.cc @@ -15,9 +15,9 @@ Settings::Settings() { void Settings::save() { QSettings s("settings", QSettings::IniFormat); s.setValue("fps", fps); - s.setValue("cached_segment", cached_segment_limit); + s.setValue("max_cached_minutes", max_cached_minutes); s.setValue("chart_height", chart_height); - s.setValue("max_chart_x_range", max_chart_x_range); + s.setValue("chart_range", chart_range); s.setValue("chart_column_count", chart_column_count); s.setValue("last_dir", last_dir); s.setValue("window_state", window_state); @@ -28,9 +28,9 @@ void Settings::save() { void Settings::load() { QSettings s("settings", QSettings::IniFormat); fps = s.value("fps", 10).toInt(); - cached_segment_limit = s.value("cached_segment", 5).toInt(); + max_cached_minutes = s.value("cached_minutes", 5).toInt(); chart_height = s.value("chart_height", 200).toInt(); - max_chart_x_range = s.value("max_chart_x_range", 3 * 60).toInt(); + chart_range = s.value("chart_range", 3 * 60).toInt(); chart_column_count = s.value("chart_column_count", 1).toInt(); last_dir = s.value("last_dir", QDir::homePath()).toString(); window_state = s.value("window_state").toByteArray(); @@ -50,23 +50,17 @@ SettingsDlg::SettingsDlg(QWidget *parent) : QDialog(parent) { fps->setValue(settings.fps); form_layout->addRow("FPS", fps); - cached_segment = new QSpinBox(this); - cached_segment->setRange(5, 60); - cached_segment->setSingleStep(1); - cached_segment->setValue(settings.cached_segment_limit); - form_layout->addRow(tr("Cached segments limit"), cached_segment); - - max_chart_x_range = new QSpinBox(this); - max_chart_x_range->setRange(3, 60); - max_chart_x_range->setSingleStep(1); - max_chart_x_range->setValue(settings.max_chart_x_range / 60); - form_layout->addRow(tr("Chart range (minutes)"), max_chart_x_range); + cached_minutes = new QSpinBox(this); + cached_minutes->setRange(5, 60); + cached_minutes->setSingleStep(1); + cached_minutes->setValue(settings.max_cached_minutes); + form_layout->addRow(tr("Max Cached Minutes"), cached_minutes); chart_height = new QSpinBox(this); chart_height->setRange(100, 500); chart_height->setSingleStep(10); chart_height->setValue(settings.chart_height); - form_layout->addRow(tr("Chart height"), chart_height); + form_layout->addRow(tr("Chart Height"), chart_height); auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); form_layout->addRow(buttonBox); @@ -78,9 +72,8 @@ SettingsDlg::SettingsDlg(QWidget *parent) : QDialog(parent) { void SettingsDlg::save() { settings.fps = fps->value(); - settings.cached_segment_limit = cached_segment->value(); + settings.max_cached_minutes = cached_minutes->value(); settings.chart_height = chart_height->value(); - settings.max_chart_x_range = max_chart_x_range->value() * 60; settings.save(); accept(); emit settings.changed(); diff --git a/tools/cabana/settings.h b/tools/cabana/settings.h index ed0ba3a549..86ebd73113 100644 --- a/tools/cabana/settings.h +++ b/tools/cabana/settings.h @@ -14,10 +14,10 @@ public: void load(); int fps = 10; - int cached_segment_limit = 5; + int max_cached_minutes = 5; int chart_height = 200; int chart_column_count = 1; - int max_chart_x_range = 3 * 60; // 3 minutes + int chart_range = 3 * 60; // e minutes QString last_dir; QByteArray geometry; QByteArray video_splitter_state; @@ -34,9 +34,8 @@ public: SettingsDlg(QWidget *parent); void save(); QSpinBox *fps; - QSpinBox *cached_segment; + QSpinBox *cached_minutes; QSpinBox *chart_height; - QSpinBox *max_chart_x_range; }; extern Settings settings; diff --git a/tools/cabana/streams/livestream.h b/tools/cabana/streams/livestream.h index 17301d48e7..95c6e3811c 100644 --- a/tools/cabana/streams/livestream.h +++ b/tools/cabana/streams/livestream.h @@ -17,7 +17,7 @@ public: protected: void streamThread(); - void updateCachedNS() { cache_ns = (settings.cached_segment_limit * 60) * 1e9; } + void updateCachedNS() { cache_ns = (settings.max_cached_minutes * 60) * 1e9; } mutable std::mutex lock; mutable std::vector events_vector; diff --git a/tools/cabana/streams/replaystream.cc b/tools/cabana/streams/replaystream.cc index 97a3911adf..fd58f7a409 100644 --- a/tools/cabana/streams/replaystream.cc +++ b/tools/cabana/streams/replaystream.cc @@ -4,7 +4,7 @@ ReplayStream::ReplayStream(QObject *parent) : AbstractStream(parent, false) { QObject::connect(&settings, &Settings::changed, [this]() { - if (replay) replay->setSegmentCacheLimit(settings.cached_segment_limit); + if (replay) replay->setSegmentCacheLimit(settings.max_cached_minutes); }); } @@ -18,7 +18,7 @@ static bool event_filter(const Event *e, void *opaque) { bool ReplayStream::loadRoute(const QString &route, const QString &data_dir, uint32_t replay_flags) { replay = new Replay(route, {"can", "roadEncodeIdx", "wideRoadEncodeIdx", "carParams"}, {}, nullptr, replay_flags, data_dir, this); - replay->setSegmentCacheLimit(settings.cached_segment_limit); + replay->setSegmentCacheLimit(settings.max_cached_minutes); replay->installEventFilter(event_filter, this); QObject::connect(replay, &Replay::seekedTo, this, &AbstractStream::seekedTo); QObject::connect(replay, &Replay::segmentsMerged, this, &AbstractStream::eventsMerged); From c88853afaeab9c9ccf7f7c693c69d9d3e77ae6ac Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 21 Jan 2023 04:45:47 +0800 Subject: [PATCH 134/484] cabana: fix wrong column count displayed in combobox (#27020) fix wrong column count displayed in combobox --- tools/cabana/chartswidget.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index edca0e4d6a..d80ab7a2a0 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -75,7 +75,7 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { column_count = std::clamp(settings.chart_column_count, 1, columns_cb->count()); max_chart_range = std::clamp(settings.chart_range, 1, settings.max_cached_minutes * 60); display_range = {0, max_chart_range}; - columns_cb->setCurrentIndex(column_count); + columns_cb->setCurrentIndex(column_count - 1); range_slider->setValue(max_chart_range); updateToolBar(); From b8ec32103a4fab1ede5fc431eabc23118da82856 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 20 Jan 2023 13:03:02 -0800 Subject: [PATCH 135/484] boardd: include SPI panda in list (#27018) * boardd: include SPI panda in list * hexlify * fix hexlify * cleanup * little more Co-authored-by: Comma Device --- selfdrive/boardd/panda.cc | 23 ++++++++++++++++++++++- selfdrive/boardd/panda_comms.h | 5 ++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/selfdrive/boardd/panda.cc b/selfdrive/boardd/panda.cc index d4d53aa230..b12b26fb62 100644 --- a/selfdrive/boardd/panda.cc +++ b/selfdrive/boardd/panda.cc @@ -3,7 +3,9 @@ #include #include +#include #include +#include #include "cereal/messaging/messaging.h" #include "common/swaglog.h" @@ -40,7 +42,26 @@ bool Panda::comms_healthy() { } std::vector Panda::list() { - return PandaUsbHandle::list(); + std::vector serials = PandaUsbHandle::list(); + + // check SPI + const int uid_len = 12; + uint8_t uid[uid_len] = {0}; + PandaSpiHandle spi_handle("/dev/spidev0.0"); + int ret = spi_handle.control_read(0xc3, 0, 0, uid, uid_len); + if (ret == uid_len) { + std::stringstream stream; + for (int i = 0; i < uid_len; i++) { + stream << std::hex << std::setw(2) << std::setfill('0') << int(uid[i]); + } + + // might be on USB too + if (std::find(serials.begin(), serials.end(), stream.str()) == serials.end()) { + serials.push_back(stream.str()); + } + } + + return serials; } void Panda::set_safety_model(cereal::CarParams::SafetyModel safety_model, uint16_t safety_param) { diff --git a/selfdrive/boardd/panda_comms.h b/selfdrive/boardd/panda_comms.h index bd262dfa0e..9d1f69e671 100644 --- a/selfdrive/boardd/panda_comms.h +++ b/selfdrive/boardd/panda_comms.h @@ -30,9 +30,6 @@ public: virtual int control_read(uint8_t request, uint16_t param1, uint16_t param2, unsigned char *data, uint16_t length, unsigned int timeout=TIMEOUT) = 0; virtual int bulk_write(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout=TIMEOUT) = 0; virtual int bulk_read(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout=TIMEOUT) = 0; - -protected: - std::recursive_mutex hw_lock; }; class PandaUsbHandle : public PandaCommsHandle { @@ -50,6 +47,7 @@ public: private: libusb_context *ctx = NULL; libusb_device_handle *dev_handle = NULL; + std::recursive_mutex hw_lock; void handle_usb_issue(int err, const char func[]); }; @@ -69,6 +67,7 @@ private: int spi_fd = -1; uint8_t tx_buf[SPI_BUF_SIZE]; uint8_t rx_buf[SPI_BUF_SIZE]; + inline static std::recursive_mutex hw_lock; int wait_for_ack(spi_ioc_transfer &transfer, uint8_t ack); int bulk_transfer(uint8_t endpoint, uint8_t *tx_data, uint16_t tx_len, uint8_t *rx_data, uint16_t rx_len); From 75fd2e119946d2e5a850f924b5f33945435cad3f Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 20 Jan 2023 13:11:51 -0800 Subject: [PATCH 136/484] skip lupdate on device --- selfdrive/ui/SConscript | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 0f28f5ccc3..2fb7227f63 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -74,12 +74,13 @@ translation_sources = [f"#selfdrive/ui/translations/{l}.ts" for l in languages.v translation_targets = [src.replace(".ts", ".qm") for src in translation_sources] lrelease_bin = 'third_party/qt5/larch64/bin/lrelease' if arch == 'larch64' else 'lrelease' -lupdate = qt_env.Command(translation_sources, qt_src, "selfdrive/ui/update_translations.py") lrelease = qt_env.Command(translation_targets, translation_sources, f"{lrelease_bin} $SOURCES") -qt_env.Depends(lrelease, lupdate) -qt_env.NoClean(translation_sources) -qt_env.Precious(translation_sources) -qt_env.NoCache(lupdate) +if arch != 'larch64': + lupdate = qt_env.Command(translation_sources, qt_src, "selfdrive/ui/update_translations.py") + qt_env.Depends(lrelease, lupdate) + qt_env.NoClean(translation_sources) + qt_env.Precious(translation_sources) + qt_env.NoCache(lupdate) # setup and factory resetter if GetOption('extras'): From e883976a32a4190123d891f51cc567dfd77e6c85 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 20 Jan 2023 14:11:43 -0800 Subject: [PATCH 137/484] set path for extra qt bins on device (#27022) * set path for extra qt bins on device * no print Co-authored-by: Comma Device --- SConstruct | 1 + selfdrive/ui/SConscript | 11 +++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/SConstruct b/SConstruct index 8864164f15..31aa6ecced 100644 --- a/SConstruct +++ b/SConstruct @@ -311,6 +311,7 @@ else: qt_libs = [f"Qt5{m}" for m in qt_modules] if arch == "larch64": qt_libs += ["GLESv2", "wayland-client"] + qt_env.PrependENVPath('PATH', Dir("#third_party/qt5/larch64/bin/").abspath) elif arch != "Darwin": qt_libs += ["GL"] diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 2fb7227f63..0f28f5ccc3 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -74,13 +74,12 @@ translation_sources = [f"#selfdrive/ui/translations/{l}.ts" for l in languages.v translation_targets = [src.replace(".ts", ".qm") for src in translation_sources] lrelease_bin = 'third_party/qt5/larch64/bin/lrelease' if arch == 'larch64' else 'lrelease' +lupdate = qt_env.Command(translation_sources, qt_src, "selfdrive/ui/update_translations.py") lrelease = qt_env.Command(translation_targets, translation_sources, f"{lrelease_bin} $SOURCES") -if arch != 'larch64': - lupdate = qt_env.Command(translation_sources, qt_src, "selfdrive/ui/update_translations.py") - qt_env.Depends(lrelease, lupdate) - qt_env.NoClean(translation_sources) - qt_env.Precious(translation_sources) - qt_env.NoCache(lupdate) +qt_env.Depends(lrelease, lupdate) +qt_env.NoClean(translation_sources) +qt_env.Precious(translation_sources) +qt_env.NoCache(lupdate) # setup and factory resetter if GetOption('extras'): From e507830a1c21490701d53093777bd2b5727202ca Mon Sep 17 00:00:00 2001 From: AlexandreSato <66435071+AlexandreSato@users.noreply.github.com> Date: Fri, 20 Jan 2023 20:28:48 -0300 Subject: [PATCH 138/484] Multilang: new pt-BR translations (#27021) * improve pt-BR translations * update pt-BR translation Co-authored-by: Shane Smiskol --- selfdrive/ui/translations/main_pt-BR.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index 92bb552d3d..445de72fc3 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -1047,11 +1047,11 @@ trabalho definido openpilot longitudinal control may come in a future update. - + O controle longitudinal openpilot pode vir em uma atualização futura. An experimental version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - + Uma versão experimental do controle longitudinal openpilot pode ser testada, juntamente com o modo Experimental, em branches de desenvolvimento. Enable experimental longitudinal control to allow Experimental mode. From fa6e6a4eb52010466efb5a65d3b9ee7b59e86a58 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 21 Jan 2023 07:43:42 +0800 Subject: [PATCH 139/484] cabana: add no-vipc flag (#26999) --- tools/cabana/cabana.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/cabana/cabana.cc b/tools/cabana/cabana.cc index ce0cb443cb..fc6c37fb72 100644 --- a/tools/cabana/cabana.cc +++ b/tools/cabana/cabana.cc @@ -22,6 +22,7 @@ int main(int argc, char *argv[]) { cmd_parser.addOption({"stream", "read can messages from live streaming"}); cmd_parser.addOption({"zmq", "the ip address on which to receive zmq messages", "zmq"}); cmd_parser.addOption({"data_dir", "local directory with routes", "data_dir"}); + cmd_parser.addOption({"no-vipc", "do not output video"}); cmd_parser.process(app); const QStringList args = cmd_parser.positionalArguments(); if (args.empty() && !cmd_parser.isSet("demo") && !cmd_parser.isSet("stream")) { @@ -44,6 +45,8 @@ int main(int argc, char *argv[]) { replay_flags |= REPLAY_FLAG_ECAM; } else if (cmd_parser.isSet("qcam")) { replay_flags |= REPLAY_FLAG_QCAMERA; + } else if (cmd_parser.isSet("no-vipc")) { + replay_flags |= REPLAY_FLAG_NO_VIPC; } auto replay_stream = new ReplayStream(&app); stream.reset(replay_stream); From e710dc1b7ed5fb3c30263bd81e844a03e0889049 Mon Sep 17 00:00:00 2001 From: Comma Device Date: Fri, 20 Jan 2023 21:07:01 -0600 Subject: [PATCH 140/484] lower spi log level --- selfdrive/boardd/spi.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/boardd/spi.cc b/selfdrive/boardd/spi.cc index 8a6bf8be7f..2779494ae4 100644 --- a/selfdrive/boardd/spi.cc +++ b/selfdrive/boardd/spi.cc @@ -214,7 +214,7 @@ int PandaSpiHandle::wait_for_ack(spi_ioc_transfer &transfer, uint8_t ack) { // handle timeout if (millis_since_boot() - start_millis > SPI_ACK_TIMEOUT) { - LOGE("SPI: timed out waiting for ACK"); + LOGD("SPI: timed out waiting for ACK"); return -1; } } From 027c27cd690120d8ff8823f0b53cc2c9d6af7df3 Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Fri, 20 Jan 2023 23:19:01 -0700 Subject: [PATCH 141/484] Laikad: add test (#27028) * add laikad tropo test Co-authored-by: Kurt Nistelberger --- selfdrive/locationd/test/test_laikad.py | 34 +++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/selfdrive/locationd/test/test_laikad.py b/selfdrive/locationd/test/test_laikad.py index 6059eab68f..d89f521228 100755 --- a/selfdrive/locationd/test/test_laikad.py +++ b/selfdrive/locationd/test/test_laikad.py @@ -22,7 +22,24 @@ def get_log(segs=range(0)): logs = [] for i in segs: logs.extend(LogReader(get_url("4cf7a6ad03080c90|2021-09-29--13-46-36", i))) - return [m for m in logs if m.which() == 'ubloxGnss'] + + all_logs = [m for m in logs if m.which() == 'ubloxGnss'] + low_gnss = [] + for m in all_logs: + if m.ubloxGnss.which() != 'measurementReport': + continue + + MAX_MEAS = 7 + if m.ubloxGnss.measurementReport.numMeas > MAX_MEAS: + mb = m.as_builder() + mb.ubloxGnss.measurementReport.numMeas = MAX_MEAS + mb.ubloxGnss.measurementReport.measurements = list(m.ubloxGnss.measurementReport.measurements)[:MAX_MEAS] + mb.ubloxGnss.measurementReport.measurements[0].pseudorange += 1000 + low_gnss.append(mb.as_reader()) + else: + low_gnss.append(m) + + return all_logs, low_gnss def verify_messages(lr, laikad, return_one_success=False): @@ -59,8 +76,9 @@ class TestLaikad(unittest.TestCase): @classmethod def setUpClass(cls): - logs = get_log(range(1)) + logs, low_gnss = get_log(range(1)) cls.logs = logs + cls.low_gnss = low_gnss first_gps_time = get_first_gps_time(logs) cls.first_gps_time = first_gps_time @@ -254,6 +272,18 @@ class TestLaikad(unittest.TestCase): # Verify orbit data is not downloaded mock_method.assert_not_called() + def test_low_gnss_meas(self): + cnt = 0 + laikad = Laikad() + for m in self.low_gnss: + msg = laikad.process_gnss_msg(m.ubloxGnss, m.logMonoTime, block=True) + if msg is None: + continue + gm = msg.gnssMeasurements + if len(gm.correctedMeasurements) != 0 and gm.positionECEF.valid: + cnt += 1 + self.assertEqual(cnt, 554) + def dict_has_values(self, dct): self.assertGreater(len(dct), 0) self.assertGreater(min([len(v) for v in dct.values()]), 0) From 945d0c7696e79fd1889743f112d68060c0b67650 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sun, 22 Jan 2023 04:41:55 +0800 Subject: [PATCH 142/484] cabana: new chart on top (#27031) --- tools/cabana/chartswidget.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index d80ab7a2a0..a9b1d66fb9 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -224,11 +224,11 @@ void ChartsWidget::setColumnCount(int n) { void ChartsWidget::updateLayout() { int n = column_count; - for (; n >= 1; --n) { + for (; n > 1; --n) { if ((n * (CHART_MIN_WIDTH + charts_layout->spacing())) < rect().width()) break; } for (int i = 0; i < charts.size(); ++i) { - charts_layout->addWidget(charts[i], i / n, i % n); + charts_layout->addWidget(charts[charts.size() - i - 1], i / n, i % n); } } From 51cb45e7a88d1f0cf908292df2438c23df35d523 Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Sat, 21 Jan 2023 16:07:58 -0500 Subject: [PATCH 143/484] VW MQB: Add FW for 2015 Volkswagen Passat (#27032) --- selfdrive/car/volkswagen/values.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py index 6ae4969cc0..51161f2972 100755 --- a/selfdrive/car/volkswagen/values.py +++ b/selfdrive/car/volkswagen/values.py @@ -568,6 +568,7 @@ FW_VERSIONS = { (Ecu.engine, 0x7e0, None): [ b'\xf1\x8703N906026E \xf1\x892114', b'\xf1\x8704E906023AH\xf1\x893379', + b'\xf1\x8704L906026DP\xf1\x891538', b'\xf1\x8704L906026ET\xf1\x891990', b'\xf1\x8704L906026FP\xf1\x892012', b'\xf1\x8704L906026GA\xf1\x892013', @@ -586,6 +587,7 @@ FW_VERSIONS = { ], (Ecu.srs, 0x715, None): [ b'\xf1\x873Q0959655AE\xf1\x890195\xf1\x82\r56140056130012416612124111', + b'\xf1\x873Q0959655AF\xf1\x890195\xf1\x82\r56140056130012026612120211', b'\xf1\x873Q0959655AN\xf1\x890306\xf1\x82\r58160058140013036914110311', b'\xf1\x873Q0959655BA\xf1\x890195\xf1\x82\r56140056130012516612125111', b'\xf1\x873Q0959655BB\xf1\x890195\xf1\x82\r56140056130012026612120211', @@ -596,6 +598,7 @@ FW_VERSIONS = { (Ecu.eps, 0x712, None): [ b'\xf1\x873Q0909144J \xf1\x895063\xf1\x82\x0566B00611A1', b'\xf1\x873Q0909144J \xf1\x895063\xf1\x82\x0566B00711A1', + b'\xf1\x875Q0909143K \xf1\x892033\xf1\x820514B0060703', b'\xf1\x875Q0909143M \xf1\x892041\xf1\x820522B0060803', b'\xf1\x875Q0909143M \xf1\x892041\xf1\x820522B0080803', b'\xf1\x875Q0909144AB\xf1\x891082\xf1\x82\00521B00606A1', @@ -604,6 +607,7 @@ FW_VERSIONS = { b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567B0020600', ], (Ecu.fwdRadar, 0x757, None): [ + b'\xf1\x873Q0907572A \xf1\x890126', b'\xf1\x873Q0907572A \xf1\x890130', b'\xf1\x873Q0907572B \xf1\x890192', b'\xf1\x873Q0907572C \xf1\x890195', From 35a943030ba67b89a13bdf95257e0b349ffb0ef7 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sun, 22 Jan 2023 08:00:39 +0800 Subject: [PATCH 144/484] cabana: add new plot btn to create chart (#27023) --- tools/cabana/chartswidget.cc | 48 ++++++++++++++++++++++++------------ tools/cabana/chartswidget.h | 4 ++- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index a9b1d66fb9..ca52e44474 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -24,6 +24,8 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { QToolBar *toolbar = new QToolBar(tr("Charts"), this); toolbar->setIconSize({16, 16}); + QAction *new_plot_btn = toolbar->addAction(bootstrapPixmap("file-plus"), ""); + new_plot_btn->setToolTip(tr("New Plot")); toolbar->addWidget(title_label = new QLabel()); title_label->setContentsMargins(0, 0, 12, 0); columns_cb = new QComboBox(this); @@ -83,6 +85,7 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { QObject::connect(can, &AbstractStream::eventsMerged, this, &ChartsWidget::eventsMerged); QObject::connect(can, &AbstractStream::updated, this, &ChartsWidget::updateState); QObject::connect(range_slider, &QSlider::valueChanged, this, &ChartsWidget::setMaxChartRange); + QObject::connect(new_plot_btn, &QAction::triggered, this, &ChartsWidget::newChart); QObject::connect(remove_all_btn, &QAction::triggered, this, &ChartsWidget::removeAll); QObject::connect(reset_zoom_btn, &QAction::triggered, this, &ChartsWidget::zoomReset); QObject::connect(columns_cb, SIGNAL(activated(int)), SLOT(setColumnCount(int))); @@ -185,26 +188,28 @@ ChartView *ChartsWidget::findChart(const QString &id, const Signal *sig) { return nullptr; } +ChartView *ChartsWidget::createChart() { + auto chart = new ChartView(this); + chart->setFixedHeight(settings.chart_height); + chart->setMinimumWidth(CHART_MIN_WIDTH); + chart->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + chart->chart()->setTheme(use_dark_theme ? QChart::QChart::ChartThemeDark : QChart::ChartThemeLight); + QObject::connect(chart, &ChartView::remove, [=]() { removeChart(chart); }); + QObject::connect(chart, &ChartView::zoomIn, this, &ChartsWidget::zoomIn); + QObject::connect(chart, &ChartView::zoomReset, this, &ChartsWidget::zoomReset); + QObject::connect(chart, &ChartView::seriesRemoved, this, &ChartsWidget::seriesChanged); + QObject::connect(chart, &ChartView::seriesAdded, this, &ChartsWidget::seriesChanged); + QObject::connect(chart, &ChartView::axisYUpdated, [this]() { align_charts_timer->start(100); }); + charts.push_back(chart); + updateLayout(); + return chart; +} + void ChartsWidget::showChart(const QString &id, const Signal *sig, bool show, bool merge) { setUpdatesEnabled(false); ChartView *chart = findChart(id, sig); if (show && !chart) { - chart = merge && charts.size() > 0 ? charts.back() : nullptr; - if (!chart) { - chart = new ChartView(this); - chart->setFixedHeight(settings.chart_height); - chart->setMinimumWidth(CHART_MIN_WIDTH); - chart->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); - chart->chart()->setTheme(use_dark_theme ? QChart::QChart::ChartThemeDark : QChart::ChartThemeLight); - QObject::connect(chart, &ChartView::remove, [=]() { removeChart(chart); }); - QObject::connect(chart, &ChartView::zoomIn, this, &ChartsWidget::zoomIn); - QObject::connect(chart, &ChartView::zoomReset, this, &ChartsWidget::zoomReset); - QObject::connect(chart, &ChartView::seriesRemoved, this, &ChartsWidget::seriesChanged); - QObject::connect(chart, &ChartView::seriesAdded, this, &ChartsWidget::seriesChanged); - QObject::connect(chart, &ChartView::axisYUpdated, [this]() { align_charts_timer->start(100); }); - charts.push_back(chart); - updateLayout(); - } + chart = merge && charts.size() > 0 ? charts.back() : createChart(); chart->addSeries(id, sig); updateState(); } else if (!show && chart) { @@ -237,6 +242,17 @@ void ChartsWidget::resizeEvent(QResizeEvent *event) { updateLayout(); } +void ChartsWidget::newChart() { + SeriesSelector dlg(this); + if (dlg.exec() == QDialog::Accepted) { + QList series_list = dlg.series(); + if (!series_list.isEmpty()) { + auto c = createChart(); + c->addSeries(series_list); + } + } +} + void ChartsWidget::removeChart(ChartView *chart) { charts.removeOne(chart); chart->deleteLater(); diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index 94218918df..f134136323 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -23,6 +23,7 @@ class ChartView : public QChartView { public: ChartView(QWidget *parent = nullptr); void addSeries(const QString &msg_id, const Signal *sig); + void addSeries(const QList &series_list); void removeSeries(const QString &msg_id, const Signal *sig); bool hasSeries(const QString &msg_id, const Signal *sig) const; void updateSeries(const Signal *sig = nullptr, const std::vector *events = nullptr, bool clear = true); @@ -69,7 +70,6 @@ private: void drawForeground(QPainter *painter, const QRectF &rect) override; void applyNiceNumbers(qreal min, qreal max); qreal niceNumber(qreal x, bool ceiling); - void addSeries(const QList &series_list); QValueAxis *axis_x; QValueAxis *axis_y; @@ -100,6 +100,8 @@ signals: private: void resizeEvent(QResizeEvent *event) override; void alignCharts(); + void newChart(); + ChartView * createChart(); void removeChart(ChartView *chart); void eventsMerged(); void updateState(); From d7bcbfd54bd4a2ba34423222557181ff50d55134 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sat, 21 Jan 2023 17:28:56 -0800 Subject: [PATCH 145/484] rawgpsd: enable gps power (#27034) --- selfdrive/sensord/rawgps/rawgpsd.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/selfdrive/sensord/rawgps/rawgpsd.py b/selfdrive/sensord/rawgps/rawgpsd.py index 1c65051665..3fa5e927a2 100755 --- a/selfdrive/sensord/rawgps/rawgpsd.py +++ b/selfdrive/sensord/rawgps/rawgpsd.py @@ -11,7 +11,9 @@ from struct import unpack_from, calcsize, pack from cereal import log import cereal.messaging as messaging +from common.gpio import gpio_init, gpio_set from laika.gps_time import GPSTime +from system.hardware.tici.pins import GPIO from system.swaglog import cloudlog from selfdrive.sensord.rawgps.modemdiag import ModemDiag, DIAG_LOG_F, setup_logs, send_recv from selfdrive.sensord.rawgps.structs import (dict_unpacker, position_report, relist, @@ -182,6 +184,7 @@ def main() -> NoReturn: def cleanup(sig, frame): cloudlog.warning(f"caught sig {sig}, disabling quectel gps") + gpio_set(GPIO.UBLOX_PWR_EN, False) teardown_quectel(diag) cloudlog.warning("quectel cleanup done") sys.exit(0) @@ -190,6 +193,8 @@ def main() -> NoReturn: setup_quectel(diag) cloudlog.warning("quectel setup done") + gpio_init(GPIO.UBLOX_PWR_EN, True) + gpio_set(GPIO.UBLOX_PWR_EN, True) pm = messaging.PubMaster(['qcomGnss', 'gpsLocation']) From 66ff2de0a29438c80e8834ac78a696ca21b30601 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sat, 21 Jan 2023 19:42:10 -0800 Subject: [PATCH 146/484] boardd: connect to SPI panda by UID (#27035) * move list * connect by serial * cleanup hw serial * little more * rm excessive print Co-authored-by: Comma Device --- selfdrive/boardd/boardd.cc | 4 +-- selfdrive/boardd/panda.cc | 31 ++++++++------------ selfdrive/boardd/panda.h | 2 +- selfdrive/boardd/panda_comms.cc | 2 +- selfdrive/boardd/panda_comms.h | 1 + selfdrive/boardd/spi.cc | 50 +++++++++++++++++++++++++-------- 6 files changed, 54 insertions(+), 36 deletions(-) diff --git a/selfdrive/boardd/boardd.cc b/selfdrive/boardd/boardd.cc index e626882191..bc454aa54e 100644 --- a/selfdrive/boardd/boardd.cc +++ b/selfdrive/boardd/boardd.cc @@ -225,9 +225,9 @@ void can_send_thread(std::vector pandas, bool fake_send) { //Dont send if older than 1 second if ((nanos_since_boot() - event.getLogMonoTime() < 1e9) && !fake_send) { for (const auto& panda : pandas) { - LOGT("sending sendcan to panda: %s", (panda->hw_serial).c_str()); + LOGT("sending sendcan to panda: %s", (panda->hw_serial()).c_str()); panda->can_send(event.getSendcan()); - LOGT("sendcan sent to panda: %s", (panda->hw_serial).c_str()); + LOGT("sendcan sent to panda: %s", (panda->hw_serial()).c_str()); } } } diff --git a/selfdrive/boardd/panda.cc b/selfdrive/boardd/panda.cc index b12b26fb62..600a46c809 100644 --- a/selfdrive/boardd/panda.cc +++ b/selfdrive/boardd/panda.cc @@ -3,20 +3,18 @@ #include #include -#include #include -#include #include "cereal/messaging/messaging.h" #include "common/swaglog.h" #include "common/util.h" Panda::Panda(std::string serial, uint32_t bus_offset) : bus_offset(bus_offset) { - // TODO: support SPI here one day... - if (serial.find("spi") != std::string::npos) { - handle = std::make_unique(serial); - } else { + // try USB first, then SPI + try { handle = std::make_unique(serial); + } catch (std::exception &e) { + handle = std::make_unique(serial); } hw_type = get_hw_type(); @@ -41,23 +39,16 @@ bool Panda::comms_healthy() { return handle->comms_healthy; } +std::string Panda::hw_serial() { + return handle->hw_serial; +} + std::vector Panda::list() { std::vector serials = PandaUsbHandle::list(); - // check SPI - const int uid_len = 12; - uint8_t uid[uid_len] = {0}; - PandaSpiHandle spi_handle("/dev/spidev0.0"); - int ret = spi_handle.control_read(0xc3, 0, 0, uid, uid_len); - if (ret == uid_len) { - std::stringstream stream; - for (int i = 0; i < uid_len; i++) { - stream << std::hex << std::setw(2) << std::setfill('0') << int(uid[i]); - } - - // might be on USB too - if (std::find(serials.begin(), serials.end(), stream.str()) == serials.end()) { - serials.push_back(stream.str()); + for (auto s : PandaSpiHandle::list()) { + if (std::find(serials.begin(), serials.end(), s) == serials.end()) { + serials.push_back(s); } } diff --git a/selfdrive/boardd/panda.h b/selfdrive/boardd/panda.h index 156efe7ef3..69df2e2b66 100644 --- a/selfdrive/boardd/panda.h +++ b/selfdrive/boardd/panda.h @@ -48,13 +48,13 @@ private: public: Panda(std::string serial="", uint32_t bus_offset=0); - std::string hw_serial; cereal::PandaState::PandaType hw_type = cereal::PandaState::PandaType::UNKNOWN; bool has_rtc = false; const uint32_t bus_offset; bool connected(); bool comms_healthy(); + std::string hw_serial(); // Static functions static std::vector list(); diff --git a/selfdrive/boardd/panda_comms.cc b/selfdrive/boardd/panda_comms.cc index e73cb69318..120d2f67d5 100644 --- a/selfdrive/boardd/panda_comms.cc +++ b/selfdrive/boardd/panda_comms.cc @@ -44,7 +44,7 @@ PandaUsbHandle::PandaUsbHandle(std::string serial) : PandaCommsHandle(serial) { ret = libusb_get_string_descriptor_ascii(dev_handle, desc.iSerialNumber, desc_serial, std::size(desc_serial)); if (ret < 0) { goto fail; } - auto hw_serial = std::string((char *)desc_serial, ret); + hw_serial = std::string((char *)desc_serial, ret); if (serial.empty() || serial == hw_serial) { break; } diff --git a/selfdrive/boardd/panda_comms.h b/selfdrive/boardd/panda_comms.h index 9d1f69e671..b669d77e7f 100644 --- a/selfdrive/boardd/panda_comms.h +++ b/selfdrive/boardd/panda_comms.h @@ -21,6 +21,7 @@ public: virtual ~PandaCommsHandle() {}; virtual void cleanup() = 0; + std::string hw_serial; std::atomic connected = true; std::atomic comms_healthy = true; static std::vector list(); diff --git a/selfdrive/boardd/spi.cc b/selfdrive/boardd/spi.cc index 2779494ae4..bcc446b050 100644 --- a/selfdrive/boardd/spi.cc +++ b/selfdrive/boardd/spi.cc @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include "common/util.h" #include "common/timing.h" @@ -28,6 +30,7 @@ struct __attribute__((packed)) spi_header { const int SPI_MAX_RETRIES = 5; const int SPI_ACK_TIMEOUT = 50; // milliseconds +const std::string SPI_DEVICE = "/dev/spidev0.0"; class LockEx { public: @@ -48,38 +51,56 @@ private: PandaSpiHandle::PandaSpiHandle(std::string serial) : PandaCommsHandle(serial) { - LOGD("opening SPI panda: %s", serial.c_str()); + int ret; + const int uid_len = 12; + uint8_t uid[uid_len] = {0}; - int err; uint32_t spi_mode = SPI_MODE_0; uint32_t spi_speed = 30000000; uint8_t spi_bits_per_word = 8; - spi_fd = open(serial.c_str(), O_RDWR); + spi_fd = open(SPI_DEVICE.c_str(), O_RDWR); if (spi_fd < 0) { - LOGE("failed opening SPI device %d", err); + LOGE("failed opening SPI device %d", spi_fd); goto fail; } // SPI settings - err = util::safe_ioctl(spi_fd, SPI_IOC_WR_MODE, &spi_mode); - if (err < 0) { - LOGE("failed setting SPI mode %d", err); + ret = util::safe_ioctl(spi_fd, SPI_IOC_WR_MODE, &spi_mode); + if (ret < 0) { + LOGE("failed setting SPI mode %d", ret); goto fail; } - err = util::safe_ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &spi_speed); - if (err < 0) { + ret = util::safe_ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &spi_speed); + if (ret < 0) { LOGE("failed setting SPI speed"); goto fail; } - err = util::safe_ioctl(spi_fd, SPI_IOC_WR_BITS_PER_WORD, &spi_bits_per_word); - if (err < 0) { + ret = util::safe_ioctl(spi_fd, SPI_IOC_WR_BITS_PER_WORD, &spi_bits_per_word); + if (ret < 0) { LOGE("failed setting SPI bits per word"); goto fail; } + // get hw UID/serial + ret = control_read(0xc3, 0, 0, uid, uid_len); + if (ret == uid_len) { + std::stringstream stream; + for (int i = 0; i < uid_len; i++) { + stream << std::hex << std::setw(2) << std::setfill('0') << int(uid[i]); + } + hw_serial = stream.str(); + } else { + LOGD("failed to get serial %d", ret); + goto fail; + } + + if (!serial.empty() && (serial != hw_serial)) { + goto fail; + } + return; fail: @@ -163,7 +184,12 @@ int PandaSpiHandle::bulk_transfer(uint8_t endpoint, uint8_t *tx_data, uint16_t t } std::vector PandaSpiHandle::list() { - // TODO: list all pandas available over SPI + try { + PandaSpiHandle sh(""); + return {sh.hw_serial}; + } catch (std::exception &e) { + // no panda on SPI + } return {}; } From 3018d1c8ff93c15edf37aa8ab8c745438133b200 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sat, 21 Jan 2023 19:43:38 -0800 Subject: [PATCH 147/484] bump panda --- panda | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda b/panda index e83b2189c1..4750449f3c 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit e83b2189c148499b8ba430433886791217f3389a +Subproject commit 4750449f3cc41e2dc8141b4818025c5c538dad17 From 1e4721ad62d8953ed2e55ebcb4c02d7e06d393df Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 23 Jan 2023 05:20:20 +0800 Subject: [PATCH 148/484] cabana: fix memory corruption in live stream (#27037) --- tools/cabana/streams/livestream.cc | 33 ++++++++++++++++++++---------- tools/cabana/streams/livestream.h | 5 +++-- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/tools/cabana/streams/livestream.cc b/tools/cabana/streams/livestream.cc index bc5b811f44..b0af9f3281 100644 --- a/tools/cabana/streams/livestream.cc +++ b/tools/cabana/streams/livestream.cc @@ -4,8 +4,11 @@ LiveStream::LiveStream(QObject *parent, QString address) : zmq_address(address), if (!zmq_address.isEmpty()) { setenv("ZMQ", "1", 1); } - updateCachedNS(); - QObject::connect(&settings, &Settings::changed, this, &LiveStream::updateCachedNS); + + timer = new QTimer(this); + timer->callOnTimeout(this, &LiveStream::removeExpiredEvents); + timer->start(3 * 1000); + stream_thread = new QThread(this); QObject::connect(stream_thread, &QThread::started, [=]() { streamThread(); }); QObject::connect(stream_thread, &QThread::finished, stream_thread, &QThread::deleteLater); @@ -37,23 +40,17 @@ void LiveStream::streamThread() { AlignedBuffer *buf = messages.emplace_back(new AlignedBuffer()); Event *evt = ::new Event(buf->align(msg)); delete msg; - { std::lock_guard lk(lock); can_events.push_back(evt); - if ((evt->mono_time - can_events.front()->mono_time) > cache_ns) { - ::delete can_events.front(); - delete messages.front(); - can_events.pop_front(); - messages.pop_front(); - } } + if (start_ts == 0) { start_ts = evt->mono_time; emit streamStarted(); } current_ts = evt->mono_time; - if (start_ts > current_ts) { + if (current_ts < start_ts) { qDebug() << "stream is looping back to old time stamp"; start_ts = current_ts.load(); } @@ -62,9 +59,23 @@ void LiveStream::streamThread() { } } -const std::vector *LiveStream::events() const { +void LiveStream::removeExpiredEvents() { std::lock_guard lk(lock); + if (can_events.size() > 0) { + const uint64_t max_ns = settings.max_cached_minutes * 60 * 1e9; + const uint64_t last_ns = can_events.back()->mono_time; + while (!can_events.empty() && (last_ns - can_events.front()->mono_time) > max_ns) { + ::delete can_events.front(); + delete messages.front(); + can_events.pop_front(); + messages.pop_front(); + } + } +} + +const std::vector *LiveStream::events() const { events_vector.clear(); + std::lock_guard lk(lock); events_vector.reserve(can_events.size()); std::copy(can_events.begin(), can_events.end(), std::back_inserter(events_vector)); return &events_vector; diff --git a/tools/cabana/streams/livestream.h b/tools/cabana/streams/livestream.h index 95c6e3811c..db1638d761 100644 --- a/tools/cabana/streams/livestream.h +++ b/tools/cabana/streams/livestream.h @@ -1,5 +1,6 @@ #pragma once +#include #include "tools/cabana/streams/abstractstream.h" class LiveStream : public AbstractStream { @@ -17,7 +18,7 @@ public: protected: void streamThread(); - void updateCachedNS() { cache_ns = (settings.max_cached_minutes * 60) * 1e9; } + void removeExpiredEvents(); mutable std::mutex lock; mutable std::vector events_vector; @@ -25,7 +26,7 @@ protected: std::deque messages; std::atomic start_ts = 0; std::atomic current_ts = 0; - std::atomic cache_ns = 0; const QString zmq_address; QThread *stream_thread; + QTimer *timer; }; From a0f91b0a8b1f9e8d14adfb32691c8dec66807d13 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 23 Jan 2023 05:20:40 +0800 Subject: [PATCH 149/484] cabana: fix chart range not updated when replay is seeking back. (#27036) --- tools/cabana/chartswidget.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index ca52e44474..2965ae0476 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -135,7 +135,7 @@ void ChartsWidget::updateState() { const double cur_sec = can->currentSec(); if (!is_zoomed) { double pos = (cur_sec - display_range.first) / max_chart_range; - if (pos > 0.8) { + if (pos < 0 || pos > 0.8) { const double min_event_sec = (can->events()->front()->mono_time / (double)1e9) - can->routeStartTime(); display_range.first = std::floor(std::max(min_event_sec, cur_sec - max_chart_range * 0.2)); } From 42937056481be5959664d00c8dd0a78220574695 Mon Sep 17 00:00:00 2001 From: Comma Device Date: Sun, 22 Jan 2023 16:26:55 -0600 Subject: [PATCH 150/484] test manager: use ensure_running to start relevant procs --- selfdrive/manager/process.py | 6 +++++- selfdrive/manager/test/test_manager.py | 27 +++++++++++++------------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/selfdrive/manager/process.py b/selfdrive/manager/process.py index dabfbe4ee0..ce18073d9b 100644 --- a/selfdrive/manager/process.py +++ b/selfdrive/manager/process.py @@ -290,10 +290,11 @@ class DaemonProcess(ManagerProcess): def ensure_running(procs: ValuesView[ManagerProcess], started: bool, params=None, CP: car.CarParams=None, - not_run: Optional[List[str]]=None) -> None: + not_run: Optional[List[str]]=None) -> List[ManagerProcess]: if not_run is None: not_run = [] + running = [] for p in procs: # Conditions that make a process run run = any(( @@ -311,7 +312,10 @@ def ensure_running(procs: ValuesView[ManagerProcess], started: bool, params=None if run: p.start() + running.append(p) else: p.stop(block=False) p.check_watchdog(started) + + return running diff --git a/selfdrive/manager/test/test_manager.py b/selfdrive/manager/test/test_manager.py index 6d4df0423a..8712c9478e 100755 --- a/selfdrive/manager/test/test_manager.py +++ b/selfdrive/manager/test/test_manager.py @@ -4,17 +4,17 @@ import signal import time import unittest +from cereal import car from common.params import Params import selfdrive.manager.manager as manager -from selfdrive.manager.process import DaemonProcess +from selfdrive.manager.process import ensure_running, DaemonProcess from selfdrive.manager.process_config import managed_processes from system.hardware import HARDWARE os.environ['FAKEUPLOAD'] = "1" MAX_STARTUP_TIME = 3 -ALL_PROCESSES = [p.name for p in managed_processes.values() if (type(p) is not DaemonProcess) and p.enabled and (p.name not in ['pandad', ])] - +BLACKLIST_PROCS = ['manage_athenad', 'pandad', 'pigeond'] class TestManager(unittest.TestCase): def setUp(self): @@ -47,24 +47,25 @@ class TestManager(unittest.TestCase): HARDWARE.set_power_save(False) manager.manager_init() manager.manager_prepare() - for p in ALL_PROCESSES: - managed_processes[p].start() + + CP = car.CarParams.new_message() + procs = ensure_running(managed_processes.values(), True, Params(), CP, not_run=BLACKLIST_PROCS) time.sleep(10) - for p in reversed(ALL_PROCESSES): - with self.subTest(proc=p): - state = managed_processes[p].get_process_state_msg() - self.assertTrue(state.running, f"{p} not running") - exit_code = managed_processes[p].stop(retry=False) + for p in procs: + with self.subTest(proc=p.name): + state = p.get_process_state_msg() + self.assertTrue(state.running, f"{p.name} not running") + exit_code = p.stop(retry=False) - self.assertTrue(exit_code is not None, f"{p} failed to exit") + self.assertTrue(exit_code is not None, f"{p.name} failed to exit") # TODO: interrupted blocking read exits with 1 in cereal. use a more unique return code exit_codes = [0, 1] - if managed_processes[p].sigkill: + if p.sigkill: exit_codes = [-signal.SIGKILL] - self.assertIn(exit_code, exit_codes, f"{p} died with {exit_code}") + self.assertIn(exit_code, exit_codes, f"{p.name} died with {exit_code}") if __name__ == "__main__": From 100fe10c7c326f3d22685ca8a0ebb486c57c85cf Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 22 Jan 2023 15:08:36 -0800 Subject: [PATCH 151/484] jenkins: tmp disable sim test due to ZMQ bug (#27043) --- Jenkinsfile | 2 ++ selfdrive/manager/test/test_manager.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index bd138bed76..1841c68bba 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -81,6 +81,7 @@ pipeline { parallel { + /* stage('simulator') { agent { dockerfile { @@ -108,6 +109,7 @@ pipeline { } } } + */ stage('build') { agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } } diff --git a/selfdrive/manager/test/test_manager.py b/selfdrive/manager/test/test_manager.py index 8712c9478e..889ab3d279 100755 --- a/selfdrive/manager/test/test_manager.py +++ b/selfdrive/manager/test/test_manager.py @@ -7,7 +7,7 @@ import unittest from cereal import car from common.params import Params import selfdrive.manager.manager as manager -from selfdrive.manager.process import ensure_running, DaemonProcess +from selfdrive.manager.process import ensure_running from selfdrive.manager.process_config import managed_processes from system.hardware import HARDWARE From f5d253764cc3219fae26361af07bf5de9bf94b0a Mon Sep 17 00:00:00 2001 From: "Michael G. Inso" <68110223+MiChaelinzo@users.noreply.github.com> Date: Mon, 23 Jan 2023 20:49:48 +0300 Subject: [PATCH 152/484] Update cell.sh (#27055) You can combine the two nmcli commands into a single command by specifying both ipv4 and ipv6 options at the same time. You can use the nmcli command with the --wait option to wait for the connection to be fully established before exiting. You can use nmcli command with the --ask option to prompt the user to confirm the action before proceeding. --- scripts/cell.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/cell.sh b/scripts/cell.sh index cae701eccc..3623fe5b16 100755 --- a/scripts/cell.sh +++ b/scripts/cell.sh @@ -1,5 +1,3 @@ #!/usr/bin/bash - -nmcli connection modify --temporary lte ipv4.route-metric 1 -nmcli connection modify --temporary lte ipv6.route-metric 1 -nmcli con up lte +nmcli connection modify --temporary lte ipv4.route-metric 1 ipv6.route-metric 1 +nmcli con up --wait --ask lte From e49c7fa2bf3bbc2eb6aa851c51d82f1cfd6b9212 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 24 Jan 2023 02:16:11 +0800 Subject: [PATCH 153/484] cabana: fix slider crash on exit if loadThumbnails is running (#27052) --- tools/cabana/videowidget.cc | 5 +++++ tools/cabana/videowidget.h | 1 + 2 files changed, 6 insertions(+) diff --git a/tools/cabana/videowidget.cc b/tools/cabana/videowidget.cc index 7d46769a47..729302adaa 100644 --- a/tools/cabana/videowidget.cc +++ b/tools/cabana/videowidget.cc @@ -133,6 +133,11 @@ Slider::Slider(QWidget *parent) : QSlider(Qt::Horizontal, parent) { QObject::connect(can, &AbstractStream::streamStarted, this, &Slider::streamStarted); } +Slider::~Slider() { + abort_load_thumbnail = true; + thumnail_future.waitForFinished(); +} + void Slider::streamStarted() { abort_load_thumbnail = true; thumnail_future.waitForFinished(); diff --git a/tools/cabana/videowidget.h b/tools/cabana/videowidget.h index 6b4c02c573..392db23512 100644 --- a/tools/cabana/videowidget.h +++ b/tools/cabana/videowidget.h @@ -18,6 +18,7 @@ class Slider : public QSlider { public: Slider(QWidget *parent); + ~Slider(); private: void mousePressEvent(QMouseEvent *e) override; From b2c6d5d34a1cc194aaf98c4963677a5581bf0a94 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 23 Jan 2023 20:16:50 -0800 Subject: [PATCH 154/484] Honda: merge Passport platform into Pilot (#27061) * Move Passport FW versions to Pilot * lol, all duplicates already in pilot * No Passport * Better params for both --- selfdrive/car/honda/carstate.py | 2 +- selfdrive/car/honda/interface.py | 8 ++-- selfdrive/car/honda/values.py | 55 +++++++-------------------- selfdrive/car/tests/routes.py | 2 +- selfdrive/car/tests/test_models.py | 2 +- selfdrive/car/torque_data/params.yaml | 1 - 6 files changed, 21 insertions(+), 49 deletions(-) diff --git a/selfdrive/car/honda/carstate.py b/selfdrive/car/honda/carstate.py index a37667fd3a..16880d1b1f 100644 --- a/selfdrive/car/honda/carstate.py +++ b/selfdrive/car/honda/carstate.py @@ -274,7 +274,7 @@ class CarState(CarStateBase): ret.cruiseState.available = bool(cp.vl[self.main_on_sig_msg]["MAIN_ON"]) # Gets rid of Pedal Grinding noise when brake is pressed at slow speeds for some models - if self.CP.carFingerprint in (CAR.PILOT, CAR.PASSPORT, CAR.RIDGELINE): + if self.CP.carFingerprint in (CAR.PILOT, CAR.RIDGELINE): if ret.brake > 0.1: ret.brakePressed = True diff --git a/selfdrive/car/honda/interface.py b/selfdrive/car/honda/interface.py index 990238ae5d..da921ccda7 100755 --- a/selfdrive/car/honda/interface.py +++ b/selfdrive/car/honda/interface.py @@ -231,11 +231,11 @@ class CarInterface(CarInterfaceBase): else: ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end - elif candidate in (CAR.PILOT, CAR.PASSPORT): - ret.mass = 4204. * CV.LB_TO_KG + STD_CARGO_KG # average weight - ret.wheelbase = 2.82 + elif candidate == CAR.PILOT: + ret.mass = 4278. * CV.LB_TO_KG + STD_CARGO_KG # average weight + ret.wheelbase = 2.86 ret.centerToFront = ret.wheelbase * 0.428 - ret.steerRatio = 17.25 # as spec + ret.steerRatio = 16.0 # as spec ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end tire_stiffness_factor = 0.444 ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.38], [0.11]] diff --git a/selfdrive/car/honda/values.py b/selfdrive/car/honda/values.py index 2906b23bda..1455609226 100644 --- a/selfdrive/car/honda/values.py +++ b/selfdrive/car/honda/values.py @@ -91,7 +91,6 @@ class CAR: ACURA_RDX = "ACURA RDX 2018" ACURA_RDX_3G = "ACURA RDX 2020" PILOT = "HONDA PILOT 2017" - PASSPORT = "HONDA PASSPORT 2021" RIDGELINE = "HONDA RIDGELINE 2017" INSIGHT = "HONDA INSIGHT 2019" HONDA_E = "HONDA E 2020" @@ -142,8 +141,10 @@ CAR_INFO: Dict[str, Optional[Union[HondaCarInfo, List[HondaCarInfo]]]] = { CAR.ODYSSEY_CHN: None, # Chinese version of Odyssey CAR.ACURA_RDX: HondaCarInfo("Acura RDX 2016-18", "AcuraWatch Plus", min_steer_speed=12. * CV.MPH_TO_MS), CAR.ACURA_RDX_3G: HondaCarInfo("Acura RDX 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS), - CAR.PILOT: HondaCarInfo("Honda Pilot 2016-22", min_steer_speed=12. * CV.MPH_TO_MS), - CAR.PASSPORT: HondaCarInfo("Honda Passport 2019-21", "All", min_steer_speed=12. * CV.MPH_TO_MS), + CAR.PILOT: [ + HondaCarInfo("Honda Pilot 2016-22", min_steer_speed=12. * CV.MPH_TO_MS), + HondaCarInfo("Honda Passport 2019-21", "All", min_steer_speed=12. * CV.MPH_TO_MS), + ], CAR.RIDGELINE: HondaCarInfo("Honda Ridgeline 2017-22", min_steer_speed=12. * CV.MPH_TO_MS), CAR.INSIGHT: HondaCarInfo("Honda Insight 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS), CAR.HONDA_E: HondaCarInfo("Honda e 2020", "All", min_steer_speed=3. * CV.MPH_TO_MS), @@ -1067,6 +1068,8 @@ FW_VERSIONS = { b'28101-5EZ-A060\x00\x00', b'28101-5EZ-A100\x00\x00', b'28101-5EZ-A210\x00\x00', + b'28101-5EZ-A600\x00\x00', + b'28101-5EZ-A430\x00\x00', ], (Ecu.programmedFuelInjection, 0x18da10f1, None): [ b'37805-RLV-4060\x00\x00', @@ -1078,6 +1081,9 @@ FW_VERSIONS = { b'37805-RLV-C520\x00\x00', b'37805-RLV-C530\x00\x00', b'37805-RLV-C910\x00\x00', + b'37805-RLV-B220\x00\x00', + b'37805-RLV-B210\x00\x00', + b'37805-RLV-L160\x00\x00', ], (Ecu.gateway, 0x18daeff1, None): [ b'38897-TG7-A030\x00\x00', @@ -1110,6 +1116,7 @@ FW_VERSIONS = { b'36161-TGS-A130\x00\x00', b'36161-TGT-A030\x00\x00', b'36161-TGT-A130\x00\x00', + b'36161-TGS-A030\x00\x00', ], (Ecu.srs, 0x18da53f1, None): [ b'77959-TG7-A020\x00\x00', @@ -1147,6 +1154,9 @@ FW_VERSIONS = { b'78109-TGS-AP20\x00\x00', b'78109-TGT-AJ20\x00\x00', b'78109-TGT-AK30\x00\x00', + b'78109-TGS-AT20\x00\x00', + b'78109-TGS-AX20\x00\x00', + b'78109-TGS-AJ20\x00\x00', ], (Ecu.vsa, 0x18da28f1, None): [ b'57114-TG7-A130\x00\x00', @@ -1163,42 +1173,6 @@ FW_VERSIONS = { b'57114-TGT-A530\x00\x00', ], }, - CAR.PASSPORT: { - (Ecu.programmedFuelInjection, 0x18da10f1, None): [ - b'37805-RLV-B220\x00\x00', - b'37805-RLV-B210\x00\x00', - b'37805-RLV-L160\x00\x00', - ], - (Ecu.eps, 0x18da30f1, None): [ - b'39990-TGS-A230\x00\x00', - ], - (Ecu.fwdRadar, 0x18dab0f1, None): [ - b'36161-TGS-A030\x00\x00', - b'36161-TGS-A130\x00\x00', - ], - (Ecu.gateway, 0x18daeff1, None): [ - b'38897-TG7-A040\x00\x00', - b'38897-TG7-A030\x00\x00', - ], - (Ecu.srs, 0x18da53f1, None): [ - b'77959-TGS-A010\x00\x00', - ], - (Ecu.shiftByWire, 0x18da0bf1, None): [ - b'54008-TG7-A530\x00\x00', - ], - (Ecu.transmission, 0x18da1ef1, None): [ - b'28101-5EZ-A600\x00\x00', - b'28101-5EZ-A430\x00\x00', - ], - (Ecu.combinationMeter, 0x18da60f1, None): [ - b'78109-TGS-AT20\x00\x00', - b'78109-TGS-AX20\x00\x00', - b'78109-TGS-AJ20\x00\x00', - ], - (Ecu.vsa, 0x18da28f1, None): [ - b'57114-TGS-A530\x00\x00', - ], - }, CAR.ACURA_RDX: { (Ecu.vsa, 0x18da28f1, None): [ b'57114-TX5-A220\x00\x00', @@ -1499,7 +1473,6 @@ DBC = { CAR.ODYSSEY: dbc_dict('honda_odyssey_exl_2018_generated', 'acura_ilx_2016_nidec'), CAR.ODYSSEY_CHN: dbc_dict('honda_odyssey_extreme_edition_2018_china_can_generated', 'acura_ilx_2016_nidec'), CAR.PILOT: dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'), - CAR.PASSPORT: dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'), CAR.RIDGELINE: dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'), CAR.INSIGHT: dbc_dict('honda_insight_ex_2019_can_generated', None), CAR.HONDA_E: dbc_dict('acura_rdx_2020_can_generated', None), @@ -1514,7 +1487,7 @@ STEER_THRESHOLD = { HONDA_NIDEC_ALT_PCM_ACCEL = {CAR.ODYSSEY} HONDA_NIDEC_ALT_SCM_MESSAGES = {CAR.ACURA_ILX, CAR.ACURA_RDX, CAR.CRV, CAR.CRV_EU, CAR.FIT, CAR.FREED, CAR.HRV, CAR.ODYSSEY_CHN, - CAR.PILOT, CAR.PASSPORT, CAR.RIDGELINE} + CAR.PILOT, CAR.RIDGELINE} HONDA_BOSCH = {CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_5G, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022} HONDA_BOSCH_ALT_BRAKE_SIGNAL = {CAR.ACCORD, CAR.CRV_5G, CAR.ACURA_RDX_3G} diff --git a/selfdrive/car/tests/routes.py b/selfdrive/car/tests/routes.py index ac0521cc68..29cea9c030 100644 --- a/selfdrive/car/tests/routes.py +++ b/selfdrive/car/tests/routes.py @@ -72,7 +72,7 @@ routes = [ CarTestRoute("d83f36766f8012a5|2020-02-05--18-42-21", HONDA.CIVIC_BOSCH_DIESEL), CarTestRoute("f0890d16a07a236b|2021-05-25--17-27-22", HONDA.INSIGHT), CarTestRoute("07d37d27996096b6|2020-03-04--21-57-27", HONDA.PILOT), - CarTestRoute("684e8f96bd491a0e|2021-11-03--11-08-42", HONDA.PASSPORT), + CarTestRoute("684e8f96bd491a0e|2021-11-03--11-08-42", HONDA.PILOT), # Passport CarTestRoute("0a78dfbacc8504ef|2020-03-04--13-29-55", HONDA.CIVIC_BOSCH), CarTestRoute("f34a60d68d83b1e5|2020-10-06--14-35-55", HONDA.ACURA_RDX), CarTestRoute("54fd8451b3974762|2021-04-01--14-50-10", HONDA.RIDGELINE), diff --git a/selfdrive/car/tests/test_models.py b/selfdrive/car/tests/test_models.py index b6f78c4b47..4627b9a570 100755 --- a/selfdrive/car/tests/test_models.py +++ b/selfdrive/car/tests/test_models.py @@ -245,7 +245,7 @@ class TestCarModelBase(unittest.TestCase): # TODO: remove this exception once this mismatch is resolved brake_pressed = CS.brakePressed if CS.brakePressed and not self.safety.get_brake_pressed_prev(): - if self.CP.carFingerprint in (HONDA.PILOT, HONDA.PASSPORT, HONDA.RIDGELINE) and CS.brake > 0.05: + if self.CP.carFingerprint in (HONDA.PILOT, HONDA.RIDGELINE) and CS.brake > 0.05: brake_pressed = False checks['brakePressed'] += brake_pressed != self.safety.get_brake_pressed_prev() checks['regenBraking'] += CS.regenBraking != self.safety.get_regen_braking_prev() diff --git a/selfdrive/car/torque_data/params.yaml b/selfdrive/car/torque_data/params.yaml index eb1a04cee6..397b29525d 100644 --- a/selfdrive/car/torque_data/params.yaml +++ b/selfdrive/car/torque_data/params.yaml @@ -22,7 +22,6 @@ HONDA FIT 2018: [1.5719981427109775, 0.5712761407108976, 0.110773383324281] HONDA HRV 2019: [2.0661212805710205, 0.7521343418694775, 0.17760375789242094] HONDA INSIGHT 2019: [1.5201671214069354, 0.5660229120683284, 0.25808042580281876] HONDA ODYSSEY 2018: [1.8774809275211801, 0.8394431662987996, 0.2096978613792822] -HONDA PASSPORT 2021: [1.5305538930036766, 0.7956063674638759, 0.19599407381531284] HONDA PILOT 2017: [1.7262026201812795, 0.9470005614967523, 0.21351430733218763] HONDA RIDGELINE 2017: [1.4146525028237624, 0.7356572861629564, 0.23307177552211328] HYUNDAI ELANTRA 2021: [3.169, 2.1259108157250735, 0.0819] From 7090a8841935d3a1c67fd915e4898a9880c237b5 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 24 Jan 2023 00:04:56 -0800 Subject: [PATCH 155/484] Fix fuzz_fw_fingerprint.py script --- selfdrive/debug/internal/fuzz_fw_fingerprint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/debug/internal/fuzz_fw_fingerprint.py b/selfdrive/debug/internal/fuzz_fw_fingerprint.py index 1ea133cc19..a18390fef3 100755 --- a/selfdrive/debug/internal/fuzz_fw_fingerprint.py +++ b/selfdrive/debug/internal/fuzz_fw_fingerprint.py @@ -28,7 +28,7 @@ if __name__ == "__main__": for candidate, fws in FWS.items(): fw_dict = {} for (tp, addr, subaddr), fw_list in fws.items(): - fw_dict[(addr, subaddr)] = random.choice(fw_list) + fw_dict[(addr, subaddr)] = [random.choice(fw_list)] matches = match_fw_to_car_fuzzy(fw_dict, log=False, exclude=candidate) From 75f6dc385d86f587504ad2b8d676dcf5a3f40ddd Mon Sep 17 00:00:00 2001 From: Robbe Derks Date: Tue, 24 Jan 2023 14:10:31 +0100 Subject: [PATCH 156/484] bump panda --- panda | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda b/panda index 4750449f3c..687676be40 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 4750449f3cc41e2dc8141b4818025c5c538dad17 +Subproject commit 687676be4025d9caf9520be89cb328d6a3cc3e10 From b2675cef9a9d4993859b0cd924a1ca74281024f9 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 25 Jan 2023 03:36:40 +0800 Subject: [PATCH 157/484] cabana: colorful logs based on activity (#27008) * color logs * remove space * update in updateColors --- tools/cabana/SConscript | 2 +- tools/cabana/historylog.cc | 34 +++++++-- tools/cabana/historylog.h | 7 +- tools/cabana/messageswidget.cc | 46 +----------- tools/cabana/messageswidget.h | 7 -- tools/cabana/streams/abstractstream.cc | 62 +--------------- tools/cabana/streams/abstractstream.h | 11 +-- tools/cabana/util.cc | 99 ++++++++++++++++++++++++++ tools/cabana/util.h | 38 ++++++++++ 9 files changed, 176 insertions(+), 130 deletions(-) create mode 100644 tools/cabana/util.cc create mode 100644 tools/cabana/util.h diff --git a/tools/cabana/SConscript b/tools/cabana/SConscript index 9b172a544f..fbf3c03d36 100644 --- a/tools/cabana/SConscript +++ b/tools/cabana/SConscript @@ -21,7 +21,7 @@ cabana_env = qt_env.Clone() prev_moc_path = cabana_env['QT_MOCHPREFIX'] cabana_env['QT_MOCHPREFIX'] = os.path.dirname(prev_moc_path) + '/cabana/moc_' cabana_lib = cabana_env.Library("cabana_lib", ['mainwin.cc', 'streams/livestream.cc', 'streams/abstractstream.cc', 'streams/replaystream.cc', 'binaryview.cc', 'chartswidget.cc', 'historylog.cc', 'videowidget.cc', 'signaledit.cc', 'dbcmanager.cc', - 'commands.cc', 'messageswidget.cc', 'settings.cc', 'detailwidget.cc', 'tools/findsimilarbits.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) + 'commands.cc', 'messageswidget.cc', 'settings.cc', 'util.cc', 'detailwidget.cc', 'tools/findsimilarbits.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) cabana_env.Program('_cabana', ['cabana.cc', cabana_lib, asset_obj], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) if GetOption('test'): diff --git a/tools/cabana/historylog.cc b/tools/cabana/historylog.cc index f0f40697b8..4a975afc4d 100644 --- a/tools/cabana/historylog.cc +++ b/tools/cabana/historylog.cc @@ -16,16 +16,16 @@ void HistoryLogModel::setDisplayType(HistoryLogModel::DisplayType type) { QVariant HistoryLogModel::data(const QModelIndex &index, int role) const { const bool display_signals = display_type == HistoryLogModel::Signals; + const auto &m = messages[index.row()]; if (role == Qt::DisplayRole) { - const auto &m = messages[index.row()]; if (index.column() == 0) { return QString::number((m.mono_time / (double)1e9) - can->routeStartTime(), 'f', 2); } - return display_signals ? QString::number(m.sig_values[index.column() - 1]) : m.data; - } else if (role == Qt::FontRole && index.column() == 1 && !display_signals) { - return QFontDatabase::systemFont(QFontDatabase::FixedFont); + return display_signals ? QString::number(m.sig_values[index.column() - 1]) : toHex(m.data); } else if (role == Qt::ToolTipRole && index.column() > 0 && display_signals) { return tr("double click to open the chart"); + } else if (role == Qt::UserRole && index.column() == 1 && !display_signals) { + return HexColors::toVariantList(m.colors); } return {}; } @@ -45,6 +45,7 @@ void HistoryLogModel::refresh() { beginResetModel(); last_fetch_time = 0; messages.clear(); + hex_colors.clear(); updateState(); endResetModel(); } @@ -91,6 +92,7 @@ void HistoryLogModel::updateState() { if ((has_more_data = !new_msgs.empty())) { beginInsertRows({}, 0, new_msgs.size() - 1); messages.insert(messages.begin(), std::move_iterator(new_msgs.begin()), std::move_iterator(new_msgs.end())); + updateColors(); endInsertRows(); } last_fetch_time = current_time; @@ -103,11 +105,29 @@ void HistoryLogModel::fetchMore(const QModelIndex &parent) { if ((has_more_data = !new_msgs.empty())) { beginInsertRows({}, messages.size(), messages.size() + new_msgs.size() - 1); messages.insert(messages.end(), std::move_iterator(new_msgs.begin()), std::move_iterator(new_msgs.end())); + if (!dynamic_mode) { + updateColors(); + } endInsertRows(); } } } +void HistoryLogModel::updateColors() { + if (display_type == HistoryLogModel::Hex) { + const auto freq = can->lastMessage(msg_id).freq; + if (dynamic_mode) { + for (auto it = messages.rbegin(); it != messages.rend(); ++it) { + it->colors = hex_colors.compute(it->data, it->mono_time / (double)1e9, freq); + } + } else { + for (auto it = messages.begin(); it != messages.end(); ++it) { + it->colors = hex_colors.compute(it->data, it->mono_time / (double)1e9, freq); + } + } + } +} + template std::deque HistoryLogModel::fetchData(InputIt first, InputIt last, uint64_t min_time) { std::deque msgs; @@ -124,7 +144,7 @@ std::deque HistoryLogModel::fetchData(InputIt first, I if (!filter_cmp || filter_cmp(values[filter_sig_idx], filter_value)) { auto &m = msgs.emplace_back(); m.mono_time = (*it)->mono_time; - m.data = toHex(QByteArray((char *)dat.begin(), dat.size())); + m.data = QByteArray((char *)dat.begin(), dat.size()); m.sig_values = values; if (msgs.size() >= batch_size && min_time == 0) return msgs; @@ -135,6 +155,7 @@ std::deque HistoryLogModel::fetchData(InputIt first, I } return msgs; } + template std::deque HistoryLogModel::fetchData<>(std::vector::iterator first, std::vector::iterator last, uint64_t min_time); template std::deque HistoryLogModel::fetchData<>(std::vector::reverse_iterator first, std::vector::reverse_iterator last, uint64_t min_time); @@ -183,6 +204,7 @@ HistoryLog::HistoryLog(QWidget *parent) : QTableView(parent) { horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | (Qt::Alignment)Qt::TextWordWrap); horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); verticalHeader()->setVisible(false); + setItemDelegateForColumn(1, new MessageBytesDelegate(this)); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); } @@ -225,8 +247,8 @@ LogsWidget::LogsWidget(QWidget *parent) : QWidget(parent) { QObject::connect(can, &AbstractStream::seekedTo, model, &HistoryLogModel::refresh); QObject::connect(can, &AbstractStream::eventsMerged, model, &HistoryLogModel::segmentsMerged); + dynamic_mode->setChecked(true); if (can->liveStreaming()) { - dynamic_mode->setChecked(true); dynamic_mode->setEnabled(false); } } diff --git a/tools/cabana/historylog.h b/tools/cabana/historylog.h index 2ab204af15..34af713fd3 100644 --- a/tools/cabana/historylog.h +++ b/tools/cabana/historylog.h @@ -41,12 +41,14 @@ public: } void setDynamicMode(int state); void segmentsMerged(); + void updateColors(); void refresh(); struct Message { uint64_t mono_time = 0; QVector sig_values; - QString data; + QByteArray data; + QVector colors; }; template @@ -54,6 +56,7 @@ public: std::deque fetchData(uint64_t from_time, uint64_t min_time = 0); QString msg_id; + HexColors hex_colors; bool has_more_data = true; const int batch_size = 50; int filter_sig_idx = -1; @@ -62,7 +65,7 @@ public: std::function filter_cmp = nullptr; std::deque messages; std::vector sigs; - bool dynamic_mode = false; + bool dynamic_mode = true; DisplayType display_type = HistoryLogModel::Signals; }; diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index 0d06604fdc..353fe26340 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -79,15 +79,8 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const { case 3: return can_data.count; case 4: return toHex(can_data.dat); } - } else if (role == Qt::FontRole && index.column() == columnCount() - 1) { - return QFontDatabase::systemFont(QFontDatabase::FixedFont); } else if (role == Qt::UserRole && index.column() == 4) { - - QList colors; - for (int i = 0; i < can_data.dat.size(); i++){ - colors.append(can_data.colors[i]); - } - return colors; + return HexColors::toVariantList(can_data.colors); } return {}; } @@ -174,40 +167,3 @@ void MessageListModel::sort(int column, Qt::SortOrder order) { sortMessages(); } } - - -MessageBytesDelegate::MessageBytesDelegate(QObject *parent) : QStyledItemDelegate(parent) { -} - -void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { - QList colors = index.data(Qt::UserRole).toList(); - - QStyleOptionViewItemV4 opt = option; - initStyleOption(&opt, index); - - const QFont font = index.data(Qt::FontRole).value(); - painter->setFont(font); - - QRect rect = opt.rect; - QString bytes = QString(opt.text); - - QRect pos = rect; - QRect space = painter->boundingRect(pos, opt.displayAlignment, " "); - pos.setX(pos.x() + space.width()); - - if ((option.state & QStyle::State_Selected) && (option.state & QStyle::State_Active)) { - painter->setPen(option.palette.color(QPalette::HighlightedText)); - } else { - painter->setPen(option.palette.color(QPalette::Text)); - } - - int i = 0; - for (auto &byte : bytes.split(" ")) { - QRect sz = painter->boundingRect(pos, opt.displayAlignment, byte); - const int m = space.width() / 2; - painter->fillRect(sz.marginsAdded(QMargins(m + 1, m, m, m)), colors[i].value()); - painter->drawText(pos, opt.displayAlignment, byte); - pos.setX(pos.x() + sz.width() + space.width()); - i++; - } -} diff --git a/tools/cabana/messageswidget.h b/tools/cabana/messageswidget.h index 187e4715b9..49f57b78e6 100644 --- a/tools/cabana/messageswidget.h +++ b/tools/cabana/messageswidget.h @@ -42,10 +42,3 @@ protected: QString current_msg_id; MessageListModel *model; }; - -class MessageBytesDelegate : public QStyledItemDelegate { - Q_OBJECT -public: - MessageBytesDelegate(QObject *parent); - void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; -}; diff --git a/tools/cabana/streams/abstractstream.cc b/tools/cabana/streams/abstractstream.cc index 6ae1900184..b188c41826 100644 --- a/tools/cabana/streams/abstractstream.cc +++ b/tools/cabana/streams/abstractstream.cc @@ -17,15 +17,9 @@ void AbstractStream::process(QHash *messages) { processing = false; } -static QColor blend(QColor a, QColor b) { - return QColor((a.red() + b.red()) / 2, (a.green() + b.green()) / 2, (a.blue() + b.blue()) / 2, (a.alpha() + b.alpha()) / 2); -} - bool AbstractStream::updateEvent(const Event *event) { static std::unique_ptr new_msgs = std::make_unique>(); - static QHash prev_dat; - static QHash> colors; - static QHash> last_change_t; + static QHash hex_colors; static double prev_update_ts = 0; if (event->which == cereal::Event::Which::CAN) { @@ -48,59 +42,7 @@ bool AbstractStream::updateEvent(const Event *event) { if (double delta = (current_sec - counters_begin_sec); delta > 0) { data.freq = data.count / delta; } - - // Init colors - if (colors[id].size() != data.dat.size()) { - colors[id].clear(); - for (int i = 0; i < data.dat.size(); i++){ - colors[id].append(QColor(0, 0, 0, 0)); - } - } - - // Init last_change_t - if (last_change_t[id].size() != data.dat.size()) { - last_change_t[id].clear(); - for (int i = 0; i < data.dat.size(); i++){ - last_change_t[id].append(data.ts); - } - } - - // Compute background color for the bytes based on changes - if (prev_dat[id].size() == data.dat.size()) { - for (int i = 0; i < data.dat.size(); i++){ - uint8_t last = prev_dat[id][i]; - uint8_t cur = data.dat[i]; - - const int periodic_threshold = 10; - const int start_alpha = 128; - const float fade_time = 2.0; - - if (last != cur) { - double delta_t = data.ts - last_change_t[id][i]; - - if (delta_t * data.freq > periodic_threshold) { - // Last change was while ago, choose color based on delta up or down - if (cur > last) { - colors[id][i] = QColor(0, 187, 255, start_alpha); // Cyan - } else { - colors[id][i] = QColor(255, 0, 0, start_alpha); // Red - } - } else { - // Periodic changes - colors[id][i] = blend(colors[id][i], QColor(102, 86, 169, start_alpha / 2)); // Greyish/Blue - } - - last_change_t[id][i] = data.ts; - } else { - // Fade out - float alpha_delta = 1.0 / (data.freq + 1) / fade_time; - colors[id][i].setAlphaF(std::max(0.0, colors[id][i].alphaF() - alpha_delta)); - } - } - } - - data.colors = colors[id]; - prev_dat[id] = data.dat; + data.colors = hex_colors[id].compute(data.dat, data.ts, data.freq); } double ts = millis_since_boot(); diff --git a/tools/cabana/streams/abstractstream.h b/tools/cabana/streams/abstractstream.h index 1fe90c8595..9a0187da5e 100644 --- a/tools/cabana/streams/abstractstream.h +++ b/tools/cabana/streams/abstractstream.h @@ -6,6 +6,7 @@ #include #include "tools/cabana/settings.h" +#include "tools/cabana/util.h" #include "tools/replay/replay.h" struct CanData { @@ -15,7 +16,7 @@ struct CanData { uint32_t count = 0; uint32_t freq = 0; QByteArray dat; - QList colors; + QVector colors; }; class AbstractStream : public QObject { @@ -64,13 +65,5 @@ protected: QHash counters; }; -inline QString toHex(const QByteArray &dat) { return dat.toHex(' ').toUpper(); } -inline char toHex(uint value) { return "0123456789ABCDEF"[value & 0xF]; } -inline const QString &getColor(int i) { - // TODO: add more colors - static const QString SIGNAL_COLORS[] = {"#9FE2BF", "#40E0D0", "#6495ED", "#CCCCFF", "#FF7F50", "#FFBF00"}; - return SIGNAL_COLORS[i % std::size(SIGNAL_COLORS)]; -} - // A global pointer referring to the unique AbstractStream object extern AbstractStream *can; diff --git a/tools/cabana/util.cc b/tools/cabana/util.cc new file mode 100644 index 0000000000..0684d3c59a --- /dev/null +++ b/tools/cabana/util.cc @@ -0,0 +1,99 @@ +#include "tools/cabana/util.h" + +#include +#include + +static QColor blend(QColor a, QColor b) { + return QColor((a.red() + b.red()) / 2, (a.green() + b.green()) / 2, (a.blue() + b.blue()) / 2, (a.alpha() + b.alpha()) / 2); +} + +const QVector &HexColors::compute(const QByteArray &dat, double ts, uint32_t freq) { + if (prev_dat.size() != dat.size()) { + colors.resize(dat.size()); + last_change_t.resize(dat.size()); + std::fill(colors.begin(), colors.end(), QColor(0, 0, 0, 0)); + std::fill(last_change_t.begin(), last_change_t.end(), ts); + } else { + for (int i = 0; i < dat.size(); ++i) { + const uint8_t last = prev_dat[i]; + const uint8_t cur = dat[i]; + + if (last != cur) { + double delta_t = ts - last_change_t[i]; + if (delta_t * freq > periodic_threshold) { + // Last change was while ago, choose color based on delta up or down + if (cur > last) { + colors[i] = QColor(0, 187, 255, start_alpha); // Cyan + } else { + colors[i] = QColor(255, 0, 0, start_alpha); // Red + } + } else { + // Periodic changes + colors[i] = blend(colors[i], QColor(102, 86, 169, start_alpha / 2)); // Greyish/Blue + } + + last_change_t[i] = ts; + } else { + // Fade out + float alpha_delta = 1.0 / (freq + 1) / fade_time; + colors[i].setAlphaF(std::max(0.0, colors[i].alphaF() - alpha_delta)); + } + } + } + + prev_dat = dat; + return colors; +} + +void HexColors::clear() { + prev_dat.clear(); + last_change_t.clear(); + colors.clear(); +} + +QList HexColors::toVariantList(const QVector &colors) { + QList ret; + ret.reserve(colors.size()); + for (auto &c : colors) ret.append(c); + return ret; +} + +// MessageBytesDelegate + +MessageBytesDelegate::MessageBytesDelegate(QObject *parent) : QStyledItemDelegate(parent) { + fixed_font = QFontDatabase::systemFont(QFontDatabase::FixedFont); +} + +void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { + QList colors = index.data(Qt::UserRole).toList(); + if (colors.empty()) { + QStyledItemDelegate::paint(painter, option, index); + return; + } + QStyleOptionViewItemV4 opt = option; + initStyleOption(&opt, index); + + if ((option.state & QStyle::State_Selected) && (option.state & QStyle::State_Active)) { + painter->setPen(option.palette.color(QPalette::HighlightedText)); + } else { + painter->setPen(option.palette.color(QPalette::Text)); + } + + painter->setFont(fixed_font); + QRect space = painter->boundingRect(opt.rect, opt.displayAlignment, " "); + QRect pos = painter->boundingRect(opt.rect, opt.displayAlignment, "00"); + pos.moveLeft(pos.x() + space.width()); + + int m = space.width() / 2; + const QMargins margins(m + 1, m, m, m); + + int i = 0; + for (auto &byte : opt.text.split(" ")) { + if (i < colors.size()) { + painter->fillRect(pos.marginsAdded(margins), colors[i].value()); + } + painter->drawText(pos, opt.displayAlignment, byte); + pos.moveLeft(pos.right() + space.width()); + i++; + } +} diff --git a/tools/cabana/util.h b/tools/cabana/util.h new file mode 100644 index 0000000000..146410d7c6 --- /dev/null +++ b/tools/cabana/util.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include +#include +#include + +class HexColors { +public: + const QVector &compute(const QByteArray &dat, double ts, uint32_t freq); + static QList toVariantList(const QVector &colors); + void clear(); + +private: + const int periodic_threshold = 10; + const int start_alpha = 128; + const float fade_time = 2.0; + QByteArray prev_dat; + QVector last_change_t; + QVector colors; +}; + +class MessageBytesDelegate : public QStyledItemDelegate { + Q_OBJECT +public: + MessageBytesDelegate(QObject *parent); + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + QFont fixed_font; +}; + +inline QString toHex(const QByteArray &dat) { return dat.toHex(' ').toUpper(); } +inline char toHex(uint value) { return "0123456789ABCDEF"[value & 0xF]; } +inline const QString &getColor(int i) { + // TODO: add more colors + static const QString SIGNAL_COLORS[] = {"#9FE2BF", "#40E0D0", "#6495ED", "#CCCCFF", "#FF7F50", "#FFBF00"}; + return SIGNAL_COLORS[i % std::size(SIGNAL_COLORS)]; +} From 690999a2a6138368a93e818cbcebc984c7e58b91 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 24 Jan 2023 11:42:32 -0800 Subject: [PATCH 158/484] CI: increase submodule fetch depth --- release/check-submodules.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/check-submodules.sh b/release/check-submodules.sh index 182042e6b4..bc85a43c57 100755 --- a/release/check-submodules.sh +++ b/release/check-submodules.sh @@ -1,7 +1,7 @@ #!/bin/bash while read hash submodule ref; do - git -C $submodule fetch --depth 100 origin master + git -C $submodule fetch --depth 200 origin master git -C $submodule branch -r --contains $hash | grep "origin/master" if [ "$?" -eq 0 ]; then echo "$submodule ok" From 64ed44e3b51524ee3412600f647223dc9e7d6f87 Mon Sep 17 00:00:00 2001 From: George Hotz <72895+geohot@users.noreply.github.com> Date: Tue, 24 Jan 2023 14:06:56 -0800 Subject: [PATCH 159/484] tinygrad: update to new version (#27067) * tinygrad: bump and retain OPENCL behavior * add graph.py to release, bump tiny * update files_common and read from it in scons * bump with typo fix * switch tinygrad backend to GPU * update ref commit Co-authored-by: Comma Device --- release/files_common | 14 +++----- selfdrive/modeld/SConscript | 34 +++++-------------- .../process_replay/model_replay_ref_commit | 2 +- tinygrad_repo | 2 +- 4 files changed, 16 insertions(+), 36 deletions(-) diff --git a/release/files_common b/release/files_common index edb37bc0de..547bb58144 100644 --- a/release/files_common +++ b/release/files_common @@ -580,17 +580,13 @@ opendbc/tesla_can.dbc opendbc/tesla_radar.dbc opendbc/tesla_powertrain.dbc -tinygrad_repo/openpilot/compile.py tinygrad_repo/accel/opencl/* +tinygrad_repo/tinygrad/llops/ops_opencl.py +tinygrad_repo/openpilot/compile.py tinygrad_repo/extra/onnx.py tinygrad_repo/extra/thneed.py tinygrad_repo/extra/utils.py tinygrad_repo/tinygrad/llops/ops_gpu.py -tinygrad_repo/tinygrad/llops/ops_opencl.py -tinygrad_repo/tinygrad/helpers.py -tinygrad_repo/tinygrad/mlops.py -tinygrad_repo/tinygrad/ops.py -tinygrad_repo/tinygrad/shapetracker.py -tinygrad_repo/tinygrad/tensor.py -tinygrad_repo/tinygrad/nn/__init__.py -tinygrad_repo/tinygrad/nn/optim.py +tinygrad_repo/tinygrad/shape/* +tinygrad_repo/tinygrad/nn/* +tinygrad_repo/tinygrad/*.py diff --git a/selfdrive/modeld/SConscript b/selfdrive/modeld/SConscript index 82338e456b..7bbc1b3477 100644 --- a/selfdrive/modeld/SConscript +++ b/selfdrive/modeld/SConscript @@ -70,30 +70,14 @@ lenv.Program('_dmonitoringmodeld', [ if use_thneed and arch == "larch64" or GetOption('pc_thneed'): fn = File("models/supercombo").abspath - if GetOption('pc_thneed'): - cmd = f"cd {Dir('#').abspath}/tinygrad_repo && GPU=1 NATIVE_EXPLOG=1 OPTWG=1 UNSAFE_FLOAT4=1 DEBUGCL=1 python3 openpilot/compile.py {fn}.onnx {fn}.thneed" - else: - cmd = f"cd {Dir('#').abspath}/tinygrad_repo && FLOAT16=1 MATMUL=1 PYOPENCL_NO_CACHE=1 NATIVE_EXPLOG=1 OPTWG=1 UNSAFE_FLOAT4=1 DEBUGCL=1 python3 openpilot/compile.py {fn}.onnx {fn}.thneed" - - # is there a better way then listing all of tinygrad? - lenv.Command(fn + ".thneed", [fn + ".onnx", - "#tinygrad_repo/openpilot/compile.py", - "#tinygrad_repo/accel/opencl/conv.cl", - "#tinygrad_repo/accel/opencl/matmul.cl", - "#tinygrad_repo/accel/opencl/ops_opencl.py", - "#tinygrad_repo/accel/opencl/preprocessing.py", - "#tinygrad_repo/extra/onnx.py", - "#tinygrad_repo/extra/thneed.py", - "#tinygrad_repo/extra/utils.py", - "#tinygrad_repo/tinygrad/llops/ops_gpu.py", - "#tinygrad_repo/tinygrad/llops/ops_opencl.py", - "#tinygrad_repo/tinygrad/helpers.py", - "#tinygrad_repo/tinygrad/mlops.py", - "#tinygrad_repo/tinygrad/ops.py", - "#tinygrad_repo/tinygrad/shapetracker.py", - "#tinygrad_repo/tinygrad/tensor.py", - "#tinygrad_repo/tinygrad/nn/__init__.py" - ], cmd) + tinygrad_opts = ["NATIVE_EXPLOG=1", "VALIDHACKS=1", "OPTWG=1", "IMAGE=2", "GPU=1", "CLCACHE=0"] + if not GetOption('pc_thneed'): + # use FLOAT16 on device for speed + don't cache the CL kernels for space + tinygrad_opts += ["FLOAT16=1", "PYOPENCL_NO_CACHE=1"] + cmd = f"cd {Dir('#').abspath}/tinygrad_repo && " + ' '.join(tinygrad_opts) + f" python3 openpilot/compile.py {fn}.onnx {fn}.thneed" + + tinygrad_files = sum([lenv.Glob("#"+x) for x in open(File("#release/files_common").abspath).read().split("\n") if x.startswith("tinygrad_repo/")], []) + lenv.Command(fn + ".thneed", [fn + ".onnx"] + tinygrad_files, cmd) llenv = lenv.Clone() if GetOption('pc_thneed'): @@ -116,4 +100,4 @@ llenv.Program('_modeld', [ lenv.Program('_navmodeld', [ "navmodeld.cc", "models/nav.cc", - ]+common_model, LIBS=libs + transformations) \ No newline at end of file + ]+common_model, LIBS=libs + transformations) diff --git a/selfdrive/test/process_replay/model_replay_ref_commit b/selfdrive/test/process_replay/model_replay_ref_commit index 22e021dcaf..9eefc73792 100644 --- a/selfdrive/test/process_replay/model_replay_ref_commit +++ b/selfdrive/test/process_replay/model_replay_ref_commit @@ -1 +1 @@ -db587bfef2317c5a3471632ac47381457e1be853 \ No newline at end of file +8233a12f97f483645e7691b8c54552a2f861a7ad diff --git a/tinygrad_repo b/tinygrad_repo index 8e22d5ee67..2e1d47b166 160000 --- a/tinygrad_repo +++ b/tinygrad_repo @@ -1 +1 @@ -Subproject commit 8e22d5ee675277181e1eff644dde9e844fc40fce +Subproject commit 2e1d47b16625ff343516287cdd9e4bcb26f5c4ef From 544ad25a0b9af70e2b22f3ec4e0fd8788929fd2d Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 25 Jan 2023 07:07:11 +0800 Subject: [PATCH 160/484] cabana: support pause and slow motion playback in live stream mode (#27051) * support pause and slow motion playback in live stream mode * override * virtual functions * move to function fix * lock handleEvent * show speed controls in video widget * fix pause signal * set margins * cleanup --- tools/cabana/mainwin.cc | 11 +++--- tools/cabana/streams/livestream.cc | 53 +++++++++++++++++++--------- tools/cabana/streams/livestream.h | 15 ++++++-- tools/cabana/videowidget.cc | 55 +++++++++++++++++++----------- tools/cabana/videowidget.h | 1 + 5 files changed, 90 insertions(+), 45 deletions(-) diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index d2e4ff9ca7..e49148160d 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -126,15 +126,14 @@ void MainWindow::createDockWindows() { // splitter between video and charts video_splitter = new QSplitter(Qt::Vertical, this); - if (!can->liveStreaming()) { - video_widget = new VideoWidget(this); - video_splitter->addWidget(video_widget); - QObject::connect(charts_widget, &ChartsWidget::rangeChanged, video_widget, &VideoWidget::rangeChanged); - } + video_widget = new VideoWidget(this); + video_splitter->addWidget(video_widget); + QObject::connect(charts_widget, &ChartsWidget::rangeChanged, video_widget, &VideoWidget::rangeChanged); + video_splitter->addWidget(charts_container); video_splitter->setStretchFactor(1, 1); video_splitter->restoreState(settings.video_splitter_state); - if (!can->liveStreaming() && video_splitter->sizes()[0] == 0) { + if (can->liveStreaming() || video_splitter->sizes()[0] == 0) { // display video at minimum size. video_splitter->setSizes({1, 1}); } diff --git a/tools/cabana/streams/livestream.cc b/tools/cabana/streams/livestream.cc index b0af9f3281..3edd2a7758 100644 --- a/tools/cabana/streams/livestream.cc +++ b/tools/cabana/streams/livestream.cc @@ -1,10 +1,6 @@ #include "tools/cabana/streams/livestream.h" LiveStream::LiveStream(QObject *parent, QString address) : zmq_address(address), AbstractStream(parent, true) { - if (!zmq_address.isEmpty()) { - setenv("ZMQ", "1", 1); - } - timer = new QTimer(this); timer->callOnTimeout(this, &LiveStream::removeExpiredEvents); timer->start(3 * 1000); @@ -24,12 +20,14 @@ LiveStream::~LiveStream() { } void LiveStream::streamThread() { + if (!zmq_address.isEmpty()) { + setenv("ZMQ", "1", 1); + } std::unique_ptr context(Context::create()); std::string address = zmq_address.isEmpty() ? "127.0.0.1" : zmq_address.toStdString(); std::unique_ptr sock(SubSocket::create(context.get(), "can", address)); assert(sock != NULL); sock->setTimeout(50); - // run as fast as messages come in while (!QThread::currentThread()->isInterruptionRequested()) { Message *msg = sock->receive(true); @@ -40,22 +38,40 @@ void LiveStream::streamThread() { AlignedBuffer *buf = messages.emplace_back(new AlignedBuffer()); Event *evt = ::new Event(buf->align(msg)); delete msg; - { - std::lock_guard lk(lock); - can_events.push_back(evt); - } - if (start_ts == 0) { - start_ts = evt->mono_time; - emit streamStarted(); - } - current_ts = evt->mono_time; + handleEvent(evt); + // TODO: write stream to log file to replay it with cabana --data_dir flag. + } +} + +void LiveStream::handleEvent(Event *evt) { + std::lock_guard lk(lock); + can_events.push_back(evt); + + current_ts = evt->mono_time; + if (start_ts == 0 || current_ts < start_ts) { if (current_ts < start_ts) { qDebug() << "stream is looping back to old time stamp"; - start_ts = current_ts.load(); + } + start_ts = current_ts.load(); + emit streamStarted(); + } + + if (!pause_) { + if (speed_ < 1 && last_update_ts > 0) { + auto it = std::upper_bound(can_events.begin(), can_events.end(), last_update_event_ts, [](uint64_t ts, auto &e) { + return ts < e->mono_time; + }); + + if (it != can_events.end() && + (nanos_since_boot() - last_update_ts) < ((*it)->mono_time - last_update_event_ts) / speed_) { + return; + } + evt = (*it); } updateEvent(evt); - // TODO: write stream to log file to replay it with cabana --data_dir flag. + last_update_event_ts = evt->mono_time; + last_update_ts = nanos_since_boot(); } } @@ -80,3 +96,8 @@ const std::vector *LiveStream::events() const { std::copy(can_events.begin(), can_events.end(), std::back_inserter(events_vector)); return &events_vector; } + +void LiveStream::pause(bool pause) { + pause_ = pause; + emit paused(); +} diff --git a/tools/cabana/streams/livestream.h b/tools/cabana/streams/livestream.h index db1638d761..ba20032b67 100644 --- a/tools/cabana/streams/livestream.h +++ b/tools/cabana/streams/livestream.h @@ -8,17 +8,21 @@ class LiveStream : public AbstractStream { public: LiveStream(QObject *parent, QString address = {}); - ~LiveStream(); + virtual ~LiveStream(); inline QString routeName() const override { return QString("Live Streaming From %1").arg(zmq_address.isEmpty() ? "127.0.0.1" : zmq_address); } inline double routeStartTime() const override { return start_ts / (double)1e9; } inline double currentSec() const override { return (current_ts - start_ts) / (double)1e9; } + void setSpeed(float speed) override { speed_ = std::min(1.0, speed); } + bool isPaused() const override { return pause_; } + void pause(bool pause) override; const std::vector *events() const override; protected: - void streamThread(); - void removeExpiredEvents(); + virtual void handleEvent(Event *evt); + virtual void streamThread(); + virtual void removeExpiredEvents(); mutable std::mutex lock; mutable std::vector events_vector; @@ -26,6 +30,11 @@ protected: std::deque messages; std::atomic start_ts = 0; std::atomic current_ts = 0; + std::atomic speed_ = 1; + std::atomic pause_ = false; + uint64_t last_update_event_ts = 0; + uint64_t last_update_ts = 0; + const QString zmq_address; QThread *stream_thread; QTimer *timer; diff --git a/tools/cabana/videowidget.cc b/tools/cabana/videowidget.cc index 729302adaa..445a21c755 100644 --- a/tools/cabana/videowidget.cc +++ b/tools/cabana/videowidget.cc @@ -27,22 +27,9 @@ VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent) { main_layout->addWidget(frame); QVBoxLayout *frame_layout = new QVBoxLayout(frame); - cam_widget = new CameraWidget("camerad", can->visionStreamType(), false); - frame_layout->addWidget(cam_widget); - - // slider controls - slider_layout = new QHBoxLayout(); - time_label = new ElidedLabel("00:00"); - time_label->setToolTip(tr("Click to set current time")); - slider_layout->addWidget(time_label); - - slider = new Slider(this); - slider->setSingleStep(0); - slider_layout->addWidget(slider); - - end_time_label = new QLabel(this); - slider_layout->addWidget(end_time_label); - frame_layout->addLayout(slider_layout); + if (!can->liveStreaming()) { + frame_layout->addWidget(createCameraWidget()); + } // btn controls QHBoxLayout *control_layout = new QHBoxLayout(); @@ -52,6 +39,8 @@ VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent) { QButtonGroup *group = new QButtonGroup(this); group->setExclusive(true); for (float speed : {0.1, 0.5, 1., 2.}) { + if (can->liveStreaming() && speed > 1) continue; + QPushButton *btn = new QPushButton(QString("%1x").arg(speed), this); btn->setCheckable(true); QObject::connect(btn, &QPushButton::clicked, [=]() { can->setSpeed(speed); }); @@ -61,6 +50,33 @@ VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent) { } frame_layout->addLayout(control_layout); + QObject::connect(play_btn, &QPushButton::clicked, []() { can->pause(!can->isPaused()); }); + QObject::connect(can, &AbstractStream::paused, this, &VideoWidget::updatePlayBtnState); + QObject::connect(can, &AbstractStream::resume, this, &VideoWidget::updatePlayBtnState); + updatePlayBtnState(); +} + +QWidget *VideoWidget::createCameraWidget() { + QWidget *w = new QWidget(this); + QVBoxLayout *l = new QVBoxLayout(w); + l->setContentsMargins(0, 0, 0, 0); + cam_widget = new CameraWidget("camerad", can->visionStreamType(), false); + l->addWidget(cam_widget); + + // slider controls + slider_layout = new QHBoxLayout(); + time_label = new ElidedLabel("00:00"); + time_label->setToolTip(tr("Click to set current time")); + slider_layout->addWidget(time_label); + + slider = new Slider(this); + slider->setSingleStep(0); + slider_layout->addWidget(slider); + + end_time_label = new QLabel(this); + slider_layout->addWidget(end_time_label); + l->addLayout(slider_layout); + cam_widget->setMinimumHeight(100); cam_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); @@ -69,15 +85,12 @@ VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent) { QObject::connect(slider, &QSlider::sliderReleased, [this]() { can->seekTo(slider->value() / 1000.0); }); QObject::connect(slider, &QSlider::valueChanged, [=](int value) { time_label->setText(formatTime(value / 1000)); }); QObject::connect(cam_widget, &CameraWidget::clicked, []() { can->pause(!can->isPaused()); }); - QObject::connect(play_btn, &QPushButton::clicked, []() { can->pause(!can->isPaused()); }); QObject::connect(can, &AbstractStream::updated, this, &VideoWidget::updateState); - QObject::connect(can, &AbstractStream::paused, this, &VideoWidget::updatePlayBtnState); - QObject::connect(can, &AbstractStream::resume, this, &VideoWidget::updatePlayBtnState); QObject::connect(can, &AbstractStream::streamStarted, [this]() { end_time_label->setText(formatTime(can->totalSeconds())); slider->setRange(0, can->totalSeconds() * 1000); }); - updatePlayBtnState(); + return w; } void VideoWidget::timeLabelClicked() { @@ -101,6 +114,8 @@ void VideoWidget::timeLabelClicked() { } void VideoWidget::rangeChanged(double min, double max, bool is_zoomed) { + if (can->liveStreaming()) return; + if (!is_zoomed) { min = 0; max = can->totalSeconds(); diff --git a/tools/cabana/videowidget.h b/tools/cabana/videowidget.h index 392db23512..51197eedd6 100644 --- a/tools/cabana/videowidget.h +++ b/tools/cabana/videowidget.h @@ -49,6 +49,7 @@ protected: void updateState(); void updatePlayBtnState(); void timeLabelClicked(); + QWidget *createCameraWidget(); CameraWidget *cam_widget; QLabel *end_time_label; From 500e379b4343fb3c3bd017106683636467517cc3 Mon Sep 17 00:00:00 2001 From: Ryan Date: Tue, 24 Jan 2023 18:40:38 -0500 Subject: [PATCH 161/484] VW MQB: Add FW for 2021 Volkswagen Tiguan. (#26966) Co-authored-by: Shane Smiskol --- selfdrive/car/volkswagen/values.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py index 51161f2972..bdb43c542a 100755 --- a/selfdrive/car/volkswagen/values.py +++ b/selfdrive/car/volkswagen/values.py @@ -710,10 +710,12 @@ FW_VERSIONS = { b'\xf1\x875N0906259 \xf1\x890002', b'\xf1\x875NA907115E \xf1\x890005', b'\xf1\x8783A907115B \xf1\x890005', + b'\xf1\x8783A907115F \xf1\x890002', b'\xf1\x8783A907115G \xf1\x890001', ], (Ecu.transmission, 0x7e1, None): [ b'\xf1\x8709G927158DT\xf1\x893698', + b'\xf1\x8709G927158FM\xf1\x893757', b'\xf1\x8709G927158GC\xf1\x893821', b'\xf1\x8709G927158GD\xf1\x893820', b'\xf1\x870D9300043 \xf1\x895202', From d1c87faedc3f93327aa890e2b5155df9596bb971 Mon Sep 17 00:00:00 2001 From: Jason Wen <47793918+sunnyhaibin@users.noreply.github.com> Date: Tue, 24 Jan 2023 19:11:12 -0500 Subject: [PATCH 162/484] Hyundai: Add FW Versions for Elantra 2023 (#27026) * Hyundai: Add FW Versions for Elantra 2023 * only short where possible * regen Co-authored-by: Shane Smiskol --- docs/CARS.md | 2 +- selfdrive/car/hyundai/values.py | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index 0d4c53ac01..4a46360d4f 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -57,7 +57,7 @@ A supported vehicle is one that just works when you install a comma three. All s |Honda|Pilot 2016-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| |Honda|Ridgeline 2017-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|| |Hyundai|Elantra 2017-19|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai B|| -|Hyundai|Elantra 2021-22|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| +|Hyundai|Elantra 2021-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| |Hyundai|Elantra GT 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|| |Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| |Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|Stock|19 mph|37 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai J|| diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 31cbe68d13..b3cb46699a 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -145,7 +145,7 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { HyundaiCarInfo("Hyundai Elantra GT 2017-19", harness=Harness.hyundai_e), HyundaiCarInfo("Hyundai i30 2017-19", harness=Harness.hyundai_e), ], - CAR.ELANTRA_2021: HyundaiCarInfo("Hyundai Elantra 2021-22", video_link="https://youtu.be/_EdYQtV52-c", harness=Harness.hyundai_k), + CAR.ELANTRA_2021: HyundaiCarInfo("Hyundai Elantra 2021-23", video_link="https://youtu.be/_EdYQtV52-c", harness=Harness.hyundai_k), CAR.ELANTRA_HEV_2021: HyundaiCarInfo("Hyundai Elantra Hybrid 2021-23", video_link="https://youtu.be/_EdYQtV52-c", harness=Harness.hyundai_k), CAR.HYUNDAI_GENESIS: HyundaiCarInfo("Hyundai Genesis 2015-16", min_enable_speed=19 * CV.MPH_TO_MS, harness=Harness.hyundai_j), # TODO: check 2015 packages CAR.IONIQ: HyundaiCarInfo("Hyundai Ioniq Hybrid 2017-19", harness=Harness.hyundai_c), @@ -1322,25 +1322,26 @@ FW_VERSIONS = { (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00CN7_ SCC F-CUP 1.00 1.01 99110-AA000 ', b'\xf1\x00CN7_ SCC FHCUP 1.00 1.01 99110-AA000 ', + b'\xf1\x00CN7_ SCC FNCUP 1.00 1.01 99110-AA000 ', b'\xf1\x8799110AA000\xf1\x00CN7_ SCC FHCUP 1.00 1.01 99110-AA000 ', b'\xf1\x8799110AA000\xf1\x00CN7_ SCC F-CUP 1.00 1.01 99110-AA000 ', ], (Ecu.eps, 0x7d4, None): [ b'\xf1\x87\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf1\x00CN7 MDPS C 1.00 1.06 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 4CNDC106', b'\xf1\x8756310/AA070\xf1\x00CN7 MDPS C 1.00 1.06 56310/AA070 4CNDC106', - b'\xf1\x8756310AA050\x00\xf1\x00CN7 MDPS C 1.00 1.06 56310AA050\x00 4CNDC106', b'\xf1\x8756310AA050\x00\xf1\x00CN7 MDPS C 1.00 1.06 56310AA050\x00 4CNDC106\xf1\xa01.06', + b'\xf1\x00CN7 MDPS C 1.00 1.06 56310AA050\x00 4CNDC106', ], (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.00 99210-AB000 200819', b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.03 99210-AA000 200819', b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.01 99210-AB000 210205', b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.06 99210-AA000 220111', + b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.03 99210-AB000 220426', ], (Ecu.abs, 0x7d1, None): [ b'\xf1\x00CN ESC \t 101 \x10\x03 58910-AB800', b'\xf1\x8758910-AA800\xf1\x00CN ESC \t 104 \x08\x03 58910-AA800', - b'\xf1\x8758910-AB800\xf1\x00CN ESC \t 101 \x10\x03 58910-AB800', b'\xf1\x8758910-AA800\xf1\x00CN ESC \t 105 \x10\x03 58910-AA800', b'\xf1\x8758910-AB800\xf1\x00CN ESC \t 101 \x10\x03 58910-AB800\xf1\xa01.01', ], @@ -1358,6 +1359,7 @@ FW_VERSIONS = { b'\xf1\x81HM6M2_0a0_FF0', b'\xf1\x82CNCVD0AMFCXCSFFB', b'\xf1\x870\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf1\x81HM6M2_0a0_G80', + b'\xf1\x870\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf1\x81HM6M2_0a0_HC0', ], }, CAR.ELANTRA_HEV_2021: { From 81956649c8fad46beb6668009f2136b341260434 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 25 Jan 2023 08:14:08 +0800 Subject: [PATCH 163/484] cabana: improve layout (#27039) improve layout --- tools/cabana/binaryview.cc | 7 ++++++- tools/cabana/binaryview.h | 1 + tools/cabana/detailwidget.cc | 1 - tools/cabana/detailwidget.h | 1 + tools/cabana/mainwin.cc | 1 - 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/tools/cabana/binaryview.cc b/tools/cabana/binaryview.cc index 84c2dd2a52..eaa33724e8 100644 --- a/tools/cabana/binaryview.cc +++ b/tools/cabana/binaryview.cc @@ -11,6 +11,7 @@ // BinaryView const int CELL_HEIGHT = 36; +const int VERTICAL_HEADER_WIDTH = 30; inline int get_bit_index(const QModelIndex &index, bool little_endian) { return index.row() * 8 + (little_endian ? 7 - index.column() : index.column()); @@ -33,6 +34,10 @@ BinaryView::BinaryView(QWidget *parent) : QTableView(parent) { setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); } +QSize BinaryView::minimumSizeHint() const { + return {horizontalHeader()->minimumSectionSize() * 9 + VERTICAL_HEADER_WIDTH + 2, 0}; +} + void BinaryView::highlight(const Signal *sig) { if (sig != hovered_sig) { hovered_sig = sig; @@ -215,7 +220,7 @@ QVariant BinaryViewModel::headerData(int section, Qt::Orientation orientation, i if (orientation == Qt::Vertical) { switch (role) { case Qt::DisplayRole: return section; - case Qt::SizeHintRole: return QSize(30, 0); + case Qt::SizeHintRole: return QSize(VERTICAL_HEADER_WIDTH, 0); case Qt::TextAlignmentRole: return Qt::AlignCenter; } } diff --git a/tools/cabana/binaryview.h b/tools/cabana/binaryview.h index e936efc658..55de71572e 100644 --- a/tools/cabana/binaryview.h +++ b/tools/cabana/binaryview.h @@ -60,6 +60,7 @@ public: void highlight(const Signal *sig); QSet getOverlappingSignals() const; inline void updateState() { model->updateState(); } + QSize minimumSizeHint() const override; signals: void signalClicked(const Signal *sig); diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index 78e5a6a6f6..7f59087643 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -16,7 +16,6 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(charts), QWidget(parent) { undo_stack = new QUndoStack(this); - setMinimumWidth(500); QWidget *main_widget = new QWidget(this); QVBoxLayout *main_layout = new QVBoxLayout(main_widget); main_layout->setContentsMargins(0, 0, 0, 0); diff --git a/tools/cabana/detailwidget.h b/tools/cabana/detailwidget.h index 18c58ab6bf..b07629084a 100644 --- a/tools/cabana/detailwidget.h +++ b/tools/cabana/detailwidget.h @@ -31,6 +31,7 @@ public: DetailWidget(ChartsWidget *charts, QWidget *parent); void setMessage(const QString &message_id); void dbcMsgChanged(int show_form_idx = -1); + QSize minimumSizeHint() const override { return binary_view->minimumSizeHint(); } QUndoStack *undo_stack = nullptr; private: diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index e49148160d..bd32cfdfcc 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -24,7 +24,6 @@ void qLogMessageHandler(QtMsgType type, const QMessageLogContext &context, const MainWindow::MainWindow() : QMainWindow() { createDockWindows(); detail_widget = new DetailWidget(charts_widget, this); - detail_widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); setCentralWidget(detail_widget); createActions(); createStatusBar(); From e2e780e7c650878c2b3305531efe8239d6a99933 Mon Sep 17 00:00:00 2001 From: ZwX1616 Date: Tue, 24 Jan 2023 16:34:41 -0800 Subject: [PATCH 164/484] ci: intra-camera timing check (#27069) * test frame time * 1 is still much tighter than 5 --- system/camerad/test/test_camerad.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/system/camerad/test/test_camerad.py b/system/camerad/test/test_camerad.py index 34411e4ef1..f03c531b20 100755 --- a/system/camerad/test/test_camerad.py +++ b/system/camerad/test/test_camerad.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import time import unittest +import numpy as np from collections import defaultdict import cereal.messaging as messaging @@ -12,6 +13,8 @@ from system.hardware import TICI TEST_TIMESPAN = 30 LAG_FRAME_TOLERANCE = {log.FrameData.ImageSensor.ar0231: 0.5, # ARs use synced pulses for frame starts log.FrameData.ImageSensor.ox03c10: 1.0} # OXs react to out-of-sync at next frame +FRAME_DELTA_TOLERANCE = {log.FrameData.ImageSensor.ar0231: 1.0, + log.FrameData.ImageSensor.ox03c10: 1.0} CAMERAS = ('roadCameraState', 'driverCameraState', 'wideRoadCameraState') @@ -36,10 +39,16 @@ class TestCamerad(unittest.TestCase): managed_processes['camerad'].stop() cls.log_by_frame_id = defaultdict(list) + cls.sensor_type = None for cam, msgs in cls.logs.items(): + if cls.sensor_type is None: + cls.sensor_type = getattr(msgs[0], msgs[0].which()).sensor.raw expected_frames = service_list[cam].frequency * TEST_TIMESPAN assert expected_frames*0.95 < len(msgs) < expected_frames*1.05, f"unexpected frame count {cam}: {expected_frames=}, got {len(msgs)}" + dts = np.abs(np.diff([getattr(m, m.which()).timestampSof/1e6 for m in msgs]) - 1000/service_list[cam].frequency) + assert (dts < FRAME_DELTA_TOLERANCE[cls.sensor_type]).all(), f"{cam} dts(ms) out of spec: max diff {dts.max()}, 99 percentile {np.percentile(dts, 99)}" + for m in msgs: cls.log_by_frame_id[getattr(m, m.which()).frameId].append(m) @@ -64,17 +73,16 @@ class TestCamerad(unittest.TestCase): assert len(skips) == 0, f"Found frame skips, missing cameras for the following frames: {skips}" def test_frame_sync(self): - sensor_type = [getattr(msgs[0], msgs[0].which()).sensor for frame_id, msgs in self.log_by_frame_id.items()][0].raw frame_times = {frame_id: [getattr(m, m.which()).timestampSof for m in msgs] for frame_id, msgs in self.log_by_frame_id.items()} diffs = {frame_id: (max(ts) - min(ts))/1e6 for frame_id, ts in frame_times.items()} def get_desc(fid, diff): cam_times = [(m.which(), getattr(m, m.which()).timestampSof/1e6) for m in self.log_by_frame_id[fid]] return (diff, cam_times) - laggy_frames = {k: get_desc(k, v) for k, v in diffs.items() if v > LAG_FRAME_TOLERANCE[sensor_type]} + laggy_frames = {k: get_desc(k, v) for k, v in diffs.items() if v > LAG_FRAME_TOLERANCE[self.sensor_type]} def in_tol(diff): - return 50 - LAG_FRAME_TOLERANCE[sensor_type] < diff and diff < 50 + LAG_FRAME_TOLERANCE[sensor_type] + return 50 - LAG_FRAME_TOLERANCE[self.sensor_type] < diff and diff < 50 + LAG_FRAME_TOLERANCE[self.sensor_type] if len(laggy_frames) != 0 and all( in_tol(laggy_frames[lf][0]) for lf in laggy_frames): print("TODO: handle camera out of sync") else: From 1e9ac45221104923708a08a9198d35099a1df23d Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 24 Jan 2023 18:37:00 -0800 Subject: [PATCH 165/484] Honda Nidec: gather available ECUs from camera (#27071) Query bus 0 for Nidec data collection --- selfdrive/car/honda/values.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/selfdrive/car/honda/values.py b/selfdrive/car/honda/values.py index 1455609226..1e0d22379c 100644 --- a/selfdrive/car/honda/values.py +++ b/selfdrive/car/honda/values.py @@ -155,6 +155,13 @@ FW_QUERY_CONFIG = FwQueryConfig( Request( [StdQueries.UDS_VERSION_REQUEST], [StdQueries.UDS_VERSION_RESPONSE], + bus=1, + ), + # Query Nidec PT bus from camera for data collection + Request( + [StdQueries.UDS_VERSION_REQUEST], + [StdQueries.UDS_VERSION_RESPONSE], + bus=0, ), ], ) From 7ba8e56e877e8d0332def1fb4fedc09945776132 Mon Sep 17 00:00:00 2001 From: Vivek Aithal Date: Tue, 24 Jan 2023 19:22:13 -0800 Subject: [PATCH 166/484] [RAM 1500 5TH GEN] Update offline torque parameters and enable torqued (#27072) * make run torque learnable * update ram offline values * add ram 1500 to torqued * update refs --- selfdrive/car/torque_data/override.yaml | 1 - selfdrive/car/torque_data/params.yaml | 1 + selfdrive/locationd/torqued.py | 18 +++++++++++++----- selfdrive/test/process_replay/ref_commit | 2 +- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/selfdrive/car/torque_data/override.yaml b/selfdrive/car/torque_data/override.yaml index 3a9f92c046..6238c6ff99 100644 --- a/selfdrive/car/torque_data/override.yaml +++ b/selfdrive/car/torque_data/override.yaml @@ -21,7 +21,6 @@ FORD FOCUS 4TH GEN: [.nan, 1.5, .nan] COMMA BODY: [.nan, 1000, .nan] # Totally new cars -RAM 1500 5TH GEN: [2.0, 2.0, 0.0] RAM HD 5TH GEN: [1.4, 1.4, 0.0] SUBARU OUTBACK 6TH GEN: [2.3, 2.3, 0.11] CHEVROLET BOLT EV 2022: [2.0, 2.0, 0.05] diff --git a/selfdrive/car/torque_data/params.yaml b/selfdrive/car/torque_data/params.yaml index 397b29525d..6a607f3a8a 100644 --- a/selfdrive/car/torque_data/params.yaml +++ b/selfdrive/car/torque_data/params.yaml @@ -56,6 +56,7 @@ LEXUS RX 2020: [1.5228812994274734, 1.431102486563665, 0.164117] LEXUS RX HYBRID 2017: [1.6984261557042386, 1.3211501880159107, 0.1820354534928893] LEXUS RX HYBRID 2020: [1.5522309889823778, 1.255230465866663, 0.2220954003055114] MAZDA CX-9 2021: [1.7601682915983443, 1.0889677335154337, 0.17713792194297195] +RAM 1500 5TH GEN: [1.550848, 2.0, 0.132261] SKODA SUPERB 3RD GEN: [1.166437404652981, 1.1686163012668165, 0.12194533036948708] SUBARU FORESTER 2019: [3.6617001649776793, 2.342197172531713, 0.11075960785398745] SUBARU IMPREZA LIMITED 2019: [1.0670704910352047, 0.8234374840709592, 0.20986563268614938] diff --git a/selfdrive/locationd/torqued.py b/selfdrive/locationd/torqued.py index 588bca1578..cb8101bbd4 100755 --- a/selfdrive/locationd/torqued.py +++ b/selfdrive/locationd/torqued.py @@ -22,7 +22,9 @@ FIT_POINTS_TOTAL_QLOG = 600 MIN_VEL = 15 # m/s FRICTION_FACTOR = 1.5 # ~85% of data coverage FACTOR_SANITY = 0.3 +FACTOR_SANITY_QLOG = 0.5 FRICTION_SANITY = 0.5 +FRICTION_SANITY_QLOG = 0.8 STEER_MIN_THRESHOLD = 0.02 MIN_FILTER_DECAY = 50 MAX_FILTER_DECAY = 250 @@ -35,6 +37,7 @@ MIN_ENGAGE_BUFFER = 2 # secs VERSION = 1 # bump this to invalidate old parameter caches ALLOWED_CARS = ['toyota', 'hyundai'] +ALLOWED_PLATFORMS = ['RAM 1500 5TH GEN'] # for adding individual platforms from a brand without torqued def slope2rot(slope): @@ -100,15 +103,20 @@ class TorqueEstimator: self.min_bucket_points = MIN_BUCKET_POINTS / 10 self.min_points_total = MIN_POINTS_TOTAL_QLOG self.fit_points = FIT_POINTS_TOTAL_QLOG + self.factor_sanity = FACTOR_SANITY_QLOG + self.friction_sanity = FRICTION_SANITY_QLOG + else: self.min_bucket_points = MIN_BUCKET_POINTS self.min_points_total = MIN_POINTS_TOTAL self.fit_points = FIT_POINTS_TOTAL + self.factor_sanity = FACTOR_SANITY + self.friction_sanity = FRICTION_SANITY self.offline_friction = 0.0 self.offline_latAccelFactor = 0.0 self.resets = 0.0 - self.use_params = CP.carName in ALLOWED_CARS + self.use_params = (CP.carName in ALLOWED_CARS) or (CP.carFingerprint in ALLOWED_PLATFORMS) if CP.lateralTuning.which() == 'torque': self.offline_friction = CP.lateralTuning.torque.friction @@ -123,10 +131,10 @@ class TorqueEstimator: 'points': [] } self.decay = MIN_FILTER_DECAY - self.min_lataccel_factor = (1.0 - FACTOR_SANITY) * self.offline_latAccelFactor - self.max_lataccel_factor = (1.0 + FACTOR_SANITY) * self.offline_latAccelFactor - self.min_friction = (1.0 - FRICTION_SANITY) * self.offline_friction - self.max_friction = (1.0 + FRICTION_SANITY) * self.offline_friction + self.min_lataccel_factor = (1.0 - self.factor_sanity) * self.offline_latAccelFactor + self.max_lataccel_factor = (1.0 + self.factor_sanity) * self.offline_latAccelFactor + self.min_friction = (1.0 - self.friction_sanity) * self.offline_friction + self.max_friction = (1.0 + self.friction_sanity) * self.offline_friction # try to restore cached params params = Params() diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 1aa3aa6f1e..fb714b6f57 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -acc5655633af6ade096ad41f680fd8a28c2e3790 \ No newline at end of file +3639e42ed538ee5d94ec2572cf27af6260633fad \ No newline at end of file From a68633283f7269bb6ab2c2670cce0473512992ec Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 24 Jan 2023 20:36:44 -0800 Subject: [PATCH 167/484] Civic 2022: add camera versions (#27073) * Add fwdRadar for Honda Civic 2022 * remove debug cmt --- selfdrive/car/honda/values.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/selfdrive/car/honda/values.py b/selfdrive/car/honda/values.py index 1e0d22379c..e3448b31be 100644 --- a/selfdrive/car/honda/values.py +++ b/selfdrive/car/honda/values.py @@ -1441,6 +1441,12 @@ FW_VERSIONS = { b'78108-T21-A230\x00\x00', b'78108-T22-A020\x00\x00', ], + (Ecu.fwdRadar, 0x18dab0f1, None): [ + b'36161-T20-A070\x00\x00', + b'36161-T20-A080\x00\x00', + b'36161-T20-A060\x00\x00', + b'36161-T47-A070\x00\x00', + ], (Ecu.vsa, 0x18DA28F1, None): [ b'57114-T20-AB40\x00\x00', b'57114-T43-JB30\x00\x00', From 95d45a8d64f5e7bd54cd3e96aec83f44177a0fa7 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 24 Jan 2023 21:24:33 -0800 Subject: [PATCH 168/484] Honda Civic 2022: gather FW versions for unknown ECU (#27074) * Add unknown civic 2022 extra ecu * Update selfdrive/car/honda/values.py --- selfdrive/car/honda/values.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/selfdrive/car/honda/values.py b/selfdrive/car/honda/values.py index e3448b31be..d10ec0a7e6 100644 --- a/selfdrive/car/honda/values.py +++ b/selfdrive/car/honda/values.py @@ -164,6 +164,10 @@ FW_QUERY_CONFIG = FwQueryConfig( bus=0, ), ], + extra_ecus=[ + # The only other ECU on PT bus accessible by camera on radarless Civic + (Ecu.unknown, 0x18DAB3F1, None), + ], ) FW_VERSIONS = { From f2966e9ef4548c6365715f8e5bd55f4e4838dcdc Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 24 Jan 2023 22:30:29 -0800 Subject: [PATCH 169/484] Honda FPv2: log extra UDS identifier (#27076) * Add debugging uds ID * add comment * remove spaces * try chaining the queries (temp debug) this works * works! - Revert "try chaining the queries (temp debug)" This reverts commit c53d0f64aecdbd6224b0fd524f96637d29320cd1. --- selfdrive/car/honda/values.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/selfdrive/car/honda/values.py b/selfdrive/car/honda/values.py index d10ec0a7e6..07eed0cfc2 100644 --- a/selfdrive/car/honda/values.py +++ b/selfdrive/car/honda/values.py @@ -4,9 +4,10 @@ from typing import Dict, List, Optional, Union from cereal import car from common.conversions import Conversions as CV +from panda.python import uds from selfdrive.car import dbc_dict from selfdrive.car.docs_definitions import CarFootnote, CarInfo, Column, Harness -from selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries +from selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16 Ecu = car.CarParams.Ecu VisualAlert = car.CarControl.HUDControl.VisualAlert @@ -150,13 +151,27 @@ CAR_INFO: Dict[str, Optional[Union[HondaCarInfo, List[HondaCarInfo]]]] = { CAR.HONDA_E: HondaCarInfo("Honda e 2020", "All", min_steer_speed=3. * CV.MPH_TO_MS), } +HONDA_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \ + p16(0xF112) +HONDA_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \ + p16(0xF112) + FW_QUERY_CONFIG = FwQueryConfig( requests=[ + # Currently used to fingerprint Request( [StdQueries.UDS_VERSION_REQUEST], [StdQueries.UDS_VERSION_RESPONSE], bus=1, ), + + # Data collection requests: + # Log extra identifiers for current ECUs + Request( + [HONDA_VERSION_REQUEST], + [HONDA_VERSION_RESPONSE], + bus=1, + ), # Query Nidec PT bus from camera for data collection Request( [StdQueries.UDS_VERSION_REQUEST], From 01fa0ca69dd7d6633bab754c3122efe0bc5a330c Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 24 Jan 2023 23:34:55 -0800 Subject: [PATCH 170/484] Hyundai: clean up fingerprint test (#27079) * Clean up Hyundai test * fine --- selfdrive/car/hyundai/tests/test_hyundai.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/selfdrive/car/hyundai/tests/test_hyundai.py b/selfdrive/car/hyundai/tests/test_hyundai.py index a52027f448..6ecfa03796 100755 --- a/selfdrive/car/hyundai/tests/test_hyundai.py +++ b/selfdrive/car/hyundai/tests/test_hyundai.py @@ -2,24 +2,19 @@ import unittest from cereal import car -from selfdrive.car.car_helpers import get_interface_attr -from selfdrive.car.fw_versions import FW_QUERY_CONFIGS -from selfdrive.car.hyundai.values import CANFD_CAR +from selfdrive.car.hyundai.values import CANFD_CAR, FW_QUERY_CONFIG, FW_VERSIONS Ecu = car.CarParams.Ecu - ECU_NAME = {v: k for k, v in Ecu.schema.enumerants.items()} -VERSIONS = get_interface_attr("FW_VERSIONS", ignore_none=True) class TestHyundaiFingerprint(unittest.TestCase): def test_auxiliary_request_ecu_whitelist(self): # Asserts only auxiliary Ecus can exist in database for CAN-FD cars - config = FW_QUERY_CONFIGS['hyundai'] - whitelisted_ecus = {ecu for r in config.requests for ecu in r.whitelist_ecus if r.bus > 3} + whitelisted_ecus = {ecu for r in FW_QUERY_CONFIG.requests for ecu in r.whitelist_ecus if r.bus > 3} for car_model in CANFD_CAR: - ecus = {fw[0] for fw in VERSIONS['hyundai'][car_model].keys()} + ecus = {fw[0] for fw in FW_VERSIONS[car_model].keys()} ecus_not_in_whitelist = ecus - whitelisted_ecus ecu_strings = ", ".join([f'Ecu.{ECU_NAME[ecu]}' for ecu in ecus_not_in_whitelist]) self.assertEqual(len(ecus_not_in_whitelist), 0, f'{car_model}: Car model has ECUs not in auxiliary request whitelists: {ecu_strings}') From 845a63d884ae30f03831e112e636c8d3b06dcaeb Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 25 Jan 2023 00:10:22 -0800 Subject: [PATCH 171/484] Honda: test all FW is in correct format (#27078) * test honda FW is expected * fix static --- selfdrive/car/honda/tests/test_honda.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100755 selfdrive/car/honda/tests/test_honda.py diff --git a/selfdrive/car/honda/tests/test_honda.py b/selfdrive/car/honda/tests/test_honda.py new file mode 100755 index 0000000000..7a8c86fb0a --- /dev/null +++ b/selfdrive/car/honda/tests/test_honda.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +import re +import unittest + +from selfdrive.car.honda.values import FW_VERSIONS + +HONDA_FW_VERSION_RE = br"\d{5}-[A-Z0-9]{3}(-|,)[A-Z0-9]{4}(\x00){2}$" + + +class TestHondaFingerprint(unittest.TestCase): + def test_fw_version_format(self): + # Asserts all FW versions follow an expected format + for fw_by_ecu in FW_VERSIONS.values(): + for fws in fw_by_ecu.values(): + for fw in fws: + self.assertTrue(re.match(HONDA_FW_VERSION_RE, fw) is not None, fw) + + +if __name__ == "__main__": + unittest.main() From 0756c7f374d26945ae27b0cfcabdede9f2060e87 Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Wed, 25 Jan 2023 10:46:06 -0800 Subject: [PATCH 172/484] make process replay more reliable --- selfdrive/test/process_replay/process_replay.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index a7d53bf9da..06af4469f2 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -568,7 +568,7 @@ def cpp_replay_process(cfg, lr, fingerprint=None): resp_sockets = cfg.pub_sub[msg.which()] if cfg.should_recv_callback is None else cfg.should_recv_callback(msg) for s in resp_sockets: - response = messaging.recv_one(sockets[s]) + response = messaging.recv_one_retry(sockets[s]) if response is None: print(f"Warning, no response received {i}") From 892835da7ad02d109794c5dc8e79ec938eebd572 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 26 Jan 2023 02:51:31 +0800 Subject: [PATCH 173/484] cabana: fix a wrong settings key (#27085) fix wrong key --- tools/cabana/settings.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cabana/settings.cc b/tools/cabana/settings.cc index 8c26b265e0..e06d149831 100644 --- a/tools/cabana/settings.cc +++ b/tools/cabana/settings.cc @@ -28,7 +28,7 @@ void Settings::save() { void Settings::load() { QSettings s("settings", QSettings::IniFormat); fps = s.value("fps", 10).toInt(); - max_cached_minutes = s.value("cached_minutes", 5).toInt(); + max_cached_minutes = s.value("max_cached_minutes", 5).toInt(); chart_height = s.value("chart_height", 200).toInt(); chart_range = s.value("chart_range", 3 * 60).toInt(); chart_column_count = s.value("chart_column_count", 1).toInt(); From 2de2bab2864eeaea8104ac0cbb619c5c25b8b4d6 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 26 Jan 2023 02:59:05 +0800 Subject: [PATCH 174/484] cabana: fix divde by zero for std::log10 (#27077) fix divde by zero for std::log10 --- tools/cabana/chartswidget.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 2965ae0476..5d0e5d7a52 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -332,6 +332,9 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { } qreal ChartView::getYAsixLabelWidth() const { + if (axis_y->max() <= axis_y->min() || axis_y->tickCount() <= 1) { + return 0; + } QFontMetrics fm(axis_y->labelsFont()); int n = qMax(int(-qFloor(std::log10((axis_y->max() - axis_y->min()) / (axis_y->tickCount() - 1)))), 0) + 1; return qMax(fm.width(QString::number(axis_y->min(), 'f', n)), fm.width(QString::number(axis_y->max(), 'f', n))) + 20; From dd2e44232cfc3028d19875e971c6feb036ae6890 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 25 Jan 2023 19:18:24 -0800 Subject: [PATCH 175/484] bump panda --- panda | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda b/panda index 687676be40..ae051c94a3 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 687676be4025d9caf9520be89cb328d6a3cc3e10 +Subproject commit ae051c94a393550ab8d70c594fc9dfb49bc9aed8 From 3cd60a47244905c069de8bf0aa7a20b38b7ccb62 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Wed, 25 Jan 2023 20:21:14 -0800 Subject: [PATCH 176/484] Ford cleanup (#27090) * fordcan cleanup Co-authored-by: Cameron Clough * no fordcan Co-authored-by: Cameron Clough * more Co-authored-by: Cameron Clough * these weren't even used in CS, follow rest of the brands * we won't use this * rename to more standard powertrain can bus * no arguments for lka anymore * Revert "rename to more standard powertrain can bus" This reverts commit 0bc3f79f9bad2218c48a350bbf5ec6f726e0a028. Co-authored-by: Shane Smiskol --- selfdrive/car/ford/carcontroller.py | 23 +++++----- selfdrive/car/ford/carstate.py | 2 +- selfdrive/car/ford/fordcan.py | 66 +++++++++++++++-------------- selfdrive/car/ford/interface.py | 9 ++-- selfdrive/car/ford/values.py | 3 -- 5 files changed, 50 insertions(+), 53 deletions(-) diff --git a/selfdrive/car/ford/carcontroller.py b/selfdrive/car/ford/carcontroller.py index 8c391bb276..8164a8a970 100644 --- a/selfdrive/car/ford/carcontroller.py +++ b/selfdrive/car/ford/carcontroller.py @@ -2,7 +2,7 @@ import math from cereal import car from common.numpy_fast import clip, interp from opendbc.can.packer import CANPacker -from selfdrive.car.ford import fordcan +from selfdrive.car.ford.fordcan import create_acc_ui_msg, create_button_msg, create_lat_ctl_msg, create_lka_msg, create_lkas_ui_msg from selfdrive.car.ford.values import CANBUS, CarControllerParams VisualAlert = car.CarControl.HUDControl.VisualAlert @@ -46,16 +46,15 @@ class CarController: ### acc buttons ### if CC.cruiseControl.cancel: - can_sends.append(fordcan.create_button_command(self.packer, CS.buttons_stock_values, cancel=True)) - can_sends.append(fordcan.create_button_command(self.packer, CS.buttons_stock_values, cancel=True, bus=CANBUS.main)) + can_sends.append(create_button_msg(self.packer, CS.buttons_stock_values, cancel=True)) + can_sends.append(create_button_msg(self.packer, CS.buttons_stock_values, cancel=True, bus=CANBUS.main)) elif CC.cruiseControl.resume and (self.frame % CarControllerParams.BUTTONS_STEP) == 0: - can_sends.append(fordcan.create_button_command(self.packer, CS.buttons_stock_values, resume=True)) - can_sends.append(fordcan.create_button_command(self.packer, CS.buttons_stock_values, resume=True, bus=CANBUS.main)) + can_sends.append(create_button_msg(self.packer, CS.buttons_stock_values, resume=True)) + can_sends.append(create_button_msg(self.packer, CS.buttons_stock_values, resume=True, bus=CANBUS.main)) # if stock lane centering isn't off, send a button press to toggle it off # the stock system checks for steering pressed, and eventually disengages cruise control elif CS.acc_tja_status_stock_values["Tja_D_Stat"] != 0 and (self.frame % CarControllerParams.ACC_UI_STEP) == 0: - can_sends.append(fordcan.create_button_command(self.packer, CS.buttons_stock_values, tja_toggle=True)) - + can_sends.append(create_button_msg(self.packer, CS.buttons_stock_values, tja_toggle=True)) ### lateral control ### if CC.latActive: @@ -84,21 +83,19 @@ class CarController: precision = 1 # 0=Comfortable, 1=Precise (the stock system always uses comfortable) self.apply_angle_last = apply_angle - can_sends.append(fordcan.create_lka_command(self.packer, 0, 0)) - can_sends.append(fordcan.create_tja_command(self.packer, lca_rq, ramp_type, precision, - 0, path_angle, 0, 0)) - + can_sends.append(create_lka_msg(self.packer)) + can_sends.append(create_lat_ctl_msg(self.packer, lca_rq, ramp_type, precision, 0, path_angle, 0, 0)) ### ui ### send_ui = (self.main_on_last != main_on) or (self.lkas_enabled_last != CC.latActive) or (self.steer_alert_last != steer_alert) # send lkas ui command at 1Hz or if ui state changes if (self.frame % CarControllerParams.LKAS_UI_STEP) == 0 or send_ui: - can_sends.append(fordcan.create_lkas_ui_command(self.packer, main_on, CC.latActive, steer_alert, hud_control, CS.lkas_status_stock_values)) + can_sends.append(create_lkas_ui_msg(self.packer, main_on, CC.latActive, steer_alert, hud_control, CS.lkas_status_stock_values)) # send acc ui command at 20Hz or if ui state changes if (self.frame % CarControllerParams.ACC_UI_STEP) == 0 or send_ui: - can_sends.append(fordcan.create_acc_ui_command(self.packer, main_on, CC.latActive, hud_control, CS.acc_tja_status_stock_values)) + can_sends.append(create_acc_ui_msg(self.packer, main_on, CC.latActive, hud_control, CS.acc_tja_status_stock_values)) self.main_on_last = main_on self.lkas_enabled_last = CC.latActive diff --git a/selfdrive/car/ford/carstate.py b/selfdrive/car/ford/carstate.py index 2276b1208a..215900fef6 100644 --- a/selfdrive/car/ford/carstate.py +++ b/selfdrive/car/ford/carstate.py @@ -118,7 +118,7 @@ class CarState(CarStateBase): ("DrStatRl_B_Actl", "BodyInfo_3_FD1"), # BCM Door open, rear left ("DrStatRr_B_Actl", "BodyInfo_3_FD1"), # BCM Door open, rear right ("FirstRowBuckleDriver", "RCMStatusMessage2_FD1"), # RCM Seatbelt status, driver - ("HeadLghtHiFlash_D_Stat", "Steering_Data_FD1"), # SCCM Passthru the remaining buttons + ("HeadLghtHiFlash_D_Stat", "Steering_Data_FD1"), # SCCM Passthrough the remaining buttons ("WiprFront_D_Stat", "Steering_Data_FD1"), ("LghtAmb_D_Sns", "Steering_Data_FD1"), ("AccButtnGapDecPress", "Steering_Data_FD1"), diff --git a/selfdrive/car/ford/fordcan.py b/selfdrive/car/ford/fordcan.py index 373ce096c6..af80894ff8 100644 --- a/selfdrive/car/ford/fordcan.py +++ b/selfdrive/car/ford/fordcan.py @@ -4,9 +4,9 @@ from selfdrive.car.ford.values import CANBUS HUDControl = car.CarControl.HUDControl -def create_lka_command(packer, angle_deg: float, curvature: float): +def create_lka_msg(packer): """ - Creates a CAN message for the Ford LKAS Command. + Creates a CAN message for the Ford LKA Command. This command can apply "Lane Keeping Aid" manoeuvres, which are subject to the PSCM lockout. @@ -16,52 +16,54 @@ def create_lka_command(packer, angle_deg: float, curvature: float): values = { "LkaDrvOvrrd_D_Rq": 0, # driver override level? [0|3] "LkaActvStats_D2_Req": 0, # action [0|7] - "LaRefAng_No_Req": angle_deg, # angle [-102.4|102.3] degrees + "LaRefAng_No_Req": 0, # angle [-102.4|102.3] degrees "LaRampType_B_Req": 0, # Ramp speed: 0=Smooth, 1=Quick - "LaCurvature_No_Calc": curvature, # curvature [-0.01024|0.01023] 1/meter + "LaCurvature_No_Calc": 0, # curvature [-0.01024|0.01023] 1/meter "LdwActvStats_D_Req": 0, # LDW status [0|7] "LdwActvIntns_D_Req": 0, # LDW intensity [0|3], shake alert strength } return packer.make_can_msg("Lane_Assist_Data1", CANBUS.main, values) -def create_tja_command(packer, lca_rq: int, ramp_type: int, precision: int, path_offset: float, path_angle: float, curvature_rate: float, curvature: float): +def create_lat_ctl_msg(packer, lca_rq: int, ramp_type: int, precision: int, path_offset: float, path_angle: float, + curvature: float, curvature_rate: float): """ Creates a CAN message for the Ford TJA/LCA Command. - This command can apply "Lane Centering" manoeuvres: continuous lane centering for traffic jam - assist and highway driving. It is not subject to the PSCM lockout. + This command can apply "Lane Centering" manoeuvres: continuous lane centering for traffic jam assist and highway + driving. It is not subject to the PSCM lockout. - Ford lane centering command uses a third order polynomial to describe the road centerline. The - polynomial is defined by the following coefficients: - c0: lateral offset between the vehicle and the centerline - c1: heading angle between the vehicle and the centerline - c2: curvature of the centerline + Ford lane centering command uses a third order polynomial to describe the road centerline. The polynomial is defined + by the following coefficients: + c0: lateral offset between the vehicle and the centerline (positive is right) + c1: heading angle between the vehicle and the centerline (positive is right) + c2: curvature of the centerline (positive is left) c3: rate of change of curvature of the centerline - As the PSCM combines this information with other sensor data, such as the vehicle's yaw rate and - speed, the steering angle cannot be easily controlled. + As the PSCM combines this information with other sensor data, such as the vehicle's yaw rate and speed, the steering + angle cannot be easily controlled. - The PSCM should be configured to accept TJA/LCA commands before these commands will be processed. - This can be done using tools such as Forscan. + The PSCM should be configured to accept TJA/LCA commands before these commands will be processed. This can be done + using tools such as Forscan. Frequency is 20Hz. """ values = { - "LatCtlRng_L_Max": 0, # Unknown [0|126] meter - "HandsOffCnfm_B_Rq": 0, # Unknown: 0=Inactive, 1=Active [0|1] - "LatCtl_D_Rq": lca_rq, # Mode: 0=None, 1=ContinuousPathFollowing, 2=InterventionLeft, 3=InterventionRight, 4-7=NotUsed [0|7] - "LatCtlRampType_D_Rq": ramp_type, # Ramp speed: 0=Slow, 1=Medium, 2=Fast, 3=Immediate [0|3] - "LatCtlPrecision_D_Rq": precision, # Precision: 0=Comfortable, 1=Precise, 2/3=NotUsed [0|3] - "LatCtlPathOffst_L_Actl": path_offset, # Path offset [-5.12|5.11] meter - "LatCtlPath_An_Actl": path_angle, # Path angle [-0.5|0.5235] radians - "LatCtlCurv_NoRate_Actl": curvature_rate, # Curvature rate [-0.001024|0.00102375] 1/meter^2 - "LatCtlCurv_No_Actl": curvature, # Curvature [-0.02|0.02094] 1/meter + "LatCtlRng_L_Max": 0, # Unknown [0|126] meter + "HandsOffCnfm_B_Rq": 0, # Unknown: 0=Inactive, 1=Active [0|1] + "LatCtl_D_Rq": lca_rq, # Mode: 0=None, 1=ContinuousPathFollowing, 2=InterventionLeft, + # 3=InterventionRight, 4-7=NotUsed [0|7] + "LatCtlRampType_D_Rq": ramp_type, # Ramp speed: 0=Slow, 1=Medium, 2=Fast, 3=Immediate [0|3] + "LatCtlPrecision_D_Rq": precision, # Precision: 0=Comfortable, 1=Precise, 2/3=NotUsed [0|3] + "LatCtlPathOffst_L_Actl": path_offset, # Path offset [-5.12|5.11] meter + "LatCtlPath_An_Actl": path_angle, # Path angle [-0.5|0.5235] radians + "LatCtlCurv_NoRate_Actl": curvature_rate, # Curvature rate [-0.001024|0.00102375] 1/meter^2 + "LatCtlCurv_No_Actl": curvature, # Curvature [-0.02|0.02094] 1/meter } return packer.make_can_msg("LateralMotionControl", CANBUS.main, values) -def create_lkas_ui_command(packer, main_on: bool, enabled: bool, steer_alert: bool, hud_control, stock_values: dict): +def create_lkas_ui_msg(packer, main_on: bool, enabled: bool, steer_alert: bool, hud_control, stock_values: dict): """ Creates a CAN message for the Ford IPC IPMA/LKAS status. @@ -107,16 +109,15 @@ def create_lkas_ui_command(packer, main_on: bool, enabled: bool, steer_alert: bo values = { **stock_values, - "LaActvStats_D_Dsply": lines, # LKAS status (lines) [0|31] - "LaHandsOff_D_Dsply": hands_on_wheel_dsply, # 0=HandsOn, 1=Level1 (w/o chime), 2=Level2 (w/ chime), 3=Suppressed + "LaActvStats_D_Dsply": lines, # LKAS status (lines) [0|31] + "LaHandsOff_D_Dsply": hands_on_wheel_dsply, # 0=HandsOn, 1=Level1 (w/o chime), 2=Level2 (w/ chime), 3=Suppressed } return packer.make_can_msg("IPMA_Data", CANBUS.main, values) -def create_acc_ui_command(packer, main_on: bool, enabled: bool, hud_control, stock_values: dict): +def create_acc_ui_msg(packer, main_on: bool, enabled: bool, hud_control, stock_values: dict): """ - Creates a CAN message for the Ford IPC adaptive cruise, forward collision warning and traffic jam - assist status. + Creates a CAN message for the Ford IPC adaptive cruise, forward collision warning and traffic jam assist status. Stock functionality is maintained by passing through unmodified signals. @@ -148,7 +149,8 @@ def create_acc_ui_command(packer, main_on: bool, enabled: bool, hud_control, sto return packer.make_can_msg("ACCDATA_3", CANBUS.main, values) -def create_button_command(packer, stock_values: dict, cancel = False, resume = False, tja_toggle = False, bus: int = CANBUS.camera): +def create_button_msg(packer, stock_values: dict, cancel=False, resume=False, tja_toggle=False, + bus: int = CANBUS.camera): """ Creates a CAN message for the Ford SCCM buttons/switches. diff --git a/selfdrive/car/ford/interface.py b/selfdrive/car/ford/interface.py index f3d77bc05a..b0e97677b5 100644 --- a/selfdrive/car/ford/interface.py +++ b/selfdrive/car/ford/interface.py @@ -2,10 +2,11 @@ from cereal import car from common.conversions import Conversions as CV from selfdrive.car import STD_CARGO_KG, get_safety_config -from selfdrive.car.ford.values import CAR, Ecu, TransmissionType, GearShifter +from selfdrive.car.ford.values import CAR, Ecu from selfdrive.car.interfaces import CarInterfaceBase -CarParams = car.CarParams +TransmissionType = car.CarParams.TransmissionType +GearShifter = car.CarState.GearShifter class CarInterface(CarInterfaceBase): @@ -13,10 +14,10 @@ class CarInterface(CarInterfaceBase): def _get_params(ret, candidate, fingerprint, car_fw, experimental_long): ret.carName = "ford" ret.dashcamOnly = True - ret.safetyConfigs = [get_safety_config(CarParams.SafetyModel.ford)] + ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.ford)] # Angle-based steering - ret.steerControlType = CarParams.SteerControlType.angle + ret.steerControlType = car.CarParams.SteerControlType.angle ret.steerActuatorDelay = 0.4 ret.steerLimitTimer = 1.0 diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index 0e6ef464b3..87cb98878d 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -9,9 +9,6 @@ from selfdrive.car.docs_definitions import CarInfo, Harness from selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries Ecu = car.CarParams.Ecu -TransmissionType = car.CarParams.TransmissionType -GearShifter = car.CarState.GearShifter - AngleRateLimit = namedtuple('AngleRateLimit', ['speed_points', 'max_angle_diff_points']) From d410206f14fc7a0207464905a1ff2f684bd3ed39 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Wed, 25 Jan 2023 20:56:03 -0800 Subject: [PATCH 177/484] Ford: add Bronco Sport 2021-22 (#27092) * cleanup * add Ford Bronco Sport 2021 fw VIN: 3FMCR9B61MRA17853 62241b0c7fea4589|2022-10-25--13-40-05--0 * add test route * add Ford Bronco Sport 2022 fw VIN: 3FMCR9B69NRD15990 54827bf84c38b14f|2023-01-23--14-58-23--0 --- selfdrive/car/ford/interface.py | 12 +++++++++--- selfdrive/car/ford/values.py | 26 +++++++++++++++++++++++++ selfdrive/car/tests/routes.py | 1 + selfdrive/car/torque_data/override.yaml | 1 + 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/selfdrive/car/ford/interface.py b/selfdrive/car/ford/interface.py index b0e97677b5..3139aec9b1 100644 --- a/selfdrive/car/ford/interface.py +++ b/selfdrive/car/ford/interface.py @@ -13,15 +13,21 @@ class CarInterface(CarInterfaceBase): @staticmethod def _get_params(ret, candidate, fingerprint, car_fw, experimental_long): ret.carName = "ford" - ret.dashcamOnly = True ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.ford)] - # Angle-based steering + # These cars are dashcam only until the port is finished + ret.dashcamOnly = True + ret.steerControlType = car.CarParams.SteerControlType.angle ret.steerActuatorDelay = 0.4 ret.steerLimitTimer = 1.0 - if candidate == CAR.ESCAPE_MK4: + if candidate == CAR.BRONCO_SPORT_MK1: + ret.wheelbase = 2.67 + ret.steerRatio = 17.7 # learned + ret.mass = 1625 + STD_CARGO_KG + + elif candidate == CAR.ESCAPE_MK4: ret.wheelbase = 2.71 ret.steerRatio = 14.3 # Copied from Focus ret.mass = 1750 + STD_CARGO_KG diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index 87cb98878d..0124a9134f 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -40,6 +40,7 @@ class CANBUS: class CAR: + BRONCO_SPORT_MK1 = "FORD BRONCO SPORT 1ST GEN" ESCAPE_MK4 = "FORD ESCAPE 4TH GEN" EXPLORER_MK6 = "FORD EXPLORER 6TH GEN" FOCUS_MK4 = "FORD FOCUS 4TH GEN" @@ -60,6 +61,7 @@ class FordCarInfo(CarInfo): CAR_INFO: Dict[str, Union[CarInfo, List[CarInfo]]] = { + CAR.BRONCO_SPORT_MK1: FordCarInfo("Ford Bronco Sport 2021-22"), CAR.ESCAPE_MK4: [ FordCarInfo("Ford Escape 2020-21"), FordCarInfo("Ford Kuga 2020-21", "Driver Assistance Pack"), @@ -85,6 +87,30 @@ FW_QUERY_CONFIG = FwQueryConfig( ) FW_VERSIONS = { + CAR.BRONCO_SPORT_MK1: { + (Ecu.eps, 0x730, None): [ + b'LX6C-14D003-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + b'LX6C-14D003-AK\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + ], + (Ecu.abs, 0x760, None): [ + b'LX6C-2D053-RD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + b'LX6C-2D053-RE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + ], + (Ecu.fwdRadar, 0x764, None): [ + b'LB5T-14D049-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + ], + (Ecu.fwdCamera, 0x706, None): [ + b'M1PT-14F397-AC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + ], + (Ecu.engine, 0x7E0, None): [ + b'M1PA-14C204-GF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + b'N1PA-14C204-AC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + ], + (Ecu.shiftByWire, 0x732, None): [ + b'LX6P-14G395-AD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + b'PZ1P-14G395-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + ], + }, CAR.ESCAPE_MK4: { (Ecu.eps, 0x730, None): [ b'LX6C-14D003-AF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', diff --git a/selfdrive/car/tests/routes.py b/selfdrive/car/tests/routes.py index 29cea9c030..3e0a95a38f 100644 --- a/selfdrive/car/tests/routes.py +++ b/selfdrive/car/tests/routes.py @@ -42,6 +42,7 @@ routes = [ CarTestRoute("221c253375af4ee9|2022-06-15--18-38-24", CHRYSLER.RAM_1500), CarTestRoute("8fb5eabf914632ae|2022-08-04--17-28-53", CHRYSLER.RAM_HD, segment=6), + CarTestRoute("54827bf84c38b14f|2023-01-25--14-14-11", FORD.BRONCO_SPORT_MK1), CarTestRoute("62241b0c7fea4589|2022-09-01--15-32-49", FORD.EXPLORER_MK6), #TestRoute("f1b4c567731f4a1b|2018-04-30--10-15-35", FORD.FUSION), diff --git a/selfdrive/car/torque_data/override.yaml b/selfdrive/car/torque_data/override.yaml index 6238c6ff99..1506b4176f 100644 --- a/selfdrive/car/torque_data/override.yaml +++ b/selfdrive/car/torque_data/override.yaml @@ -12,6 +12,7 @@ TESLA AP1 MODEL S: [.nan, 2.5, .nan] TESLA AP2 MODEL S: [.nan, 2.5, .nan] # Guess +FORD BRONCO SPORT 1ST GEN: [.nan, 1.5, .nan] FORD ESCAPE 4TH GEN: [.nan, 1.5, .nan] FORD EXPLORER 6TH GEN: [.nan, 1.5, .nan] FORD FOCUS 4TH GEN: [.nan, 1.5, .nan] From 23298615561b5c8d41df009ba27ac01845c2abe5 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 25 Jan 2023 21:01:32 -0800 Subject: [PATCH 178/484] Ford: send empty LKA message (#27094) Eradication --- selfdrive/car/ford/fordcan.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/selfdrive/car/ford/fordcan.py b/selfdrive/car/ford/fordcan.py index af80894ff8..c3f6e74651 100644 --- a/selfdrive/car/ford/fordcan.py +++ b/selfdrive/car/ford/fordcan.py @@ -6,23 +6,14 @@ HUDControl = car.CarControl.HUDControl def create_lka_msg(packer): """ - Creates a CAN message for the Ford LKA Command. + Creates an empty CAN message for the Ford LKA Command. This command can apply "Lane Keeping Aid" manoeuvres, which are subject to the PSCM lockout. Frequency is 20Hz. """ - values = { - "LkaDrvOvrrd_D_Rq": 0, # driver override level? [0|3] - "LkaActvStats_D2_Req": 0, # action [0|7] - "LaRefAng_No_Req": 0, # angle [-102.4|102.3] degrees - "LaRampType_B_Req": 0, # Ramp speed: 0=Smooth, 1=Quick - "LaCurvature_No_Calc": 0, # curvature [-0.01024|0.01023] 1/meter - "LdwActvStats_D_Req": 0, # LDW status [0|7] - "LdwActvIntns_D_Req": 0, # LDW intensity [0|3], shake alert strength - } - return packer.make_can_msg("Lane_Assist_Data1", CANBUS.main, values) + return packer.make_can_msg("Lane_Assist_Data1", CANBUS.main, {}) def create_lat_ctl_msg(packer, lca_rq: int, ramp_type: int, precision: int, path_offset: float, path_angle: float, From cf69653440a308c6e7abb7d56847b389b64c819f Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Wed, 25 Jan 2023 21:18:35 -0800 Subject: [PATCH 179/484] Ford: add Maverick 2022 (#27093) * Ford: add Maverick 2022 VIN: 3FTTW8E36NRA74219 a6427650bff6b419|2022-09-19--10-51-30 * Ford: add missing Maverick 2022 FW VIN: 3FTTW8F98NRA75889 c845bd8c366e6f3d|2022-12-03--18-57-00--0 * steer ratio --- selfdrive/car/ford/interface.py | 5 +++++ selfdrive/car/ford/values.py | 23 +++++++++++++++++++++++ selfdrive/car/tests/routes.py | 1 + selfdrive/car/torque_data/override.yaml | 1 + 4 files changed, 30 insertions(+) diff --git a/selfdrive/car/ford/interface.py b/selfdrive/car/ford/interface.py index 3139aec9b1..ce4e3e30aa 100644 --- a/selfdrive/car/ford/interface.py +++ b/selfdrive/car/ford/interface.py @@ -42,6 +42,11 @@ class CarInterface(CarInterfaceBase): ret.steerRatio = 13.8 # learned ret.mass = 1350 + STD_CARGO_KG + elif candidate == CAR.MAVERICK_MK1: + ret.wheelbase = 3.076 + ret.steerRatio = 17.0 + ret.mass = 1650 + STD_CARGO_KG + else: raise ValueError(f"Unsupported car: {candidate}") diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index 0124a9134f..1348073801 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -44,6 +44,7 @@ class CAR: ESCAPE_MK4 = "FORD ESCAPE 4TH GEN" EXPLORER_MK6 = "FORD EXPLORER 6TH GEN" FOCUS_MK4 = "FORD FOCUS 4TH GEN" + MAVERICK_MK1 = "FORD MAVERICK 1ST GEN" class RADAR: @@ -68,6 +69,7 @@ CAR_INFO: Dict[str, Union[CarInfo, List[CarInfo]]] = { ], CAR.EXPLORER_MK6: FordCarInfo("Ford Explorer 2020-22"), CAR.FOCUS_MK4: FordCarInfo("Ford Focus EU 2019", "Driver Assistance Pack"), + CAR.MAVERICK_MK1: FordCarInfo("Ford Maverick 2022", "Co-Pilot360 Assist"), } FW_QUERY_CONFIG = FwQueryConfig( @@ -186,4 +188,25 @@ FW_VERSIONS = { (Ecu.shiftByWire, 0x732, None): [ ], }, + CAR.MAVERICK_MK1: { + (Ecu.eps, 0x730, None): [ + b'NZ6C-14D003-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + ], + (Ecu.abs, 0x760, None): [ + b'NZ6C-2D053-AG\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + ], + (Ecu.fwdRadar, 0x764, None): [ + b'NZ6T-14D049-AA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + ], + (Ecu.fwdCamera, 0x706, None): [ + b'NZ6T-14F397-AC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + ], + (Ecu.engine, 0x7E0, None): [ + b'NZ6A-14C204-PA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + b'NZ6A-14C204-ZA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + ], + (Ecu.shiftByWire, 0x732, None): [ + b'NZ6P-14G395-AD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + ], + }, } diff --git a/selfdrive/car/tests/routes.py b/selfdrive/car/tests/routes.py index 3e0a95a38f..d1b73afcf6 100644 --- a/selfdrive/car/tests/routes.py +++ b/selfdrive/car/tests/routes.py @@ -18,6 +18,7 @@ from selfdrive.car.body.values import CAR as COMMA non_tested_cars = [ FORD.ESCAPE_MK4, FORD.FOCUS_MK4, + FORD.MAVERICK_MK1, GM.CADILLAC_ATS, GM.HOLDEN_ASTRA, GM.MALIBU, diff --git a/selfdrive/car/torque_data/override.yaml b/selfdrive/car/torque_data/override.yaml index 1506b4176f..733aa34343 100644 --- a/selfdrive/car/torque_data/override.yaml +++ b/selfdrive/car/torque_data/override.yaml @@ -16,6 +16,7 @@ FORD BRONCO SPORT 1ST GEN: [.nan, 1.5, .nan] FORD ESCAPE 4TH GEN: [.nan, 1.5, .nan] FORD EXPLORER 6TH GEN: [.nan, 1.5, .nan] FORD FOCUS 4TH GEN: [.nan, 1.5, .nan] +FORD MAVERICK 1ST GEN: [.nan, 1.5, .nan] ### # No steering wheel From 2bdce2d92ec859398aaa749037d1fa58d3b5eec7 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 25 Jan 2023 21:39:46 -0800 Subject: [PATCH 180/484] Ford: use curvature signal for lateral control (#27091) * do carcontroller Co-authored-by: Cameron Clough * values done * just send latActive in, cleaner * Update selfdrive/car/ford/values.py * fix a crash * adjust ramp as well Co-authored-by: Cameron Clough --- selfdrive/car/ford/carcontroller.py | 49 ++++++++++------------------- selfdrive/car/ford/fordcan.py | 4 +-- selfdrive/car/ford/values.py | 13 ++++---- 3 files changed, 26 insertions(+), 40 deletions(-) diff --git a/selfdrive/car/ford/carcontroller.py b/selfdrive/car/ford/carcontroller.py index 8164a8a970..99a6648ff9 100644 --- a/selfdrive/car/ford/carcontroller.py +++ b/selfdrive/car/ford/carcontroller.py @@ -1,28 +1,14 @@ import math from cereal import car -from common.numpy_fast import clip, interp +from common.numpy_fast import clip from opendbc.can.packer import CANPacker +from selfdrive.car import apply_std_steer_angle_limits from selfdrive.car.ford.fordcan import create_acc_ui_msg, create_button_msg, create_lat_ctl_msg, create_lka_msg, create_lkas_ui_msg from selfdrive.car.ford.values import CANBUS, CarControllerParams VisualAlert = car.CarControl.HUDControl.VisualAlert -def apply_ford_steer_angle_limits(apply_angle, apply_angle_last, vEgo): - # rate limit - steer_up = apply_angle_last * apply_angle > 0. and abs(apply_angle) > abs(apply_angle_last) - rate_limit = CarControllerParams.RATE_LIMIT_UP if steer_up else CarControllerParams.RATE_LIMIT_DOWN - max_angle_diff = interp(vEgo, rate_limit.speed_points, rate_limit.max_angle_diff_points) - apply_angle = clip(apply_angle, (apply_angle_last - max_angle_diff), (apply_angle_last + max_angle_diff)) - - # absolute limit (LatCtlPath_An_Actl) - apply_path_angle = math.radians(apply_angle) / CarControllerParams.LKAS_STEER_RATIO - apply_path_angle = clip(apply_path_angle, -0.5, 0.5235) - apply_angle = math.degrees(apply_path_angle) * CarControllerParams.LKAS_STEER_RATIO - - return apply_angle - - class CarController: def __init__(self, dbc_name, CP, VM): self.CP = CP @@ -30,7 +16,7 @@ class CarController: self.packer = CANPacker(dbc_name) self.frame = 0 - self.apply_angle_last = 0 + self.apply_curvature_last = 0 self.main_on_last = False self.lkas_enabled_last = False self.steer_alert_last = False @@ -57,34 +43,33 @@ class CarController: can_sends.append(create_button_msg(self.packer, CS.buttons_stock_values, tja_toggle=True)) ### lateral control ### - if CC.latActive: - lca_rq = 1 - apply_angle = apply_ford_steer_angle_limits(actuators.steeringAngleDeg, self.apply_angle_last, CS.out.vEgo) - else: - lca_rq = 0 - apply_angle = 0. - # send steering commands at 20Hz if (self.frame % CarControllerParams.STEER_STEP) == 0: - # use LatCtlPath_An_Actl to actuate steering - path_angle = math.radians(apply_angle) / CarControllerParams.LKAS_STEER_RATIO + if CC.latActive: + # apply limits to curvature + apply_curvature = -self.VM.calc_curvature(math.radians(actuators.steeringAngleDeg), CS.out.vEgo, 0.0) + apply_curvature = apply_std_steer_angle_limits(apply_curvature, self.apply_curvature_last, CS.out.vEgo, CarControllerParams) + # clip to signal range + apply_curvature = clip(apply_curvature, -CarControllerParams.CURVATURE_MAX, CarControllerParams.CURVATURE_MAX) + else: + apply_curvature = 0. # set slower ramp type when small steering angle change # 0=Slow, 1=Medium, 2=Fast, 3=Immediately steer_change = abs(CS.out.steeringAngleDeg - actuators.steeringAngleDeg) - if steer_change < 2.0: + if steer_change < 2.5: ramp_type = 0 - elif steer_change < 4.0: + elif steer_change < 5.0: ramp_type = 1 - elif steer_change < 6.0: + elif steer_change < 7.5: ramp_type = 2 else: ramp_type = 3 precision = 1 # 0=Comfortable, 1=Precise (the stock system always uses comfortable) - self.apply_angle_last = apply_angle + self.apply_curvature_last = apply_curvature can_sends.append(create_lka_msg(self.packer)) - can_sends.append(create_lat_ctl_msg(self.packer, lca_rq, ramp_type, precision, 0, path_angle, 0, 0)) + can_sends.append(create_lat_ctl_msg(self.packer, CC.latActive, ramp_type, precision, 0., 0., -apply_curvature, 0.)) ### ui ### send_ui = (self.main_on_last != main_on) or (self.lkas_enabled_last != CC.latActive) or (self.steer_alert_last != steer_alert) @@ -102,7 +87,7 @@ class CarController: self.steer_alert_last = steer_alert new_actuators = actuators.copy() - new_actuators.steeringAngleDeg = self.apply_angle_last + new_actuators.curvature = self.apply_curvature_last self.frame += 1 return new_actuators, can_sends diff --git a/selfdrive/car/ford/fordcan.py b/selfdrive/car/ford/fordcan.py index c3f6e74651..dcda59bce2 100644 --- a/selfdrive/car/ford/fordcan.py +++ b/selfdrive/car/ford/fordcan.py @@ -16,7 +16,7 @@ def create_lka_msg(packer): return packer.make_can_msg("Lane_Assist_Data1", CANBUS.main, {}) -def create_lat_ctl_msg(packer, lca_rq: int, ramp_type: int, precision: int, path_offset: float, path_angle: float, +def create_lat_ctl_msg(packer, lat_active: bool, ramp_type: int, precision: int, path_offset: float, path_angle: float, curvature: float, curvature_rate: float): """ Creates a CAN message for the Ford TJA/LCA Command. @@ -42,7 +42,7 @@ def create_lat_ctl_msg(packer, lca_rq: int, ramp_type: int, precision: int, path values = { "LatCtlRng_L_Max": 0, # Unknown [0|126] meter "HandsOffCnfm_B_Rq": 0, # Unknown: 0=Inactive, 1=Active [0|1] - "LatCtl_D_Rq": lca_rq, # Mode: 0=None, 1=ContinuousPathFollowing, 2=InterventionLeft, + "LatCtl_D_Rq": 1 if lat_active else 0, # Mode: 0=None, 1=ContinuousPathFollowing, 2=InterventionLeft, # 3=InterventionRight, 4-7=NotUsed [0|7] "LatCtlRampType_D_Rq": ramp_type, # Ramp speed: 0=Slow, 1=Medium, 2=Fast, 3=Immediate [0|3] "LatCtlPrecision_D_Rq": precision, # Precision: 0=Comfortable, 1=Precise, 2/3=NotUsed [0|3] diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index 1348073801..550224e791 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -9,7 +9,7 @@ from selfdrive.car.docs_definitions import CarInfo, Harness from selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries Ecu = car.CarParams.Ecu -AngleRateLimit = namedtuple('AngleRateLimit', ['speed_points', 'max_angle_diff_points']) +CurvatureLimit = namedtuple('CurvatureLimit', ['speed_points', 'max_angle_diff_points']) class CarControllerParams: @@ -22,12 +22,13 @@ class CarControllerParams: # Message: Steering_Data_FD1, but send twice as fast BUTTONS_STEP = 10 / 2 - LKAS_STEER_RATIO = 2.75 # Approximate ratio between LatCtlPath_An_Actl and steering angle in radians - # TODO: remove this once we understand how the EPS calculates the steering angle better - STEER_DRIVER_ALLOWANCE = 0.8 # Driver intervention threshold in Nm + CURVATURE_MAX = 0.02 # Max curvature for steering command, m^-1 + STEER_DRIVER_ALLOWANCE = 0.8 # Driver intervention threshold, Nm - RATE_LIMIT_UP = AngleRateLimit(speed_points=[0., 5., 15.], max_angle_diff_points=[5., .8, .15]) - RATE_LIMIT_DOWN = AngleRateLimit(speed_points=[0., 5., 15.], max_angle_diff_points=[5., 3.5, 0.4]) + # Curvature rate limits + # TODO: unify field names used by curvature and angle control cars + ANGLE_RATE_LIMIT_UP = CurvatureLimit(speed_points=[5, 15, 25], max_angle_diff_points=[0.005, 0.00056, 0.0002]) + ANGLE_RATE_LIMIT_DOWN = CurvatureLimit(speed_points=[5, 15, 25], max_angle_diff_points=[0.008, 0.00089, 0.00032]) def __init__(self, CP): pass From e90571eea1c4be8de848b043ada7f9812a9754a7 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Wed, 25 Jan 2023 21:42:24 -0800 Subject: [PATCH 181/484] Ford: disable radar parser (#27096) The radar parser caused many false positive FCWs. --- selfdrive/car/ford/values.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index 550224e791..2ae0d3643e 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -53,7 +53,7 @@ class RADAR: DELPHI_MRR = 'FORD_CADS' -DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict("ford_lincoln_base_pt", RADAR.DELPHI_MRR)) +DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict("ford_lincoln_base_pt", None)) @dataclass From c7790d0e071255ca304638877e0f45edd6a9d372 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Wed, 25 Jan 2023 22:03:00 -0800 Subject: [PATCH 182/484] Ford: params fixup (#27097) * reduce steer actuator delay to 0.25s * update steer ratio --- selfdrive/car/ford/interface.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/selfdrive/car/ford/interface.py b/selfdrive/car/ford/interface.py index ce4e3e30aa..cea39d8b00 100644 --- a/selfdrive/car/ford/interface.py +++ b/selfdrive/car/ford/interface.py @@ -19,27 +19,27 @@ class CarInterface(CarInterfaceBase): ret.dashcamOnly = True ret.steerControlType = car.CarParams.SteerControlType.angle - ret.steerActuatorDelay = 0.4 + ret.steerActuatorDelay = 0.25 ret.steerLimitTimer = 1.0 if candidate == CAR.BRONCO_SPORT_MK1: ret.wheelbase = 2.67 - ret.steerRatio = 17.7 # learned + ret.steerRatio = 17.7 ret.mass = 1625 + STD_CARGO_KG elif candidate == CAR.ESCAPE_MK4: ret.wheelbase = 2.71 - ret.steerRatio = 14.3 # Copied from Focus + ret.steerRatio = 16.7 ret.mass = 1750 + STD_CARGO_KG elif candidate == CAR.EXPLORER_MK6: ret.wheelbase = 3.025 - ret.steerRatio = 16.8 # learned + ret.steerRatio = 16.8 ret.mass = 2050 + STD_CARGO_KG elif candidate == CAR.FOCUS_MK4: ret.wheelbase = 2.7 - ret.steerRatio = 13.8 # learned + ret.steerRatio = 15.0 ret.mass = 1350 + STD_CARGO_KG elif candidate == CAR.MAVERICK_MK1: From 71c21eff724c1ab6cbba955d572b916612615551 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Wed, 25 Jan 2023 22:03:41 -0800 Subject: [PATCH 183/484] Ford: add missing Maverick 2022 XLT FW (#27098) Ford: add Maverick 2022 XLT fw VIN: `3FTTW8E39NRB00148` 933ad9d04ac82693|2023-01-04--17-31-33--0 --- selfdrive/car/ford/values.py | 1 + 1 file changed, 1 insertion(+) diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index 2ae0d3643e..acb625b7cc 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -203,6 +203,7 @@ FW_VERSIONS = { b'NZ6T-14F397-AC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', ], (Ecu.engine, 0x7E0, None): [ + b'NZ6A-14C204-AAA\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'NZ6A-14C204-PA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'NZ6A-14C204-ZA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', ], From c7c0f1e33c46d0ffbf3f748c108b5ebca559c00d Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 26 Jan 2023 00:37:13 -0800 Subject: [PATCH 184/484] Ford: fix curvature rate limits --- selfdrive/car/ford/values.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index acb625b7cc..dc0f4bbb2e 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -4,12 +4,11 @@ from enum import Enum from typing import Dict, List, Union from cereal import car -from selfdrive.car import dbc_dict +from selfdrive.car import AngleRateLimit, dbc_dict from selfdrive.car.docs_definitions import CarInfo, Harness from selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries Ecu = car.CarParams.Ecu -CurvatureLimit = namedtuple('CurvatureLimit', ['speed_points', 'max_angle_diff_points']) class CarControllerParams: @@ -27,8 +26,8 @@ class CarControllerParams: # Curvature rate limits # TODO: unify field names used by curvature and angle control cars - ANGLE_RATE_LIMIT_UP = CurvatureLimit(speed_points=[5, 15, 25], max_angle_diff_points=[0.005, 0.00056, 0.0002]) - ANGLE_RATE_LIMIT_DOWN = CurvatureLimit(speed_points=[5, 15, 25], max_angle_diff_points=[0.008, 0.00089, 0.00032]) + ANGLE_RATE_LIMIT_UP = AngleRateLimit(speed_bp=[5, 15, 25], angle_v=[0.005, 0.00056, 0.0002]) + ANGLE_RATE_LIMIT_DOWN = AngleRateLimit(speed_bp=[5, 15, 25], angle_v=[0.008, 0.00089, 0.00032]) def __init__(self, CP): pass From ad6ee2669cb41d20b691b3c139bd04beb60290ce Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 26 Jan 2023 00:47:29 -0800 Subject: [PATCH 185/484] fix static analysis --- selfdrive/car/ford/values.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index dc0f4bbb2e..b93be61448 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -1,4 +1,4 @@ -from collections import defaultdict, namedtuple +from collections import defaultdict from dataclasses import dataclass from enum import Enum from typing import Dict, List, Union From 4d231a9d96a54ba0ed163e4aa487876f811a97cb Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 26 Jan 2023 00:48:59 -0800 Subject: [PATCH 186/484] Ram 1500: more generic EPS steer to zero check (#27086) more generic check --- selfdrive/car/chrysler/interface.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/selfdrive/car/chrysler/interface.py b/selfdrive/car/chrysler/interface.py index 2f058165ac..c8504d6fb1 100755 --- a/selfdrive/car/chrysler/interface.py +++ b/selfdrive/car/chrysler/interface.py @@ -58,9 +58,9 @@ class CarInterface(CarInterfaceBase): ret.mass = 2493. + STD_CARGO_KG CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) ret.minSteerSpeed = 14.5 - for fw in car_fw: - if fw.ecu == 'eps' and fw.fwVersion.startswith((b"68312176", b"68273275")): - ret.minSteerSpeed = 0. + # Older EPS FW allow steer to zero + if any(fw.ecu == 'eps' and fw.fwVersion[:4] <= b"6831" for fw in car_fw): + ret.minSteerSpeed = 0. elif candidate == CAR.RAM_HD: ret.steerActuatorDelay = 0.2 From 1d5d57a3847285af4bcce934dad49ffa65397fc1 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 27 Jan 2023 03:02:17 +0800 Subject: [PATCH 187/484] cabana: fix video_splitter created twice (#27101) --- tools/cabana/mainwin.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index bd32cfdfcc..1468073017 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -121,8 +121,6 @@ void MainWindow::createDockWindows() { charts_layout->setContentsMargins(0, 0, 0, 0); charts_layout->addWidget(charts_widget); - video_splitter = new QSplitter(Qt::Vertical,this); - // splitter between video and charts video_splitter = new QSplitter(Qt::Vertical, this); video_widget = new VideoWidget(this); From e90c7a487b109aba0a0856f0c1b586ab0900bdfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Chybicki?= Date: Thu, 26 Jan 2023 21:30:46 +0100 Subject: [PATCH 188/484] Santa Fe HEV 2022: add missing FW versions (#27050) * Santa Fe Hev 2022 Fingerprint * Concatenated second value for transmission * fixes Co-authored-by: Shane Smiskol --- selfdrive/car/hyundai/values.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index b3cb46699a..dbac8033d8 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -766,19 +766,23 @@ FW_VERSIONS = { }, CAR.SANTA_FE_HEV_2022: { (Ecu.fwdRadar, 0x7d0, None): [ - b'\xf1\x8799110CL500\xf1\x00TMhe SCC FHCUP 1.00 1.00 99110-CL500 ', + b'\xf1\x00TMhe SCC FHCUP 1.00 1.00 99110-CL500 ', ], (Ecu.eps, 0x7d4, None): [ b'\xf1\x00TM MDPS C 1.00 1.02 56310-CLAC0 4TSHC102', + b'\xf1\x00TM MDPS R 1.00 1.05 57700-CL000 4TSHP105', ], (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00TMH MFC AT EUR LHD 1.00 1.06 99211-S1500 220727', b'\xf1\x00TMH MFC AT USA LHD 1.00 1.03 99211-S1500 210224', ], (Ecu.transmission, 0x7e1, None): [ + b'\xf1\x00PSBG2333 E16\x00\x00\x00\x00\x00\x00\x00TTM2H16UA3I\x94\xac\x8f', b'\xf1\x87959102T250\x00\x00\x00\x00\x00\xf1\x81E14\x00\x00\x00\x00\x00\x00\x00\xf1\x00PSBG2333 E14\x00\x00\x00\x00\x00\x00\x00TTM2H16SA2\x80\xd7l\xb2', ], (Ecu.engine, 0x7e0, None): [ b'\xf1\x87391312MTC1', + b'\xf1\x87391312MTE0', ], }, CAR.SANTA_FE_PHEV_2022: { From 27e8f8ff3e3336fe4d5737cf7c766d16725706bc Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 27 Jan 2023 04:32:25 +0800 Subject: [PATCH 189/484] cabana: improve detail view (#27099) * improve detailview * cleanup layout --- tools/cabana/detailwidget.cc | 39 ++++++++++++++++-------------------- tools/cabana/detailwidget.h | 2 +- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index 7f59087643..233bc493df 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -19,7 +19,6 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart QWidget *main_widget = new QWidget(this); QVBoxLayout *main_layout = new QVBoxLayout(main_widget); main_layout->setContentsMargins(0, 0, 0, 0); - main_layout->setSpacing(0); // tabbar tabbar = new QTabBar(this); @@ -29,10 +28,6 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart tabbar->setContextMenuPolicy(Qt::CustomContextMenu); main_layout->addWidget(tabbar); - QFrame *title_frame = new QFrame(this); - QVBoxLayout *frame_layout = new QVBoxLayout(title_frame); - title_frame->setFrameShape(QFrame::StyledPanel); - // message title toolbar = new QToolBar(this); toolbar->setIconSize({16, 16}); @@ -48,21 +43,15 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart toolbar->addAction(bootstrapPixmap("pencil"), "", this, &DetailWidget::editMsg)->setToolTip(tr("Edit Message")); remove_msg_act = toolbar->addAction(bootstrapPixmap("x-lg"), "", this, &DetailWidget::removeMsg); remove_msg_act->setToolTip(tr("Remove Message")); - toolbar->setVisible(false); - frame_layout->addWidget(toolbar); + main_layout->addWidget(toolbar); // warning warning_widget = new QWidget(this); QHBoxLayout *warning_hlayout = new QHBoxLayout(warning_widget); - warning_hlayout->setContentsMargins(0, 0, 0, 0); - QLabel *warning_icon = new QLabel(this); - warning_icon->setPixmap(style()->standardPixmap(QStyle::SP_MessageBoxWarning).scaledToWidth(24, Qt::SmoothTransformation)); - warning_hlayout->addWidget(warning_icon, 0, Qt::AlignTop); - warning_label = new QLabel(this); - warning_hlayout->addWidget(warning_label, 1, Qt::AlignLeft); + warning_hlayout->addWidget(warning_icon = new QLabel(this), 0, Qt::AlignTop); + warning_hlayout->addWidget(warning_label = new QLabel(this), 1, Qt::AlignLeft); warning_widget->hide(); - frame_layout->addWidget(warning_widget); - main_layout->addWidget(title_frame); + main_layout->addWidget(warning_widget); // msg widget QWidget *msg_widget = new QWidget(this); @@ -85,9 +74,9 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart tab_widget = new QTabWidget(this); tab_widget->setTabPosition(QTabWidget::South); - tab_widget->addTab(scroll, "&Msg"); + tab_widget->addTab(scroll, bootstrapPixmap("file-earmark-ruled"), "&Msg"); history_log = new LogsWidget(this); - tab_widget->addTab(history_log, "&Logs"); + tab_widget->addTab(history_log, bootstrapPixmap("stopwatch"), "&Logs"); main_layout->addWidget(tab_widget); stacked_layout = new QStackedLayout(this); @@ -174,8 +163,14 @@ void DetailWidget::dbcMsgChanged(int show_form_idx) { form->setChartOpened(charts->hasSignal(msg_id, sig)); ++i; } - if (msg->size != can->lastMessage(msg_id).dat.size()) + if (msg->size != can->lastMessage(msg_id).dat.size()) { warnings.push_back(tr("Message size (%1) is incorrect.").arg(msg->size)); + } + for (auto s : binary_view->getOverlappingSignals()) { + warnings.push_back(tr("%1 has overlapping bits.").arg(s->name.c_str())); + } + } else { + warnings.push_back(tr("Drag-Select in binary view to create new signal.")); } for (/**/; i < signal_list.size(); ++i) signal_list[i]->hide(); @@ -184,10 +179,10 @@ void DetailWidget::dbcMsgChanged(int show_form_idx) { remove_msg_act->setEnabled(msg != nullptr); name_label->setText(msgName(msg_id)); - for (auto s : binary_view->getOverlappingSignals()) - warnings.push_back(tr("%1 has overlapping bits.").arg(s->name.c_str())); - - warning_label->setText(warnings.join('\n')); + if (!warnings.isEmpty()) { + warning_label->setText(warnings.join('\n')); + warning_icon->setPixmap(bootstrapPixmap(msg ? "exclamation-triangle" : "info-circle")); + } warning_widget->setVisible(!warnings.isEmpty()); setUpdatesEnabled(true); } diff --git a/tools/cabana/detailwidget.h b/tools/cabana/detailwidget.h index b07629084a..c59a6fca50 100644 --- a/tools/cabana/detailwidget.h +++ b/tools/cabana/detailwidget.h @@ -47,7 +47,7 @@ private: void updateState(const QHash * msgs = nullptr); QString msg_id; - QLabel *name_label, *time_label, *warning_label; + QLabel *name_label, *time_label, *warning_icon, *warning_label; QWidget *warning_widget; QVBoxLayout *signals_layout; QTabBar *tabbar; From 0f37ff6c0284b91881984ce5cf2d74c48bdf7e68 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 27 Jan 2023 04:37:04 +0800 Subject: [PATCH 190/484] cabana: cleanup historylog and fix some known issues (#27100) improve history logs --- tools/cabana/historylog.cc | 156 ++++++++++++------------------------- tools/cabana/historylog.h | 35 +++------ 2 files changed, 60 insertions(+), 131 deletions(-) diff --git a/tools/cabana/historylog.cc b/tools/cabana/historylog.cc index 4a975afc4d..970aafd342 100644 --- a/tools/cabana/historylog.cc +++ b/tools/cabana/historylog.cc @@ -1,30 +1,20 @@ #include "tools/cabana/historylog.h" -#include #include #include #include // HistoryLogModel -void HistoryLogModel::setDisplayType(HistoryLogModel::DisplayType type) { - if (display_type != type) { - display_type = type; - refresh(); - } -} - QVariant HistoryLogModel::data(const QModelIndex &index, int role) const { - const bool display_signals = display_type == HistoryLogModel::Signals; + const bool show_signals = display_signals_mode && sigs.size() > 0; const auto &m = messages[index.row()]; if (role == Qt::DisplayRole) { if (index.column() == 0) { return QString::number((m.mono_time / (double)1e9) - can->routeStartTime(), 'f', 2); } - return display_signals ? QString::number(m.sig_values[index.column() - 1]) : toHex(m.data); - } else if (role == Qt::ToolTipRole && index.column() > 0 && display_signals) { - return tr("double click to open the chart"); - } else if (role == Qt::UserRole && index.column() == 1 && !display_signals) { + return show_signals ? QString::number(m.sig_values[index.column() - 1]) : toHex(m.data); + } else if (role == Qt::UserRole && index.column() == 1 && !show_signals) { return HexColors::toVariantList(m.colors); } return {}; @@ -36,7 +26,6 @@ void HistoryLogModel::setMessage(const QString &message_id) { if (auto dbc_msg = dbc()->msg(msg_id)) { sigs = dbc_msg->getSignals(); } - display_type = !sigs.empty() ? HistoryLogModel::Signals : HistoryLogModel::Hex; filter_cmp = nullptr; refresh(); } @@ -51,17 +40,15 @@ void HistoryLogModel::refresh() { } QVariant HistoryLogModel::headerData(int section, Qt::Orientation orientation, int role) const { - const bool display_signals = display_type == HistoryLogModel::Signals && !sigs.empty(); if (orientation == Qt::Horizontal) { + const bool show_signals = display_signals_mode && !sigs.empty(); if (role == Qt::DisplayRole || role == Qt::ToolTipRole) { if (section == 0) { return "Time"; } - return display_signals ? QString::fromStdString(sigs[section - 1]->name).replace('_', ' ') : "Data"; - } else if (role == Qt::BackgroundRole && section > 0 && display_signals) { + return show_signals ? QString::fromStdString(sigs[section - 1]->name).replace('_', ' ') : "Data"; + } else if (role == Qt::BackgroundRole && section > 0 && show_signals) { return QBrush(QColor(getColor(section - 1))); - } else if (role == Qt::ForegroundRole && section > 0 && display_signals) { - return QBrush(Qt::black); } } return {}; @@ -72,6 +59,11 @@ void HistoryLogModel::setDynamicMode(int state) { refresh(); } +void HistoryLogModel::setDisplayType(int type) { + display_signals_mode = type == 0; + refresh(); +} + void HistoryLogModel::segmentsMerged() { if (!dynamic_mode) { has_more_data = true; @@ -114,7 +106,7 @@ void HistoryLogModel::fetchMore(const QModelIndex &parent) { } void HistoryLogModel::updateColors() { - if (display_type == HistoryLogModel::Hex) { + if (!display_signals_mode || sigs.empty()) { const auto freq = can->lastMessage(msg_id).freq; if (dynamic_mode) { for (auto it = messages.rbegin(); it != messages.rend(); ++it) { @@ -162,10 +154,9 @@ template std::deque HistoryLogModel::fetchData<>(std:: std::deque HistoryLogModel::fetchData(uint64_t from_time, uint64_t min_time) { auto events = can->events(); if (dynamic_mode) { - auto it = std::lower_bound(events->rbegin(), events->rend(), from_time, [=](auto &e, uint64_t ts) { - return e->mono_time > ts; + auto it = std::upper_bound(events->rbegin(), events->rend(), from_time, [=](uint64_t ts, auto &e) { + return e->mono_time < ts; }); - if (it != events->rend()) ++it; return fetchData(it, events->rend(), min_time); } else { assert(min_time == 0); @@ -189,128 +180,77 @@ QSize HeaderView::sectionSizeFromContents(int logicalIndex) const { void HeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const { auto bg_role = model()->headerData(logicalIndex, Qt::Horizontal, Qt::BackgroundRole); if (bg_role.isValid()) { - QPen pen(model()->headerData(logicalIndex, Qt::Horizontal, Qt::ForegroundRole).value(), 1); - painter->setPen(pen); painter->fillRect(rect, bg_role.value()); } QString text = model()->headerData(logicalIndex, Qt::Horizontal, Qt::DisplayRole).toString(); painter->drawText(rect.adjusted(5, 3, -5, -3), defaultAlignment(), text); } -// HistoryLog - -HistoryLog::HistoryLog(QWidget *parent) : QTableView(parent) { - setHorizontalHeader(new HeaderView(Qt::Horizontal, this)); - horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | (Qt::Alignment)Qt::TextWordWrap); - horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); - verticalHeader()->setVisible(false); - setItemDelegateForColumn(1, new MessageBytesDelegate(this)); - setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); -} - // LogsWidget LogsWidget::LogsWidget(QWidget *parent) : QWidget(parent) { QVBoxLayout *main_layout = new QVBoxLayout(this); QHBoxLayout *h = new QHBoxLayout(); - - display_type_cb = new QComboBox(); - display_type_cb->addItems({"Signal value", "Hex value"}); - h->addWidget(display_type_cb); - - signals_cb = new QComboBox(this); - signals_cb->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); - h->addWidget(signals_cb); - comp_box = new QComboBox(); + filters_widget = new QWidget(this); + QHBoxLayout *filter_layout = new QHBoxLayout(filters_widget); + filter_layout->setContentsMargins(0, 0, 0, 0); + filter_layout->addWidget(display_type_cb = new QComboBox(this)); + filter_layout->addWidget(signals_cb = new QComboBox(this)); + filter_layout->addWidget(comp_box = new QComboBox(this)); + filter_layout->addWidget(value_edit = new QLineEdit(this)); + h->addWidget(filters_widget); + h->addStretch(0); + h->addWidget(dynamic_mode = new QCheckBox(tr("Dynamic")), 0, Qt::AlignRight); + + display_type_cb->addItems({"Signal Value", "Hex Value"}); comp_box->addItems({">", "=", "!=", "<"}); - h->addWidget(comp_box); - value_edit = new QLineEdit(this); value_edit->setClearButtonEnabled(true); value_edit->setValidator(new QDoubleValidator(-500000, 500000, 6, this)); - h->addWidget(value_edit); - dynamic_mode = new QCheckBox(tr("Dynamic")); - h->addWidget(dynamic_mode, 0, Qt::AlignRight); - main_layout->addLayout(h); - - model = new HistoryLogModel(this); - logs = new HistoryLog(this); - logs->setModel(model); - main_layout->addWidget(logs); + dynamic_mode->setChecked(true); + dynamic_mode->setEnabled(!can->liveStreaming()); - QObject::connect(logs, &QTableView::doubleClicked, this, &LogsWidget::doubleClicked); - QObject::connect(display_type_cb, SIGNAL(activated(int)), this, SLOT(displayTypeChanged())); + main_layout->addLayout(h); + main_layout->addWidget(logs = new QTableView(this)); + logs->setModel(model = new HistoryLogModel(this)); + logs->setItemDelegateForColumn(1, new MessageBytesDelegate(this)); + logs->setHorizontalHeader(new HeaderView(Qt::Horizontal, this)); + logs->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | (Qt::Alignment)Qt::TextWordWrap); + logs->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + logs->verticalHeader()->setVisible(false); + + QObject::connect(display_type_cb, SIGNAL(activated(int)), model, SLOT(setDisplayType(int))); + QObject::connect(dynamic_mode, &QCheckBox::stateChanged, model, &HistoryLogModel::setDynamicMode); QObject::connect(signals_cb, SIGNAL(activated(int)), this, SLOT(setFilter())); QObject::connect(comp_box, SIGNAL(activated(int)), this, SLOT(setFilter())); QObject::connect(value_edit, &QLineEdit::textChanged, this, &LogsWidget::setFilter); - QObject::connect(dynamic_mode, &QCheckBox::stateChanged, model, &HistoryLogModel::setDynamicMode); QObject::connect(can, &AbstractStream::seekedTo, model, &HistoryLogModel::refresh); QObject::connect(can, &AbstractStream::eventsMerged, model, &HistoryLogModel::segmentsMerged); - - dynamic_mode->setChecked(true); - if (can->liveStreaming()) { - dynamic_mode->setEnabled(false); - } } void LogsWidget::setMessage(const QString &message_id) { model->setMessage(message_id); - cur_filter_text = ""; - value_edit->setText(""); - signals_cb->clear(); - comp_box->setCurrentIndex(0); - bool has_signals = model->sigs.size() > 0; - if (has_signals) { + bool has_signal = model->sigs.size(); + if (has_signal) { + signals_cb->clear(); for (auto s : model->sigs) { signals_cb->addItem(s->name.c_str()); } } - display_type_cb->setCurrentIndex(has_signals ? 0 : 1); - display_type_cb->setVisible(has_signals); - comp_box->setVisible(has_signals); - value_edit->setVisible(has_signals); - signals_cb->setVisible(has_signals); + value_edit->clear(); + comp_box->setCurrentIndex(0); + filters_widget->setVisible(has_signal); } -static bool not_equal(double l, double r) { return l != r; } - void LogsWidget::setFilter() { - if (cur_filter_text.isEmpty() && value_edit->text().isEmpty()) { - return; - } + if (value_edit->text().isEmpty() && !value_edit->isModified()) return; - std::function cmp; + std::function cmp = nullptr; switch (comp_box->currentIndex()) { case 0: cmp = std::greater{}; break; case 1: cmp = std::equal_to{}; break; - case 2: cmp = not_equal; break; + case 2: cmp = [](double l, double r) { return l != r; }; break; // not equal case 3: cmp = std::less{}; break; } model->setFilter(signals_cb->currentIndex(), value_edit->text(), cmp); - cur_filter_text = value_edit->text(); -} - -void LogsWidget::displayTypeChanged() { - model->setDisplayType(display_type_cb->currentIndex() == 0 ? HistoryLogModel::Signals : HistoryLogModel::Hex); -} - -void LogsWidget::showEvent(QShowEvent *event) { - if (dynamic_mode->isChecked()) { - model->refresh(); - } -} - -void LogsWidget::updateState() { - if (dynamic_mode->isChecked()) { - model->updateState(); - } -} - -void LogsWidget::doubleClicked(const QModelIndex &index) { - if (index.isValid()) { - if (model->display_type == HistoryLogModel::Signals && model->sigs.size() > 0 && index.column() > 0) { - emit openChart(model->msg_id, model->sigs[index.column()-1]); - } - can->seekTo(model->messages[index.row()].mono_time / (double)1e9 - can->routeStartTime()); - } } diff --git a/tools/cabana/historylog.h b/tools/cabana/historylog.h index 34af713fd3..83b5f623ea 100644 --- a/tools/cabana/historylog.h +++ b/tools/cabana/historylog.h @@ -21,29 +21,27 @@ class HistoryLogModel : public QAbstractTableModel { Q_OBJECT public: - enum DisplayType { - Signals, - Hex - }; - HistoryLogModel(QObject *parent) : QAbstractTableModel(parent) {} void setMessage(const QString &message_id); void updateState(); void setFilter(int sig_idx, const QString &value, std::function cmp); - void setDisplayType(DisplayType type); QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; void fetchMore(const QModelIndex &parent) override; inline bool canFetchMore(const QModelIndex &parent) const override { return has_more_data; } int rowCount(const QModelIndex &parent = QModelIndex()) const override { return messages.size(); } int columnCount(const QModelIndex &parent = QModelIndex()) const override { - return display_type == HistoryLogModel::Hex ? 2 : std::max(1ul, sigs.size()) + 1; + return display_signals_mode && !sigs.empty() ? sigs.size() + 1 : 2; } - void setDynamicMode(int state); - void segmentsMerged(); void updateColors(); void refresh(); +public slots: + void setDisplayType(int type); + void setDynamicMode(int state); + void segmentsMerged(); + +public: struct Message { uint64_t mono_time = 0; QVector sig_values; @@ -66,13 +64,7 @@ public: std::deque messages; std::vector sigs; bool dynamic_mode = true; - DisplayType display_type = HistoryLogModel::Signals; -}; - -class HistoryLog : public QTableView { -public: - HistoryLog(QWidget *parent); - int sizeHintForColumn(int column) const override { return -1; }; + bool display_signals_mode = true; }; class LogsWidget : public QWidget { @@ -81,23 +73,20 @@ class LogsWidget : public QWidget { public: LogsWidget(QWidget *parent); void setMessage(const QString &message_id); - void updateState(); + void updateState() {if (dynamic_mode->isChecked()) model->updateState(); } + void showEvent(QShowEvent *event) override { if (dynamic_mode->isChecked()) model->refresh(); } signals: void openChart(const QString &msg_id, const Signal *sig); private slots: void setFilter(); - void displayTypeChanged(); private: - void doubleClicked(const QModelIndex &index); - void showEvent(QShowEvent *event) override; - - HistoryLog *logs; + QTableView *logs; HistoryLogModel *model; QCheckBox *dynamic_mode; QComboBox *signals_cb, *comp_box, *display_type_cb; QLineEdit *value_edit; - QString cur_filter_text; + QWidget *filters_widget; }; From de5f792b3e893711a47c8f4db04162ab259ef286 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 27 Jan 2023 04:41:17 +0800 Subject: [PATCH 191/484] cabana: only repaint changed cells in BinaryView::highlight (#27046) dont update all cells on highlight --- tools/cabana/binaryview.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/cabana/binaryview.cc b/tools/cabana/binaryview.cc index eaa33724e8..010fe0fc88 100644 --- a/tools/cabana/binaryview.cc +++ b/tools/cabana/binaryview.cc @@ -40,8 +40,14 @@ QSize BinaryView::minimumSizeHint() const { void BinaryView::highlight(const Signal *sig) { if (sig != hovered_sig) { + for (int i = 0; i < model->items.size(); ++i) { + auto &item_sigs = model->items[i].sigs; + if ((sig && item_sigs.contains(sig)) || (hovered_sig && item_sigs.contains(hovered_sig))) { + auto index = model->index(i / model->columnCount(), i % model->columnCount()); + emit model->dataChanged(index, index, {Qt::DisplayRole}); + } + } hovered_sig = sig; - model->dataChanged(model->index(0, 0), model->index(model->rowCount() - 1, model->columnCount() - 1)); emit signalHovered(hovered_sig); } } From 752b4edb928f9c9efb4d00429cdb7213c940159b Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 26 Jan 2023 12:54:53 -0800 Subject: [PATCH 192/484] HKG: Update Genesis GV60 harnesses (#27033) * Update values.py * generate --- docs/CARS.md | 5 +++-- selfdrive/car/hyundai/values.py | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index 4a46360d4f..390d2bf349 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,7 +4,7 @@ A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system. -# 223 Supported Cars +# 224 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness|Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| @@ -32,7 +32,8 @@ A supported vehicle is one that just works when you install a comma three. All s |Genesis|G70 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F|| |Genesis|G80 2017-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| |Genesis|G90 2017-18|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| -|Genesis|GV60 2023[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| +|Genesis|GV60 (Advanced Trim) 2023[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|| +|Genesis|GV60 (Performance Trim) 2023[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| |Genesis|GV70 2022-23[5](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| |GMC|Acadia 2018[3](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|| |GMC|Sierra 1500 2020-21|Driver Alert Package II|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|| diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index dbac8033d8..c0cf23b50d 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -225,7 +225,10 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { ], # Genesis - CAR.GENESIS_GV60_EV_1ST_GEN: HyundaiCarInfo("Genesis GV60 2023", "All", harness=Harness.hyundai_k), + CAR.GENESIS_GV60_EV_1ST_GEN: [ + HyundaiCarInfo("Genesis GV60 (Advanced Trim) 2023", "All", harness=Harness.hyundai_a), + HyundaiCarInfo("Genesis GV60 (Performance Trim) 2023", "All", harness=Harness.hyundai_k), + ], CAR.GENESIS_G70: HyundaiCarInfo("Genesis G70 2018-19", "All", harness=Harness.hyundai_f), CAR.GENESIS_G70_2020: HyundaiCarInfo("Genesis G70 2020", "All", harness=Harness.hyundai_f), CAR.GENESIS_GV70_1ST_GEN: HyundaiCarInfo("Genesis GV70 2022-23", "All", harness=Harness.hyundai_l), From 08060e42c4bab39b85a64707f8eb801580ed2245 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 27 Jan 2023 04:56:38 +0800 Subject: [PATCH 193/484] cabana: saving & restoring columns widths (#27080) saving & restoring columns widths --- tools/cabana/mainwin.cc | 4 ++++ tools/cabana/messageswidget.cc | 1 - tools/cabana/messageswidget.h | 3 +++ tools/cabana/settings.cc | 2 ++ tools/cabana/settings.h | 1 + 5 files changed, 10 insertions(+), 1 deletion(-) diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index 1468073017..517d0b1b73 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -29,11 +29,13 @@ MainWindow::MainWindow() : QMainWindow() { createStatusBar(); createShortcuts(); + // restore states restoreGeometry(settings.geometry); if (isMaximized()) { setGeometry(QApplication::desktop()->availableGeometry(this)); } restoreState(settings.window_state); + messages_widget->restoreHeaderState(settings.message_header_state); qRegisterMetaType("uint64_t"); qRegisterMetaType("ReplyMsgType"); @@ -285,11 +287,13 @@ void MainWindow::closeEvent(QCloseEvent *event) { if (floating_window) floating_window->deleteLater(); + // save states settings.geometry = saveGeometry(); settings.window_state = saveState(); if (!can->liveStreaming()) { settings.video_splitter_state = video_splitter->saveState(); } + settings.message_header_state = messages_widget->saveHeaderState(); settings.save(); QWidget::closeEvent(event); } diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index 353fe26340..69c6d298c0 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -1,7 +1,6 @@ #include "tools/cabana/messageswidget.h" #include -#include #include #include #include diff --git a/tools/cabana/messageswidget.h b/tools/cabana/messageswidget.h index 49f57b78e6..69fdd26170 100644 --- a/tools/cabana/messageswidget.h +++ b/tools/cabana/messageswidget.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -33,6 +34,8 @@ class MessagesWidget : public QWidget { public: MessagesWidget(QWidget *parent); void selectMessage(const QString &message_id); + QByteArray saveHeaderState() const { return table_widget->horizontalHeader()->saveState(); } + bool restoreHeaderState(const QByteArray &state) const { return table_widget->horizontalHeader()->restoreState(state); } signals: void msgSelectionChanged(const QString &message_id); diff --git a/tools/cabana/settings.cc b/tools/cabana/settings.cc index e06d149831..7d34d5dec0 100644 --- a/tools/cabana/settings.cc +++ b/tools/cabana/settings.cc @@ -23,6 +23,7 @@ void Settings::save() { s.setValue("window_state", window_state); s.setValue("geometry", geometry); s.setValue("video_splitter_state", video_splitter_state); + s.setValue("message_header_state", message_header_state); } void Settings::load() { @@ -36,6 +37,7 @@ void Settings::load() { window_state = s.value("window_state").toByteArray(); geometry = s.value("geometry").toByteArray(); video_splitter_state = s.value("video_splitter_state").toByteArray(); + message_header_state = s.value("message_header_state").toByteArray(); } // SettingsDlg diff --git a/tools/cabana/settings.h b/tools/cabana/settings.h index 86ebd73113..7b0cbc9f5d 100644 --- a/tools/cabana/settings.h +++ b/tools/cabana/settings.h @@ -22,6 +22,7 @@ public: QByteArray geometry; QByteArray video_splitter_state; QByteArray window_state; + QByteArray message_header_state; signals: void changed(); From 9201267fb7ba0080a3c508a6a258f76cbc265079 Mon Sep 17 00:00:00 2001 From: Vivek Aithal Date: Thu, 26 Jan 2023 14:48:46 -0800 Subject: [PATCH 194/484] torqued: Update reset logic (#27103) * update reset logic * bugfix --- selfdrive/locationd/torqued.py | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/selfdrive/locationd/torqued.py b/selfdrive/locationd/torqued.py index cb8101bbd4..5449152686 100755 --- a/selfdrive/locationd/torqued.py +++ b/selfdrive/locationd/torqued.py @@ -31,8 +31,6 @@ MAX_FILTER_DECAY = 250 LAT_ACC_THRESHOLD = 1 STEER_BUCKET_BOUNDS = [(-0.5, -0.3), (-0.3, -0.2), (-0.2, -0.1), (-0.1, 0), (0, 0.1), (0.1, 0.2), (0.2, 0.3), (0.3, 0.5)] MIN_BUCKET_POINTS = np.array([100, 300, 500, 500, 500, 500, 300, 100]) -MAX_RESETS = 5.0 -MAX_INVALID_THRESHOLD = 10 MIN_ENGAGE_BUFFER = 2 # secs VERSION = 1 # bump this to invalidate old parameter caches @@ -173,7 +171,6 @@ class TorqueEstimator: def reset(self): self.resets += 1.0 - self.invalid_values_tracker = 0.0 self.decay = MIN_FILTER_DECAY self.raw_points = defaultdict(lambda: deque(maxlen=self.hist_len)) self.filtered_points = PointBuckets(x_bounds=STEER_BUCKET_BOUNDS, min_points=self.min_bucket_points, min_points_total=self.min_points_total) @@ -198,12 +195,6 @@ class TorqueEstimator: self.filtered_params[param].update(value) self.filtered_params[param].update_alpha(self.decay) - def is_sane(self, latAccelFactor, latAccelOffset, friction): - if any([val is None or np.isnan(val) for val in [latAccelFactor, latAccelOffset, friction]]): - return False - return (self.max_friction >= friction >= self.min_friction) and\ - (self.max_lataccel_factor >= latAccelFactor >= self.min_lataccel_factor) - def handle_log(self, t, which, msg): if which == "carControl": self.raw_points["carControl_t"].append(t + self.lag) @@ -233,23 +224,20 @@ class TorqueEstimator: liveTorqueParameters.useParams = self.use_params if self.filtered_points.is_valid(): - latAccelFactor, latAccelOffset, friction_coeff = self.estimate_params() + latAccelFactor, latAccelOffset, frictionCoeff = self.estimate_params() liveTorqueParameters.latAccelFactorRaw = float(latAccelFactor) liveTorqueParameters.latAccelOffsetRaw = float(latAccelOffset) - liveTorqueParameters.frictionCoefficientRaw = float(friction_coeff) + liveTorqueParameters.frictionCoefficientRaw = float(frictionCoeff) - if self.is_sane(latAccelFactor, latAccelOffset, friction_coeff): - liveTorqueParameters.liveValid = True - self.update_params({'latAccelFactor': latAccelFactor, 'latAccelOffset': latAccelOffset, 'frictionCoefficient': friction_coeff}) - self.invalid_values_tracker = max(0.0, self.invalid_values_tracker - 0.5) - else: - cloudlog.exception("Live torque parameters are outside acceptable bounds.") + if any([val is None or np.isnan(val) for val in [latAccelFactor, latAccelOffset, frictionCoeff]]): + cloudlog.exception("Live torque parameters are invalid.") liveTorqueParameters.liveValid = False - self.invalid_values_tracker += 1.0 - # Reset when ~10 invalid over 5 secs - if self.invalid_values_tracker > MAX_INVALID_THRESHOLD: - # Do not reset the filter as it may cause a drastic jump, just reset points - self.reset() + self.reset() + else: + liveTorqueParameters.liveValid = True + latAccelFactor = np.clip(latAccelFactor, self.min_lataccel_factor, self.max_lataccel_factor) + frictionCoeff = np.clip(frictionCoeff, self.min_friction, self.max_friction) + self.update_params({'latAccelFactor': latAccelFactor, 'latAccelOffset': latAccelOffset, 'frictionCoefficient': frictionCoeff}) else: liveTorqueParameters.liveValid = False From 6f0d35a09f54866ff2c7a76993c2f11e96fdf869 Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Thu, 26 Jan 2023 18:25:15 -0700 Subject: [PATCH 195/484] Glonass ephemeris support (#27088) * add glonass kaitai parsing * add kaita generated files * remove glonass from build * add string non immediate type * fix kaitai bug * add patch file * fix scons order * Update selfdrive/locationd/SConscript Co-authored-by: Adeeb Shihadeh Co-authored-by: Kurt Nistelberger Co-authored-by: Adeeb Shihadeh --- selfdrive/locationd/SConscript | 5 + selfdrive/locationd/generated/glonass.cpp | 201 +++++++++++++++++++ selfdrive/locationd/generated/glonass.h | 232 ++++++++++++++++++++++ selfdrive/locationd/generated/gps.cpp | 6 +- selfdrive/locationd/generated/gps.h | 4 +- selfdrive/locationd/generated/ubx.cpp | 8 +- selfdrive/locationd/glonass.ksy | 107 ++++++++++ selfdrive/locationd/glonass_fix.patch | 13 ++ selfdrive/locationd/gps.ksy | 2 +- 9 files changed, 566 insertions(+), 12 deletions(-) create mode 100644 selfdrive/locationd/generated/glonass.cpp create mode 100644 selfdrive/locationd/generated/glonass.h create mode 100644 selfdrive/locationd/glonass.ksy create mode 100644 selfdrive/locationd/glonass_fix.patch diff --git a/selfdrive/locationd/SConscript b/selfdrive/locationd/SConscript index 4b7fba19b6..7024196efd 100644 --- a/selfdrive/locationd/SConscript +++ b/selfdrive/locationd/SConscript @@ -7,6 +7,11 @@ if GetOption('kaitai'): cmd = f"kaitai-struct-compiler --target cpp_stl --outdir {generated} $SOURCES" env.Command(['generated/ubx.cpp', 'generated/ubx.h'], 'ubx.ksy', cmd) env.Command(['generated/gps.cpp', 'generated/gps.h'], 'gps.ksy', cmd) + glonass = env.Command(['generated/glonass.cpp', 'generated/glonass.h'], 'glonass.ksy', cmd) + + # kaitai issue: https://github.com/kaitai-io/kaitai_struct/issues/910 + patch = env.Command(None, 'glonass_fix.patch', 'git apply $SOURCES') + env.Depends(patch, glonass) env.Program("ubloxd", ["ubloxd.cc", "ublox_msg.cc", "generated/ubx.cpp", "generated/gps.cpp"], LIBS=loc_libs) diff --git a/selfdrive/locationd/generated/glonass.cpp b/selfdrive/locationd/generated/glonass.cpp new file mode 100644 index 0000000000..149134fbb5 --- /dev/null +++ b/selfdrive/locationd/generated/glonass.cpp @@ -0,0 +1,201 @@ +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +#include "glonass.h" + +glonass_t::glonass_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, glonass_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = this; + + try { + _read(); + } catch(...) { + _clean_up(); + throw; + } +} + +void glonass_t::_read() { + m_idle_chip = m__io->read_bits_int_be(1); + m_string_number = m__io->read_bits_int_be(4); + //m__io->align_to_byte(); + switch (string_number()) { + case 4: { + m_data = new string_4_t(m__io, this, m__root); + break; + } + case 1: { + m_data = new string_1_t(m__io, this, m__root); + break; + } + case 3: { + m_data = new string_3_t(m__io, this, m__root); + break; + } + case 2: { + m_data = new string_2_t(m__io, this, m__root); + break; + } + default: { + m_data = new string_non_immediate_t(m__io, this, m__root); + break; + } + } + m_hamming_code = m__io->read_bits_int_be(8); + m_pad_1 = m__io->read_bits_int_be(11); + m_superframe_number = m__io->read_bits_int_be(16); + m_pad_2 = m__io->read_bits_int_be(8); + m_frame_number = m__io->read_bits_int_be(8); +} + +glonass_t::~glonass_t() { + _clean_up(); +} + +void glonass_t::_clean_up() { + if (m_data) { + delete m_data; m_data = 0; + } +} + +glonass_t::string_4_t::string_4_t(kaitai::kstream* p__io, glonass_t* p__parent, glonass_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + + try { + _read(); + } catch(...) { + _clean_up(); + throw; + } +} + +void glonass_t::string_4_t::_read() { + m_tau_n = m__io->read_bits_int_be(22); + m_delta_tau_n = m__io->read_bits_int_be(5); + m_e_n = m__io->read_bits_int_be(5); + m_not_used_1 = m__io->read_bits_int_be(14); + m_p4 = m__io->read_bits_int_be(1); + m_f_t = m__io->read_bits_int_be(4); + m_not_used_2 = m__io->read_bits_int_be(3); + m_n_t = m__io->read_bits_int_be(11); + m_n = m__io->read_bits_int_be(5); + m_m = m__io->read_bits_int_be(2); +} + +glonass_t::string_4_t::~string_4_t() { + _clean_up(); +} + +void glonass_t::string_4_t::_clean_up() { +} + +glonass_t::string_non_immediate_t::string_non_immediate_t(kaitai::kstream* p__io, glonass_t* p__parent, glonass_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + + try { + _read(); + } catch(...) { + _clean_up(); + throw; + } +} + +void glonass_t::string_non_immediate_t::_read() { + m_data_1 = m__io->read_bits_int_be(64); + m_data_2 = m__io->read_bits_int_be(8); +} + +glonass_t::string_non_immediate_t::~string_non_immediate_t() { + _clean_up(); +} + +void glonass_t::string_non_immediate_t::_clean_up() { +} + +glonass_t::string_1_t::string_1_t(kaitai::kstream* p__io, glonass_t* p__parent, glonass_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + + try { + _read(); + } catch(...) { + _clean_up(); + throw; + } +} + +void glonass_t::string_1_t::_read() { + m_not_used = m__io->read_bits_int_be(2); + m_p1 = m__io->read_bits_int_be(2); + m_t_k = m__io->read_bits_int_be(12); + m_x_vel = m__io->read_bits_int_be(24); + m_x_speedup = m__io->read_bits_int_be(5); + m_x = m__io->read_bits_int_be(27); +} + +glonass_t::string_1_t::~string_1_t() { + _clean_up(); +} + +void glonass_t::string_1_t::_clean_up() { +} + +glonass_t::string_2_t::string_2_t(kaitai::kstream* p__io, glonass_t* p__parent, glonass_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + + try { + _read(); + } catch(...) { + _clean_up(); + throw; + } +} + +void glonass_t::string_2_t::_read() { + m_b_n = m__io->read_bits_int_be(3); + m_p2 = m__io->read_bits_int_be(1); + m_t_b = m__io->read_bits_int_be(7); + m_not_used = m__io->read_bits_int_be(5); + m_y_vel = m__io->read_bits_int_be(24); + m_y_speedup = m__io->read_bits_int_be(5); + m_y = m__io->read_bits_int_be(27); +} + +glonass_t::string_2_t::~string_2_t() { + _clean_up(); +} + +void glonass_t::string_2_t::_clean_up() { +} + +glonass_t::string_3_t::string_3_t(kaitai::kstream* p__io, glonass_t* p__parent, glonass_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + + try { + _read(); + } catch(...) { + _clean_up(); + throw; + } +} + +void glonass_t::string_3_t::_read() { + m_p3 = m__io->read_bits_int_be(1); + m_gamma_n = m__io->read_bits_int_be(11); + m_not_used = m__io->read_bits_int_be(1); + m_p = m__io->read_bits_int_be(2); + m_l_n = m__io->read_bits_int_be(1); + m_z_vel = m__io->read_bits_int_be(24); + m_z_speedup = m__io->read_bits_int_be(5); + m_z = m__io->read_bits_int_be(27); +} + +glonass_t::string_3_t::~string_3_t() { + _clean_up(); +} + +void glonass_t::string_3_t::_clean_up() { +} diff --git a/selfdrive/locationd/generated/glonass.h b/selfdrive/locationd/generated/glonass.h new file mode 100644 index 0000000000..48bdabfe96 --- /dev/null +++ b/selfdrive/locationd/generated/glonass.h @@ -0,0 +1,232 @@ +#ifndef GLONASS_H_ +#define GLONASS_H_ + +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +#include "kaitai/kaitaistruct.h" +#include + +#if KAITAI_STRUCT_VERSION < 9000L +#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required" +#endif + +class glonass_t : public kaitai::kstruct { + +public: + class string_4_t; + class string_non_immediate_t; + class string_1_t; + class string_2_t; + class string_3_t; + + glonass_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, glonass_t* p__root = 0); + +private: + void _read(); + void _clean_up(); + +public: + ~glonass_t(); + + class string_4_t : public kaitai::kstruct { + + public: + + string_4_t(kaitai::kstream* p__io, glonass_t* p__parent = 0, glonass_t* p__root = 0); + + private: + void _read(); + void _clean_up(); + + public: + ~string_4_t(); + + private: + uint64_t m_tau_n; + uint64_t m_delta_tau_n; + uint64_t m_e_n; + uint64_t m_not_used_1; + bool m_p4; + uint64_t m_f_t; + uint64_t m_not_used_2; + uint64_t m_n_t; + uint64_t m_n; + uint64_t m_m; + glonass_t* m__root; + glonass_t* m__parent; + + public: + uint64_t tau_n() const { return m_tau_n; } + uint64_t delta_tau_n() const { return m_delta_tau_n; } + uint64_t e_n() const { return m_e_n; } + uint64_t not_used_1() const { return m_not_used_1; } + bool p4() const { return m_p4; } + uint64_t f_t() const { return m_f_t; } + uint64_t not_used_2() const { return m_not_used_2; } + uint64_t n_t() const { return m_n_t; } + uint64_t n() const { return m_n; } + uint64_t m() const { return m_m; } + glonass_t* _root() const { return m__root; } + glonass_t* _parent() const { return m__parent; } + }; + + class string_non_immediate_t : public kaitai::kstruct { + + public: + + string_non_immediate_t(kaitai::kstream* p__io, glonass_t* p__parent = 0, glonass_t* p__root = 0); + + private: + void _read(); + void _clean_up(); + + public: + ~string_non_immediate_t(); + + private: + uint64_t m_data_1; + uint64_t m_data_2; + glonass_t* m__root; + glonass_t* m__parent; + + public: + uint64_t data_1() const { return m_data_1; } + uint64_t data_2() const { return m_data_2; } + glonass_t* _root() const { return m__root; } + glonass_t* _parent() const { return m__parent; } + }; + + class string_1_t : public kaitai::kstruct { + + public: + + string_1_t(kaitai::kstream* p__io, glonass_t* p__parent = 0, glonass_t* p__root = 0); + + private: + void _read(); + void _clean_up(); + + public: + ~string_1_t(); + + private: + uint64_t m_not_used; + uint64_t m_p1; + uint64_t m_t_k; + uint64_t m_x_vel; + uint64_t m_x_speedup; + uint64_t m_x; + glonass_t* m__root; + glonass_t* m__parent; + + public: + uint64_t not_used() const { return m_not_used; } + uint64_t p1() const { return m_p1; } + uint64_t t_k() const { return m_t_k; } + uint64_t x_vel() const { return m_x_vel; } + uint64_t x_speedup() const { return m_x_speedup; } + uint64_t x() const { return m_x; } + glonass_t* _root() const { return m__root; } + glonass_t* _parent() const { return m__parent; } + }; + + class string_2_t : public kaitai::kstruct { + + public: + + string_2_t(kaitai::kstream* p__io, glonass_t* p__parent = 0, glonass_t* p__root = 0); + + private: + void _read(); + void _clean_up(); + + public: + ~string_2_t(); + + private: + uint64_t m_b_n; + bool m_p2; + uint64_t m_t_b; + uint64_t m_not_used; + uint64_t m_y_vel; + uint64_t m_y_speedup; + uint64_t m_y; + glonass_t* m__root; + glonass_t* m__parent; + + public: + uint64_t b_n() const { return m_b_n; } + bool p2() const { return m_p2; } + uint64_t t_b() const { return m_t_b; } + uint64_t not_used() const { return m_not_used; } + uint64_t y_vel() const { return m_y_vel; } + uint64_t y_speedup() const { return m_y_speedup; } + uint64_t y() const { return m_y; } + glonass_t* _root() const { return m__root; } + glonass_t* _parent() const { return m__parent; } + }; + + class string_3_t : public kaitai::kstruct { + + public: + + string_3_t(kaitai::kstream* p__io, glonass_t* p__parent = 0, glonass_t* p__root = 0); + + private: + void _read(); + void _clean_up(); + + public: + ~string_3_t(); + + private: + bool m_p3; + uint64_t m_gamma_n; + bool m_not_used; + uint64_t m_p; + bool m_l_n; + uint64_t m_z_vel; + uint64_t m_z_speedup; + uint64_t m_z; + glonass_t* m__root; + glonass_t* m__parent; + + public: + bool p3() const { return m_p3; } + uint64_t gamma_n() const { return m_gamma_n; } + bool not_used() const { return m_not_used; } + uint64_t p() const { return m_p; } + bool l_n() const { return m_l_n; } + uint64_t z_vel() const { return m_z_vel; } + uint64_t z_speedup() const { return m_z_speedup; } + uint64_t z() const { return m_z; } + glonass_t* _root() const { return m__root; } + glonass_t* _parent() const { return m__parent; } + }; + +private: + bool m_idle_chip; + uint64_t m_string_number; + kaitai::kstruct* m_data; + uint64_t m_hamming_code; + uint64_t m_pad_1; + uint64_t m_superframe_number; + uint64_t m_pad_2; + uint64_t m_frame_number; + glonass_t* m__root; + kaitai::kstruct* m__parent; + +public: + bool idle_chip() const { return m_idle_chip; } + uint64_t string_number() const { return m_string_number; } + kaitai::kstruct* data() const { return m_data; } + uint64_t hamming_code() const { return m_hamming_code; } + uint64_t pad_1() const { return m_pad_1; } + uint64_t superframe_number() const { return m_superframe_number; } + uint64_t pad_2() const { return m_pad_2; } + uint64_t frame_number() const { return m_frame_number; } + glonass_t* _root() const { return m__root; } + kaitai::kstruct* _parent() const { return m__parent; } +}; + +#endif // GLONASS_H_ diff --git a/selfdrive/locationd/generated/gps.cpp b/selfdrive/locationd/generated/gps.cpp index 9b020735bb..8e1cb85b95 100644 --- a/selfdrive/locationd/generated/gps.cpp +++ b/selfdrive/locationd/generated/gps.cpp @@ -274,9 +274,9 @@ gps_t::tlm_t::tlm_t(kaitai::kstream* p__io, gps_t* p__parent, gps_t* p__root) : } void gps_t::tlm_t::_read() { - m_magic = m__io->read_bytes(1); - if (!(magic() == std::string("\x8B", 1))) { - throw kaitai::validation_not_equal_error(std::string("\x8B", 1), magic(), _io(), std::string("/types/tlm/seq/0")); + m_preamble = m__io->read_bytes(1); + if (!(preamble() == std::string("\x8B", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x8B", 1), preamble(), _io(), std::string("/types/tlm/seq/0")); } m_tlm = m__io->read_bits_int_be(14); m_integrity_status = m__io->read_bits_int_be(1); diff --git a/selfdrive/locationd/generated/gps.h b/selfdrive/locationd/generated/gps.h index 293e2e4a05..9dfc5031f5 100644 --- a/selfdrive/locationd/generated/gps.h +++ b/selfdrive/locationd/generated/gps.h @@ -273,7 +273,7 @@ public: ~tlm_t(); private: - std::string m_magic; + std::string m_preamble; uint64_t m_tlm; bool m_integrity_status; bool m_reserved; @@ -281,7 +281,7 @@ public: gps_t* m__parent; public: - std::string magic() const { return m_magic; } + std::string preamble() const { return m_preamble; } uint64_t tlm() const { return m_tlm; } bool integrity_status() const { return m_integrity_status; } bool reserved() const { return m_reserved; } diff --git a/selfdrive/locationd/generated/ubx.cpp b/selfdrive/locationd/generated/ubx.cpp index 5e743e1ee7..34fe1e52ca 100644 --- a/selfdrive/locationd/generated/ubx.cpp +++ b/selfdrive/locationd/generated/ubx.cpp @@ -89,13 +89,10 @@ void ubx_t::rxm_rawx_t::_read() { m_num_meas = m__io->read_u1(); m_rec_stat = m__io->read_u1(); m_reserved1 = m__io->read_bytes(3); - int l_measurements = num_meas(); m__raw_measurements = new std::vector(); - m__raw_measurements->reserve(l_measurements); m__io__raw_measurements = new std::vector(); - m__io__raw_measurements->reserve(l_measurements); m_measurements = new std::vector(); - m_measurements->reserve(l_measurements); + const int l_measurements = num_meas(); for (int i = 0; i < l_measurements; i++) { m__raw_measurements->push_back(m__io->read_bytes(32)); kaitai::kstream* io__raw_measurements = new kaitai::kstream(m__raw_measurements->at(m__raw_measurements->size() - 1)); @@ -184,9 +181,8 @@ void ubx_t::rxm_sfrbx_t::_read() { m_reserved2 = m__io->read_bytes(1); m_version = m__io->read_u1(); m_reserved3 = m__io->read_bytes(1); - int l_body = num_words(); m_body = new std::vector(); - m_body->reserve(l_body); + const int l_body = num_words(); for (int i = 0; i < l_body; i++) { m_body->push_back(m__io->read_u4le()); } diff --git a/selfdrive/locationd/glonass.ksy b/selfdrive/locationd/glonass.ksy new file mode 100644 index 0000000000..95ca78c16f --- /dev/null +++ b/selfdrive/locationd/glonass.ksy @@ -0,0 +1,107 @@ +# http://gauss.gge.unb.ca/GLONASS.ICD.pdf +meta: + id: glonass + endian: be + bit-endian: be +seq: + - id: idle_chip + type: b1 + - id: string_number + type: b4 + - id: data + type: + switch-on: string_number + cases: + 1: string_1 + 2: string_2 + 3: string_3 + 4: string_4 + _: string_non_immediate + - id: hamming_code + type: b8 + - id: pad_1 + type: b11 + - id: superframe_number + type: b16 + - id: pad_2 + type: b8 + - id: frame_number + type: b8 + +types: + string_1: + seq: + - id: not_used + type: b2 + - id: p1 + type: b2 + - id: t_k + type: b12 + - id: x_vel + type: b24 + - id: x_speedup + type: b5 + - id: x + type: b27 + string_2: + seq: + - id: b_n + type: b3 + - id: p2 + type: b1 + - id: t_b + type: b7 + - id: not_used + type: b5 + - id: y_vel + type: b24 + - id: y_speedup + type: b5 + - id: y + type: b27 + string_3: + seq: + - id: p3 + type: b1 + - id: gamma_n + type: b11 + - id: not_used + type: b1 + - id: p + type: b2 + - id: l_n + type: b1 + - id: z_vel + type: b24 + - id: z_speedup + type: b5 + - id: z + type: b27 + string_4: + seq: + - id: tau_n + type: b22 + - id: delta_tau_n + type: b5 + - id: e_n + type: b5 + - id: not_used_1 + type: b14 + - id: p4 + type: b1 + - id: f_t + type: b4 + - id: not_used_2 + type: b3 + - id: n_t + type: b11 + - id: n + type: b5 + - id: m + type: b2 + string_non_immediate: + seq: + - id: data_1 + type: b64 + - id: data_2 + type: b8 \ No newline at end of file diff --git a/selfdrive/locationd/glonass_fix.patch b/selfdrive/locationd/glonass_fix.patch new file mode 100644 index 0000000000..2f99e93637 --- /dev/null +++ b/selfdrive/locationd/glonass_fix.patch @@ -0,0 +1,13 @@ +diff --git a/selfdrive/locationd/generated/glonass.cpp b/selfdrive/locationd/generated/glonass.cpp +index 6a48fe62c..149134fbb 100644 +--- a/selfdrive/locationd/generated/glonass.cpp ++++ b/selfdrive/locationd/generated/glonass.cpp +@@ -17,7 +17,7 @@ glonass_t::glonass_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, glonass + void glonass_t::_read() { + m_idle_chip = m__io->read_bits_int_be(1); + m_string_number = m__io->read_bits_int_be(4); +- m__io->align_to_byte(); ++ //m__io->align_to_byte(); + switch (string_number()) { + case 4: { + m_data = new string_4_t(m__io, this, m__root); diff --git a/selfdrive/locationd/gps.ksy b/selfdrive/locationd/gps.ksy index 6f5cde316b..893ad1b25b 100644 --- a/selfdrive/locationd/gps.ksy +++ b/selfdrive/locationd/gps.ksy @@ -19,7 +19,7 @@ seq: types: tlm: seq: - - id: magic + - id: preamble contents: [0x8b] - id: tlm type: b14 From 7316cd03fb71be3e27588cb7a6b9cffafbebe34f Mon Sep 17 00:00:00 2001 From: YassineYousfi Date: Thu, 26 Jan 2023 19:09:12 -0800 Subject: [PATCH 196/484] new model: fixed weight decay logic (#27068) * 07a3a2b2-6dbe-43c3-9bb9-a8023932e054/449 cdfa9e3c-2807-49fc-bdc7-a985f8ff1644/700 * update refs --- selfdrive/modeld/models/supercombo.onnx | 4 ++-- selfdrive/test/process_replay/model_replay_ref_commit | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/selfdrive/modeld/models/supercombo.onnx b/selfdrive/modeld/models/supercombo.onnx index 8805b3dce8..2daf01ea14 100644 --- a/selfdrive/modeld/models/supercombo.onnx +++ b/selfdrive/modeld/models/supercombo.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db746e3753de84367595fedd089c2bd41b06bd401ea28e085663533d0e63d74b -size 45962473 +oid sha256:3d8f2f9d53908e7a5496412956548dab1aa08ed0b5fb9d1c685f275369f07a57 +size 45962515 diff --git a/selfdrive/test/process_replay/model_replay_ref_commit b/selfdrive/test/process_replay/model_replay_ref_commit index 9eefc73792..fa4fa9540d 100644 --- a/selfdrive/test/process_replay/model_replay_ref_commit +++ b/selfdrive/test/process_replay/model_replay_ref_commit @@ -1 +1 @@ -8233a12f97f483645e7691b8c54552a2f861a7ad +2a255ecea3665a3d295210195fd8ef0791ab49a0 From 4f4f4afe8a9bca7fdaf70c2b05ef6e90263aaac8 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 26 Jan 2023 19:11:44 -0800 Subject: [PATCH 197/484] Ford: set lateral ramp type to 0 (#27106) set ramp type always to 0 --- selfdrive/car/ford/carcontroller.py | 15 +-------------- selfdrive/car/ford/fordcan.py | 9 +++++---- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/selfdrive/car/ford/carcontroller.py b/selfdrive/car/ford/carcontroller.py index 99a6648ff9..ab5cf267e0 100644 --- a/selfdrive/car/ford/carcontroller.py +++ b/selfdrive/car/ford/carcontroller.py @@ -54,22 +54,9 @@ class CarController: else: apply_curvature = 0. - # set slower ramp type when small steering angle change - # 0=Slow, 1=Medium, 2=Fast, 3=Immediately - steer_change = abs(CS.out.steeringAngleDeg - actuators.steeringAngleDeg) - if steer_change < 2.5: - ramp_type = 0 - elif steer_change < 5.0: - ramp_type = 1 - elif steer_change < 7.5: - ramp_type = 2 - else: - ramp_type = 3 - precision = 1 # 0=Comfortable, 1=Precise (the stock system always uses comfortable) - self.apply_curvature_last = apply_curvature can_sends.append(create_lka_msg(self.packer)) - can_sends.append(create_lat_ctl_msg(self.packer, CC.latActive, ramp_type, precision, 0., 0., -apply_curvature, 0.)) + can_sends.append(create_lat_ctl_msg(self.packer, CC.latActive, 0., 0., -apply_curvature, 0.)) ### ui ### send_ui = (self.main_on_last != main_on) or (self.lkas_enabled_last != CC.latActive) or (self.steer_alert_last != steer_alert) diff --git a/selfdrive/car/ford/fordcan.py b/selfdrive/car/ford/fordcan.py index dcda59bce2..9ddde80f87 100644 --- a/selfdrive/car/ford/fordcan.py +++ b/selfdrive/car/ford/fordcan.py @@ -16,8 +16,7 @@ def create_lka_msg(packer): return packer.make_can_msg("Lane_Assist_Data1", CANBUS.main, {}) -def create_lat_ctl_msg(packer, lat_active: bool, ramp_type: int, precision: int, path_offset: float, path_angle: float, - curvature: float, curvature_rate: float): +def create_lat_ctl_msg(packer, lat_active: bool, path_offset: float, path_angle: float, curvature: float, curvature_rate: float): """ Creates a CAN message for the Ford TJA/LCA Command. @@ -44,8 +43,10 @@ def create_lat_ctl_msg(packer, lat_active: bool, ramp_type: int, precision: int, "HandsOffCnfm_B_Rq": 0, # Unknown: 0=Inactive, 1=Active [0|1] "LatCtl_D_Rq": 1 if lat_active else 0, # Mode: 0=None, 1=ContinuousPathFollowing, 2=InterventionLeft, # 3=InterventionRight, 4-7=NotUsed [0|7] - "LatCtlRampType_D_Rq": ramp_type, # Ramp speed: 0=Slow, 1=Medium, 2=Fast, 3=Immediate [0|3] - "LatCtlPrecision_D_Rq": precision, # Precision: 0=Comfortable, 1=Precise, 2/3=NotUsed [0|3] + "LatCtlRampType_D_Rq": 0, # Ramp speed: 0=Slow, 1=Medium, 2=Fast, 3=Immediate [0|3] + # Makes no difference with curvature control + "LatCtlPrecision_D_Rq": 1, # Precision: 0=Comfortable, 1=Precise, 2/3=NotUsed [0|3] + # The stock system always uses comfortable "LatCtlPathOffst_L_Actl": path_offset, # Path offset [-5.12|5.11] meter "LatCtlPath_An_Actl": path_angle, # Path angle [-0.5|0.5235] radians "LatCtlCurv_NoRate_Actl": curvature_rate, # Curvature rate [-0.001024|0.00102375] 1/meter^2 From ad5b3ea410425d8d5d929e626a626c8d868ecdff Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 26 Jan 2023 20:02:23 -0800 Subject: [PATCH 198/484] controls: add curvature to actuators (#27108) * Add curvature to actuators * Use it in CC * revert ford stuff * Update ref_commit --- selfdrive/controls/controlsd.py | 1 + selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index c0b01bfc99..21d8fe2d66 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -606,6 +606,7 @@ class Controls: actuators.steer, actuators.steeringAngleDeg, lac_log = self.LaC.update(CC.latActive, CS, self.VM, lp, self.last_actuators, self.steer_limited, self.desired_curvature, self.desired_curvature_rate, self.sm['liveLocationKalman']) + actuators.curvature = self.desired_curvature else: lac_log = log.ControlsState.LateralDebugState.new_message() if self.sm.rcv_frame['testJoystick'] > 0: diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index fb714b6f57..63db584c87 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -3639e42ed538ee5d94ec2572cf27af6260633fad \ No newline at end of file +634d4ff195345a4a2508e497744aa08addec9237 From 50fc560b43bb24570400834edcaaa84d7270f38c Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 27 Jan 2023 12:29:28 +0800 Subject: [PATCH 199/484] cabana: improve mainwin (#27053) * open recent * improve mainwin * move to submenu * new file & remind save before load dd --- tools/cabana/cabana.cc | 1 + tools/cabana/mainwin.cc | 155 +++++++++++++++++++++++++++++---------- tools/cabana/mainwin.h | 18 ++++- tools/cabana/settings.cc | 2 + tools/cabana/settings.h | 1 + 5 files changed, 133 insertions(+), 44 deletions(-) diff --git a/tools/cabana/cabana.cc b/tools/cabana/cabana.cc index fc6c37fb72..d9b6b5948b 100644 --- a/tools/cabana/cabana.cc +++ b/tools/cabana/cabana.cc @@ -12,6 +12,7 @@ int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); initApp(argc, argv); QApplication app(argc, argv); + app.setApplicationDisplayName("Cabana"); QCommandLineParser cmd_parser; cmd_parser.addHelpOption(); diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index 517d0b1b73..7d99b698bb 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -61,22 +61,33 @@ MainWindow::MainWindow() : QMainWindow() { QObject::connect(charts_widget, &ChartsWidget::dock, this, &MainWindow::dockCharts); QObject::connect(can, &AbstractStream::streamStarted, this, &MainWindow::loadDBCFromFingerprint); QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &MainWindow::DBCFileChanged); - QObject::connect(detail_widget->undo_stack, &QUndoStack::indexChanged, [this](int index) { - setWindowTitle(tr("%1%2 - Cabana").arg(index > 0 ? "* " : "").arg(dbc()->name())); - }); + QObject::connect(detail_widget->undo_stack, &QUndoStack::cleanChanged, [this](bool clean) { setWindowModified(!clean); }); } void MainWindow::createActions() { QMenu *file_menu = menuBar()->addMenu(tr("&File")); - file_menu->addAction(tr("Open DBC File..."), this, &MainWindow::loadDBCFromFile)->setShortcuts(QKeySequence::Open); + file_menu->addAction(tr("New DBC File"), this, &MainWindow::newFile)->setShortcuts(QKeySequence::New); + file_menu->addAction(tr("Open DBC File..."), this, &MainWindow::openFile)->setShortcuts(QKeySequence::Open); file_menu->addAction(tr("Load DBC From Clipboard"), this, &MainWindow::loadDBCFromClipboard); + open_recent_menu = file_menu->addMenu(tr("Open &Recent")); + for (int i = 0; i < MAX_RECENT_FILES; ++i) { + recent_files_acts[i] = new QAction(this); + recent_files_acts[i]->setVisible(false); + QObject::connect(recent_files_acts[i], &QAction::triggered, this, &MainWindow::openRecentFile); + open_recent_menu->addAction(recent_files_acts[i]); + } + updateRecentFileActions(); + file_menu->addSeparator(); - file_menu->addAction(tr("Save DBC..."), this, &MainWindow::saveDBCToFile)->setShortcuts(QKeySequence::Save); - file_menu->addAction(tr("Save DBC As..."), this, &MainWindow::saveAsDBCToFile)->setShortcuts(QKeySequence::SaveAs); + file_menu->addAction(tr("Save DBC..."), this, &MainWindow::save)->setShortcuts(QKeySequence::Save); + file_menu->addAction(tr("Save DBC As..."), this, &MainWindow::saveAs)->setShortcuts(QKeySequence::SaveAs); file_menu->addAction(tr("Copy DBC To Clipboard"), this, &MainWindow::saveDBCToClipboard); file_menu->addSeparator(); file_menu->addAction(tr("Settings..."), this, &MainWindow::setOption)->setShortcuts(QKeySequence::Preferences); + file_menu->addSeparator(); + file_menu->addAction(tr("E&xit"), qApp, &QApplication::closeAllWindows)->setShortcuts(QKeySequence::Quit); + QMenu *edit_menu = menuBar()->addMenu(tr("&Edit")); auto undo_act = detail_widget->undo_stack->createUndoAction(this, tr("&Undo")); undo_act->setShortcuts(QKeySequence::Undo); @@ -181,34 +192,57 @@ void MainWindow::DBCFileChanged() { detail_widget->undo_stack->clear(); int index = dbc_combo->findText(QFileInfo(dbc()->name()).baseName()); dbc_combo->setCurrentIndex(index); - setWindowTitle(tr("%1 - Cabana").arg(dbc()->name())); + setWindowFilePath(QString("%1").arg(dbc()->name())); } -void MainWindow::loadDBCFromName(const QString &name) { - if (name != dbc()->name()) { - dbc()->open(name); +void MainWindow::newFile() { + remindSaveChanges(); + dbc()->open("untitled.dbc", ""); +} + +void MainWindow::openFile() { + remindSaveChanges(); + QString fn = QFileDialog::getOpenFileName(this, tr("Open File"), settings.last_dir, "DBC (*.dbc)"); + if (!fn.isEmpty()) { + loadFile(fn); } } -void MainWindow::loadDBCFromFile() { - file_name = QFileDialog::getOpenFileName(this, tr("Open File"), settings.last_dir, "DBC (*.dbc)"); - if (!file_name.isEmpty()) { - settings.last_dir = QFileInfo(file_name).absolutePath(); - QFile file(file_name); +void MainWindow::loadFile(const QString &fn) { + if (!fn.isEmpty()) { + QFile file(fn); if (file.open(QIODevice::ReadOnly)) { - auto dbc_name = QFileInfo(file_name).baseName(); + auto dbc_name = QFileInfo(fn).baseName(); dbc()->open(dbc_name, file.readAll()); + setCurrentFile(fn); + statusBar()->showMessage(tr("DBC File %1 loaded").arg(fn), 2000); } } } +void MainWindow::openRecentFile() { + if (auto action = qobject_cast(sender())) { + remindSaveChanges(); + loadFile(action->data().toString()); + } +} + +void MainWindow::loadDBCFromName(const QString &name) { + if (name != dbc()->name()) { + remindSaveChanges(); + dbc()->open(name); + } +} + void MainWindow::loadDBCFromClipboard() { + remindSaveChanges(); QString dbc_str = QGuiApplication::clipboard()->text(); - dbc()->open("From Clipboard", dbc_str); + dbc()->open("from_clipboard.dbc", dbc_str); QMessageBox::information(this, tr("Load From Clipboard"), tr("DBC Successfully Loaded!")); } void MainWindow::loadDBCFromFingerprint() { + remindSaveChanges(); auto fingerprint = can->carFingerprint(); video_dock->setWindowTitle(tr("ROUTE: %1 FINGERPINT: %2").arg(can->routeName()).arg(fingerprint.isEmpty() ? tr("Unknown Car") : fingerprint)); if (!fingerprint.isEmpty()) { @@ -218,25 +252,31 @@ void MainWindow::loadDBCFromFingerprint() { return; } } - dbc()->open("New_DBC", ""); + newFile(); } -void MainWindow::saveDBCToFile() { - if (file_name.isEmpty()) { - saveAsDBCToFile(); +void MainWindow::save() { + if (current_file.isEmpty()) { + saveAs(); } else { - settings.last_dir = QFileInfo(file_name).absolutePath(); - QFile file(file_name); - if (file.open(QIODevice::WriteOnly)) { - file.write(dbc()->generateDBC().toUtf8()); - } + saveFile(current_file); } } -void MainWindow::saveAsDBCToFile() { - file_name = QFileDialog::getSaveFileName(this, tr("Save File"), QDir::cleanPath(settings.last_dir + "/untitled.dbc"), tr("DBC (*.dbc)")); - if (!file_name.isEmpty()) { - saveDBCToFile(); +void MainWindow::saveFile(const QString &fn) { + QFile file(fn); + if (file.open(QIODevice::WriteOnly)) { + file.write(dbc()->generateDBC().toUtf8()); + detail_widget->undo_stack->setClean(); + setCurrentFile(fn); + statusBar()->showMessage(tr("File saved"), 2000); + } +} + +void MainWindow::saveAs() { + QString fn = QFileDialog::getSaveFileName(this, tr("Save File"), QDir::cleanPath(settings.last_dir + "/untitled.dbc"), tr("DBC (*.dbc)")); + if (!fn.isEmpty()) { + saveFile(fn); } } @@ -245,6 +285,49 @@ void MainWindow::saveDBCToClipboard() { QMessageBox::information(this, tr("Copy To Clipboard"), tr("DBC Successfully copied!")); } +void MainWindow::setCurrentFile(const QString &fn) { + current_file = fn; + setWindowFilePath(QString("%1").arg(fn)); + settings.recent_files.removeAll(fn); + settings.recent_files.prepend(fn); + while (settings.recent_files.size() > MAX_RECENT_FILES) { + settings.recent_files.removeLast(); + } + settings.last_dir = QFileInfo(fn).absolutePath(); + updateRecentFileActions(); +} + +void MainWindow::updateRecentFileActions() { + int num_recent_files = std::min(settings.recent_files.size(), MAX_RECENT_FILES); + + for (int i = 0; i < num_recent_files; ++i) { + QString text = tr("&%1 %2").arg(i + 1).arg(QFileInfo(settings.recent_files[i]).fileName()); + recent_files_acts[i]->setText(text); + recent_files_acts[i]->setData(settings.recent_files[i]); + recent_files_acts[i]->setVisible(true); + } + for (int i = num_recent_files; i < MAX_RECENT_FILES; ++i) { + recent_files_acts[i]->setVisible(false); + } + open_recent_menu->setEnabled(num_recent_files > 0); +} + +void MainWindow::remindSaveChanges() { + bool discard_changes = false; + while (!detail_widget->undo_stack->isClean() && !discard_changes) { + int ret = (QMessageBox::question(this, tr("Unsaved Changes"), + tr("You have unsaved changes. Press ok to save them, cancel to discard."), + QMessageBox::Ok | QMessageBox::Cancel)); + if (ret == QMessageBox::Ok) { + save(); + } else { + discard_changes = true; + } + } + detail_widget->undo_stack->clear(); + current_file = ""; +} + void MainWindow::updateDownloadProgress(uint64_t cur, uint64_t total, bool success) { if (success && cur < total) { progress_bar->setValue((cur / (double)total) * 100); @@ -264,7 +347,7 @@ void MainWindow::dockCharts(bool dock) { } else if (!dock && !floating_window) { floating_window = new QWidget(this); floating_window->setWindowFlags(Qt::Window); - floating_window->setWindowTitle("Charts - Cabana"); + floating_window->setWindowTitle("Charts"); floating_window->setLayout(new QVBoxLayout()); floating_window->layout()->addWidget(charts_widget); floating_window->installEventFilter(charts_widget); @@ -273,15 +356,7 @@ void MainWindow::dockCharts(bool dock) { } void MainWindow::closeEvent(QCloseEvent *event) { - if (detail_widget->undo_stack->index() > 0) { - auto ret = QMessageBox::question(this, tr("Unsaved Changes"), - tr("Are you sure you want to exit without saving?\nAny unsaved changes will be lost."), - QMessageBox::Yes | QMessageBox::No); - if (ret == QMessageBox::No) { - event->ignore(); - return; - } - } + remindSaveChanges(); main_win = nullptr; if (floating_window) diff --git a/tools/cabana/mainwin.h b/tools/cabana/mainwin.h index a38834b997..4e65fa01cf 100644 --- a/tools/cabana/mainwin.h +++ b/tools/cabana/mainwin.h @@ -23,12 +23,14 @@ public: void showStatusMessage(const QString &msg, int timeout = 0) { statusBar()->showMessage(msg, timeout); } public slots: + void newFile(); + void openFile(); + void openRecentFile(); void loadDBCFromName(const QString &name); void loadDBCFromFingerprint(); - void loadDBCFromFile(); void loadDBCFromClipboard(); - void saveDBCToFile(); - void saveAsDBCToFile(); + void save(); + void saveAs(); void saveDBCToClipboard(); signals: @@ -36,6 +38,11 @@ signals: void updateProgressBar(uint64_t cur, uint64_t total, bool success); protected: + void remindSaveChanges(); + void saveFile(const QString &fn); + void loadFile(const QString &fn); + void setCurrentFile(const QString &fn); + void updateRecentFileActions(); void createActions(); void createDockWindows(); QComboBox *createDBCSelector(); @@ -58,5 +65,8 @@ protected: QJsonDocument fingerprint_to_dbc; QComboBox *dbc_combo; QSplitter *video_splitter;; - QString file_name = ""; + QString current_file = ""; + enum { MAX_RECENT_FILES = 15 }; + QAction *recent_files_acts[MAX_RECENT_FILES] = {}; + QMenu *open_recent_menu = nullptr; }; diff --git a/tools/cabana/settings.cc b/tools/cabana/settings.cc index 7d34d5dec0..78969c76c3 100644 --- a/tools/cabana/settings.cc +++ b/tools/cabana/settings.cc @@ -23,6 +23,7 @@ void Settings::save() { s.setValue("window_state", window_state); s.setValue("geometry", geometry); s.setValue("video_splitter_state", video_splitter_state); + s.setValue("recent_files", recent_files); s.setValue("message_header_state", message_header_state); } @@ -37,6 +38,7 @@ void Settings::load() { window_state = s.value("window_state").toByteArray(); geometry = s.value("geometry").toByteArray(); video_splitter_state = s.value("video_splitter_state").toByteArray(); + recent_files = s.value("recent_files").toStringList(); message_header_state = s.value("message_header_state").toByteArray(); } diff --git a/tools/cabana/settings.h b/tools/cabana/settings.h index 7b0cbc9f5d..56c6a992ae 100644 --- a/tools/cabana/settings.h +++ b/tools/cabana/settings.h @@ -22,6 +22,7 @@ public: QByteArray geometry; QByteArray video_splitter_state; QByteArray window_state; + QStringList recent_files; QByteArray message_header_state; signals: From 1d9b4c2248f2e4b575e37b6b134880086f1ba0b4 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 26 Jan 2023 21:05:39 -0800 Subject: [PATCH 200/484] cleanup panda mcu definitions (#27104) * cleanup panda mcu definitions * cleanup panda mcu definitions * bump * panda master --- panda | 2 +- selfdrive/boardd/pandad.py | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/panda b/panda index ae051c94a3..76d0459182 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit ae051c94a393550ab8d70c594fc9dfb49bc9aed8 +Subproject commit 76d0459182d544d17c86d8b352e9cc1c26035f4d diff --git a/selfdrive/boardd/pandad.py b/selfdrive/boardd/pandad.py index 971756002b..f61d9ee1a6 100755 --- a/selfdrive/boardd/pandad.py +++ b/selfdrive/boardd/pandad.py @@ -7,7 +7,7 @@ import subprocess from typing import List, NoReturn from functools import cmp_to_key -from panda import DEFAULT_FW_FN, DEFAULT_H7_FW_FN, MCU_TYPE_H7, Panda, PandaDFU +from panda import Panda, PandaDFU from common.basedir import BASEDIR from common.params import Params from system.hardware import HARDWARE @@ -15,10 +15,8 @@ from system.swaglog import cloudlog def get_expected_signature(panda: Panda) -> bytes: - fn = DEFAULT_H7_FW_FN if (panda.get_mcu_type() == MCU_TYPE_H7) else DEFAULT_FW_FN - try: - return Panda.get_signature_from_firmware(fn) + return Panda.get_signature_from_firmware(panda.get_mcu_type().config.app_path) except Exception: cloudlog.exception("Error computing expected signature") return b"" From 701b7d89b65b6cf936ed6893de09c45f6c982ca4 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 26 Jan 2023 22:29:01 -0800 Subject: [PATCH 201/484] Ford: use curvature without roll compensation (#27110) * Ford: use actuators.curvature * ford is mathless --- selfdrive/car/ford/carcontroller.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/selfdrive/car/ford/carcontroller.py b/selfdrive/car/ford/carcontroller.py index ab5cf267e0..06da87bf34 100644 --- a/selfdrive/car/ford/carcontroller.py +++ b/selfdrive/car/ford/carcontroller.py @@ -1,4 +1,3 @@ -import math from cereal import car from common.numpy_fast import clip from opendbc.can.packer import CANPacker @@ -46,10 +45,8 @@ class CarController: # send steering commands at 20Hz if (self.frame % CarControllerParams.STEER_STEP) == 0: if CC.latActive: - # apply limits to curvature - apply_curvature = -self.VM.calc_curvature(math.radians(actuators.steeringAngleDeg), CS.out.vEgo, 0.0) - apply_curvature = apply_std_steer_angle_limits(apply_curvature, self.apply_curvature_last, CS.out.vEgo, CarControllerParams) - # clip to signal range + # apply limits to curvature and clip to signal range + apply_curvature = apply_std_steer_angle_limits(actuators.curvature, self.apply_curvature_last, CS.out.vEgo, CarControllerParams) apply_curvature = clip(apply_curvature, -CarControllerParams.CURVATURE_MAX, CarControllerParams.CURVATURE_MAX) else: apply_curvature = 0. From 226d80964d4586392266fd2e711e6b6c003602de Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 26 Jan 2023 23:14:05 -0800 Subject: [PATCH 202/484] Ford: lower lateral actuator delay (#27113) Update interface.py --- selfdrive/car/ford/interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/car/ford/interface.py b/selfdrive/car/ford/interface.py index cea39d8b00..14592a5f68 100644 --- a/selfdrive/car/ford/interface.py +++ b/selfdrive/car/ford/interface.py @@ -19,7 +19,7 @@ class CarInterface(CarInterfaceBase): ret.dashcamOnly = True ret.steerControlType = car.CarParams.SteerControlType.angle - ret.steerActuatorDelay = 0.25 + ret.steerActuatorDelay = 0.2 ret.steerLimitTimer = 1.0 if candidate == CAR.BRONCO_SPORT_MK1: From 0eed31f1a68315121a3319464869cc622947ebba Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 27 Jan 2023 11:27:07 -0800 Subject: [PATCH 203/484] process replay: show process name in timeout error --- selfdrive/test/process_replay/process_replay.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 06af4469f2..7172c112aa 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -554,7 +554,7 @@ def cpp_replay_process(cfg, lr, fingerprint=None): managed_processes[cfg.proc_name].start() try: - with Timeout(TIMEOUT): + with Timeout(TIMEOUT, error_msg=f"timed out testing process {repr(cfg.proc_name)}"): while not all(pm.all_readers_updated(s) for s in cfg.pub_sub.keys()): time.sleep(0) From f0078eafac20ac5af7e9683e61045bfb524f82cf Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 28 Jan 2023 03:33:52 +0800 Subject: [PATCH 204/484] cabana: set time column to fixed width (#27117) improve column width --- tools/cabana/historylog.cc | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tools/cabana/historylog.cc b/tools/cabana/historylog.cc index 970aafd342..a2a90e4f0c 100644 --- a/tools/cabana/historylog.cc +++ b/tools/cabana/historylog.cc @@ -170,11 +170,16 @@ std::deque HistoryLogModel::fetchData(uint64_t from_ti // HeaderView QSize HeaderView::sectionSizeFromContents(int logicalIndex) const { - int default_size = qMax(100, rect().width() / model()->columnCount()); - const QString text = model()->headerData(logicalIndex, this->orientation(), Qt::DisplayRole).toString(); - const QRect rect = fontMetrics().boundingRect({0, 0, default_size, 2000}, defaultAlignment(), text); - QSize size = rect.size() + QSize{10, 6}; - return {qMax(size.width(), default_size), size.height()}; + static QSize time_col_size = fontMetrics().boundingRect({0, 0, 200, 200}, defaultAlignment(), "000000.000").size() + QSize(10, 6); + if (logicalIndex == 0) { + return time_col_size; + } else { + int default_size = qMax(100, (rect().width() - time_col_size.width()) / (model()->columnCount() - 1)); + const QString text = model()->headerData(logicalIndex, this->orientation(), Qt::DisplayRole).toString(); + const QRect rect = fontMetrics().boundingRect({0, 0, default_size, 2000}, defaultAlignment(), text); + QSize size = rect.size() + QSize{10, 6}; + return QSize{qMax(size.width(), default_size), size.height()}; + } } void HeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const { From e1e043231720712d7c1e2d7342f13756d68efcde Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 28 Jan 2023 03:34:17 +0800 Subject: [PATCH 205/484] cabana: display warning if failed to load dbc from clipboard (#27115) display warning if failed to load from clipboard --- tools/cabana/mainwin.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index 7d99b698bb..d674ec9059 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -238,7 +238,11 @@ void MainWindow::loadDBCFromClipboard() { remindSaveChanges(); QString dbc_str = QGuiApplication::clipboard()->text(); dbc()->open("from_clipboard.dbc", dbc_str); - QMessageBox::information(this, tr("Load From Clipboard"), tr("DBC Successfully Loaded!")); + if (dbc()->messages().size() > 0) { + QMessageBox::information(this, tr("Load From Clipboard"), tr("DBC Successfully Loaded!")); + } else { + QMessageBox::warning(this, tr("Load From Clipboard"), tr("Failed to parse dbc from clipboard!\nMake sure that you paste the text with correct format.")); + } } void MainWindow::loadDBCFromFingerprint() { From 4f25bd05f29c81d622ad57a8d608006ab1baf5e6 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 27 Jan 2023 13:06:33 -0800 Subject: [PATCH 206/484] process replay: test routes in dashcam (#27118) * fix routes with dashcam override param * move to bottom --- selfdrive/test/process_replay/process_replay.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 7172c112aa..606dcad9c4 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -448,6 +448,9 @@ def setup_env(simulation=False, CP=None, cfg=None, controlsState=None): if CP.openpilotLongitudinalControl: params.put_bool("ExperimentalLongitudinalEnabled", True) + # controlsd process configuration assume all routes are out of dashcam + params.put_bool("DashcamOverride", True) + def python_replay_process(cfg, lr, fingerprint=None): sub_sockets = [s for _, sub in cfg.pub_sub.items() for s in sub] From 60f73657def5aefdae7e1ceb6d32ae9b0cc3b880 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 27 Jan 2023 13:44:37 -0800 Subject: [PATCH 207/484] Ford: set radarOffCan (#27120) Fix --- selfdrive/car/ford/interface.py | 1 + 1 file changed, 1 insertion(+) diff --git a/selfdrive/car/ford/interface.py b/selfdrive/car/ford/interface.py index 14592a5f68..7c6a8ecbe4 100644 --- a/selfdrive/car/ford/interface.py +++ b/selfdrive/car/ford/interface.py @@ -18,6 +18,7 @@ class CarInterface(CarInterfaceBase): # These cars are dashcam only until the port is finished ret.dashcamOnly = True + ret.radarOffCan = True ret.steerControlType = car.CarParams.SteerControlType.angle ret.steerActuatorDelay = 0.2 ret.steerLimitTimer = 1.0 From cb62a5397e1a0d9603308e143339bf6e31c3d2e4 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 27 Jan 2023 14:14:04 -0800 Subject: [PATCH 208/484] process replay: add Ford Bronco Sport segment (#27112) * Add Ford segment to process replay * fix dashcam cars * clean up * fix NotImplementedError * Update refs --- selfdrive/test/process_replay/ref_commit | 2 +- selfdrive/test/process_replay/test_processes.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 63db584c87..dcd2f7b224 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -634d4ff195345a4a2508e497744aa08addec9237 +ec92fe65806256cb5180cfd5ec60895efcc08da2 \ No newline at end of file diff --git a/selfdrive/test/process_replay/test_processes.py b/selfdrive/test/process_replay/test_processes.py index c58909bf7f..569090f606 100755 --- a/selfdrive/test/process_replay/test_processes.py +++ b/selfdrive/test/process_replay/test_processes.py @@ -29,6 +29,7 @@ source_segments = [ ("SUBARU", "341dccd5359e3c97|2022-09-12--10-35-33--3"), # SUBARU.OUTBACK ("GM", "0c58b6a25109da2b|2021-02-23--16-35-50--11"), # GM.VOLT ("GM2", "376bf99325883932|2022-10-27--13-41-22--1"), # GM.BOLT_EUV + ("FORD", "54827bf84c38b14f|2023-01-26--21-59-07--4"), # FORD.BRONCO_SPORT_MK1 ("NISSAN", "35336926920f3571|2021-02-12--18-38-48--46"), # NISSAN.XTRAIL ("VOLKSWAGEN", "de9592456ad7d144|2021-06-29--11-00-15--6"), # VOLKSWAGEN.GOLF ("MAZDA", "bd6a637565e91581|2021-10-30--15-14-53--4"), # MAZDA.CX9_2021 @@ -52,6 +53,7 @@ segments = [ ("SUBARU", "regen1E72BBDCED5|2022-09-27--15-55-31--0"), ("GM", "regen45B05A80EF6|2022-09-27--15-57-22--0"), ("GM2", "376bf99325883932|2022-10-27--13-41-22--1"), + ("FORD", "54827bf84c38b14f|2023-01-26--21-59-07--4"), ("NISSAN", "regenC19D899B46D|2022-09-27--15-59-13--0"), ("VOLKSWAGEN", "regenD8F7AC4BD0D|2022-09-27--16-41-45--0"), ("MAZDA", "regenFC3F9ECBB64|2022-09-27--16-03-09--0"), From 8d447e8a081e878a55707cc2840a6d08d88102ca Mon Sep 17 00:00:00 2001 From: Vivek Aithal Date: Fri, 27 Jan 2023 15:20:59 -0800 Subject: [PATCH 209/484] [RAM 1500 5TH GEN] Disable Live Torque (#27121) * revert ram on live torque * update refs * update refs --- selfdrive/car/torque_data/override.yaml | 1 + selfdrive/car/torque_data/params.yaml | 1 - selfdrive/locationd/torqued.py | 3 +-- selfdrive/test/process_replay/ref_commit | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/selfdrive/car/torque_data/override.yaml b/selfdrive/car/torque_data/override.yaml index 733aa34343..0a49d39914 100644 --- a/selfdrive/car/torque_data/override.yaml +++ b/selfdrive/car/torque_data/override.yaml @@ -23,6 +23,7 @@ FORD MAVERICK 1ST GEN: [.nan, 1.5, .nan] COMMA BODY: [.nan, 1000, .nan] # Totally new cars +RAM 1500 5TH GEN: [2.0, 2.0, 0.0] RAM HD 5TH GEN: [1.4, 1.4, 0.0] SUBARU OUTBACK 6TH GEN: [2.3, 2.3, 0.11] CHEVROLET BOLT EV 2022: [2.0, 2.0, 0.05] diff --git a/selfdrive/car/torque_data/params.yaml b/selfdrive/car/torque_data/params.yaml index 6a607f3a8a..397b29525d 100644 --- a/selfdrive/car/torque_data/params.yaml +++ b/selfdrive/car/torque_data/params.yaml @@ -56,7 +56,6 @@ LEXUS RX 2020: [1.5228812994274734, 1.431102486563665, 0.164117] LEXUS RX HYBRID 2017: [1.6984261557042386, 1.3211501880159107, 0.1820354534928893] LEXUS RX HYBRID 2020: [1.5522309889823778, 1.255230465866663, 0.2220954003055114] MAZDA CX-9 2021: [1.7601682915983443, 1.0889677335154337, 0.17713792194297195] -RAM 1500 5TH GEN: [1.550848, 2.0, 0.132261] SKODA SUPERB 3RD GEN: [1.166437404652981, 1.1686163012668165, 0.12194533036948708] SUBARU FORESTER 2019: [3.6617001649776793, 2.342197172531713, 0.11075960785398745] SUBARU IMPREZA LIMITED 2019: [1.0670704910352047, 0.8234374840709592, 0.20986563268614938] diff --git a/selfdrive/locationd/torqued.py b/selfdrive/locationd/torqued.py index 5449152686..fcc068d34e 100755 --- a/selfdrive/locationd/torqued.py +++ b/selfdrive/locationd/torqued.py @@ -35,7 +35,6 @@ MIN_ENGAGE_BUFFER = 2 # secs VERSION = 1 # bump this to invalidate old parameter caches ALLOWED_CARS = ['toyota', 'hyundai'] -ALLOWED_PLATFORMS = ['RAM 1500 5TH GEN'] # for adding individual platforms from a brand without torqued def slope2rot(slope): @@ -114,7 +113,7 @@ class TorqueEstimator: self.offline_friction = 0.0 self.offline_latAccelFactor = 0.0 self.resets = 0.0 - self.use_params = (CP.carName in ALLOWED_CARS) or (CP.carFingerprint in ALLOWED_PLATFORMS) + self.use_params = CP.carName in ALLOWED_CARS if CP.lateralTuning.which() == 'torque': self.offline_friction = CP.lateralTuning.torque.friction diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index dcd2f7b224..51de0f0013 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -ec92fe65806256cb5180cfd5ec60895efcc08da2 \ No newline at end of file +844c84eae269e74b246b039d2b33fd44e98a5b68 \ No newline at end of file From 07b32750c16fe4e71abbf81e07aa839ac60b5343 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sun, 29 Jan 2023 04:03:59 +0800 Subject: [PATCH 210/484] cabana: remove extra frame border in logs (#27133) remove extra frame border --- tools/cabana/historylog.cc | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tools/cabana/historylog.cc b/tools/cabana/historylog.cc index a2a90e4f0c..ccc2c5533d 100644 --- a/tools/cabana/historylog.cc +++ b/tools/cabana/historylog.cc @@ -195,8 +195,13 @@ void HeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalI LogsWidget::LogsWidget(QWidget *parent) : QWidget(parent) { QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(0, 0, 0, 0); + main_layout->setSpacing(0); + + QWidget *toolbar = new QWidget(this); + toolbar->setAutoFillBackground(true); + QHBoxLayout *h = new QHBoxLayout(toolbar); - QHBoxLayout *h = new QHBoxLayout(); filters_widget = new QWidget(this); QHBoxLayout *filter_layout = new QHBoxLayout(filters_widget); filter_layout->setContentsMargins(0, 0, 0, 0); @@ -215,7 +220,11 @@ LogsWidget::LogsWidget(QWidget *parent) : QWidget(parent) { dynamic_mode->setChecked(true); dynamic_mode->setEnabled(!can->liveStreaming()); - main_layout->addLayout(h); + main_layout->addWidget(toolbar); + QFrame *line = new QFrame(this); + line->setFrameStyle(QFrame::HLine | QFrame::Sunken); + main_layout->addWidget(line);; + main_layout->addWidget(logs = new QTableView(this)); logs->setModel(model = new HistoryLogModel(this)); logs->setItemDelegateForColumn(1, new MessageBytesDelegate(this)); @@ -223,6 +232,7 @@ LogsWidget::LogsWidget(QWidget *parent) : QWidget(parent) { logs->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | (Qt::Alignment)Qt::TextWordWrap); logs->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); logs->verticalHeader()->setVisible(false); + logs->setFrameShape(QFrame::NoFrame); QObject::connect(display_type_cb, SIGNAL(activated(int)), model, SLOT(setDisplayType(int))); QObject::connect(dynamic_mode, &QCheckBox::stateChanged, model, &HistoryLogModel::setDynamicMode); From 68c0012bf62bcbe9a273d8d4caca0c1ce0d8e9bb Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Sat, 28 Jan 2023 21:05:51 +0100 Subject: [PATCH 211/484] cabana: DoubleValidator fix locale (#27129) --- tools/cabana/signaledit.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/cabana/signaledit.cc b/tools/cabana/signaledit.cc index cc57cf0f3e..112c2807f9 100644 --- a/tools/cabana/signaledit.cc +++ b/tools/cabana/signaledit.cc @@ -12,6 +12,8 @@ SignalForm::SignalForm(QWidget *parent) : QWidget(parent) { auto double_validator = new QDoubleValidator(this); + double_validator->setLocale(QLocale::C); // Match locale of QString::toDouble() instead of system + QVBoxLayout *main_layout = new QVBoxLayout(this); QFormLayout *form_layout = new QFormLayout(); main_layout->addLayout(form_layout); From ba5c6715a8099eb5850382a5b7918da33a6c13dd Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Sat, 28 Jan 2023 21:06:04 +0100 Subject: [PATCH 212/484] cabana: fix crash when combining graphs on MacOS (#27128) --- tools/cabana/chartswidget.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 5d0e5d7a52..3ecdbb08bd 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -578,8 +578,9 @@ void ChartView::mousePressEvent(QMouseEvent *event) { if (dropAction == Qt::MoveAction) { return; } + } else { + QChartView::mousePressEvent(event); } - QChartView::mousePressEvent(event); } void ChartView::mouseReleaseEvent(QMouseEvent *event) { From 7f853819767bb92488b9614b8a7b3e0210f9ecb5 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sat, 28 Jan 2023 15:00:27 -0800 Subject: [PATCH 213/484] bump panda (#27136) * bump panda * bump --- panda | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda b/panda index 76d0459182..e7f36a2992 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 76d0459182d544d17c86d8b352e9cc1c26035f4d +Subproject commit e7f36a2992b353d7d66fbb697d166d4de8718c99 From aa71947ebc9c286a13f86458ea0e970ca0d7a0c7 Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Sat, 28 Jan 2023 16:47:26 -0800 Subject: [PATCH 214/484] add signs to glonass ksy --- selfdrive/locationd/generated/glonass.cpp | 176 ++++++++++++++++++-- selfdrive/locationd/generated/glonass.h | 191 +++++++++++++++++++--- selfdrive/locationd/glonass.ksy | 119 +++++++++++--- 3 files changed, 425 insertions(+), 61 deletions(-) diff --git a/selfdrive/locationd/generated/glonass.cpp b/selfdrive/locationd/generated/glonass.cpp index 149134fbb5..a342bdff22 100644 --- a/selfdrive/locationd/generated/glonass.cpp +++ b/selfdrive/locationd/generated/glonass.cpp @@ -31,6 +31,10 @@ void glonass_t::_read() { m_data = new string_3_t(m__io, this, m__root); break; } + case 5: { + m_data = new string_5_t(m__io, this, m__root); + break; + } case 2: { m_data = new string_2_t(m__io, this, m__root); break; @@ -60,6 +64,8 @@ void glonass_t::_clean_up() { glonass_t::string_4_t::string_4_t(kaitai::kstream* p__io, glonass_t* p__parent, glonass_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; + f_tau_n = false; + f_delta_tau_n = false; try { _read(); @@ -70,8 +76,10 @@ glonass_t::string_4_t::string_4_t(kaitai::kstream* p__io, glonass_t* p__parent, } void glonass_t::string_4_t::_read() { - m_tau_n = m__io->read_bits_int_be(22); - m_delta_tau_n = m__io->read_bits_int_be(5); + m_tau_n_sign = m__io->read_bits_int_be(1); + m_tau_n_value = m__io->read_bits_int_be(21); + m_delta_tau_n_sign = m__io->read_bits_int_be(1); + m_delta_tau_n_value = m__io->read_bits_int_be(4); m_e_n = m__io->read_bits_int_be(5); m_not_used_1 = m__io->read_bits_int_be(14); m_p4 = m__io->read_bits_int_be(1); @@ -89,6 +97,22 @@ glonass_t::string_4_t::~string_4_t() { void glonass_t::string_4_t::_clean_up() { } +int32_t glonass_t::string_4_t::tau_n() { + if (f_tau_n) + return m_tau_n; + m_tau_n = ((tau_n_sign()) ? ((tau_n_value() - (1 << 21))) : (tau_n_value())); + f_tau_n = true; + return m_tau_n; +} + +int32_t glonass_t::string_4_t::delta_tau_n() { + if (f_delta_tau_n) + return m_delta_tau_n; + m_delta_tau_n = ((delta_tau_n_sign()) ? ((delta_tau_n_value() - (1 << 4))) : (delta_tau_n_value())); + f_delta_tau_n = true; + return m_delta_tau_n; +} + glonass_t::string_non_immediate_t::string_non_immediate_t(kaitai::kstream* p__io, glonass_t* p__parent, glonass_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; @@ -113,9 +137,40 @@ glonass_t::string_non_immediate_t::~string_non_immediate_t() { void glonass_t::string_non_immediate_t::_clean_up() { } +glonass_t::string_5_t::string_5_t(kaitai::kstream* p__io, glonass_t* p__parent, glonass_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + + try { + _read(); + } catch(...) { + _clean_up(); + throw; + } +} + +void glonass_t::string_5_t::_read() { + m_n_a = m__io->read_bits_int_be(11); + m_tau_e = m__io->read_bits_int_be(32); + m_not_used = m__io->read_bits_int_be(1); + m_n_4 = m__io->read_bits_int_be(5); + m_tau_gps = m__io->read_bits_int_be(22); + m_l_n = m__io->read_bits_int_be(1); +} + +glonass_t::string_5_t::~string_5_t() { + _clean_up(); +} + +void glonass_t::string_5_t::_clean_up() { +} + glonass_t::string_1_t::string_1_t(kaitai::kstream* p__io, glonass_t* p__parent, glonass_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; + f_x_vel = false; + f_x_accel = false; + f_x = false; try { _read(); @@ -129,9 +184,12 @@ void glonass_t::string_1_t::_read() { m_not_used = m__io->read_bits_int_be(2); m_p1 = m__io->read_bits_int_be(2); m_t_k = m__io->read_bits_int_be(12); - m_x_vel = m__io->read_bits_int_be(24); - m_x_speedup = m__io->read_bits_int_be(5); - m_x = m__io->read_bits_int_be(27); + m_x_vel_sign = m__io->read_bits_int_be(1); + m_x_vel_value = m__io->read_bits_int_be(23); + m_x_accel_sign = m__io->read_bits_int_be(1); + m_x_accel_value = m__io->read_bits_int_be(4); + m_x_sign = m__io->read_bits_int_be(1); + m_x_value = m__io->read_bits_int_be(26); } glonass_t::string_1_t::~string_1_t() { @@ -141,9 +199,36 @@ glonass_t::string_1_t::~string_1_t() { void glonass_t::string_1_t::_clean_up() { } +int32_t glonass_t::string_1_t::x_vel() { + if (f_x_vel) + return m_x_vel; + m_x_vel = ((x_vel_sign()) ? ((x_vel_value() - (1 << 23))) : (x_vel_value())); + f_x_vel = true; + return m_x_vel; +} + +int32_t glonass_t::string_1_t::x_accel() { + if (f_x_accel) + return m_x_accel; + m_x_accel = ((x_accel_sign()) ? ((x_accel_value() - (1 << 4))) : (x_accel_value())); + f_x_accel = true; + return m_x_accel; +} + +int32_t glonass_t::string_1_t::x() { + if (f_x) + return m_x; + m_x = ((x_sign()) ? ((x_value() - (1 << 26))) : (x_value())); + f_x = true; + return m_x; +} + glonass_t::string_2_t::string_2_t(kaitai::kstream* p__io, glonass_t* p__parent, glonass_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; + f_y_vel = false; + f_y_accel = false; + f_y = false; try { _read(); @@ -158,9 +243,12 @@ void glonass_t::string_2_t::_read() { m_p2 = m__io->read_bits_int_be(1); m_t_b = m__io->read_bits_int_be(7); m_not_used = m__io->read_bits_int_be(5); - m_y_vel = m__io->read_bits_int_be(24); - m_y_speedup = m__io->read_bits_int_be(5); - m_y = m__io->read_bits_int_be(27); + m_y_vel_sign = m__io->read_bits_int_be(1); + m_y_vel_value = m__io->read_bits_int_be(23); + m_y_accel_sign = m__io->read_bits_int_be(1); + m_y_accel_value = m__io->read_bits_int_be(4); + m_y_sign = m__io->read_bits_int_be(1); + m_y_value = m__io->read_bits_int_be(26); } glonass_t::string_2_t::~string_2_t() { @@ -170,9 +258,37 @@ glonass_t::string_2_t::~string_2_t() { void glonass_t::string_2_t::_clean_up() { } +int32_t glonass_t::string_2_t::y_vel() { + if (f_y_vel) + return m_y_vel; + m_y_vel = ((y_vel_sign()) ? ((y_vel_value() - (1 << 23))) : (y_vel_value())); + f_y_vel = true; + return m_y_vel; +} + +int32_t glonass_t::string_2_t::y_accel() { + if (f_y_accel) + return m_y_accel; + m_y_accel = ((y_accel_sign()) ? ((y_accel_value() - (1 << 4))) : (y_accel_value())); + f_y_accel = true; + return m_y_accel; +} + +int32_t glonass_t::string_2_t::y() { + if (f_y) + return m_y; + m_y = ((y_sign()) ? ((y_value() - (1 << 26))) : (y_value())); + f_y = true; + return m_y; +} + glonass_t::string_3_t::string_3_t(kaitai::kstream* p__io, glonass_t* p__parent, glonass_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; + f_gamma_n = false; + f_z_vel = false; + f_z_accel = false; + f_z = false; try { _read(); @@ -184,13 +300,17 @@ glonass_t::string_3_t::string_3_t(kaitai::kstream* p__io, glonass_t* p__parent, void glonass_t::string_3_t::_read() { m_p3 = m__io->read_bits_int_be(1); - m_gamma_n = m__io->read_bits_int_be(11); + m_gamma_n_sign = m__io->read_bits_int_be(1); + m_gamma_n_value = m__io->read_bits_int_be(10); m_not_used = m__io->read_bits_int_be(1); m_p = m__io->read_bits_int_be(2); m_l_n = m__io->read_bits_int_be(1); - m_z_vel = m__io->read_bits_int_be(24); - m_z_speedup = m__io->read_bits_int_be(5); - m_z = m__io->read_bits_int_be(27); + m_z_vel_sign = m__io->read_bits_int_be(1); + m_z_vel_value = m__io->read_bits_int_be(23); + m_z_accel_sign = m__io->read_bits_int_be(1); + m_z_accel_value = m__io->read_bits_int_be(4); + m_z_sign = m__io->read_bits_int_be(1); + m_z_value = m__io->read_bits_int_be(26); } glonass_t::string_3_t::~string_3_t() { @@ -199,3 +319,35 @@ glonass_t::string_3_t::~string_3_t() { void glonass_t::string_3_t::_clean_up() { } + +int32_t glonass_t::string_3_t::gamma_n() { + if (f_gamma_n) + return m_gamma_n; + m_gamma_n = ((gamma_n_sign()) ? ((gamma_n_value() - (1 << 10))) : (gamma_n_value())); + f_gamma_n = true; + return m_gamma_n; +} + +int32_t glonass_t::string_3_t::z_vel() { + if (f_z_vel) + return m_z_vel; + m_z_vel = ((z_vel_sign()) ? ((z_vel_value() - (1 << 23))) : (z_vel_value())); + f_z_vel = true; + return m_z_vel; +} + +int32_t glonass_t::string_3_t::z_accel() { + if (f_z_accel) + return m_z_accel; + m_z_accel = ((z_accel_sign()) ? ((z_accel_value() - (1 << 4))) : (z_accel_value())); + f_z_accel = true; + return m_z_accel; +} + +int32_t glonass_t::string_3_t::z() { + if (f_z) + return m_z; + m_z = ((z_sign()) ? ((z_value() - (1 << 26))) : (z_value())); + f_z = true; + return m_z; +} diff --git a/selfdrive/locationd/generated/glonass.h b/selfdrive/locationd/generated/glonass.h index 48bdabfe96..3b7917ef4b 100644 --- a/selfdrive/locationd/generated/glonass.h +++ b/selfdrive/locationd/generated/glonass.h @@ -15,6 +15,7 @@ class glonass_t : public kaitai::kstruct { public: class string_4_t; class string_non_immediate_t; + class string_5_t; class string_1_t; class string_2_t; class string_3_t; @@ -42,8 +43,24 @@ public: ~string_4_t(); private: - uint64_t m_tau_n; - uint64_t m_delta_tau_n; + bool f_tau_n; + int32_t m_tau_n; + + public: + int32_t tau_n(); + + private: + bool f_delta_tau_n; + int32_t m_delta_tau_n; + + public: + int32_t delta_tau_n(); + + private: + bool m_tau_n_sign; + uint64_t m_tau_n_value; + bool m_delta_tau_n_sign; + uint64_t m_delta_tau_n_value; uint64_t m_e_n; uint64_t m_not_used_1; bool m_p4; @@ -56,8 +73,10 @@ public: glonass_t* m__parent; public: - uint64_t tau_n() const { return m_tau_n; } - uint64_t delta_tau_n() const { return m_delta_tau_n; } + bool tau_n_sign() const { return m_tau_n_sign; } + uint64_t tau_n_value() const { return m_tau_n_value; } + bool delta_tau_n_sign() const { return m_delta_tau_n_sign; } + uint64_t delta_tau_n_value() const { return m_delta_tau_n_value; } uint64_t e_n() const { return m_e_n; } uint64_t not_used_1() const { return m_not_used_1; } bool p4() const { return m_p4; } @@ -96,6 +115,40 @@ public: glonass_t* _parent() const { return m__parent; } }; + class string_5_t : public kaitai::kstruct { + + public: + + string_5_t(kaitai::kstream* p__io, glonass_t* p__parent = 0, glonass_t* p__root = 0); + + private: + void _read(); + void _clean_up(); + + public: + ~string_5_t(); + + private: + uint64_t m_n_a; + uint64_t m_tau_e; + bool m_not_used; + uint64_t m_n_4; + uint64_t m_tau_gps; + bool m_l_n; + glonass_t* m__root; + glonass_t* m__parent; + + public: + uint64_t n_a() const { return m_n_a; } + uint64_t tau_e() const { return m_tau_e; } + bool not_used() const { return m_not_used; } + uint64_t n_4() const { return m_n_4; } + uint64_t tau_gps() const { return m_tau_gps; } + bool l_n() const { return m_l_n; } + glonass_t* _root() const { return m__root; } + glonass_t* _parent() const { return m__parent; } + }; + class string_1_t : public kaitai::kstruct { public: @@ -109,13 +162,37 @@ public: public: ~string_1_t(); + private: + bool f_x_vel; + int32_t m_x_vel; + + public: + int32_t x_vel(); + + private: + bool f_x_accel; + int32_t m_x_accel; + + public: + int32_t x_accel(); + + private: + bool f_x; + int32_t m_x; + + public: + int32_t x(); + private: uint64_t m_not_used; uint64_t m_p1; uint64_t m_t_k; - uint64_t m_x_vel; - uint64_t m_x_speedup; - uint64_t m_x; + bool m_x_vel_sign; + uint64_t m_x_vel_value; + bool m_x_accel_sign; + uint64_t m_x_accel_value; + bool m_x_sign; + uint64_t m_x_value; glonass_t* m__root; glonass_t* m__parent; @@ -123,9 +200,12 @@ public: uint64_t not_used() const { return m_not_used; } uint64_t p1() const { return m_p1; } uint64_t t_k() const { return m_t_k; } - uint64_t x_vel() const { return m_x_vel; } - uint64_t x_speedup() const { return m_x_speedup; } - uint64_t x() const { return m_x; } + bool x_vel_sign() const { return m_x_vel_sign; } + uint64_t x_vel_value() const { return m_x_vel_value; } + bool x_accel_sign() const { return m_x_accel_sign; } + uint64_t x_accel_value() const { return m_x_accel_value; } + bool x_sign() const { return m_x_sign; } + uint64_t x_value() const { return m_x_value; } glonass_t* _root() const { return m__root; } glonass_t* _parent() const { return m__parent; } }; @@ -143,14 +223,38 @@ public: public: ~string_2_t(); + private: + bool f_y_vel; + int32_t m_y_vel; + + public: + int32_t y_vel(); + + private: + bool f_y_accel; + int32_t m_y_accel; + + public: + int32_t y_accel(); + + private: + bool f_y; + int32_t m_y; + + public: + int32_t y(); + private: uint64_t m_b_n; bool m_p2; uint64_t m_t_b; uint64_t m_not_used; - uint64_t m_y_vel; - uint64_t m_y_speedup; - uint64_t m_y; + bool m_y_vel_sign; + uint64_t m_y_vel_value; + bool m_y_accel_sign; + uint64_t m_y_accel_value; + bool m_y_sign; + uint64_t m_y_value; glonass_t* m__root; glonass_t* m__parent; @@ -159,9 +263,12 @@ public: bool p2() const { return m_p2; } uint64_t t_b() const { return m_t_b; } uint64_t not_used() const { return m_not_used; } - uint64_t y_vel() const { return m_y_vel; } - uint64_t y_speedup() const { return m_y_speedup; } - uint64_t y() const { return m_y; } + bool y_vel_sign() const { return m_y_vel_sign; } + uint64_t y_vel_value() const { return m_y_vel_value; } + bool y_accel_sign() const { return m_y_accel_sign; } + uint64_t y_accel_value() const { return m_y_accel_value; } + bool y_sign() const { return m_y_sign; } + uint64_t y_value() const { return m_y_value; } glonass_t* _root() const { return m__root; } glonass_t* _parent() const { return m__parent; } }; @@ -179,27 +286,63 @@ public: public: ~string_3_t(); + private: + bool f_gamma_n; + int32_t m_gamma_n; + + public: + int32_t gamma_n(); + + private: + bool f_z_vel; + int32_t m_z_vel; + + public: + int32_t z_vel(); + + private: + bool f_z_accel; + int32_t m_z_accel; + + public: + int32_t z_accel(); + + private: + bool f_z; + int32_t m_z; + + public: + int32_t z(); + private: bool m_p3; - uint64_t m_gamma_n; + bool m_gamma_n_sign; + uint64_t m_gamma_n_value; bool m_not_used; uint64_t m_p; bool m_l_n; - uint64_t m_z_vel; - uint64_t m_z_speedup; - uint64_t m_z; + bool m_z_vel_sign; + uint64_t m_z_vel_value; + bool m_z_accel_sign; + uint64_t m_z_accel_value; + bool m_z_sign; + uint64_t m_z_value; glonass_t* m__root; glonass_t* m__parent; public: bool p3() const { return m_p3; } - uint64_t gamma_n() const { return m_gamma_n; } + bool gamma_n_sign() const { return m_gamma_n_sign; } + uint64_t gamma_n_value() const { return m_gamma_n_value; } bool not_used() const { return m_not_used; } uint64_t p() const { return m_p; } bool l_n() const { return m_l_n; } - uint64_t z_vel() const { return m_z_vel; } - uint64_t z_speedup() const { return m_z_speedup; } - uint64_t z() const { return m_z; } + bool z_vel_sign() const { return m_z_vel_sign; } + uint64_t z_vel_value() const { return m_z_vel_value; } + bool z_accel_sign() const { return m_z_accel_sign; } + uint64_t z_accel_value() const { return m_z_accel_value; } + bool z_sign() const { return m_z_sign; } + uint64_t z_value() const { return m_z_value; } glonass_t* _root() const { return m__root; } glonass_t* _parent() const { return m__parent; } }; diff --git a/selfdrive/locationd/glonass.ksy b/selfdrive/locationd/glonass.ksy index 95ca78c16f..36e5c91740 100644 --- a/selfdrive/locationd/glonass.ksy +++ b/selfdrive/locationd/glonass.ksy @@ -1,4 +1,6 @@ # http://gauss.gge.unb.ca/GLONASS.ICD.pdf +# some variables are misprinted but good in the old doc +# https://www.unavco.org/help/glossary/docs/ICD_GLONASS_4.0_(1998)_en.pdf meta: id: glonass endian: be @@ -16,6 +18,7 @@ seq: 2: string_2 3: string_3 4: string_4 + 5: string_5 _: string_non_immediate - id: hamming_code type: b8 @@ -37,12 +40,25 @@ types: type: b2 - id: t_k type: b12 - - id: x_vel - type: b24 - - id: x_speedup - type: b5 - - id: x - type: b27 + - id: x_vel_sign + type: b1 + - id: x_vel_value + type: b23 + - id: x_accel_sign + type: b1 + - id: x_accel_value + type: b4 + - id: x_sign + type: b1 + - id: x_value + type: b26 + instances: + x_vel: + value: 'x_vel_sign ? (x_vel_value - (1 << 23)) : x_vel_value' + x_accel: + value: 'x_accel_sign ? (x_accel_value - (1 << 4)) : x_accel_value' + x: + value: 'x_sign ? (x_value - (1 << 26)) : x_value' string_2: seq: - id: b_n @@ -53,36 +69,70 @@ types: type: b7 - id: not_used type: b5 - - id: y_vel - type: b24 - - id: y_speedup - type: b5 - - id: y - type: b27 + - id: y_vel_sign + type: b1 + - id: y_vel_value + type: b23 + - id: y_accel_sign + type: b1 + - id: y_accel_value + type: b4 + - id: y_sign + type: b1 + - id: y_value + type: b26 + instances: + y_vel: + value: 'y_vel_sign ? (y_vel_value - (1 << 23)) : y_vel_value' + y_accel: + value: 'y_accel_sign ? (y_accel_value - (1 << 4)) : y_accel_value' + y: + value: 'y_sign ? (y_value - (1 << 26)) : y_value' string_3: seq: - id: p3 type: b1 - - id: gamma_n - type: b11 + - id: gamma_n_sign + type: b1 + - id: gamma_n_value + type: b10 - id: not_used type: b1 - id: p type: b2 - id: l_n type: b1 - - id: z_vel - type: b24 - - id: z_speedup - type: b5 - - id: z - type: b27 + - id: z_vel_sign + type: b1 + - id: z_vel_value + type: b23 + - id: z_accel_sign + type: b1 + - id: z_accel_value + type: b4 + - id: z_sign + type: b1 + - id: z_value + type: b26 + instances: + gamma_n: + value: 'gamma_n_sign ? (gamma_n_value - (1 << 10)) : gamma_n_value' + z_vel: + value: 'z_vel_sign ? (z_vel_value - (1 << 23)) : z_vel_value' + z_accel: + value: 'z_accel_sign ? (z_accel_value - (1 << 4)) : z_accel_value' + z: + value: 'z_sign ? (z_value - (1 << 26)) : z_value' string_4: seq: - - id: tau_n - type: b22 - - id: delta_tau_n - type: b5 + - id: tau_n_sign + type: b1 + - id: tau_n_value + type: b21 + - id: delta_tau_n_sign + type: b1 + - id: delta_tau_n_value + type: b4 - id: e_n type: b5 - id: not_used_1 @@ -99,9 +149,28 @@ types: type: b5 - id: m type: b2 + instances: + tau_n: + value: 'tau_n_sign ? (tau_n_value - (1 << 21)) : tau_n_value' + delta_tau_n: + value: 'delta_tau_n_sign ? (delta_tau_n_value - (1 << 4)) : delta_tau_n_value' + string_5: + seq: + - id: n_a + type: b11 + - id: tau_e + type: b32 + - id: not_used + type: b1 + - id: n_4 + type: b5 + - id: tau_gps + type: b22 + - id: l_n + type: b1 string_non_immediate: seq: - id: data_1 type: b64 - id: data_2 - type: b8 \ No newline at end of file + type: b8 From 53ccec7697247a06ad64fe056dcf07e026a011af Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Sat, 28 Jan 2023 16:48:26 -0800 Subject: [PATCH 215/484] Revert "add signs to glonass ksy" This reverts commit aa71947ebc9c286a13f86458ea0e970ca0d7a0c7. --- selfdrive/locationd/generated/glonass.cpp | 176 ++------------------ selfdrive/locationd/generated/glonass.h | 191 +++------------------- selfdrive/locationd/glonass.ksy | 119 +++----------- 3 files changed, 61 insertions(+), 425 deletions(-) diff --git a/selfdrive/locationd/generated/glonass.cpp b/selfdrive/locationd/generated/glonass.cpp index a342bdff22..149134fbb5 100644 --- a/selfdrive/locationd/generated/glonass.cpp +++ b/selfdrive/locationd/generated/glonass.cpp @@ -31,10 +31,6 @@ void glonass_t::_read() { m_data = new string_3_t(m__io, this, m__root); break; } - case 5: { - m_data = new string_5_t(m__io, this, m__root); - break; - } case 2: { m_data = new string_2_t(m__io, this, m__root); break; @@ -64,8 +60,6 @@ void glonass_t::_clean_up() { glonass_t::string_4_t::string_4_t(kaitai::kstream* p__io, glonass_t* p__parent, glonass_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; - f_tau_n = false; - f_delta_tau_n = false; try { _read(); @@ -76,10 +70,8 @@ glonass_t::string_4_t::string_4_t(kaitai::kstream* p__io, glonass_t* p__parent, } void glonass_t::string_4_t::_read() { - m_tau_n_sign = m__io->read_bits_int_be(1); - m_tau_n_value = m__io->read_bits_int_be(21); - m_delta_tau_n_sign = m__io->read_bits_int_be(1); - m_delta_tau_n_value = m__io->read_bits_int_be(4); + m_tau_n = m__io->read_bits_int_be(22); + m_delta_tau_n = m__io->read_bits_int_be(5); m_e_n = m__io->read_bits_int_be(5); m_not_used_1 = m__io->read_bits_int_be(14); m_p4 = m__io->read_bits_int_be(1); @@ -97,22 +89,6 @@ glonass_t::string_4_t::~string_4_t() { void glonass_t::string_4_t::_clean_up() { } -int32_t glonass_t::string_4_t::tau_n() { - if (f_tau_n) - return m_tau_n; - m_tau_n = ((tau_n_sign()) ? ((tau_n_value() - (1 << 21))) : (tau_n_value())); - f_tau_n = true; - return m_tau_n; -} - -int32_t glonass_t::string_4_t::delta_tau_n() { - if (f_delta_tau_n) - return m_delta_tau_n; - m_delta_tau_n = ((delta_tau_n_sign()) ? ((delta_tau_n_value() - (1 << 4))) : (delta_tau_n_value())); - f_delta_tau_n = true; - return m_delta_tau_n; -} - glonass_t::string_non_immediate_t::string_non_immediate_t(kaitai::kstream* p__io, glonass_t* p__parent, glonass_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; @@ -137,40 +113,9 @@ glonass_t::string_non_immediate_t::~string_non_immediate_t() { void glonass_t::string_non_immediate_t::_clean_up() { } -glonass_t::string_5_t::string_5_t(kaitai::kstream* p__io, glonass_t* p__parent, glonass_t* p__root) : kaitai::kstruct(p__io) { - m__parent = p__parent; - m__root = p__root; - - try { - _read(); - } catch(...) { - _clean_up(); - throw; - } -} - -void glonass_t::string_5_t::_read() { - m_n_a = m__io->read_bits_int_be(11); - m_tau_e = m__io->read_bits_int_be(32); - m_not_used = m__io->read_bits_int_be(1); - m_n_4 = m__io->read_bits_int_be(5); - m_tau_gps = m__io->read_bits_int_be(22); - m_l_n = m__io->read_bits_int_be(1); -} - -glonass_t::string_5_t::~string_5_t() { - _clean_up(); -} - -void glonass_t::string_5_t::_clean_up() { -} - glonass_t::string_1_t::string_1_t(kaitai::kstream* p__io, glonass_t* p__parent, glonass_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; - f_x_vel = false; - f_x_accel = false; - f_x = false; try { _read(); @@ -184,12 +129,9 @@ void glonass_t::string_1_t::_read() { m_not_used = m__io->read_bits_int_be(2); m_p1 = m__io->read_bits_int_be(2); m_t_k = m__io->read_bits_int_be(12); - m_x_vel_sign = m__io->read_bits_int_be(1); - m_x_vel_value = m__io->read_bits_int_be(23); - m_x_accel_sign = m__io->read_bits_int_be(1); - m_x_accel_value = m__io->read_bits_int_be(4); - m_x_sign = m__io->read_bits_int_be(1); - m_x_value = m__io->read_bits_int_be(26); + m_x_vel = m__io->read_bits_int_be(24); + m_x_speedup = m__io->read_bits_int_be(5); + m_x = m__io->read_bits_int_be(27); } glonass_t::string_1_t::~string_1_t() { @@ -199,36 +141,9 @@ glonass_t::string_1_t::~string_1_t() { void glonass_t::string_1_t::_clean_up() { } -int32_t glonass_t::string_1_t::x_vel() { - if (f_x_vel) - return m_x_vel; - m_x_vel = ((x_vel_sign()) ? ((x_vel_value() - (1 << 23))) : (x_vel_value())); - f_x_vel = true; - return m_x_vel; -} - -int32_t glonass_t::string_1_t::x_accel() { - if (f_x_accel) - return m_x_accel; - m_x_accel = ((x_accel_sign()) ? ((x_accel_value() - (1 << 4))) : (x_accel_value())); - f_x_accel = true; - return m_x_accel; -} - -int32_t glonass_t::string_1_t::x() { - if (f_x) - return m_x; - m_x = ((x_sign()) ? ((x_value() - (1 << 26))) : (x_value())); - f_x = true; - return m_x; -} - glonass_t::string_2_t::string_2_t(kaitai::kstream* p__io, glonass_t* p__parent, glonass_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; - f_y_vel = false; - f_y_accel = false; - f_y = false; try { _read(); @@ -243,12 +158,9 @@ void glonass_t::string_2_t::_read() { m_p2 = m__io->read_bits_int_be(1); m_t_b = m__io->read_bits_int_be(7); m_not_used = m__io->read_bits_int_be(5); - m_y_vel_sign = m__io->read_bits_int_be(1); - m_y_vel_value = m__io->read_bits_int_be(23); - m_y_accel_sign = m__io->read_bits_int_be(1); - m_y_accel_value = m__io->read_bits_int_be(4); - m_y_sign = m__io->read_bits_int_be(1); - m_y_value = m__io->read_bits_int_be(26); + m_y_vel = m__io->read_bits_int_be(24); + m_y_speedup = m__io->read_bits_int_be(5); + m_y = m__io->read_bits_int_be(27); } glonass_t::string_2_t::~string_2_t() { @@ -258,37 +170,9 @@ glonass_t::string_2_t::~string_2_t() { void glonass_t::string_2_t::_clean_up() { } -int32_t glonass_t::string_2_t::y_vel() { - if (f_y_vel) - return m_y_vel; - m_y_vel = ((y_vel_sign()) ? ((y_vel_value() - (1 << 23))) : (y_vel_value())); - f_y_vel = true; - return m_y_vel; -} - -int32_t glonass_t::string_2_t::y_accel() { - if (f_y_accel) - return m_y_accel; - m_y_accel = ((y_accel_sign()) ? ((y_accel_value() - (1 << 4))) : (y_accel_value())); - f_y_accel = true; - return m_y_accel; -} - -int32_t glonass_t::string_2_t::y() { - if (f_y) - return m_y; - m_y = ((y_sign()) ? ((y_value() - (1 << 26))) : (y_value())); - f_y = true; - return m_y; -} - glonass_t::string_3_t::string_3_t(kaitai::kstream* p__io, glonass_t* p__parent, glonass_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; - f_gamma_n = false; - f_z_vel = false; - f_z_accel = false; - f_z = false; try { _read(); @@ -300,17 +184,13 @@ glonass_t::string_3_t::string_3_t(kaitai::kstream* p__io, glonass_t* p__parent, void glonass_t::string_3_t::_read() { m_p3 = m__io->read_bits_int_be(1); - m_gamma_n_sign = m__io->read_bits_int_be(1); - m_gamma_n_value = m__io->read_bits_int_be(10); + m_gamma_n = m__io->read_bits_int_be(11); m_not_used = m__io->read_bits_int_be(1); m_p = m__io->read_bits_int_be(2); m_l_n = m__io->read_bits_int_be(1); - m_z_vel_sign = m__io->read_bits_int_be(1); - m_z_vel_value = m__io->read_bits_int_be(23); - m_z_accel_sign = m__io->read_bits_int_be(1); - m_z_accel_value = m__io->read_bits_int_be(4); - m_z_sign = m__io->read_bits_int_be(1); - m_z_value = m__io->read_bits_int_be(26); + m_z_vel = m__io->read_bits_int_be(24); + m_z_speedup = m__io->read_bits_int_be(5); + m_z = m__io->read_bits_int_be(27); } glonass_t::string_3_t::~string_3_t() { @@ -319,35 +199,3 @@ glonass_t::string_3_t::~string_3_t() { void glonass_t::string_3_t::_clean_up() { } - -int32_t glonass_t::string_3_t::gamma_n() { - if (f_gamma_n) - return m_gamma_n; - m_gamma_n = ((gamma_n_sign()) ? ((gamma_n_value() - (1 << 10))) : (gamma_n_value())); - f_gamma_n = true; - return m_gamma_n; -} - -int32_t glonass_t::string_3_t::z_vel() { - if (f_z_vel) - return m_z_vel; - m_z_vel = ((z_vel_sign()) ? ((z_vel_value() - (1 << 23))) : (z_vel_value())); - f_z_vel = true; - return m_z_vel; -} - -int32_t glonass_t::string_3_t::z_accel() { - if (f_z_accel) - return m_z_accel; - m_z_accel = ((z_accel_sign()) ? ((z_accel_value() - (1 << 4))) : (z_accel_value())); - f_z_accel = true; - return m_z_accel; -} - -int32_t glonass_t::string_3_t::z() { - if (f_z) - return m_z; - m_z = ((z_sign()) ? ((z_value() - (1 << 26))) : (z_value())); - f_z = true; - return m_z; -} diff --git a/selfdrive/locationd/generated/glonass.h b/selfdrive/locationd/generated/glonass.h index 3b7917ef4b..48bdabfe96 100644 --- a/selfdrive/locationd/generated/glonass.h +++ b/selfdrive/locationd/generated/glonass.h @@ -15,7 +15,6 @@ class glonass_t : public kaitai::kstruct { public: class string_4_t; class string_non_immediate_t; - class string_5_t; class string_1_t; class string_2_t; class string_3_t; @@ -43,24 +42,8 @@ public: ~string_4_t(); private: - bool f_tau_n; - int32_t m_tau_n; - - public: - int32_t tau_n(); - - private: - bool f_delta_tau_n; - int32_t m_delta_tau_n; - - public: - int32_t delta_tau_n(); - - private: - bool m_tau_n_sign; - uint64_t m_tau_n_value; - bool m_delta_tau_n_sign; - uint64_t m_delta_tau_n_value; + uint64_t m_tau_n; + uint64_t m_delta_tau_n; uint64_t m_e_n; uint64_t m_not_used_1; bool m_p4; @@ -73,10 +56,8 @@ public: glonass_t* m__parent; public: - bool tau_n_sign() const { return m_tau_n_sign; } - uint64_t tau_n_value() const { return m_tau_n_value; } - bool delta_tau_n_sign() const { return m_delta_tau_n_sign; } - uint64_t delta_tau_n_value() const { return m_delta_tau_n_value; } + uint64_t tau_n() const { return m_tau_n; } + uint64_t delta_tau_n() const { return m_delta_tau_n; } uint64_t e_n() const { return m_e_n; } uint64_t not_used_1() const { return m_not_used_1; } bool p4() const { return m_p4; } @@ -115,40 +96,6 @@ public: glonass_t* _parent() const { return m__parent; } }; - class string_5_t : public kaitai::kstruct { - - public: - - string_5_t(kaitai::kstream* p__io, glonass_t* p__parent = 0, glonass_t* p__root = 0); - - private: - void _read(); - void _clean_up(); - - public: - ~string_5_t(); - - private: - uint64_t m_n_a; - uint64_t m_tau_e; - bool m_not_used; - uint64_t m_n_4; - uint64_t m_tau_gps; - bool m_l_n; - glonass_t* m__root; - glonass_t* m__parent; - - public: - uint64_t n_a() const { return m_n_a; } - uint64_t tau_e() const { return m_tau_e; } - bool not_used() const { return m_not_used; } - uint64_t n_4() const { return m_n_4; } - uint64_t tau_gps() const { return m_tau_gps; } - bool l_n() const { return m_l_n; } - glonass_t* _root() const { return m__root; } - glonass_t* _parent() const { return m__parent; } - }; - class string_1_t : public kaitai::kstruct { public: @@ -162,37 +109,13 @@ public: public: ~string_1_t(); - private: - bool f_x_vel; - int32_t m_x_vel; - - public: - int32_t x_vel(); - - private: - bool f_x_accel; - int32_t m_x_accel; - - public: - int32_t x_accel(); - - private: - bool f_x; - int32_t m_x; - - public: - int32_t x(); - private: uint64_t m_not_used; uint64_t m_p1; uint64_t m_t_k; - bool m_x_vel_sign; - uint64_t m_x_vel_value; - bool m_x_accel_sign; - uint64_t m_x_accel_value; - bool m_x_sign; - uint64_t m_x_value; + uint64_t m_x_vel; + uint64_t m_x_speedup; + uint64_t m_x; glonass_t* m__root; glonass_t* m__parent; @@ -200,12 +123,9 @@ public: uint64_t not_used() const { return m_not_used; } uint64_t p1() const { return m_p1; } uint64_t t_k() const { return m_t_k; } - bool x_vel_sign() const { return m_x_vel_sign; } - uint64_t x_vel_value() const { return m_x_vel_value; } - bool x_accel_sign() const { return m_x_accel_sign; } - uint64_t x_accel_value() const { return m_x_accel_value; } - bool x_sign() const { return m_x_sign; } - uint64_t x_value() const { return m_x_value; } + uint64_t x_vel() const { return m_x_vel; } + uint64_t x_speedup() const { return m_x_speedup; } + uint64_t x() const { return m_x; } glonass_t* _root() const { return m__root; } glonass_t* _parent() const { return m__parent; } }; @@ -223,38 +143,14 @@ public: public: ~string_2_t(); - private: - bool f_y_vel; - int32_t m_y_vel; - - public: - int32_t y_vel(); - - private: - bool f_y_accel; - int32_t m_y_accel; - - public: - int32_t y_accel(); - - private: - bool f_y; - int32_t m_y; - - public: - int32_t y(); - private: uint64_t m_b_n; bool m_p2; uint64_t m_t_b; uint64_t m_not_used; - bool m_y_vel_sign; - uint64_t m_y_vel_value; - bool m_y_accel_sign; - uint64_t m_y_accel_value; - bool m_y_sign; - uint64_t m_y_value; + uint64_t m_y_vel; + uint64_t m_y_speedup; + uint64_t m_y; glonass_t* m__root; glonass_t* m__parent; @@ -263,12 +159,9 @@ public: bool p2() const { return m_p2; } uint64_t t_b() const { return m_t_b; } uint64_t not_used() const { return m_not_used; } - bool y_vel_sign() const { return m_y_vel_sign; } - uint64_t y_vel_value() const { return m_y_vel_value; } - bool y_accel_sign() const { return m_y_accel_sign; } - uint64_t y_accel_value() const { return m_y_accel_value; } - bool y_sign() const { return m_y_sign; } - uint64_t y_value() const { return m_y_value; } + uint64_t y_vel() const { return m_y_vel; } + uint64_t y_speedup() const { return m_y_speedup; } + uint64_t y() const { return m_y; } glonass_t* _root() const { return m__root; } glonass_t* _parent() const { return m__parent; } }; @@ -286,63 +179,27 @@ public: public: ~string_3_t(); - private: - bool f_gamma_n; - int32_t m_gamma_n; - - public: - int32_t gamma_n(); - - private: - bool f_z_vel; - int32_t m_z_vel; - - public: - int32_t z_vel(); - - private: - bool f_z_accel; - int32_t m_z_accel; - - public: - int32_t z_accel(); - - private: - bool f_z; - int32_t m_z; - - public: - int32_t z(); - private: bool m_p3; - bool m_gamma_n_sign; - uint64_t m_gamma_n_value; + uint64_t m_gamma_n; bool m_not_used; uint64_t m_p; bool m_l_n; - bool m_z_vel_sign; - uint64_t m_z_vel_value; - bool m_z_accel_sign; - uint64_t m_z_accel_value; - bool m_z_sign; - uint64_t m_z_value; + uint64_t m_z_vel; + uint64_t m_z_speedup; + uint64_t m_z; glonass_t* m__root; glonass_t* m__parent; public: bool p3() const { return m_p3; } - bool gamma_n_sign() const { return m_gamma_n_sign; } - uint64_t gamma_n_value() const { return m_gamma_n_value; } + uint64_t gamma_n() const { return m_gamma_n; } bool not_used() const { return m_not_used; } uint64_t p() const { return m_p; } bool l_n() const { return m_l_n; } - bool z_vel_sign() const { return m_z_vel_sign; } - uint64_t z_vel_value() const { return m_z_vel_value; } - bool z_accel_sign() const { return m_z_accel_sign; } - uint64_t z_accel_value() const { return m_z_accel_value; } - bool z_sign() const { return m_z_sign; } - uint64_t z_value() const { return m_z_value; } + uint64_t z_vel() const { return m_z_vel; } + uint64_t z_speedup() const { return m_z_speedup; } + uint64_t z() const { return m_z; } glonass_t* _root() const { return m__root; } glonass_t* _parent() const { return m__parent; } }; diff --git a/selfdrive/locationd/glonass.ksy b/selfdrive/locationd/glonass.ksy index 36e5c91740..95ca78c16f 100644 --- a/selfdrive/locationd/glonass.ksy +++ b/selfdrive/locationd/glonass.ksy @@ -1,6 +1,4 @@ # http://gauss.gge.unb.ca/GLONASS.ICD.pdf -# some variables are misprinted but good in the old doc -# https://www.unavco.org/help/glossary/docs/ICD_GLONASS_4.0_(1998)_en.pdf meta: id: glonass endian: be @@ -18,7 +16,6 @@ seq: 2: string_2 3: string_3 4: string_4 - 5: string_5 _: string_non_immediate - id: hamming_code type: b8 @@ -40,25 +37,12 @@ types: type: b2 - id: t_k type: b12 - - id: x_vel_sign - type: b1 - - id: x_vel_value - type: b23 - - id: x_accel_sign - type: b1 - - id: x_accel_value - type: b4 - - id: x_sign - type: b1 - - id: x_value - type: b26 - instances: - x_vel: - value: 'x_vel_sign ? (x_vel_value - (1 << 23)) : x_vel_value' - x_accel: - value: 'x_accel_sign ? (x_accel_value - (1 << 4)) : x_accel_value' - x: - value: 'x_sign ? (x_value - (1 << 26)) : x_value' + - id: x_vel + type: b24 + - id: x_speedup + type: b5 + - id: x + type: b27 string_2: seq: - id: b_n @@ -69,70 +53,36 @@ types: type: b7 - id: not_used type: b5 - - id: y_vel_sign - type: b1 - - id: y_vel_value - type: b23 - - id: y_accel_sign - type: b1 - - id: y_accel_value - type: b4 - - id: y_sign - type: b1 - - id: y_value - type: b26 - instances: - y_vel: - value: 'y_vel_sign ? (y_vel_value - (1 << 23)) : y_vel_value' - y_accel: - value: 'y_accel_sign ? (y_accel_value - (1 << 4)) : y_accel_value' - y: - value: 'y_sign ? (y_value - (1 << 26)) : y_value' + - id: y_vel + type: b24 + - id: y_speedup + type: b5 + - id: y + type: b27 string_3: seq: - id: p3 type: b1 - - id: gamma_n_sign - type: b1 - - id: gamma_n_value - type: b10 + - id: gamma_n + type: b11 - id: not_used type: b1 - id: p type: b2 - id: l_n type: b1 - - id: z_vel_sign - type: b1 - - id: z_vel_value - type: b23 - - id: z_accel_sign - type: b1 - - id: z_accel_value - type: b4 - - id: z_sign - type: b1 - - id: z_value - type: b26 - instances: - gamma_n: - value: 'gamma_n_sign ? (gamma_n_value - (1 << 10)) : gamma_n_value' - z_vel: - value: 'z_vel_sign ? (z_vel_value - (1 << 23)) : z_vel_value' - z_accel: - value: 'z_accel_sign ? (z_accel_value - (1 << 4)) : z_accel_value' - z: - value: 'z_sign ? (z_value - (1 << 26)) : z_value' + - id: z_vel + type: b24 + - id: z_speedup + type: b5 + - id: z + type: b27 string_4: seq: - - id: tau_n_sign - type: b1 - - id: tau_n_value - type: b21 - - id: delta_tau_n_sign - type: b1 - - id: delta_tau_n_value - type: b4 + - id: tau_n + type: b22 + - id: delta_tau_n + type: b5 - id: e_n type: b5 - id: not_used_1 @@ -149,28 +99,9 @@ types: type: b5 - id: m type: b2 - instances: - tau_n: - value: 'tau_n_sign ? (tau_n_value - (1 << 21)) : tau_n_value' - delta_tau_n: - value: 'delta_tau_n_sign ? (delta_tau_n_value - (1 << 4)) : delta_tau_n_value' - string_5: - seq: - - id: n_a - type: b11 - - id: tau_e - type: b32 - - id: not_used - type: b1 - - id: n_4 - type: b5 - - id: tau_gps - type: b22 - - id: l_n - type: b1 string_non_immediate: seq: - id: data_1 type: b64 - id: data_2 - type: b8 + type: b8 \ No newline at end of file From d76012ec38ff5b996a329d35adc21ade17845d53 Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Sun, 29 Jan 2023 21:52:42 +0100 Subject: [PATCH 216/484] cabana: fix overlap in byte highlighting (#27139) --- tools/cabana/util.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cabana/util.cc b/tools/cabana/util.cc index 0684d3c59a..7796800103 100644 --- a/tools/cabana/util.cc +++ b/tools/cabana/util.cc @@ -85,7 +85,7 @@ void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem & pos.moveLeft(pos.x() + space.width()); int m = space.width() / 2; - const QMargins margins(m + 1, m, m, m); + const QMargins margins(m, m, m, m); int i = 0; for (auto &byte : opt.text.split(" ")) { From 3d641fa5e530bd51ec40e9f1d9b466d75d2aefdf Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Sun, 29 Jan 2023 21:54:46 +0100 Subject: [PATCH 217/484] cabana: replace space by underscore when editing signal name (#27130) cabana: replace space by _ whene editing signal name --- tools/cabana/detailwidget.cc | 2 +- tools/cabana/signaledit.cc | 2 +- tools/cabana/util.cc | 7 +++++++ tools/cabana/util.h | 9 +++++++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index 233bc493df..ef8a8f6290 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -288,7 +288,7 @@ EditMessageDialog::EditMessageDialog(const QString &msg_id, const QString &title form_layout->addRow("ID", new QLabel(msg_id)); name_edit = new QLineEdit(title, this); - name_edit->setValidator(new QRegExpValidator(QRegExp("^(\\w+)"), name_edit)); + name_edit->setValidator(new NameValidator(name_edit)); form_layout->addRow(tr("Name"), name_edit); size_spin = new QSpinBox(this); diff --git a/tools/cabana/signaledit.cc b/tools/cabana/signaledit.cc index 112c2807f9..52ce74ae08 100644 --- a/tools/cabana/signaledit.cc +++ b/tools/cabana/signaledit.cc @@ -19,7 +19,7 @@ SignalForm::SignalForm(QWidget *parent) : QWidget(parent) { main_layout->addLayout(form_layout); name = new QLineEdit(); - name->setValidator(new QRegExpValidator(QRegExp("^(\\w+)"), name)); + name->setValidator(new NameValidator(name)); form_layout->addRow(tr("Name"), name); QHBoxLayout *hl = new QHBoxLayout(this); diff --git a/tools/cabana/util.cc b/tools/cabana/util.cc index 7796800103..726cbbe2a1 100644 --- a/tools/cabana/util.cc +++ b/tools/cabana/util.cc @@ -97,3 +97,10 @@ void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem & i++; } } + +NameValidator::NameValidator(QObject *parent) : QRegExpValidator(QRegExp("^(\\w+)"), parent) { } + +QValidator::State NameValidator::validate(QString &input, int &pos) const { + input.replace(' ', '_'); + return QRegExpValidator::validate(input, pos); +} diff --git a/tools/cabana/util.h b/tools/cabana/util.h index 146410d7c6..20c85af39e 100644 --- a/tools/cabana/util.h +++ b/tools/cabana/util.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -36,3 +37,11 @@ inline const QString &getColor(int i) { static const QString SIGNAL_COLORS[] = {"#9FE2BF", "#40E0D0", "#6495ED", "#CCCCFF", "#FF7F50", "#FFBF00"}; return SIGNAL_COLORS[i % std::size(SIGNAL_COLORS)]; } + +class NameValidator : public QRegExpValidator { + Q_OBJECT + +public: + NameValidator(QObject *parent=nullptr); + QValidator::State validate(QString &input, int &pos) const override; +}; From bdb42f7f84516279f308f95496a5f5f901c4adc1 Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Sun, 29 Jan 2023 21:55:10 +0100 Subject: [PATCH 218/484] cabana: fix occasional ellipsis on y axis ticks (#27142) --- tools/cabana/chartswidget.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 3ecdbb08bd..26c05cf119 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -521,8 +521,8 @@ void ChartView::updateAxisY() { if (min_y == std::numeric_limits::max()) min_y = 0; if (max_y == std::numeric_limits::lowest()) max_y = 0; - if (max_y == min_y) { - axis_y->setRange(min_y - 1, max_y + 1); + if (std::abs(max_y - min_y) < 1e-3) { + applyNiceNumbers(min_y - 1, max_y + 1); } else { double range = max_y - min_y; applyNiceNumbers(min_y - range * 0.05, max_y + range * 0.05); @@ -539,6 +539,7 @@ void ChartView::applyNiceNumbers(qreal min, qreal max) { tick_count = int(max - min) + 1; axis_y->setRange(min * step, max * step); axis_y->setTickCount(tick_count); + axis_y->setLabelFormat("%.1f"); } // nice numbers can be expressed as form of 1*10^n, 2* 10^n or 5*10^n From f038193f4447afc8ae7b6034dec430d08f6da331 Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Sun, 29 Jan 2023 21:57:01 +0100 Subject: [PATCH 219/484] cabana: show dots when zoomed far into a signal (#27145) * cabana: show dots when zoomed far into a signal * review comments --- tools/cabana/chartswidget.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 26c05cf119..9f5b4acbd1 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -469,6 +469,18 @@ void ChartView::updatePlot(double cur, double min, double max) { axis_x->setRange(min, max); updateAxisY(); } + + // Show points when zoomed in enough + for (auto &s : sigs) { + auto begin = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->min(), [](auto &p, double x) { return p.x() < x; }); + auto end = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->max(), [](auto &p, double x) { return p.x() < x; }); + + int num_points = std::max(end - begin, 1); + int pixels_per_point = width() / num_points; + + s.series->setPointsVisible(pixels_per_point > 20); + } + scene()->invalidate({}, QGraphicsScene::ForegroundLayer); } From d60aca8dd28731f947153f9f5dcb26d0f92ac2ad Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 30 Jan 2023 05:01:57 +0800 Subject: [PATCH 220/484] cabana: only update the colors of newly fetched messages in historylog (#27144) only update new msgs color --- tools/cabana/historylog.cc | 46 ++++++++++++++++---------------------- tools/cabana/historylog.h | 1 - 2 files changed, 19 insertions(+), 28 deletions(-) diff --git a/tools/cabana/historylog.cc b/tools/cabana/historylog.cc index ccc2c5533d..44f3a63b95 100644 --- a/tools/cabana/historylog.cc +++ b/tools/cabana/historylog.cc @@ -84,7 +84,6 @@ void HistoryLogModel::updateState() { if ((has_more_data = !new_msgs.empty())) { beginInsertRows({}, 0, new_msgs.size() - 1); messages.insert(messages.begin(), std::move_iterator(new_msgs.begin()), std::move_iterator(new_msgs.end())); - updateColors(); endInsertRows(); } last_fetch_time = current_time; @@ -97,29 +96,11 @@ void HistoryLogModel::fetchMore(const QModelIndex &parent) { if ((has_more_data = !new_msgs.empty())) { beginInsertRows({}, messages.size(), messages.size() + new_msgs.size() - 1); messages.insert(messages.end(), std::move_iterator(new_msgs.begin()), std::move_iterator(new_msgs.end())); - if (!dynamic_mode) { - updateColors(); - } endInsertRows(); } } } -void HistoryLogModel::updateColors() { - if (!display_signals_mode || sigs.empty()) { - const auto freq = can->lastMessage(msg_id).freq; - if (dynamic_mode) { - for (auto it = messages.rbegin(); it != messages.rend(); ++it) { - it->colors = hex_colors.compute(it->data, it->mono_time / (double)1e9, freq); - } - } else { - for (auto it = messages.begin(); it != messages.end(); ++it) { - it->colors = hex_colors.compute(it->data, it->mono_time / (double)1e9, freq); - } - } - } -} - template std::deque HistoryLogModel::fetchData(InputIt first, InputIt last, uint64_t min_time) { std::deque msgs; @@ -153,17 +134,28 @@ template std::deque HistoryLogModel::fetchData<>(std:: std::deque HistoryLogModel::fetchData(uint64_t from_time, uint64_t min_time) { auto events = can->events(); + const auto freq = can->lastMessage(msg_id).freq; + const bool update_colors = !display_signals_mode || sigs.empty(); + if (dynamic_mode) { - auto it = std::upper_bound(events->rbegin(), events->rend(), from_time, [=](uint64_t ts, auto &e) { - return e->mono_time < ts; - }); - return fetchData(it, events->rend(), min_time); + auto first = std::upper_bound(events->rbegin(), events->rend(), from_time, [=](uint64_t ts, auto &e) { return e->mono_time < ts; }); + auto msgs = fetchData(first, events->rend(), min_time); + if (update_colors && min_time > 0) { + for (auto it = msgs.rbegin(); it != msgs.rend(); ++it) { + it->colors = hex_colors.compute(it->data, it->mono_time / (double)1e9, freq); + } + } + return msgs; } else { assert(min_time == 0); - auto it = std::upper_bound(events->begin(), events->end(), from_time, [=](uint64_t ts, auto &e) { - return ts < e->mono_time; - }); - return fetchData(it, events->end(), 0); + auto first = std::upper_bound(events->begin(), events->end(), from_time, [=](uint64_t ts, auto &e) { return ts < e->mono_time; }); + auto msgs = fetchData(first, events->end(), 0); + if (update_colors) { + for (auto it = msgs.rbegin(); it != msgs.rend(); ++it) { + it->colors = hex_colors.compute(it->data, it->mono_time / (double)1e9, freq); + } + } + return msgs; } } diff --git a/tools/cabana/historylog.h b/tools/cabana/historylog.h index 83b5f623ea..c1a2c13769 100644 --- a/tools/cabana/historylog.h +++ b/tools/cabana/historylog.h @@ -33,7 +33,6 @@ public: int columnCount(const QModelIndex &parent = QModelIndex()) const override { return display_signals_mode && !sigs.empty() ? sigs.size() + 1 : 2; } - void updateColors(); void refresh(); public slots: From a84b601d4cd2dbd1494ea1dcf31e21aacd579574 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 30 Jan 2023 05:02:20 +0800 Subject: [PATCH 221/484] Cabana: load commaai/opendbc from menu (#27114) * load from opendbc * move load from commaai/opendbc to menu * keep margins --- tools/cabana/mainwin.cc | 53 ++++++++++++++-------------------- tools/cabana/mainwin.h | 6 ++-- tools/cabana/messageswidget.cc | 1 - 3 files changed, 24 insertions(+), 36 deletions(-) diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index d674ec9059..d875d079fa 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -54,7 +53,6 @@ MainWindow::MainWindow() : QMainWindow() { fingerprint_to_dbc = QJsonDocument::fromJson(json_file.readAll()); } - QObject::connect(dbc_combo, SIGNAL(activated(const QString &)), SLOT(loadDBCFromName(const QString &))); QObject::connect(this, &MainWindow::showMessage, statusBar(), &QStatusBar::showMessage); QObject::connect(this, &MainWindow::updateProgressBar, this, &MainWindow::updateDownloadProgress); QObject::connect(messages_widget, &MessagesWidget::msgSelectionChanged, detail_widget, &DetailWidget::setMessage); @@ -68,7 +66,7 @@ void MainWindow::createActions() { QMenu *file_menu = menuBar()->addMenu(tr("&File")); file_menu->addAction(tr("New DBC File"), this, &MainWindow::newFile)->setShortcuts(QKeySequence::New); file_menu->addAction(tr("Open DBC File..."), this, &MainWindow::openFile)->setShortcuts(QKeySequence::Open); - file_menu->addAction(tr("Load DBC From Clipboard"), this, &MainWindow::loadDBCFromClipboard); + open_recent_menu = file_menu->addMenu(tr("Open &Recent")); for (int i = 0; i < MAX_RECENT_FILES; ++i) { recent_files_acts[i] = new QAction(this); @@ -78,6 +76,17 @@ void MainWindow::createActions() { } updateRecentFileActions(); + file_menu->addSeparator(); + QMenu *load_opendbc_menu = file_menu->addMenu(tr("Load DBC from commaai/opendbc")); + // load_opendbc_menu->setStyleSheet("QMenu { menu-scrollable: true; }"); + auto dbc_names = dbc()->allDBCNames(); + std::sort(dbc_names.begin(), dbc_names.end()); + for (const auto &name : dbc_names) { + load_opendbc_menu->addAction(QString::fromStdString(name), this, &MainWindow::openOpendbcFile); + } + + file_menu->addAction(tr("Load DBC From Clipboard"), this, &MainWindow::loadDBCFromClipboard); + file_menu->addSeparator(); file_menu->addAction(tr("Save DBC..."), this, &MainWindow::save)->setShortcuts(QKeySequence::Save); file_menu->addAction(tr("Save DBC As..."), this, &MainWindow::saveAs)->setShortcuts(QKeySequence::SaveAs); @@ -113,18 +122,12 @@ void MainWindow::createActions() { void MainWindow::createDockWindows() { // left panel - QWidget *messages_container = new QWidget(this); - QVBoxLayout *messages_layout = new QVBoxLayout(messages_container); - dbc_combo = createDBCSelector(); - messages_layout->addWidget(dbc_combo); messages_widget = new MessagesWidget(this); - messages_layout->addWidget(messages_widget); - QDockWidget *dock = new QDockWidget(tr("MESSAGES"), this); dock->setObjectName("MessagesPanel"); dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea | Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea); dock->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); - dock->setWidget(messages_container); + dock->setWidget(messages_widget); addDockWidget(Qt::LeftDockWidgetArea, dock); // right panel @@ -156,23 +159,6 @@ void MainWindow::createDockWindows() { addDockWidget(Qt::RightDockWidgetArea, video_dock); } -QComboBox *MainWindow::createDBCSelector() { - QComboBox *c = new QComboBox(this); - c->setEditable(true); - c->lineEdit()->setPlaceholderText(tr("Select from an existing DBC file")); - c->setInsertPolicy(QComboBox::NoInsert); - c->completer()->setCompletionMode(QCompleter::PopupCompletion); - c->completer()->setFilterMode(Qt::MatchContains); - - auto dbc_names = dbc()->allDBCNames(); - std::sort(dbc_names.begin(), dbc_names.end()); - for (const auto &name : dbc_names) { - c->addItem(QString::fromStdString(name)); - } - c->setCurrentIndex(-1); - return c; -} - void MainWindow::createStatusBar() { progress_bar = new QProgressBar(); progress_bar->setRange(0, 100); @@ -190,8 +176,6 @@ void MainWindow::createShortcuts() { void MainWindow::DBCFileChanged() { detail_widget->undo_stack->clear(); - int index = dbc_combo->findText(QFileInfo(dbc()->name()).baseName()); - dbc_combo->setCurrentIndex(index); setWindowFilePath(QString("%1").arg(dbc()->name())); } @@ -220,6 +204,13 @@ void MainWindow::loadFile(const QString &fn) { } } +void MainWindow::openOpendbcFile() { + if (auto action = qobject_cast(sender())) { + remindSaveChanges(); + loadDBCFromOpendbc(action->text()); + } +} + void MainWindow::openRecentFile() { if (auto action = qobject_cast(sender())) { remindSaveChanges(); @@ -227,7 +218,7 @@ void MainWindow::openRecentFile() { } } -void MainWindow::loadDBCFromName(const QString &name) { +void MainWindow::loadDBCFromOpendbc(const QString &name) { if (name != dbc()->name()) { remindSaveChanges(); dbc()->open(name); @@ -252,7 +243,7 @@ void MainWindow::loadDBCFromFingerprint() { if (!fingerprint.isEmpty()) { auto dbc_name = fingerprint_to_dbc[fingerprint]; if (dbc_name != QJsonValue::Undefined) { - loadDBCFromName(dbc_name.toString()); + loadDBCFromOpendbc(dbc_name.toString()); return; } } diff --git a/tools/cabana/mainwin.h b/tools/cabana/mainwin.h index 4e65fa01cf..0c207528c9 100644 --- a/tools/cabana/mainwin.h +++ b/tools/cabana/mainwin.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include @@ -26,7 +25,8 @@ public slots: void newFile(); void openFile(); void openRecentFile(); - void loadDBCFromName(const QString &name); + void openOpendbcFile(); + void loadDBCFromOpendbc(const QString &name); void loadDBCFromFingerprint(); void loadDBCFromClipboard(); void save(); @@ -45,7 +45,6 @@ protected: void updateRecentFileActions(); void createActions(); void createDockWindows(); - QComboBox *createDBCSelector(); void createStatusBar(); void createShortcuts(); void closeEvent(QCloseEvent *event) override; @@ -63,7 +62,6 @@ protected: QVBoxLayout *charts_layout; QProgressBar *progress_bar; QJsonDocument fingerprint_to_dbc; - QComboBox *dbc_combo; QSplitter *video_splitter;; QString current_file = ""; enum { MAX_RECENT_FILES = 15 }; diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index 69c6d298c0..b7c3cd3401 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -10,7 +10,6 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(0, 0, 0, 0); // message filter QLineEdit *filter = new QLineEdit(this); From fa4f37a0985802f83ec47b7fe0b29d1607ebe83c Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Sun, 29 Jan 2023 22:02:32 +0100 Subject: [PATCH 222/484] cabana: small improvements to charts (#27140) * cabana: small improvements to charts toolbar * show column count when wide enough * call setspacing * set in same place * remove call to updateToolBar() --- tools/cabana/chartswidget.cc | 20 +++++++++++++------- tools/cabana/chartswidget.h | 3 ++- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 9f5b4acbd1..e653860c3d 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -30,8 +30,8 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { title_label->setContentsMargins(0, 0, 12, 0); columns_cb = new QComboBox(this); columns_cb->addItems({"1", "2", "3", "4"}); - toolbar->addWidget(new QLabel(tr("Columns:"))); - toolbar->addWidget(columns_cb); + columns_lb_action = toolbar->addWidget(new QLabel(tr("Columns:"))); + columns_cb_action = toolbar->addWidget(columns_cb); QLabel *stretch_label = new QLabel(this); stretch_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); @@ -46,8 +46,7 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { range_slider->setPageStep(60); // 1 min toolbar->addWidget(range_slider); - toolbar->addWidget(zoom_range_lb = new QLabel()); - reset_zoom_btn = toolbar->addAction(bootstrapPixmap("arrow-counterclockwise"), ""); + reset_zoom_btn = toolbar->addAction(bootstrapPixmap("zoom-out"), ""); reset_zoom_btn->setToolTip(tr("Reset zoom (drag on chart to zoom X-Axis)")); remove_all_btn = toolbar->addAction(bootstrapPixmap("x"), ""); remove_all_btn->setToolTip(tr("Remove all charts")); @@ -55,10 +54,13 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { main_layout->addWidget(toolbar); // charts + charts_layout = new QGridLayout(); + charts_layout->setSpacing(10); + QWidget *charts_container = new QWidget(this); QVBoxLayout *charts_main_layout = new QVBoxLayout(charts_container); charts_main_layout->setContentsMargins(0, 0, 0, 0); - charts_main_layout->addLayout(charts_layout = new QGridLayout); + charts_main_layout->addLayout(charts_layout); charts_main_layout->addStretch(0); QScrollArea *charts_scroll = new QScrollArea(this); @@ -167,7 +169,6 @@ void ChartsWidget::setMaxChartRange(int value) { void ChartsWidget::updateToolBar() { range_lb->setText(QString(" %1:%2 ").arg(max_chart_range / 60, 2, 10, QLatin1Char('0')).arg(max_chart_range % 60, 2, 10, QLatin1Char('0'))); - zoom_range_lb->setText(is_zoomed ? tr("Zooming: %1 - %2").arg(zoomed_range.first, 0, 'f', 2).arg(zoomed_range.second, 0, 'f', 2) : ""); title_label->setText(tr("Charts: %1").arg(charts.size())); dock_btn->setIcon(bootstrapPixmap(docking ? "arrow-up-right" : "arrow-down-left")); dock_btn->setToolTip(docking ? tr("Undock charts") : tr("Dock charts")); @@ -230,8 +231,13 @@ void ChartsWidget::setColumnCount(int n) { void ChartsWidget::updateLayout() { int n = column_count; for (; n > 1; --n) { - if ((n * (CHART_MIN_WIDTH + charts_layout->spacing())) < rect().width()) break; + if ((n * CHART_MIN_WIDTH + (n - 1) * charts_layout->spacing()) < charts_layout->geometry().width()) break; } + + bool show_column_cb = n > 1; + columns_lb_action->setVisible(show_column_cb); + columns_cb_action->setVisible(show_column_cb); + for (int i = 0; i < charts.size(); ++i) { charts_layout->addWidget(charts[charts.size() - i - 1], i / n, i % n); } diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index f134136323..229b1e9cbe 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -116,7 +116,6 @@ private: ChartView *findChart(const QString &id, const Signal *sig); QLabel *title_label; - QLabel *zoom_range_lb; QLabel *range_lb; QSlider *range_slider; bool docking = true; @@ -131,6 +130,8 @@ private: std::pair display_range; std::pair zoomed_range; bool use_dark_theme = false; + QAction *columns_lb_action; + QAction *columns_cb_action; QComboBox *columns_cb; int column_count = 1; const int CHART_MIN_WIDTH = 300; From fdc4a7f84cb68c63cc5013a052b49663ffb6881c Mon Sep 17 00:00:00 2001 From: martinl Date: Sun, 29 Jan 2023 23:55:17 +0200 Subject: [PATCH 223/484] MacOS: build fixes (#27143) * MacOS: exclude common/gpio.cc * MacOS: no spi for boardd * MacOS: loggerd: no v4l_encoder.cc * MacOS: update libdbc path for cabana --- common/SConscript | 4 +++- selfdrive/boardd/boardd.cc | 1 + selfdrive/boardd/panda.cc | 4 ++++ selfdrive/boardd/panda_comms.h | 4 ++++ selfdrive/boardd/spi.cc | 2 ++ selfdrive/loggerd/SConscript | 2 ++ tools/cabana/SConscript | 3 +++ 7 files changed, 19 insertions(+), 1 deletion(-) diff --git a/common/SConscript b/common/SConscript index 8aee6f42a7..5d6170611f 100644 --- a/common/SConscript +++ b/common/SConscript @@ -10,11 +10,13 @@ common_libs = [ 'statlog.cc', 'swaglog.cc', 'util.cc', - 'gpio.cc', 'i2c.cc', 'watchdog.cc', ] +if arch != "Darwin": + common_libs.append('gpio.cc') + _common = fxn('common', common_libs, LIBS="json11") files = [ diff --git a/selfdrive/boardd/boardd.cc b/selfdrive/boardd/boardd.cc index bc454aa54e..5ef49d4b22 100644 --- a/selfdrive/boardd/boardd.cc +++ b/selfdrive/boardd/boardd.cc @@ -7,6 +7,7 @@ #include #include +#include #include #include #include diff --git a/selfdrive/boardd/panda.cc b/selfdrive/boardd/panda.cc index 600a46c809..4bba070eee 100644 --- a/selfdrive/boardd/panda.cc +++ b/selfdrive/boardd/panda.cc @@ -14,7 +14,9 @@ Panda::Panda(std::string serial, uint32_t bus_offset) : bus_offset(bus_offset) { try { handle = std::make_unique(serial); } catch (std::exception &e) { +#ifndef __APPLE__ handle = std::make_unique(serial); +#endif } hw_type = get_hw_type(); @@ -46,11 +48,13 @@ std::string Panda::hw_serial() { std::vector Panda::list() { std::vector serials = PandaUsbHandle::list(); +#ifndef __APPLE__ for (auto s : PandaSpiHandle::list()) { if (std::find(serials.begin(), serials.end(), s) == serials.end()) { serials.push_back(s); } } +#endif return serials; } diff --git a/selfdrive/boardd/panda_comms.h b/selfdrive/boardd/panda_comms.h index b669d77e7f..506b96b372 100644 --- a/selfdrive/boardd/panda_comms.h +++ b/selfdrive/boardd/panda_comms.h @@ -5,7 +5,9 @@ #include #include +#ifndef __APPLE__ #include +#endif #include @@ -52,6 +54,7 @@ private: void handle_usb_issue(int err, const char func[]); }; +#ifndef __APPLE__ class PandaSpiHandle : public PandaCommsHandle { public: PandaSpiHandle(std::string serial); @@ -75,3 +78,4 @@ private: int spi_transfer(uint8_t endpoint, uint8_t *tx_data, uint16_t tx_len, uint8_t *rx_data, uint16_t max_rx_len); int spi_transfer_retry(uint8_t endpoint, uint8_t *tx_data, uint16_t tx_len, uint8_t *rx_data, uint16_t max_rx_len); }; +#endif diff --git a/selfdrive/boardd/spi.cc b/selfdrive/boardd/spi.cc index bcc446b050..c9d4b2ea0e 100644 --- a/selfdrive/boardd/spi.cc +++ b/selfdrive/boardd/spi.cc @@ -1,3 +1,4 @@ +#ifndef __APPLE__ #include #include #include @@ -339,3 +340,4 @@ int PandaSpiHandle::spi_transfer(uint8_t endpoint, uint8_t *tx_data, uint16_t tx transfer_fail: return ret; } +#endif diff --git a/selfdrive/loggerd/SConscript b/selfdrive/loggerd/SConscript index 92706c53ec..3b961bce6e 100644 --- a/selfdrive/loggerd/SConscript +++ b/selfdrive/loggerd/SConscript @@ -13,6 +13,8 @@ if arch == "Darwin": # fix OpenCL del libs[libs.index('OpenCL')] env['FRAMEWORKS'] = ['OpenCL'] + # exclude v4l + del src[src.index('encoder/v4l_encoder.cc')] logger_lib = env.Library('logger', src) libs.insert(0, logger_lib) diff --git a/tools/cabana/SConscript b/tools/cabana/SConscript index fbf3c03d36..0c9ad14973 100644 --- a/tools/cabana/SConscript +++ b/tools/cabana/SConscript @@ -24,6 +24,9 @@ cabana_lib = cabana_env.Library("cabana_lib", ['mainwin.cc', 'streams/livestream 'commands.cc', 'messageswidget.cc', 'settings.cc', 'util.cc', 'detailwidget.cc', 'tools/findsimilarbits.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) cabana_env.Program('_cabana', ['cabana.cc', cabana_lib, asset_obj], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) +if arch == "Darwin": + cabana_env.Execute('install_name_tool -change opendbc/can/libdbc.dylib @loader_path/../../opendbc/can/libdbc.dylib ./_cabana') + if GetOption('test'): cabana_env.Program('tests/_test_cabana', ['tests/test_runner.cc', 'tests/test_cabana.cc', cabana_lib], LIBS=[cabana_libs]) From 7a485d4308ff263cd3432edaa27d118d264b25b5 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 30 Jan 2023 11:19:50 +0800 Subject: [PATCH 224/484] cabana: refactor and improve signal view (#26997) * refactor and improve signal view * cleanup undostack after save * set clean * fix logswidget::refresh * fix bugs in historylog * historylog:dont reset display_type on msg changed * inline displaySignals * historylog:add stretch to header * remove sizePolicy * always show toolbar * add icon to tabwidget * create new chart on top * fix 'show plot' btn state sync issue * rename removeSeries to removeItem * historylog::fix displayRange * MessageListModeL: improve setFilterString * set as current index after expanded * disable update during setmessage * ChartWidget: fix issue in updateState * skip align charts if there only one chart * fix updateLayout * cleanup historylog * updateState before end reset model * add validator * improve sigmodel * expand new signal * add comment * reserve vector * cleanup condition * single click on row to expand signal * improve layout for canfd(64 bytes) * cleanup * return false on default * show the latest signal value next to the signal name * update sig value after setmessage.to make the value updated in pause mode. * better size policy * +1 for grid size * better palette * set autofillbg --- tools/cabana/binaryview.cc | 30 +- tools/cabana/binaryview.h | 6 +- tools/cabana/chartswidget.cc | 10 +- tools/cabana/chartswidget.h | 2 +- tools/cabana/commands.cc | 11 + tools/cabana/commands.h | 6 + tools/cabana/dbcmanager.cc | 3 +- tools/cabana/dbcmanager.h | 4 +- tools/cabana/detailwidget.cc | 174 +++-------- tools/cabana/detailwidget.h | 18 +- tools/cabana/historylog.cc | 25 +- tools/cabana/historylog.h | 5 +- tools/cabana/mainwin.cc | 22 +- tools/cabana/mainwin.h | 1 + tools/cabana/signaledit.cc | 585 ++++++++++++++++++++++++----------- tools/cabana/signaledit.h | 110 ++++--- 16 files changed, 609 insertions(+), 403 deletions(-) diff --git a/tools/cabana/binaryview.cc b/tools/cabana/binaryview.cc index 010fe0fc88..4d7b3148c6 100644 --- a/tools/cabana/binaryview.cc +++ b/tools/cabana/binaryview.cc @@ -4,8 +4,10 @@ #include #include #include +#include #include +#include "tools/cabana/commands.h" #include "tools/cabana/streams/abstractstream.h" // BinaryView @@ -30,12 +32,15 @@ BinaryView::BinaryView(QWidget *parent) : QTableView(parent) { setFrameShape(QFrame::NoFrame); setShowGrid(false); setMouseTracking(true); - setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); - setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &BinaryView::refresh); + QObject::connect(UndoStack::instance(), &QUndoStack::indexChanged, this, &BinaryView::refresh); } QSize BinaryView::minimumSizeHint() const { - return {horizontalHeader()->minimumSectionSize() * 9 + VERTICAL_HEADER_WIDTH + 2, 0}; + return {(horizontalHeader()->minimumSectionSize() + 1) * 9 + VERTICAL_HEADER_WIDTH, + CELL_HEIGHT * std::min(model->rowCount(), 10)}; } void BinaryView::highlight(const Signal *sig) { @@ -124,13 +129,20 @@ void BinaryView::leaveEvent(QEvent *event) { } void BinaryView::setMessage(const QString &message_id) { - model->setMessage(message_id); + model->msg_id = message_id; + verticalScrollBar()->setValue(0); + refresh(); +} + +void BinaryView::refresh() { + if (model->msg_id.isEmpty()) return; + clearSelection(); anchor_index = QModelIndex(); resize_sig = nullptr; hovered_sig = nullptr; highlightPosition(QCursor::pos()); - updateState(); + model->refresh(); } QSet BinaryView::getOverlappingSignals() const { @@ -155,9 +167,8 @@ std::tuple BinaryView::getSelection(QModelIndex index) { // BinaryViewModel -void BinaryViewModel::setMessage(const QString &message_id) { +void BinaryViewModel::refresh() { beginResetModel(); - msg_id = message_id; items.clear(); if ((dbc_msg = dbc()->msg(msg_id))) { row_count = dbc_msg->size; @@ -184,6 +195,7 @@ void BinaryViewModel::setMessage(const QString &message_id) { items.resize(row_count * column_count); } endResetModel(); + updateState(); } void BinaryViewModel::updateState() { @@ -200,7 +212,7 @@ void BinaryViewModel::updateState() { } char hex[3] = {'\0'}; for (int i = 0; i < binary.size(); ++i) { - for (int j = 0; j < column_count - 1; ++j) { + for (int j = 0; j < 8; ++j) { items[i * column_count + j].val = ((binary[i] >> (7 - j)) & 1) != 0 ? '1' : '0'; } hex[0] = toHex(binary[i] >> 4); @@ -214,7 +226,7 @@ void BinaryViewModel::updateState() { } } - for (int i = 0; i < row_count * column_count; ++i) { + for (int i = 0; i < items.size(); ++i) { if (i >= prev_items.size() || prev_items[i].val != items[i].val || prev_items[i].bg_color != items[i].bg_color) { auto idx = index(i / column_count, i % column_count); emit dataChanged(idx, idx); diff --git a/tools/cabana/binaryview.h b/tools/cabana/binaryview.h index 55de71572e..f697ea28cb 100644 --- a/tools/cabana/binaryview.h +++ b/tools/cabana/binaryview.h @@ -21,7 +21,7 @@ public: class BinaryViewModel : public QAbstractTableModel { public: BinaryViewModel(QObject *parent) : QAbstractTableModel(parent) {} - void setMessage(const QString &message_id); + void refresh(); void updateState(); QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const { return {}; } @@ -39,12 +39,11 @@ public: QColor bg_color = QApplication::style()->standardPalette().color(QPalette::Base); bool is_msb = false; bool is_lsb = false; - QString val = "0"; + QString val = "-"; QList sigs; }; std::vector items; -private: QString msg_id; const DBCMsg *dbc_msg = nullptr; int row_count = 0; @@ -69,6 +68,7 @@ signals: void resizeSignal(const Signal *sig, int from, int size); private: + void refresh(); std::tuple getSelection(QModelIndex index); void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags flags) override; void mousePressEvent(QMouseEvent *event) override; diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index e653860c3d..580cce5521 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -374,7 +374,7 @@ void ChartView::addSeries(const QString &msg_id, const Signal *sig) { void ChartView::removeSeries(const QString &msg_id, const Signal *sig) { auto it = std::find_if(sigs.begin(), sigs.end(), [&](auto &s) { return s.msg_id == msg_id && s.sig == sig; }); if (it != sigs.end()) { - it = removeSeries(it); + it = removeItem(it); } } @@ -382,7 +382,7 @@ bool ChartView::hasSeries(const QString &msg_id, const Signal *sig) const { return std::any_of(sigs.begin(), sigs.end(), [&](auto &s) { return s.msg_id == msg_id && s.sig == sig; }); } -QList::iterator ChartView::removeSeries(const QList::iterator &it) { +QList::iterator ChartView::removeItem(const QList::iterator &it) { chart()->removeSeries(it->series); it->series->deleteLater(); QString msg_id = it->msg_id; @@ -407,7 +407,7 @@ void ChartView::signalUpdated(const Signal *sig) { void ChartView::signalRemoved(const Signal *sig) { for (auto it = sigs.begin(); it != sigs.end(); /**/) { - it = (it->sig == sig) ? removeSeries(it) : ++it; + it = (it->sig == sig) ? removeItem(it) : ++it; } } @@ -418,7 +418,7 @@ void ChartView::msgUpdated(uint32_t address) { void ChartView::msgRemoved(uint32_t address) { for (auto it = sigs.begin(); it != sigs.end(); /**/) { - it = (it->address == address) ? removeSeries(it) : ++it; + it = (it->address == address) ? removeItem(it) : ++it; } } @@ -450,7 +450,7 @@ void ChartView::manageSeries() { bool exists = std::any_of(series_list.cbegin(), series_list.cend(), [&](auto &s) { return s[0] == it->msg_id && s[2] == it->sig->name.c_str(); }); - it = exists ? ++it : removeSeries(it); + it = exists ? ++it : removeItem(it); } } } diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index 229b1e9cbe..d5572756c1 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -57,7 +57,7 @@ private slots: void manageSeries(); private: - QList::iterator removeSeries(const QList::iterator &it); + QList::iterator removeItem(const QList::iterator &it); void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *ev) override; diff --git a/tools/cabana/commands.cc b/tools/cabana/commands.cc index b3f5cb1c66..e4bf999062 100644 --- a/tools/cabana/commands.cc +++ b/tools/cabana/commands.cc @@ -1,3 +1,5 @@ +#include + #include "tools/cabana/commands.h" // EditMsgCommand @@ -73,3 +75,12 @@ EditSignalCommand::EditSignalCommand(const QString &id, const Signal *sig, const void EditSignalCommand::undo() { dbc()->updateSignal(id, new_signal.name.c_str(), old_signal); } void EditSignalCommand::redo() { dbc()->updateSignal(id, old_signal.name.c_str(), new_signal); } + +namespace UndoStack { + +QUndoStack *instance() { + static QUndoStack *undo_stack = new QUndoStack(qApp); + return undo_stack; +} + +} // namespace UndoStack diff --git a/tools/cabana/commands.h b/tools/cabana/commands.h index e46223d630..c07a00b760 100644 --- a/tools/cabana/commands.h +++ b/tools/cabana/commands.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "tools/cabana/dbcmanager.h" @@ -60,3 +61,8 @@ private: Signal old_signal = {}; Signal new_signal = {}; }; + +namespace UndoStack { + QUndoStack *instance(); + inline void push(QUndoCommand *cmd) { instance()->push(cmd); } +}; diff --git a/tools/cabana/dbcmanager.cc b/tools/cabana/dbcmanager.cc index 01bdff17a1..83f5fdb74a 100644 --- a/tools/cabana/dbcmanager.cc +++ b/tools/cabana/dbcmanager.cc @@ -68,7 +68,7 @@ void DBCManager::addSignal(const QString &id, const Signal &sig) { if (auto m = const_cast(msg(id))) { auto &s = m->sigs[sig.name.c_str()]; s = sig; - emit signalAdded(&s); + emit signalAdded(parseId(id).second, &s); } } @@ -110,6 +110,7 @@ DBCManager *dbc() { std::vector DBCMsg::getSignals() const { std::vector ret; + ret.reserve(sigs.size()); for (auto &[_, sig] : sigs) ret.push_back(&sig); std::sort(ret.begin(), ret.end(), [](auto l, auto r) { return l->start_bit < r->start_bit; }); return ret; diff --git a/tools/cabana/dbcmanager.h b/tools/cabana/dbcmanager.h index 5db6737be8..03bd16f2a5 100644 --- a/tools/cabana/dbcmanager.h +++ b/tools/cabana/dbcmanager.h @@ -8,7 +8,9 @@ struct DBCMsg { QString name; uint32_t size; + // signal must be saved as value in map to make undo stack work properly. std::map sigs; + // return vector, sort by start_bits std::vector getSignals() const; }; @@ -39,7 +41,7 @@ public: } signals: - void signalAdded(const Signal *sig); + void signalAdded(uint32_t address, const Signal *sig); void signalRemoved(const Signal *sig); void signalUpdated(const Signal *sig); void msgUpdated(uint32_t address); diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index ef8a8f6290..5e127966c1 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -4,8 +4,7 @@ #include #include #include -#include -#include +#include #include "selfdrive/ui/qt/util.h" #include "tools/cabana/commands.h" @@ -15,7 +14,6 @@ // DetailWidget DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(charts), QWidget(parent) { - undo_stack = new QUndoStack(this); QWidget *main_widget = new QWidget(this); QVBoxLayout *main_layout = new QVBoxLayout(main_widget); main_layout->setContentsMargins(0, 0, 0, 0); @@ -29,7 +27,7 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart main_layout->addWidget(tabbar); // message title - toolbar = new QToolBar(this); + QToolBar *toolbar = new QToolBar(this); toolbar->setIconSize({16, 16}); toolbar->addWidget(new QLabel("time:")); time_label = new QLabel(this); @@ -54,41 +52,35 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart main_layout->addWidget(warning_widget); // msg widget - QWidget *msg_widget = new QWidget(this); - QVBoxLayout *msg_layout = new QVBoxLayout(msg_widget); - msg_layout->setContentsMargins(0, 0, 0, 0); - // binary view - binary_view = new BinaryView(this); - msg_layout->addWidget(binary_view); - // signals - signals_layout = new QVBoxLayout(); - signals_layout->setSpacing(0); - msg_layout->addLayout(signals_layout); - msg_layout->addStretch(0); - - scroll = new QScrollArea(this); - scroll->setFrameShape(QFrame::NoFrame); - scroll->setWidget(msg_widget); - scroll->setWidgetResizable(true); - scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + splitter = new QSplitter(Qt::Vertical, this); + splitter->setAutoFillBackground(true); + splitter->addWidget(binary_view = new BinaryView(this)); + splitter->addWidget(signal_view = new SignalView(charts, this)); + binary_view->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); + signal_view->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + splitter->setStretchFactor(0, 0); + splitter->setStretchFactor(1, 1); tab_widget = new QTabWidget(this); tab_widget->setTabPosition(QTabWidget::South); - tab_widget->addTab(scroll, bootstrapPixmap("file-earmark-ruled"), "&Msg"); - history_log = new LogsWidget(this); - tab_widget->addTab(history_log, bootstrapPixmap("stopwatch"), "&Logs"); + tab_widget->addTab(splitter, bootstrapPixmap("file-earmark-ruled"), "&Msg"); + tab_widget->addTab(history_log = new LogsWidget(this), bootstrapPixmap("stopwatch"), "&Logs"); main_layout->addWidget(tab_widget); stacked_layout = new QStackedLayout(this); stacked_layout->addWidget(new WelcomeWidget(this)); stacked_layout->addWidget(main_widget); - QObject::connect(binary_view, &BinaryView::signalClicked, this, &DetailWidget::showForm); - QObject::connect(binary_view, &BinaryView::resizeSignal, this, &DetailWidget::resizeSignal); - QObject::connect(binary_view, &BinaryView::addSignal, this, &DetailWidget::addSignal); + QObject::connect(binary_view, &BinaryView::resizeSignal, signal_view->model, &SignalModel::resizeSignal); + QObject::connect(binary_view, &BinaryView::addSignal, signal_view->model, &SignalModel::addSignal); + QObject::connect(binary_view, &BinaryView::signalHovered, signal_view, &SignalView::signalHovered); + QObject::connect(binary_view, &BinaryView::signalClicked, signal_view, &SignalView::expandSignal); + QObject::connect(signal_view, &SignalView::showChart, charts, &ChartsWidget::showChart); + QObject::connect(signal_view, &SignalView::highlight, binary_view, &BinaryView::highlight); QObject::connect(tab_widget, &QTabWidget::currentChanged, [this]() { updateState(); }); QObject::connect(can, &AbstractStream::msgsReceived, this, &DetailWidget::updateState); - QObject::connect(dbc(), &DBCManager::DBCFileChanged, [this]() { dbcMsgChanged(); }); + QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &DetailWidget::refresh); + QObject::connect(UndoStack::instance(), &QUndoStack::indexChanged, this, &DetailWidget::refresh); QObject::connect(tabbar, &QTabBar::customContextMenuRequested, this, &DetailWidget::showTabBarContextMenu); QObject::connect(tabbar, &QTabBar::currentChanged, [this](int index) { if (index != -1 && tabbar->tabText(index) != msg_id) { @@ -96,14 +88,7 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart } }); QObject::connect(tabbar, &QTabBar::tabCloseRequested, tabbar, &QTabBar::removeTab); - QObject::connect(charts, &ChartsWidget::seriesChanged, this, &DetailWidget::updateChartState); - QObject::connect(history_log, &LogsWidget::openChart, [this](const QString &id, const Signal *sig) { - this->charts->showChart(id, sig, true, false); - }); - QObject::connect(undo_stack, &QUndoStack::indexChanged, [this]() { - if (undo_stack->count() > 0) - dbcMsgChanged(); - }); + QObject::connect(charts, &ChartsWidget::seriesChanged, signal_view, &SignalView::updateChartState); } void DetailWidget::showTabBarContextMenu(const QPoint &pt) { @@ -128,41 +113,27 @@ void DetailWidget::setMessage(const QString &message_id) { index = tabbar->addTab(message_id); tabbar->setTabToolTip(index, msgName(message_id)); } - tabbar->setCurrentIndex(index); - dbcMsgChanged(); - scroll->verticalScrollBar()->setValue(0); - stacked_layout->setCurrentIndex(1); -} - -void DetailWidget::dbcMsgChanged(int show_form_idx) { - if (msg_id.isEmpty()) return; setUpdatesEnabled(false); + signal_view->setMessage(msg_id); binary_view->setMessage(msg_id); history_log->setMessage(msg_id); - int i = 0; + stacked_layout->setCurrentIndex(1); + tabbar->setCurrentIndex(index); + refresh(); + splitter->setSizes({1, 2}); + + setUpdatesEnabled(true); +} + +void DetailWidget::refresh() { + if (msg_id.isEmpty()) return; + QStringList warnings; const DBCMsg *msg = dbc()->msg(msg_id); if (msg) { - for (auto sig : msg->getSignals()) { - SignalEdit *form = i < signal_list.size() ? signal_list[i] : nullptr; - if (!form) { - form = new SignalEdit(i, this); - QObject::connect(form, &SignalEdit::remove, this, &DetailWidget::removeSignal); - QObject::connect(form, &SignalEdit::save, this, &DetailWidget::saveSignal); - QObject::connect(form, &SignalEdit::showFormClicked, this, &DetailWidget::showForm); - QObject::connect(form, &SignalEdit::highlight, binary_view, &BinaryView::highlight); - QObject::connect(binary_view, &BinaryView::signalHovered, form, &SignalEdit::signalHovered); - QObject::connect(form, &SignalEdit::showChart, charts, &ChartsWidget::showChart); - signals_layout->addWidget(form); - signal_list.push_back(form); - } - form->setSignal(msg_id, sig); - form->setChartOpened(charts->hasSignal(msg_id, sig)); - ++i; - } if (msg->size != can->lastMessage(msg_id).dat.size()) { warnings.push_back(tr("Message size (%1) is incorrect.").arg(msg->size)); } @@ -172,10 +143,6 @@ void DetailWidget::dbcMsgChanged(int show_form_idx) { } else { warnings.push_back(tr("Drag-Select in binary view to create new signal.")); } - for (/**/; i < signal_list.size(); ++i) - signal_list[i]->hide(); - - toolbar->setVisible(!msg_id.isEmpty()); remove_msg_act->setEnabled(msg != nullptr); name_label->setText(msgName(msg_id)); @@ -184,10 +151,9 @@ void DetailWidget::dbcMsgChanged(int show_form_idx) { warning_icon->setPixmap(bootstrapPixmap(msg ? "exclamation-triangle" : "info-circle")); } warning_widget->setVisible(!warnings.isEmpty()); - setUpdatesEnabled(true); } -void DetailWidget::updateState(const QHash * msgs) { +void DetailWidget::updateState(const QHash *msgs) { time_label->setText(QString::number(can->currentSec(), 'f', 3)); if (msg_id.isEmpty() || (msgs && !msgs->contains(msg_id))) return; @@ -198,86 +164,18 @@ void DetailWidget::updateState(const QHash * msgs) { history_log->updateState(); } -void DetailWidget::showForm(const Signal *sig) { - setUpdatesEnabled(false); - for (auto f : signal_list) { - f->updateForm(f->sig == sig && !f->form->isVisible()); - if (f->sig == sig && f->form->isVisible()) { - QTimer::singleShot(0, [=]() { scroll->ensureWidgetVisible(f); }); - } - } - setUpdatesEnabled(true); -} - -void DetailWidget::updateChartState() { - for (auto f : signal_list) - f->setChartOpened(charts->hasSignal(f->msg_id, f->sig)); -} - void DetailWidget::editMsg() { QString id = msg_id; auto msg = dbc()->msg(id); int size = msg ? msg->size : can->lastMessage(id).dat.size(); EditMessageDialog dlg(id, msgName(id), size, this); if (dlg.exec()) { - undo_stack->push(new EditMsgCommand(msg_id, dlg.name_edit->text(), dlg.size_spin->value())); + UndoStack::push(new EditMsgCommand(msg_id, dlg.name_edit->text(), dlg.size_spin->value())); } } void DetailWidget::removeMsg() { - undo_stack->push(new RemoveMsgCommand(msg_id)); -} - -void DetailWidget::addSignal(int start_bit, int size, bool little_endian) { - auto msg = dbc()->msg(msg_id); - if (!msg) { - for (int i = 1; /**/; ++i) { - QString name = QString("NEW_MSG_%1").arg(i); - auto it = std::find_if(dbc()->messages().begin(), dbc()->messages().end(), [&](auto &m) { return m.second.name == name; }); - if (it == dbc()->messages().end()) { - undo_stack->push(new EditMsgCommand(msg_id, name, can->lastMessage(msg_id).dat.size())); - msg = dbc()->msg(msg_id); - break; - } - } - } - Signal sig = {.is_little_endian = little_endian, .factor = 1}; - for (int i = 1; /**/; ++i) { - sig.name = "NEW_SIGNAL_" + std::to_string(i); - if (msg->sigs.count(sig.name.c_str()) == 0) break; - } - updateSigSizeParamsFromRange(sig, start_bit, size); - undo_stack->push(new AddSigCommand(msg_id, sig)); -} - -void DetailWidget::resizeSignal(const Signal *sig, int start_bit, int size) { - Signal s = *sig; - updateSigSizeParamsFromRange(s, start_bit, size); - saveSignal(sig, s); -} - -void DetailWidget::saveSignal(const Signal *sig, const Signal &new_sig) { - auto msg = dbc()->msg(msg_id); - if (new_sig.name != sig->name) { - auto it = msg->sigs.find(new_sig.name.c_str()); - if (it != msg->sigs.end()) { - QString warning_str = tr("There is already a signal with the same name '%1'").arg(new_sig.name.c_str()); - QMessageBox::warning(this, tr("Failed to save signal"), warning_str); - return; - } - } - auto [start, end] = getSignalRange(&new_sig); - if (start < 0 || end >= msg->size * 8) { - QString warning_str = tr("Signal size [%1] exceed limit").arg(new_sig.size); - QMessageBox::warning(this, tr("Failed to save signal"), warning_str); - return; - } - - undo_stack->push(new EditSignalCommand(msg_id, sig, new_sig)); -} - -void DetailWidget::removeSignal(const Signal *sig) { - undo_stack->push(new RemoveSigCommand(msg_id, sig)); + UndoStack::push(new RemoveMsgCommand(msg_id)); } // EditMessageDialog diff --git a/tools/cabana/detailwidget.h b/tools/cabana/detailwidget.h index c59a6fca50..3a3f3adf0e 100644 --- a/tools/cabana/detailwidget.h +++ b/tools/cabana/detailwidget.h @@ -1,10 +1,9 @@ #pragma once +#include #include -#include #include #include -#include #include "tools/cabana/binaryview.h" #include "tools/cabana/chartswidget.h" @@ -30,18 +29,11 @@ class DetailWidget : public QWidget { public: DetailWidget(ChartsWidget *charts, QWidget *parent); void setMessage(const QString &message_id); - void dbcMsgChanged(int show_form_idx = -1); + void refresh(); QSize minimumSizeHint() const override { return binary_view->minimumSizeHint(); } - QUndoStack *undo_stack = nullptr; private: - void showForm(const Signal *sig); - void updateChartState(); void showTabBarContextMenu(const QPoint &pt); - void addSignal(int start_bit, int size, bool little_endian); - void resizeSignal(const Signal *sig, int from, int to); - void saveSignal(const Signal *sig, const Signal &new_sig); - void removeSignal(const Signal *sig); void editMsg(); void removeMsg(); void updateState(const QHash * msgs = nullptr); @@ -49,15 +41,13 @@ private: QString msg_id; QLabel *name_label, *time_label, *warning_icon, *warning_label; QWidget *warning_widget; - QVBoxLayout *signals_layout; QTabBar *tabbar; QTabWidget *tab_widget; - QToolBar *toolbar; QAction *remove_msg_act; LogsWidget *history_log; BinaryView *binary_view; - QScrollArea *scroll; + SignalView *signal_view; ChartsWidget *charts; + QSplitter *splitter; QStackedLayout *stacked_layout; - QList signal_list; }; diff --git a/tools/cabana/historylog.cc b/tools/cabana/historylog.cc index 44f3a63b95..830390f4a8 100644 --- a/tools/cabana/historylog.cc +++ b/tools/cabana/historylog.cc @@ -4,6 +4,8 @@ #include #include +#include "tools/cabana/commands.h" + // HistoryLogModel QVariant HistoryLogModel::data(const QModelIndex &index, int role) const { @@ -22,17 +24,16 @@ QVariant HistoryLogModel::data(const QModelIndex &index, int role) const { void HistoryLogModel::setMessage(const QString &message_id) { msg_id = message_id; - sigs.clear(); - if (auto dbc_msg = dbc()->msg(msg_id)) { - sigs = dbc_msg->getSignals(); - } - filter_cmp = nullptr; - refresh(); } void HistoryLogModel::refresh() { beginResetModel(); + sigs.clear(); + if (auto dbc_msg = dbc()->msg(msg_id)) { + sigs = dbc_msg->getSignals(); + } last_fetch_time = 0; + has_more_data = true; messages.clear(); hex_colors.clear(); updateState(); @@ -74,7 +75,6 @@ void HistoryLogModel::setFilter(int sig_idx, const QString &value, std::function filter_sig_idx = sig_idx; filter_value = value.toDouble(); filter_cmp = value.isEmpty() ? nullptr : cmp; - refresh(); } void HistoryLogModel::updateState() { @@ -232,11 +232,21 @@ LogsWidget::LogsWidget(QWidget *parent) : QWidget(parent) { QObject::connect(comp_box, SIGNAL(activated(int)), this, SLOT(setFilter())); QObject::connect(value_edit, &QLineEdit::textChanged, this, &LogsWidget::setFilter); QObject::connect(can, &AbstractStream::seekedTo, model, &HistoryLogModel::refresh); + QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &LogsWidget::refresh); + QObject::connect(UndoStack::instance(), &QUndoStack::indexChanged, this, &LogsWidget::refresh); QObject::connect(can, &AbstractStream::eventsMerged, model, &HistoryLogModel::segmentsMerged); } void LogsWidget::setMessage(const QString &message_id) { model->setMessage(message_id); + refresh(); +} + +void LogsWidget::refresh() { + if (model->msg_id.isEmpty()) return; + + model->setFilter(0, "", nullptr); + model->refresh(); bool has_signal = model->sigs.size(); if (has_signal) { signals_cb->clear(); @@ -260,4 +270,5 @@ void LogsWidget::setFilter() { case 3: cmp = std::less{}; break; } model->setFilter(signals_cb->currentIndex(), value_edit->text(), cmp); + model->refresh(); } diff --git a/tools/cabana/historylog.h b/tools/cabana/historylog.h index c1a2c13769..bc2e0e3914 100644 --- a/tools/cabana/historylog.h +++ b/tools/cabana/historylog.h @@ -75,13 +75,12 @@ public: void updateState() {if (dynamic_mode->isChecked()) model->updateState(); } void showEvent(QShowEvent *event) override { if (dynamic_mode->isChecked()) model->refresh(); } -signals: - void openChart(const QString &msg_id, const Signal *sig); - private slots: void setFilter(); private: + void refresh(); + QTableView *logs; HistoryLogModel *model; QCheckBox *dynamic_mode; diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index d875d079fa..3c2d46cbf2 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -14,6 +14,8 @@ #include #include +#include "tools/cabana/commands.h" + static MainWindow *main_win = nullptr; void qLogMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { if (type == QtDebugMsg) std::cout << msg.toStdString() << std::endl; @@ -59,7 +61,7 @@ MainWindow::MainWindow() : QMainWindow() { QObject::connect(charts_widget, &ChartsWidget::dock, this, &MainWindow::dockCharts); QObject::connect(can, &AbstractStream::streamStarted, this, &MainWindow::loadDBCFromFingerprint); QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &MainWindow::DBCFileChanged); - QObject::connect(detail_widget->undo_stack, &QUndoStack::cleanChanged, [this](bool clean) { setWindowModified(!clean); }); + QObject::connect(UndoStack::instance(), &QUndoStack::cleanChanged, this, &MainWindow::undoStackCleanChanged); } void MainWindow::createActions() { @@ -98,16 +100,16 @@ void MainWindow::createActions() { file_menu->addAction(tr("E&xit"), qApp, &QApplication::closeAllWindows)->setShortcuts(QKeySequence::Quit); QMenu *edit_menu = menuBar()->addMenu(tr("&Edit")); - auto undo_act = detail_widget->undo_stack->createUndoAction(this, tr("&Undo")); + auto undo_act = UndoStack::instance()->createUndoAction(this, tr("&Undo")); undo_act->setShortcuts(QKeySequence::Undo); edit_menu->addAction(undo_act); - auto redo_act = detail_widget->undo_stack->createRedoAction(this, tr("&Rndo")); + auto redo_act = UndoStack::instance()->createRedoAction(this, tr("&Rndo")); redo_act->setShortcuts(QKeySequence::Redo); edit_menu->addAction(redo_act); edit_menu->addSeparator(); QMenu *commands_menu = edit_menu->addMenu(tr("Command &List")); - auto undo_view = new QUndoView(detail_widget->undo_stack); + auto undo_view = new QUndoView(UndoStack::instance()); undo_view->setWindowTitle(tr("Command List")); QWidgetAction *commands_act = new QWidgetAction(this); commands_act->setDefaultWidget(undo_view); @@ -174,8 +176,12 @@ void MainWindow::createShortcuts() { // TODO: add more shortcuts here. } +void MainWindow::undoStackCleanChanged(bool clean) { + setWindowModified(!clean); +} + void MainWindow::DBCFileChanged() { - detail_widget->undo_stack->clear(); + UndoStack::instance()->clear(); setWindowFilePath(QString("%1").arg(dbc()->name())); } @@ -262,7 +268,7 @@ void MainWindow::saveFile(const QString &fn) { QFile file(fn); if (file.open(QIODevice::WriteOnly)) { file.write(dbc()->generateDBC().toUtf8()); - detail_widget->undo_stack->setClean(); + UndoStack::instance()->setClean(); setCurrentFile(fn); statusBar()->showMessage(tr("File saved"), 2000); } @@ -309,7 +315,7 @@ void MainWindow::updateRecentFileActions() { void MainWindow::remindSaveChanges() { bool discard_changes = false; - while (!detail_widget->undo_stack->isClean() && !discard_changes) { + while (!UndoStack::instance()->isClean() && !discard_changes) { int ret = (QMessageBox::question(this, tr("Unsaved Changes"), tr("You have unsaved changes. Press ok to save them, cancel to discard."), QMessageBox::Ok | QMessageBox::Cancel)); @@ -319,7 +325,7 @@ void MainWindow::remindSaveChanges() { discard_changes = true; } } - detail_widget->undo_stack->clear(); + UndoStack::instance()->clear(); current_file = ""; } diff --git a/tools/cabana/mainwin.h b/tools/cabana/mainwin.h index 0c207528c9..ba36d676e0 100644 --- a/tools/cabana/mainwin.h +++ b/tools/cabana/mainwin.h @@ -52,6 +52,7 @@ protected: void updateDownloadProgress(uint64_t cur, uint64_t total, bool success); void setOption(); void findSimilarBits(); + void undoStackCleanChanged(bool clean); VideoWidget *video_widget = nullptr; QDockWidget *video_dock; diff --git a/tools/cabana/signaledit.cc b/tools/cabana/signaledit.cc index 52ce74ae08..ceb7b7ba3f 100644 --- a/tools/cabana/signaledit.cc +++ b/tools/cabana/signaledit.cc @@ -1,174 +1,194 @@ #include "tools/cabana/signaledit.h" -#include -#include #include #include +#include +#include +#include +#include #include +#include "tools/cabana/commands.h" + #include "selfdrive/ui/qt/util.h" -// SignalForm +// SignalModel -SignalForm::SignalForm(QWidget *parent) : QWidget(parent) { - auto double_validator = new QDoubleValidator(this); - double_validator->setLocale(QLocale::C); // Match locale of QString::toDouble() instead of system +SignalModel::SignalModel(QObject *parent) : root(new Item), QAbstractItemModel(parent) { + QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &SignalModel::refresh); + QObject::connect(dbc(), &DBCManager::msgUpdated, this, &SignalModel::handleMsgChanged); + QObject::connect(dbc(), &DBCManager::msgRemoved, this, &SignalModel::handleMsgChanged); + QObject::connect(dbc(), &DBCManager::signalAdded, this, &SignalModel::handleSignalAdded); + QObject::connect(dbc(), &DBCManager::signalUpdated, this, &SignalModel::handleSignalUpdated); + QObject::connect(dbc(), &DBCManager::signalRemoved, this, &SignalModel::handleSignalRemoved); + QObject::connect(can, &AbstractStream::msgsReceived, this, &SignalModel::updateState); +} - QVBoxLayout *main_layout = new QVBoxLayout(this); - QFormLayout *form_layout = new QFormLayout(); - main_layout->addLayout(form_layout); - - name = new QLineEdit(); - name->setValidator(new NameValidator(name)); - form_layout->addRow(tr("Name"), name); - - QHBoxLayout *hl = new QHBoxLayout(this); - size = new QSpinBox(); - size->setMinimum(1); - hl->addWidget(size); - endianness = new QComboBox(); - endianness->addItems({"Little Endianness", "Big Endianness"}); - hl->addWidget(endianness); - sign = new QComboBox(); - sign->addItems({"Signed", "Unsigned"}); - hl->addWidget(sign); - form_layout->addRow(tr("Size"), hl); - - offset = new QLineEdit(); - offset->setValidator(double_validator); - form_layout->addRow(tr("Offset"), offset); - factor = new QLineEdit(); - factor->setValidator(double_validator); - form_layout->addRow(tr("Factor"), factor); - - expand_btn = new QToolButton(this); - expand_btn->setText(tr("more...")); - main_layout->addWidget(expand_btn, 0, Qt::AlignRight); - - // TODO: parse the following parameters in opendbc - QWidget *extra_container = new QWidget(this); - QFormLayout *extra_layout = new QFormLayout(extra_container); - unit = new QLineEdit(); - extra_layout->addRow(tr("Unit"), unit); - comment = new QLineEdit(); - extra_layout->addRow(tr("Comment"), comment); - min_val = new QLineEdit(); - min_val->setValidator(double_validator); - extra_layout->addRow(tr("Minimum value"), min_val); - max_val = new QLineEdit(); - max_val->setValidator(double_validator); - extra_layout->addRow(tr("Maximum value"), max_val); - val_desc = new QLineEdit(); - extra_layout->addRow(tr("Value descriptions"), val_desc); - - main_layout->addWidget(extra_container); - extra_container->setVisible(false); - - QObject::connect(name, &QLineEdit::editingFinished, this, &SignalForm::textBoxEditingFinished); - QObject::connect(factor, &QLineEdit::editingFinished, this, &SignalForm::textBoxEditingFinished); - QObject::connect(offset, &QLineEdit::editingFinished, this, &SignalForm::textBoxEditingFinished); - QObject::connect(size, &QSpinBox::editingFinished, this, &SignalForm::changed); - QObject::connect(sign, SIGNAL(activated(int)), SIGNAL(changed())); - QObject::connect(endianness, SIGNAL(activated(int)), SIGNAL(changed())); - QObject::connect(expand_btn, &QToolButton::clicked, [=]() { - extra_container->setVisible(!extra_container->isVisible()); - expand_btn->setText(extra_container->isVisible() ? tr("less...") : tr("more...")); - }); -} - -void SignalForm::textBoxEditingFinished() { - QLineEdit *edit = qobject_cast(QObject::sender()); - if (edit && edit->isModified()) { - edit->setModified(false); - emit changed(); - } -} - -// SignalEdit - -SignalEdit::SignalEdit(int index, QWidget *parent) : form_idx(index), QWidget(parent) { - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(0, 0, 0, 0); - main_layout->setSpacing(0); +void SignalModel::insertItem(SignalModel::Item *parent_item, int pos, const Signal *sig) { + Item *item = new Item{.sig = sig, .parent = parent_item, .title = sig->name.c_str(), .type = Item::Sig}; + parent_item->children.insert(pos, item); + QString titles[]{"Name", "Size", "Little Endian", "Signed", "Offset", "Factor", "Extra Info", "Unit", "Comment", "Minimum", "Maximum", "Description"}; + for (int i = 0; i < std::size(titles); ++i) { + item->children.push_back(new Item{.sig = sig, .parent = item, .title = titles[i], .type = (Item::Type)(i + Item::Name)}); + } +} - bg_color = QColor(getColor(form_idx)); +void SignalModel::setMessage(const QString &id) { + msg_id = id; + filter_str = ""; + refresh(); + updateState(nullptr); +} - // title bar - auto title_bar = new QWidget(this); - title_bar->setFixedHeight(32); - QHBoxLayout *title_layout = new QHBoxLayout(title_bar); - title_layout->setContentsMargins(0, 0, 0, 0); - title_bar->setStyleSheet("QToolButton {width:15px;height:15px;font-size:15px}"); - color_label = new QLabel(this); - color_label->setFixedWidth(25); - color_label->setContentsMargins(5, 0, 0, 0); - title_layout->addWidget(color_label); - icon = new QLabel(this); - title_layout->addWidget(icon); - title = new ElidedLabel(this); - title->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - title_layout->addWidget(title); - - plot_btn = new QToolButton(this); - plot_btn->setIcon(bootstrapPixmap("graph-up")); - plot_btn->setCheckable(true); - plot_btn->setAutoRaise(true); - title_layout->addWidget(plot_btn); - auto remove_btn = new QToolButton(this); - remove_btn->setAutoRaise(true); - remove_btn->setIcon(bootstrapPixmap("x")); - remove_btn->setToolTip(tr("Remove signal")); - title_layout->addWidget(remove_btn); - main_layout->addWidget(title_bar); +void SignalModel::setFilter(const QString &txt) { + filter_str = txt; + refresh(); +} - // signal form - form = new SignalForm(this); - form->setVisible(false); - main_layout->addWidget(form); - - // bottom line - QFrame *hline = new QFrame(); - hline->setFrameShape(QFrame::HLine); - hline->setFrameShadow(QFrame::Sunken); - main_layout->addWidget(hline); - - QObject::connect(title, &ElidedLabel::clicked, [this]() { emit showFormClicked(sig); }); - QObject::connect(plot_btn, &QToolButton::clicked, [this](bool checked) { - emit showChart(msg_id, sig, checked, QGuiApplication::keyboardModifiers() & Qt::ShiftModifier); - }); - QObject::connect(remove_btn, &QToolButton::clicked, [this]() { emit remove(sig); }); - QObject::connect(form, &SignalForm::changed, this, &SignalEdit::saveSignal); - setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); -} - -void SignalEdit::setSignal(const QString &message_id, const Signal *signal) { - sig = signal; - updateForm(msg_id == message_id && form->isVisible()); - msg_id = message_id; - color_label->setText(QString::number(form_idx + 1)); - color_label->setStyleSheet(QString("color:black; background-color:%2").arg(bg_color.name())); - title->setText(sig->name.c_str()); - show(); -} - -void SignalEdit::saveSignal() { - Signal s = *sig; - s.name = form->name->text().toStdString(); - s.size = form->size->text().toInt(); - s.offset = form->offset->text().toDouble(); - s.factor = form->factor->text().toDouble(); - s.is_signed = form->sign->currentIndex() == 0; - bool little_endian = form->endianness->currentIndex() == 0; - if (little_endian != s.is_little_endian) { +void SignalModel::refresh() { + beginResetModel(); + root.reset(new SignalModel::Item); + if (auto msg = dbc()->msg(msg_id)) { + for (auto &s : msg->getSignals()) { + if (filter_str.isEmpty() || QString::fromStdString(s->name).contains(filter_str, Qt::CaseInsensitive)) { + insertItem(root.get(), root->children.size(), s); + } + } + } + endResetModel(); +} + +void SignalModel::updateState(const QHash *msgs) { + if (!msgs || (msgs->contains(msg_id))) { + auto &dat = can->lastMessage(msg_id).dat; + int row = 0; + for (auto item : root->children) { + double value = get_raw_value((uint8_t *)dat.begin(), dat.size(), *item->sig); + item->sig_val = QString::number(value); + emit dataChanged(index(row, 1), index(row, 1), {Qt::DisplayRole}); + ++row; + } + } +} + +int SignalModel::rowCount(const QModelIndex &parent) const { + if (parent.column() > 0) return 0; + + auto parent_item = getItem(parent); + int row_count = parent_item->children.size(); + if (parent_item->type == Item::Sig && !parent_item->extra_expanded) { + row_count -= (Item::Desc - Item::ExtraInfo); + } + return row_count; +} + +Qt::ItemFlags SignalModel::flags(const QModelIndex &index) const { + if (!index.isValid()) return Qt::NoItemFlags; + + auto item = getItem(index); + Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; + if (index.column() == 1 && item->type != Item::Sig && item->type != Item::ExtraInfo) { + flags |= (item->type == Item::Endian || item->type == Item::Signed) ? Qt::ItemIsUserCheckable : Qt::ItemIsEditable; + } + return flags; +} + +int SignalModel::signalRow(const Signal *sig) const { + auto &children = root->children; + for (int i = 0; i < children.size(); ++i) { + if (children[i]->sig == sig) return i; + } + return -1; +} + +QModelIndex SignalModel::index(int row, int column, const QModelIndex &parent) const { + if (!hasIndex(row, column, parent)) return {}; + return createIndex(row, column, getItem(parent)->children[row]); +} + +QModelIndex SignalModel::parent(const QModelIndex &index) const { + if (!index.isValid()) return {}; + Item *parent_item = getItem(index)->parent; + return parent_item == root.get() ? QModelIndex() : createIndex(parent_item->row(), 0, parent_item); +} + +QVariant SignalModel::data(const QModelIndex &index, int role) const { + if (index.isValid()) { + const Item *item = getItem(index); + if (role == Qt::DisplayRole || role == Qt::EditRole) { + if (index.column() == 0) { + return item->type == Item::Sig ? QString::fromStdString(item->sig->name) : item->title; + } else { + switch (item->type) { + case Item::Sig: return item->sig_val; + case Item::Name: return QString::fromStdString(item->sig->name); + case Item::Size: return item->sig->size; + case Item::Offset: return QString::number(item->sig->offset, 'f', 6); + case Item::Factor: return QString::number(item->sig->factor, 'f', 6); + default: break; + } + } + } else if (role == Qt::CheckStateRole && index.column() == 1) { + if (item->type == Item::Endian) return item->sig->is_little_endian ? Qt::Checked : Qt::Unchecked; + if (item->type == Item::Signed) return item->sig->is_signed ? Qt::Checked : Qt::Unchecked; + } else if (role == Qt::DecorationRole && index.column() == 0 && item->type == Item::ExtraInfo) { + return bootstrapPixmap(item->parent->extra_expanded ? "chevron-compact-down" : "chevron-compact-up"); + } + } + return {}; +} + +bool SignalModel::setData(const QModelIndex &index, const QVariant &value, int role) { + if (role != Qt::EditRole && role != Qt::CheckStateRole) return false; + + Item *item = getItem(index); + Signal s = *item->sig; + switch (item->type) { + case Item::Name: s.name = value.toString().toStdString(); break; + case Item::Size: s.size = value.toInt(); break; + case Item::Endian: s.is_little_endian = value.toBool(); break; + case Item::Signed: s.is_signed = value.toBool(); break; + case Item::Offset: s.offset = value.toDouble(); break; + case Item::Factor: s.factor = value.toDouble(); break; + default: return false; + } + bool ret = saveSignal(item->sig, s); + emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole, Qt::CheckStateRole}); + return ret; +} + +void SignalModel::showExtraInfo(const QModelIndex &index) { + auto item = getItem(index); + if (item->type == Item::ExtraInfo) { + if (!item->parent->extra_expanded) { + item->parent->extra_expanded = true; + beginInsertRows(index.parent(), 7, 13); + endInsertRows(); + } else { + item->parent->extra_expanded = false; + beginRemoveRows(index.parent(), 7, 13); + endRemoveRows(); + } + } +} + +bool SignalModel::saveSignal(const Signal *origin_s, Signal &s) { + auto msg = dbc()->msg(msg_id); + if (s.name != origin_s->name && msg->sigs.count(s.name.c_str()) != 0) { + QString text = tr("There is already a signal with the same name '%1'").arg(s.name.c_str()); + QMessageBox::warning(nullptr, tr("Failed to save signal"), text); + return false; + } + + if (s.is_little_endian != origin_s->is_little_endian) { int start = std::floor(s.start_bit / 8); - if (little_endian) { + if (s.is_little_endian) { int end = std::floor((s.start_bit - s.size + 1) / 8); s.start_bit = start == end ? s.start_bit - s.size + 1 : bigEndianStartBitsIndex(s.start_bit); } else { int end = std::floor((s.start_bit + s.size - 1) / 8); s.start_bit = start == end ? s.start_bit + s.size - 1 : bigEndianBitIndex(s.start_bit); } - s.is_little_endian = little_endian; } if (s.is_little_endian) { s.lsb = s.start_bit; @@ -177,43 +197,254 @@ void SignalEdit::saveSignal() { s.lsb = bigEndianStartBitsIndex(bigEndianBitIndex(s.start_bit) + s.size - 1); s.msb = s.start_bit; } - if (s != *sig) { - emit save(this->sig, s); + + UndoStack::push(new EditSignalCommand(msg_id, origin_s, s)); + return true; +} + +void SignalModel::addSignal(int start_bit, int size, bool little_endian) { + auto msg = dbc()->msg(msg_id); + for (int i = 1; !msg; ++i) { + QString name = QString("NEW_MSG_%1").arg(i); + if (std::none_of(dbc()->messages().begin(), dbc()->messages().end(), [&](auto &m) { return m.second.name == name; })) { + UndoStack::push(new EditMsgCommand(msg_id, name, can->lastMessage(msg_id).dat.size())); + msg = dbc()->msg(msg_id); + } + } + + Signal sig = {.is_little_endian = little_endian, .factor = 1}; + for (int i = 1; /**/; ++i) { + sig.name = "NEW_SIGNAL_" + std::to_string(i); + if (msg->sigs.count(sig.name.c_str()) == 0) break; } + updateSigSizeParamsFromRange(sig, start_bit, size); + UndoStack::push(new AddSigCommand(msg_id, sig)); +} + +void SignalModel::resizeSignal(const Signal *sig, int start_bit, int size) { + Signal s = *sig; + updateSigSizeParamsFromRange(s, start_bit, size); + saveSignal(sig, s); +} + +void SignalModel::removeSignal(const Signal *sig) { + UndoStack::push(new RemoveSigCommand(msg_id, sig)); } -void SignalEdit::setChartOpened(bool opened) { - plot_btn->setToolTip(opened ? tr("Close Plot") : tr("Show Plot\nSHIFT click to add to previous opened chart")); - plot_btn->setChecked(opened); +void SignalModel::handleMsgChanged(uint32_t address) { + if (address == DBCManager::parseId(msg_id).second) { + refresh(); + } } -void SignalEdit::updateForm(bool visible) { - if (visible && sig) { - if (form->name->text() != sig->name.c_str()) { - form->name->setText(sig->name.c_str()); +void SignalModel::handleSignalAdded(uint32_t address, const Signal *sig) { + if (address == DBCManager::parseId(msg_id).second) { + int i = 0; + for (; i < root->children.size(); ++i) { + if (sig->start_bit < root->children[i]->sig->start_bit) break; } - form->endianness->setCurrentIndex(sig->is_little_endian ? 0 : 1); - form->sign->setCurrentIndex(sig->is_signed ? 0 : 1); - form->factor->setText(QString::number(sig->factor)); - form->offset->setText(QString::number(sig->offset)); - form->size->setValue(sig->size); + beginInsertRows({}, i, i); + insertItem(root.get(), i, sig); + endInsertRows(); + } +} + +void SignalModel::handleSignalUpdated(const Signal *sig) { + if (int row = signalRow(sig); row != -1) { + emit dataChanged(index(row, 0), index(row, 1), {Qt::DisplayRole, Qt::EditRole, Qt::CheckStateRole}); + } +} + +void SignalModel::handleSignalRemoved(const Signal *sig) { + if (int row = signalRow(sig); row != -1) { + beginRemoveRows({}, row, row); + delete root->children.takeAt(row); + endRemoveRows(); } - form->setVisible(visible); - icon->setText(visible ? "▼ " : "> "); } -void SignalEdit::signalHovered(const Signal *s) { - auto text_color = sig == s ? "white" : "black"; - auto _bg_color = sig == s ? bg_color.darker(125) : bg_color; // 4/5x brightness - color_label->setStyleSheet(QString("color:%1; background-color:%2").arg(text_color).arg(_bg_color.name())); +// SignalItemDelegate + +SignalItemDelegate::SignalItemDelegate(QObject *parent) { + name_validator = new NameValidator(this); + double_validator = new QDoubleValidator(this); + double_validator->setLocale(QLocale::C); // Match locale of QString::toDouble() instead of system +} + +void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { + auto item = (SignalModel::Item *)index.internalPointer(); + if (item && !index.parent().isValid() && index.column() == 0) { + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + if (option.state & QStyle::State_Selected) { + painter->fillRect(option.rect, option.palette.highlight()); + } + + // color label + auto bg_color = QColor(getColor(item->row())); + QRect rc{option.rect.left() + 3, option.rect.top(), 22, option.rect.height()}; + painter->setPen(Qt::NoPen); + painter->setBrush(item->highlight ? bg_color.darker(125) : bg_color); + painter->drawRoundedRect(rc.adjusted(0, 2, 0, -2), 5, 5); + painter->setPen(item->highlight ? Qt::white : Qt::black); + painter->drawText(rc, Qt::AlignCenter, QString::number(item->row() + 1)); + + // signal name + QFont font; + font.setBold(true); + painter->setFont(font); + painter->setPen((option.state & QStyle::State_Selected ? option.palette.highlightedText() : option.palette.text()).color()); + painter->drawText(option.rect.adjusted(rc.width() + 9, 0, 0, 0), option.displayAlignment, index.data(Qt::DisplayRole).toString()); + painter->restore(); + } else { + QStyledItemDelegate::paint(painter, option, index); + } } -void SignalEdit::enterEvent(QEvent *event) { - emit highlight(sig); - QWidget::enterEvent(event); +QWidget *SignalItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { + auto item = (SignalModel::Item *)index.internalPointer(); + if (item->type == SignalModel::Item::Name || item->type == SignalModel::Item::Offset || item->type == SignalModel::Item::Factor) { + QLineEdit *e = new QLineEdit(parent); + e->setFrame(false); + e->setValidator(index.row() == 0 ? name_validator : double_validator); + return e; + } else if (item->type == SignalModel::Item::Size) { + QSpinBox *spin = new QSpinBox(parent); + spin->setFrame(false); + spin->setRange(1, 64); + return spin; + } + return QStyledItemDelegate::createEditor(parent, option, index); } -void SignalEdit::leaveEvent(QEvent *event) { - emit highlight(nullptr); - QWidget::leaveEvent(event); +// SignalView + +SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts), QWidget(parent) { + // title bar + QWidget *title_bar = new QWidget(this); + title_bar->setAutoFillBackground(true); + QHBoxLayout *hl = new QHBoxLayout(title_bar); + hl->addWidget(signal_count_lb = new QLabel()); + filter_edit = new QLineEdit(this); + filter_edit->setClearButtonEnabled(true); + filter_edit->setPlaceholderText(tr("filter signals by name")); + hl->addWidget(filter_edit); + hl->addStretch(1); + auto collapse_btn = new QToolButton(); + collapse_btn->setIcon(bootstrapPixmap("dash-square")); + collapse_btn->setIconSize({12, 12}); + collapse_btn->setAutoRaise(true); + collapse_btn->setToolTip(tr("Collapse All")); + hl->addWidget(collapse_btn); + + // tree view + tree = new QTreeView(this); + tree->setModel(model = new SignalModel(this)); + tree->setItemDelegate(new SignalItemDelegate(this)); + tree->setFrameShape(QFrame::NoFrame); + tree->setHeaderHidden(true); + tree->setMouseTracking(true); + tree->setExpandsOnDoubleClick(false); + tree->header()->setSectionResizeMode(QHeaderView::Stretch); + tree->setMinimumHeight(300); + tree->setStyleSheet("QSpinBox{background-color:white;border:none;} QLineEdit{background-color:white;}"); + + QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(0, 0, 0, 0); + main_layout->setSpacing(0); + main_layout->addWidget(title_bar); + main_layout->addWidget(tree); + + QObject::connect(filter_edit, &QLineEdit::textEdited, model, &SignalModel::setFilter); + QObject::connect(collapse_btn, &QPushButton::clicked, tree, &QTreeView::collapseAll); + QObject::connect(tree, &QAbstractItemView::clicked, this, &SignalView::rowClicked); + QObject::connect(tree, &QTreeView::viewportEntered, [this]() { emit highlight(nullptr); }); + QObject::connect(tree, &QTreeView::entered, [this](const QModelIndex &index) { emit highlight(model->getItem(index)->sig); }); + QObject::connect(model, &QAbstractItemModel::modelReset, this, &SignalView::rowsChanged); + QObject::connect(model, &QAbstractItemModel::rowsInserted, this, &SignalView::rowsChanged); + QObject::connect(model, &QAbstractItemModel::rowsRemoved, this, &SignalView::rowsChanged); + QObject::connect(dbc(), &DBCManager::signalAdded, [this](uint32_t address, const Signal *sig) { expandSignal(sig); }); +} + +void SignalView::setMessage(const QString &id) { + msg_id = id; + filter_edit->clear(); + model->setMessage(id); +} + +void SignalView::rowsChanged() { + auto create_btn = [](const QString &id, const QString &tooltip) { + auto btn = new QToolButton(); + btn->setIcon(bootstrapPixmap(id)); + btn->setToolTip(tooltip); + btn->setAutoRaise(true); + return btn; + }; + + signal_count_lb->setText(tr("Signals: %1").arg(model->rowCount())); + + for (int i = 0; i < model->rowCount(); ++i) { + auto index = model->index(i, 1); + if (!tree->indexWidget(index)) { + QWidget *w = new QWidget(this); + QHBoxLayout *h = new QHBoxLayout(w); + h->setContentsMargins(0, 2, 0, 2); + h->addStretch(1); + + auto remove_btn = create_btn("x", tr("Remove signal")); + auto plot_btn = create_btn("graph-up", ""); + plot_btn->setCheckable(true); + h->addWidget(plot_btn); + h->addWidget(remove_btn); + + tree->setIndexWidget(index, w); + auto sig = model->getItem(index)->sig; + QObject::connect(remove_btn, &QToolButton::clicked, [=]() { model->removeSignal(sig); }); + QObject::connect(plot_btn, &QToolButton::clicked, [=](bool checked) { + emit showChart(msg_id, sig, checked, QGuiApplication::keyboardModifiers() & Qt::ShiftModifier); + }); + } + } + updateChartState(); +} + +void SignalView::rowClicked(const QModelIndex &index) { + auto item = model->getItem(index); + if (item->type == SignalModel::Item::Sig) { + auto sig_index = model->index(index.row(), 0, index.parent()); + tree->setExpanded(sig_index, !tree->isExpanded(sig_index)); + } else if (item->type == SignalModel::Item::ExtraInfo) { + model->showExtraInfo(index); + } +} + +void SignalView::expandSignal(const Signal *sig) { + if (int row = model->signalRow(sig); row != -1) { + auto idx = model->index(row, 0); + bool expand = !tree->isExpanded(idx); + tree->setExpanded(idx, expand); + tree->scrollTo(idx, QAbstractItemView::PositionAtTop); + if (expand) tree->setCurrentIndex(idx); + } +} + +void SignalView::updateChartState() { + int i = 0; + for (auto item : model->root->children) { + auto plot_btn = tree->indexWidget(model->index(i, 1))->findChildren()[0]; + bool chart_opened = charts->hasSignal(msg_id, item->sig); + plot_btn->setChecked(chart_opened); + plot_btn->setToolTip(chart_opened ? tr("Close Plot") : tr("Show Plot\nSHIFT click to add to previous opened plot")); + ++i; + } +} + +void SignalView::signalHovered(const Signal *sig) { + auto &children = model->root->children; + for (int i = 0; i < children.size(); ++i) { + bool highlight = children[i]->sig == sig; + if (std::exchange(children[i]->highlight, highlight) != highlight) { + emit model->dataChanged(model->index(i, 0), model->index(i, 0)); + } + } } diff --git a/tools/cabana/signaledit.h b/tools/cabana/signaledit.h index 90df3fd3f4..ae88a5b13a 100644 --- a/tools/cabana/signaledit.h +++ b/tools/cabana/signaledit.h @@ -1,59 +1,97 @@ #pragma once -#include +#include #include #include -#include -#include +#include +#include -#include "selfdrive/ui/qt/widgets/controls.h" +#include "tools/cabana/chartswidget.h" #include "tools/cabana/dbcmanager.h" #include "tools/cabana/streams/abstractstream.h" -class SignalForm : public QWidget { +class SignalModel : public QAbstractItemModel { Q_OBJECT public: - SignalForm(QWidget *parent); - void textBoxEditingFinished(); + struct Item { + enum Type {Root, Sig, Name, Size, Endian, Signed, Offset, Factor, ExtraInfo, Unit, Comment, Min, Max, Desc }; + ~Item() { qDeleteAll(children); } + inline int row() { return parent->children.indexOf(this); } - QLineEdit *name, *unit, *comment, *val_desc, *offset, *factor, *min_val, *max_val; - QSpinBox *size; - QComboBox *sign, *endianness; - QToolButton *expand_btn; + Type type = Type::Root; + Item *parent = nullptr; + QList children; -signals: - void changed(); + const Signal *sig = nullptr; + QString title; + bool highlight = false; + bool extra_expanded = false; + QString sig_val = "-"; + }; + + SignalModel(QObject *parent); + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override { return 2; } + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex &index) const override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + void setMessage(const QString &id); + void setFilter(const QString &txt); + void addSignal(int start_bit, int size, bool little_endian); + bool saveSignal(const Signal *origin_s, Signal &s); + void resizeSignal(const Signal *sig, int start_bit, int size); + void removeSignal(const Signal *sig); + inline Item *getItem(const QModelIndex &index) const { return index.isValid() ? (Item *)index.internalPointer() : root.get(); } + int signalRow(const Signal *sig) const; + void showExtraInfo(const QModelIndex &index); + +private: + void insertItem(SignalModel::Item *parent_item, int pos, const Signal *sig); + void handleSignalAdded(uint32_t address, const Signal *sig); + void handleSignalUpdated(const Signal *sig); + void handleSignalRemoved(const Signal *sig); + void handleMsgChanged(uint32_t address); + void refresh(); + void updateState(const QHash *msgs); + + QString msg_id; + QString filter_str; + std::unique_ptr root; + friend class SignalView; +}; + +class SignalItemDelegate : public QStyledItemDelegate { +public: + SignalItemDelegate(QObject *parent); + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + QValidator *name_validator, *double_validator; }; -class SignalEdit : public QWidget { +class SignalView : public QWidget { Q_OBJECT public: - SignalEdit(int index, QWidget *parent = nullptr); - void setSignal(const QString &msg_id, const Signal *sig); - void setChartOpened(bool opened); + SignalView(ChartsWidget *charts, QWidget *parent); + void setMessage(const QString &id); void signalHovered(const Signal *sig); - void updateForm(bool show); - const Signal *sig = nullptr; - SignalForm *form = nullptr; - QString msg_id; + void updateChartState(); + void expandSignal(const Signal *sig); + void rowClicked(const QModelIndex &index); + SignalModel *model = nullptr; signals: void highlight(const Signal *sig); void showChart(const QString &name, const Signal *sig, bool show, bool merge); - void remove(const Signal *sig); - void save(const Signal *sig, const Signal &new_sig); - void showFormClicked(const Signal *sig); - -protected: - void enterEvent(QEvent *event) override; - void leaveEvent(QEvent *event) override; - void saveSignal(); - - ElidedLabel *title; - QLabel *color_label; - QLabel *icon; - int form_idx = 0; - QColor bg_color; - QToolButton *plot_btn; + +private: + void rowsChanged(); + + QString msg_id; + QTreeView *tree; + QLineEdit *filter_edit; + ChartsWidget *charts; + QLabel *signal_count_lb; }; From 9822f1b0b46c5d63290e3ef8b3f103d7aba13bd9 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 30 Jan 2023 11:20:52 +0800 Subject: [PATCH 225/484] cabana: fixed the column selector is always hidden if settings.chart_column_count is 1 (#27146) fix column selector --- tools/cabana/chartswidget.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 580cce5521..8dde735a57 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -229,7 +229,7 @@ void ChartsWidget::setColumnCount(int n) { } void ChartsWidget::updateLayout() { - int n = column_count; + int n = columns_cb->count(); for (; n > 1; --n) { if ((n * CHART_MIN_WIDTH + (n - 1) * charts_layout->spacing()) < charts_layout->geometry().width()) break; } @@ -238,6 +238,7 @@ void ChartsWidget::updateLayout() { columns_lb_action->setVisible(show_column_cb); columns_cb_action->setVisible(show_column_cb); + n = std::min(column_count, n); for (int i = 0; i < charts.size(); ++i) { charts_layout->addWidget(charts[charts.size() - i - 1], i / n, i % n); } From 078f06dbaff2d1af57f1294095cfbd9c6e357ce1 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 31 Jan 2023 02:56:03 +0800 Subject: [PATCH 226/484] cabana: draw message bytes at the same width (#27150) --- tools/cabana/util.cc | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tools/cabana/util.cc b/tools/cabana/util.cc index 726cbbe2a1..023e893e11 100644 --- a/tools/cabana/util.cc +++ b/tools/cabana/util.cc @@ -65,11 +65,6 @@ MessageBytesDelegate::MessageBytesDelegate(QObject *parent) : QStyledItemDelegat } void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { - QList colors = index.data(Qt::UserRole).toList(); - if (colors.empty()) { - QStyledItemDelegate::paint(painter, option, index); - return; - } QStyleOptionViewItemV4 opt = option; initStyleOption(&opt, index); @@ -87,6 +82,7 @@ void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem & int m = space.width() / 2; const QMargins margins(m, m, m, m); + QList colors = index.data(Qt::UserRole).toList(); int i = 0; for (auto &byte : opt.text.split(" ")) { if (i < colors.size()) { From afa85e0256a482f046fcb89495a06ef67b5d673f Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 31 Jan 2023 02:57:16 +0800 Subject: [PATCH 227/484] cabana: remove the margins of the legend item (#27152) --- tools/cabana/chartswidget.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 8dde735a57..378cc52bbb 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -305,6 +305,7 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { axis_y = new QValueAxis(this); chart->addAxis(axis_x, Qt::AlignBottom); chart->addAxis(axis_y, Qt::AlignLeft); + chart->legend()->layout()->setContentsMargins(0, 0, 40, 0); chart->legend()->setShowToolTips(true); chart->layout()->setContentsMargins(0, 0, 0, 0); chart->setMargins({20, 11, 11, 11}); From 3815afbde74b69ce1057ae0162364f19f5baee0a Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 31 Jan 2023 02:57:35 +0800 Subject: [PATCH 228/484] cabana: small improvements to MessageListModel::setFilterString (#27153) --- tools/cabana/messageswidget.cc | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index b7c3cd3401..0526ff141f 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -84,27 +84,22 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const { } void MessageListModel::setFilterString(const QString &string) { - filter_str = string; - msgs.clear(); - for (auto it = can->can_msgs.begin(); it != can->can_msgs.end(); ++it) { - bool found = false; - - // Search by message id or name - if (it.key().contains(filter_str, Qt::CaseInsensitive) || msgName(it.key()).contains(filter_str, Qt::CaseInsensitive)) { - found = true; - } - + auto contains = [](const QString &id, const QString &txt) { + auto cs = Qt::CaseInsensitive; + if (id.contains(txt, cs) || msgName(id).contains(txt, cs)) return true; // Search by signal name - const DBCMsg *msg = dbc()->msg(it.key()); - if (msg != nullptr) { - for (auto &signal: msg->getSignals()) { - if (QString::fromStdString(signal->name).contains(filter_str, Qt::CaseInsensitive)) { - found = true; - } + if (const auto msg = dbc()->msg(id)) { + for (auto &signal : msg->getSignals()) { + if (QString::fromStdString(signal->name).contains(txt, cs)) return true; } } + return false; + }; - if (found) { + filter_str = string; + msgs.clear(); + for (auto it = can->can_msgs.begin(); it != can->can_msgs.end(); ++it) { + if (filter_str.isEmpty() || contains(it.key(), filter_str)) { msgs.push_back(it.key()); } } From fba4827a7b9c6441cf85799770503229a361667f Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 31 Jan 2023 05:56:34 +0800 Subject: [PATCH 229/484] cabana: restrict the rubber band in plot area. (#27154) --- tools/cabana/chartswidget.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 378cc52bbb..2edc89d857 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -656,7 +656,16 @@ void ChartView::mouseMoveEvent(QMouseEvent *ev) { } else { QToolTip::hideText(); } + QChartView::mouseMoveEvent(ev); + if (is_zooming) { + QRect rubber_rect = rubber->geometry(); + rubber_rect.setLeft(std::max(rubber_rect.left(), (int)plot_area.left())); + rubber_rect.setRight(std::min(rubber_rect.right(), (int)plot_area.right())); + if (rubber_rect != rubber->geometry()) { + rubber->setGeometry(rubber_rect); + } + } } void ChartView::dragMoveEvent(QDragMoveEvent *event) { From e2f5b164bd3dcffc5a11ef552f83296c34991949 Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Mon, 30 Jan 2023 17:34:07 -0700 Subject: [PATCH 230/484] Ubloxd: glonass parsing tests (#27125) * add glonass kaitai parsing * add kaita generated files * remove glonass from build * add string non immediate type * fix kaitai bug * cleanUp * add patch file * fix scons order * make lookup const * remove comments * rename * add to release files * remove printf * add signs * add glonass parsing test * finish up glonass kaitai tests * move cereal back to master * ignore test_runner * sign is not two complement * address comments --------- Co-authored-by: Kurt Nistelberger --- .github/workflows/selfdrive_tests.yaml | 1 + release/files_common | 5 +- selfdrive/locationd/.gitignore | 1 + selfdrive/locationd/SConscript | 6 +- selfdrive/locationd/generated/glonass.cpp | 176 ++++++++- selfdrive/locationd/generated/glonass.h | 191 ++++++++-- selfdrive/locationd/glonass.ksy | 119 ++++-- selfdrive/locationd/glonass_fix.patch | 2 +- .../locationd/test/test_glonass_kaitai.cc | 360 ++++++++++++++++++ .../locationd/test/test_glonass_runner.cc | 2 + 10 files changed, 796 insertions(+), 67 deletions(-) create mode 100644 selfdrive/locationd/test/test_glonass_kaitai.cc create mode 100644 selfdrive/locationd/test/test_glonass_runner.cc diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index a4f7c68573..d5be221303 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -219,6 +219,7 @@ jobs: $UNIT_TEST selfdrive/car && \ $UNIT_TEST selfdrive/locationd && \ selfdrive/locationd/test/_test_locationd_lib.py && \ + ./selfdrive/locationd/test/test_glonass_runner && \ $UNIT_TEST selfdrive/athena && \ $UNIT_TEST selfdrive/thermald && \ $UNIT_TEST system/hardware/tici && \ diff --git a/release/files_common b/release/files_common index 547bb58144..5b310d5c80 100644 --- a/release/files_common +++ b/release/files_common @@ -225,10 +225,7 @@ selfdrive/locationd/SConscript selfdrive/locationd/ubloxd.cc selfdrive/locationd/ublox_msg.cc selfdrive/locationd/ublox_msg.h -selfdrive/locationd/generated/ubx.cpp -selfdrive/locationd/generated/ubx.h -selfdrive/locationd/generated/gps.cpp -selfdrive/locationd/generated/gps.h +selfdrive/locationd/generated/* selfdrive/locationd/laikad.py selfdrive/locationd/locationd.h diff --git a/selfdrive/locationd/.gitignore b/selfdrive/locationd/.gitignore index 5268902785..86a228a6ff 100644 --- a/selfdrive/locationd/.gitignore +++ b/selfdrive/locationd/.gitignore @@ -3,3 +3,4 @@ ubloxd_test params_learner paramsd locationd +test/test_glonass_runner diff --git a/selfdrive/locationd/SConscript b/selfdrive/locationd/SConscript index 7024196efd..61a0ed7f42 100644 --- a/selfdrive/locationd/SConscript +++ b/selfdrive/locationd/SConscript @@ -13,7 +13,8 @@ if GetOption('kaitai'): patch = env.Command(None, 'glonass_fix.patch', 'git apply $SOURCES') env.Depends(patch, glonass) -env.Program("ubloxd", ["ubloxd.cc", "ublox_msg.cc", "generated/ubx.cpp", "generated/gps.cpp"], LIBS=loc_libs) +glonass_obj = env.Object('generated/glonass.cpp') +env.Program("ubloxd", ["ubloxd.cc", "ublox_msg.cc", "generated/ubx.cpp", "generated/gps.cpp", glonass_obj], LIBS=loc_libs) ekf_sym_cc = env.SharedObject("#rednose/helpers/ekf_sym.cc") locationd_sources = ["locationd.cc", "models/live_kf.cc", ekf_sym_cc] @@ -25,3 +26,6 @@ lenv.Depends(locationd, libkf) if File("liblocationd.cc").exists(): liblocationd = lenv.SharedLibrary("liblocationd", ["liblocationd.cc"] + locationd_sources, LIBS=loc_libs + transformations) lenv.Depends(liblocationd, libkf) + +if GetOption('test'): + env.Program("test/test_glonass_runner", ['test/test_glonass_runner.cc', 'test/test_glonass_kaitai.cc', glonass_obj], LIBS=[loc_libs]) \ No newline at end of file diff --git a/selfdrive/locationd/generated/glonass.cpp b/selfdrive/locationd/generated/glonass.cpp index 149134fbb5..cd0f96ab68 100644 --- a/selfdrive/locationd/generated/glonass.cpp +++ b/selfdrive/locationd/generated/glonass.cpp @@ -31,6 +31,10 @@ void glonass_t::_read() { m_data = new string_3_t(m__io, this, m__root); break; } + case 5: { + m_data = new string_5_t(m__io, this, m__root); + break; + } case 2: { m_data = new string_2_t(m__io, this, m__root); break; @@ -60,6 +64,8 @@ void glonass_t::_clean_up() { glonass_t::string_4_t::string_4_t(kaitai::kstream* p__io, glonass_t* p__parent, glonass_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; + f_tau_n = false; + f_delta_tau_n = false; try { _read(); @@ -70,8 +76,10 @@ glonass_t::string_4_t::string_4_t(kaitai::kstream* p__io, glonass_t* p__parent, } void glonass_t::string_4_t::_read() { - m_tau_n = m__io->read_bits_int_be(22); - m_delta_tau_n = m__io->read_bits_int_be(5); + m_tau_n_sign = m__io->read_bits_int_be(1); + m_tau_n_value = m__io->read_bits_int_be(21); + m_delta_tau_n_sign = m__io->read_bits_int_be(1); + m_delta_tau_n_value = m__io->read_bits_int_be(4); m_e_n = m__io->read_bits_int_be(5); m_not_used_1 = m__io->read_bits_int_be(14); m_p4 = m__io->read_bits_int_be(1); @@ -89,6 +97,22 @@ glonass_t::string_4_t::~string_4_t() { void glonass_t::string_4_t::_clean_up() { } +int32_t glonass_t::string_4_t::tau_n() { + if (f_tau_n) + return m_tau_n; + m_tau_n = ((tau_n_sign()) ? ((tau_n_value() * -1)) : (tau_n_value())); + f_tau_n = true; + return m_tau_n; +} + +int32_t glonass_t::string_4_t::delta_tau_n() { + if (f_delta_tau_n) + return m_delta_tau_n; + m_delta_tau_n = ((delta_tau_n_sign()) ? ((delta_tau_n_value() * -1)) : (delta_tau_n_value())); + f_delta_tau_n = true; + return m_delta_tau_n; +} + glonass_t::string_non_immediate_t::string_non_immediate_t(kaitai::kstream* p__io, glonass_t* p__parent, glonass_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; @@ -113,9 +137,40 @@ glonass_t::string_non_immediate_t::~string_non_immediate_t() { void glonass_t::string_non_immediate_t::_clean_up() { } +glonass_t::string_5_t::string_5_t(kaitai::kstream* p__io, glonass_t* p__parent, glonass_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + + try { + _read(); + } catch(...) { + _clean_up(); + throw; + } +} + +void glonass_t::string_5_t::_read() { + m_n_a = m__io->read_bits_int_be(11); + m_tau_c = m__io->read_bits_int_be(32); + m_not_used = m__io->read_bits_int_be(1); + m_n_4 = m__io->read_bits_int_be(5); + m_tau_gps = m__io->read_bits_int_be(22); + m_l_n = m__io->read_bits_int_be(1); +} + +glonass_t::string_5_t::~string_5_t() { + _clean_up(); +} + +void glonass_t::string_5_t::_clean_up() { +} + glonass_t::string_1_t::string_1_t(kaitai::kstream* p__io, glonass_t* p__parent, glonass_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; + f_x_vel = false; + f_x_accel = false; + f_x = false; try { _read(); @@ -129,9 +184,12 @@ void glonass_t::string_1_t::_read() { m_not_used = m__io->read_bits_int_be(2); m_p1 = m__io->read_bits_int_be(2); m_t_k = m__io->read_bits_int_be(12); - m_x_vel = m__io->read_bits_int_be(24); - m_x_speedup = m__io->read_bits_int_be(5); - m_x = m__io->read_bits_int_be(27); + m_x_vel_sign = m__io->read_bits_int_be(1); + m_x_vel_value = m__io->read_bits_int_be(23); + m_x_accel_sign = m__io->read_bits_int_be(1); + m_x_accel_value = m__io->read_bits_int_be(4); + m_x_sign = m__io->read_bits_int_be(1); + m_x_value = m__io->read_bits_int_be(26); } glonass_t::string_1_t::~string_1_t() { @@ -141,9 +199,36 @@ glonass_t::string_1_t::~string_1_t() { void glonass_t::string_1_t::_clean_up() { } +int32_t glonass_t::string_1_t::x_vel() { + if (f_x_vel) + return m_x_vel; + m_x_vel = ((x_vel_sign()) ? ((x_vel_value() * -1)) : (x_vel_value())); + f_x_vel = true; + return m_x_vel; +} + +int32_t glonass_t::string_1_t::x_accel() { + if (f_x_accel) + return m_x_accel; + m_x_accel = ((x_accel_sign()) ? ((x_accel_value() * -1)) : (x_accel_value())); + f_x_accel = true; + return m_x_accel; +} + +int32_t glonass_t::string_1_t::x() { + if (f_x) + return m_x; + m_x = ((x_sign()) ? ((x_value() * -1)) : (x_value())); + f_x = true; + return m_x; +} + glonass_t::string_2_t::string_2_t(kaitai::kstream* p__io, glonass_t* p__parent, glonass_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; + f_y_vel = false; + f_y_accel = false; + f_y = false; try { _read(); @@ -158,9 +243,12 @@ void glonass_t::string_2_t::_read() { m_p2 = m__io->read_bits_int_be(1); m_t_b = m__io->read_bits_int_be(7); m_not_used = m__io->read_bits_int_be(5); - m_y_vel = m__io->read_bits_int_be(24); - m_y_speedup = m__io->read_bits_int_be(5); - m_y = m__io->read_bits_int_be(27); + m_y_vel_sign = m__io->read_bits_int_be(1); + m_y_vel_value = m__io->read_bits_int_be(23); + m_y_accel_sign = m__io->read_bits_int_be(1); + m_y_accel_value = m__io->read_bits_int_be(4); + m_y_sign = m__io->read_bits_int_be(1); + m_y_value = m__io->read_bits_int_be(26); } glonass_t::string_2_t::~string_2_t() { @@ -170,9 +258,37 @@ glonass_t::string_2_t::~string_2_t() { void glonass_t::string_2_t::_clean_up() { } +int32_t glonass_t::string_2_t::y_vel() { + if (f_y_vel) + return m_y_vel; + m_y_vel = ((y_vel_sign()) ? ((y_vel_value() * -1)) : (y_vel_value())); + f_y_vel = true; + return m_y_vel; +} + +int32_t glonass_t::string_2_t::y_accel() { + if (f_y_accel) + return m_y_accel; + m_y_accel = ((y_accel_sign()) ? ((y_accel_value() * -1)) : (y_accel_value())); + f_y_accel = true; + return m_y_accel; +} + +int32_t glonass_t::string_2_t::y() { + if (f_y) + return m_y; + m_y = ((y_sign()) ? ((y_value() * -1)) : (y_value())); + f_y = true; + return m_y; +} + glonass_t::string_3_t::string_3_t(kaitai::kstream* p__io, glonass_t* p__parent, glonass_t* p__root) : kaitai::kstruct(p__io) { m__parent = p__parent; m__root = p__root; + f_gamma_n = false; + f_z_vel = false; + f_z_accel = false; + f_z = false; try { _read(); @@ -184,13 +300,17 @@ glonass_t::string_3_t::string_3_t(kaitai::kstream* p__io, glonass_t* p__parent, void glonass_t::string_3_t::_read() { m_p3 = m__io->read_bits_int_be(1); - m_gamma_n = m__io->read_bits_int_be(11); + m_gamma_n_sign = m__io->read_bits_int_be(1); + m_gamma_n_value = m__io->read_bits_int_be(10); m_not_used = m__io->read_bits_int_be(1); m_p = m__io->read_bits_int_be(2); m_l_n = m__io->read_bits_int_be(1); - m_z_vel = m__io->read_bits_int_be(24); - m_z_speedup = m__io->read_bits_int_be(5); - m_z = m__io->read_bits_int_be(27); + m_z_vel_sign = m__io->read_bits_int_be(1); + m_z_vel_value = m__io->read_bits_int_be(23); + m_z_accel_sign = m__io->read_bits_int_be(1); + m_z_accel_value = m__io->read_bits_int_be(4); + m_z_sign = m__io->read_bits_int_be(1); + m_z_value = m__io->read_bits_int_be(26); } glonass_t::string_3_t::~string_3_t() { @@ -199,3 +319,35 @@ glonass_t::string_3_t::~string_3_t() { void glonass_t::string_3_t::_clean_up() { } + +int32_t glonass_t::string_3_t::gamma_n() { + if (f_gamma_n) + return m_gamma_n; + m_gamma_n = ((gamma_n_sign()) ? ((gamma_n_value() * -1)) : (gamma_n_value())); + f_gamma_n = true; + return m_gamma_n; +} + +int32_t glonass_t::string_3_t::z_vel() { + if (f_z_vel) + return m_z_vel; + m_z_vel = ((z_vel_sign()) ? ((z_vel_value() * -1)) : (z_vel_value())); + f_z_vel = true; + return m_z_vel; +} + +int32_t glonass_t::string_3_t::z_accel() { + if (f_z_accel) + return m_z_accel; + m_z_accel = ((z_accel_sign()) ? ((z_accel_value() * -1)) : (z_accel_value())); + f_z_accel = true; + return m_z_accel; +} + +int32_t glonass_t::string_3_t::z() { + if (f_z) + return m_z; + m_z = ((z_sign()) ? ((z_value() * -1)) : (z_value())); + f_z = true; + return m_z; +} diff --git a/selfdrive/locationd/generated/glonass.h b/selfdrive/locationd/generated/glonass.h index 48bdabfe96..19867ba22b 100644 --- a/selfdrive/locationd/generated/glonass.h +++ b/selfdrive/locationd/generated/glonass.h @@ -15,6 +15,7 @@ class glonass_t : public kaitai::kstruct { public: class string_4_t; class string_non_immediate_t; + class string_5_t; class string_1_t; class string_2_t; class string_3_t; @@ -42,8 +43,24 @@ public: ~string_4_t(); private: - uint64_t m_tau_n; - uint64_t m_delta_tau_n; + bool f_tau_n; + int32_t m_tau_n; + + public: + int32_t tau_n(); + + private: + bool f_delta_tau_n; + int32_t m_delta_tau_n; + + public: + int32_t delta_tau_n(); + + private: + bool m_tau_n_sign; + uint64_t m_tau_n_value; + bool m_delta_tau_n_sign; + uint64_t m_delta_tau_n_value; uint64_t m_e_n; uint64_t m_not_used_1; bool m_p4; @@ -56,8 +73,10 @@ public: glonass_t* m__parent; public: - uint64_t tau_n() const { return m_tau_n; } - uint64_t delta_tau_n() const { return m_delta_tau_n; } + bool tau_n_sign() const { return m_tau_n_sign; } + uint64_t tau_n_value() const { return m_tau_n_value; } + bool delta_tau_n_sign() const { return m_delta_tau_n_sign; } + uint64_t delta_tau_n_value() const { return m_delta_tau_n_value; } uint64_t e_n() const { return m_e_n; } uint64_t not_used_1() const { return m_not_used_1; } bool p4() const { return m_p4; } @@ -96,6 +115,40 @@ public: glonass_t* _parent() const { return m__parent; } }; + class string_5_t : public kaitai::kstruct { + + public: + + string_5_t(kaitai::kstream* p__io, glonass_t* p__parent = 0, glonass_t* p__root = 0); + + private: + void _read(); + void _clean_up(); + + public: + ~string_5_t(); + + private: + uint64_t m_n_a; + uint64_t m_tau_c; + bool m_not_used; + uint64_t m_n_4; + uint64_t m_tau_gps; + bool m_l_n; + glonass_t* m__root; + glonass_t* m__parent; + + public: + uint64_t n_a() const { return m_n_a; } + uint64_t tau_c() const { return m_tau_c; } + bool not_used() const { return m_not_used; } + uint64_t n_4() const { return m_n_4; } + uint64_t tau_gps() const { return m_tau_gps; } + bool l_n() const { return m_l_n; } + glonass_t* _root() const { return m__root; } + glonass_t* _parent() const { return m__parent; } + }; + class string_1_t : public kaitai::kstruct { public: @@ -109,13 +162,37 @@ public: public: ~string_1_t(); + private: + bool f_x_vel; + int32_t m_x_vel; + + public: + int32_t x_vel(); + + private: + bool f_x_accel; + int32_t m_x_accel; + + public: + int32_t x_accel(); + + private: + bool f_x; + int32_t m_x; + + public: + int32_t x(); + private: uint64_t m_not_used; uint64_t m_p1; uint64_t m_t_k; - uint64_t m_x_vel; - uint64_t m_x_speedup; - uint64_t m_x; + bool m_x_vel_sign; + uint64_t m_x_vel_value; + bool m_x_accel_sign; + uint64_t m_x_accel_value; + bool m_x_sign; + uint64_t m_x_value; glonass_t* m__root; glonass_t* m__parent; @@ -123,9 +200,12 @@ public: uint64_t not_used() const { return m_not_used; } uint64_t p1() const { return m_p1; } uint64_t t_k() const { return m_t_k; } - uint64_t x_vel() const { return m_x_vel; } - uint64_t x_speedup() const { return m_x_speedup; } - uint64_t x() const { return m_x; } + bool x_vel_sign() const { return m_x_vel_sign; } + uint64_t x_vel_value() const { return m_x_vel_value; } + bool x_accel_sign() const { return m_x_accel_sign; } + uint64_t x_accel_value() const { return m_x_accel_value; } + bool x_sign() const { return m_x_sign; } + uint64_t x_value() const { return m_x_value; } glonass_t* _root() const { return m__root; } glonass_t* _parent() const { return m__parent; } }; @@ -143,14 +223,38 @@ public: public: ~string_2_t(); + private: + bool f_y_vel; + int32_t m_y_vel; + + public: + int32_t y_vel(); + + private: + bool f_y_accel; + int32_t m_y_accel; + + public: + int32_t y_accel(); + + private: + bool f_y; + int32_t m_y; + + public: + int32_t y(); + private: uint64_t m_b_n; bool m_p2; uint64_t m_t_b; uint64_t m_not_used; - uint64_t m_y_vel; - uint64_t m_y_speedup; - uint64_t m_y; + bool m_y_vel_sign; + uint64_t m_y_vel_value; + bool m_y_accel_sign; + uint64_t m_y_accel_value; + bool m_y_sign; + uint64_t m_y_value; glonass_t* m__root; glonass_t* m__parent; @@ -159,9 +263,12 @@ public: bool p2() const { return m_p2; } uint64_t t_b() const { return m_t_b; } uint64_t not_used() const { return m_not_used; } - uint64_t y_vel() const { return m_y_vel; } - uint64_t y_speedup() const { return m_y_speedup; } - uint64_t y() const { return m_y; } + bool y_vel_sign() const { return m_y_vel_sign; } + uint64_t y_vel_value() const { return m_y_vel_value; } + bool y_accel_sign() const { return m_y_accel_sign; } + uint64_t y_accel_value() const { return m_y_accel_value; } + bool y_sign() const { return m_y_sign; } + uint64_t y_value() const { return m_y_value; } glonass_t* _root() const { return m__root; } glonass_t* _parent() const { return m__parent; } }; @@ -179,27 +286,63 @@ public: public: ~string_3_t(); + private: + bool f_gamma_n; + int32_t m_gamma_n; + + public: + int32_t gamma_n(); + + private: + bool f_z_vel; + int32_t m_z_vel; + + public: + int32_t z_vel(); + + private: + bool f_z_accel; + int32_t m_z_accel; + + public: + int32_t z_accel(); + + private: + bool f_z; + int32_t m_z; + + public: + int32_t z(); + private: bool m_p3; - uint64_t m_gamma_n; + bool m_gamma_n_sign; + uint64_t m_gamma_n_value; bool m_not_used; uint64_t m_p; bool m_l_n; - uint64_t m_z_vel; - uint64_t m_z_speedup; - uint64_t m_z; + bool m_z_vel_sign; + uint64_t m_z_vel_value; + bool m_z_accel_sign; + uint64_t m_z_accel_value; + bool m_z_sign; + uint64_t m_z_value; glonass_t* m__root; glonass_t* m__parent; public: bool p3() const { return m_p3; } - uint64_t gamma_n() const { return m_gamma_n; } + bool gamma_n_sign() const { return m_gamma_n_sign; } + uint64_t gamma_n_value() const { return m_gamma_n_value; } bool not_used() const { return m_not_used; } uint64_t p() const { return m_p; } bool l_n() const { return m_l_n; } - uint64_t z_vel() const { return m_z_vel; } - uint64_t z_speedup() const { return m_z_speedup; } - uint64_t z() const { return m_z; } + bool z_vel_sign() const { return m_z_vel_sign; } + uint64_t z_vel_value() const { return m_z_vel_value; } + bool z_accel_sign() const { return m_z_accel_sign; } + uint64_t z_accel_value() const { return m_z_accel_value; } + bool z_sign() const { return m_z_sign; } + uint64_t z_value() const { return m_z_value; } glonass_t* _root() const { return m__root; } glonass_t* _parent() const { return m__parent; } }; diff --git a/selfdrive/locationd/glonass.ksy b/selfdrive/locationd/glonass.ksy index 95ca78c16f..be99f6e497 100644 --- a/selfdrive/locationd/glonass.ksy +++ b/selfdrive/locationd/glonass.ksy @@ -1,4 +1,6 @@ # http://gauss.gge.unb.ca/GLONASS.ICD.pdf +# some variables are misprinted but good in the old doc +# https://www.unavco.org/help/glossary/docs/ICD_GLONASS_4.0_(1998)_en.pdf meta: id: glonass endian: be @@ -16,6 +18,7 @@ seq: 2: string_2 3: string_3 4: string_4 + 5: string_5 _: string_non_immediate - id: hamming_code type: b8 @@ -37,12 +40,25 @@ types: type: b2 - id: t_k type: b12 - - id: x_vel - type: b24 - - id: x_speedup - type: b5 - - id: x - type: b27 + - id: x_vel_sign + type: b1 + - id: x_vel_value + type: b23 + - id: x_accel_sign + type: b1 + - id: x_accel_value + type: b4 + - id: x_sign + type: b1 + - id: x_value + type: b26 + instances: + x_vel: + value: 'x_vel_sign ? (x_vel_value * (-1)) : x_vel_value' + x_accel: + value: 'x_accel_sign ? (x_accel_value * (-1)) : x_accel_value' + x: + value: 'x_sign ? (x_value * (-1)) : x_value' string_2: seq: - id: b_n @@ -53,36 +69,70 @@ types: type: b7 - id: not_used type: b5 - - id: y_vel - type: b24 - - id: y_speedup - type: b5 - - id: y - type: b27 + - id: y_vel_sign + type: b1 + - id: y_vel_value + type: b23 + - id: y_accel_sign + type: b1 + - id: y_accel_value + type: b4 + - id: y_sign + type: b1 + - id: y_value + type: b26 + instances: + y_vel: + value: 'y_vel_sign ? (y_vel_value * (-1)) : y_vel_value' + y_accel: + value: 'y_accel_sign ? (y_accel_value * (-1)) : y_accel_value' + y: + value: 'y_sign ? (y_value * (-1)) : y_value' string_3: seq: - id: p3 type: b1 - - id: gamma_n - type: b11 + - id: gamma_n_sign + type: b1 + - id: gamma_n_value + type: b10 - id: not_used type: b1 - id: p type: b2 - id: l_n type: b1 - - id: z_vel - type: b24 - - id: z_speedup - type: b5 - - id: z - type: b27 + - id: z_vel_sign + type: b1 + - id: z_vel_value + type: b23 + - id: z_accel_sign + type: b1 + - id: z_accel_value + type: b4 + - id: z_sign + type: b1 + - id: z_value + type: b26 + instances: + gamma_n: + value: 'gamma_n_sign ? (gamma_n_value * (-1)) : gamma_n_value' + z_vel: + value: 'z_vel_sign ? (z_vel_value * (-1)) : z_vel_value' + z_accel: + value: 'z_accel_sign ? (z_accel_value * (-1)) : z_accel_value' + z: + value: 'z_sign ? (z_value * (-1)) : z_value' string_4: seq: - - id: tau_n - type: b22 - - id: delta_tau_n - type: b5 + - id: tau_n_sign + type: b1 + - id: tau_n_value + type: b21 + - id: delta_tau_n_sign + type: b1 + - id: delta_tau_n_value + type: b4 - id: e_n type: b5 - id: not_used_1 @@ -99,9 +149,28 @@ types: type: b5 - id: m type: b2 + instances: + tau_n: + value: 'tau_n_sign ? (tau_n_value * (-1)) : tau_n_value' + delta_tau_n: + value: 'delta_tau_n_sign ? (delta_tau_n_value * (-1)) : delta_tau_n_value' + string_5: + seq: + - id: n_a + type: b11 + - id: tau_c + type: b32 + - id: not_used + type: b1 + - id: n_4 + type: b5 + - id: tau_gps + type: b22 + - id: l_n + type: b1 string_non_immediate: seq: - id: data_1 type: b64 - id: data_2 - type: b8 \ No newline at end of file + type: b8 diff --git a/selfdrive/locationd/glonass_fix.patch b/selfdrive/locationd/glonass_fix.patch index 2f99e93637..fa34a8ef15 100644 --- a/selfdrive/locationd/glonass_fix.patch +++ b/selfdrive/locationd/glonass_fix.patch @@ -1,5 +1,5 @@ diff --git a/selfdrive/locationd/generated/glonass.cpp b/selfdrive/locationd/generated/glonass.cpp -index 6a48fe62c..149134fbb 100644 +index 5b17bc327..b5c6aa610 100644 --- a/selfdrive/locationd/generated/glonass.cpp +++ b/selfdrive/locationd/generated/glonass.cpp @@ -17,7 +17,7 @@ glonass_t::glonass_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, glonass diff --git a/selfdrive/locationd/test/test_glonass_kaitai.cc b/selfdrive/locationd/test/test_glonass_kaitai.cc new file mode 100644 index 0000000000..22f5202a3d --- /dev/null +++ b/selfdrive/locationd/test/test_glonass_kaitai.cc @@ -0,0 +1,360 @@ +#include +#include +#include +#include +#include +#include + +#include "catch2/catch.hpp" +#include "selfdrive/locationd/generated/glonass.h" + +typedef std::vector> string_data; + +#define IDLE_CHIP_IDX 0 +#define STRING_NUMBER_IDX 1 +// string data 1-5 +#define HC_IDX 0 +#define PAD1_IDX 1 +#define SUPERFRAME_IDX 2 +#define PAD2_IDX 3 +#define FRAME_IDX 4 + +// Indexes for string number 1 +#define ST1_NU_IDX 2 +#define ST1_P1_IDX 3 +#define ST1_T_K_IDX 4 +#define ST1_X_VEL_S_IDX 5 +#define ST1_X_VEL_V_IDX 6 +#define ST1_X_ACCEL_S_IDX 7 +#define ST1_X_ACCEL_V_IDX 8 +#define ST1_X_S_IDX 9 +#define ST1_X_V_IDX 10 +#define ST1_HC_OFF 11 + +// Indexes for string number 2 +#define ST2_BN_IDX 2 +#define ST2_P2_IDX 3 +#define ST2_TB_IDX 4 +#define ST2_NU_IDX 5 +#define ST2_Y_VEL_S_IDX 6 +#define ST2_Y_VEL_V_IDX 7 +#define ST2_Y_ACCEL_S_IDX 8 +#define ST2_Y_ACCEL_V_IDX 9 +#define ST2_Y_S_IDX 10 +#define ST2_Y_V_IDX 11 +#define ST2_HC_OFF 12 + +// Indexes for string number 3 +#define ST3_P3_IDX 2 +#define ST3_GAMMA_N_S_IDX 3 +#define ST3_GAMMA_N_V_IDX 4 +#define ST3_NU_1_IDX 5 +#define ST3_P_IDX 6 +#define ST3_L_N_IDX 7 +#define ST3_Z_VEL_S_IDX 8 +#define ST3_Z_VEL_V_IDX 9 +#define ST3_Z_ACCEL_S_IDX 10 +#define ST3_Z_ACCEL_V_IDX 11 +#define ST3_Z_S_IDX 12 +#define ST3_Z_V_IDX 13 +#define ST3_HC_OFF 14 + +// Indexes for string number 4 +#define ST4_TAU_N_S_IDX 2 +#define ST4_TAU_N_V_IDX 3 +#define ST4_DELTA_TAU_N_S_IDX 4 +#define ST4_DELTA_TAU_N_V_IDX 5 +#define ST4_E_N_IDX 6 +#define ST4_NU_1_IDX 7 +#define ST4_P4_IDX 8 +#define ST4_F_T_IDX 9 +#define ST4_NU_2_IDX 10 +#define ST4_N_T_IDX 11 +#define ST4_N_IDX 12 +#define ST4_M_IDX 13 +#define ST4_HC_OFF 14 + +// Indexes for string number 5 +#define ST5_N_A_IDX 2 +#define ST5_TAU_C_IDX 3 +#define ST5_NU_IDX 4 +#define ST5_N_4_IDX 5 +#define ST5_TAU_GPS_IDX 6 +#define ST5_L_N_IDX 7 +#define ST5_HC_OFF 8 + +// Indexes for non immediate +#define ST6_DATA_1_IDX 2 +#define ST6_DATA_2_IDX 3 +#define ST6_HC_OFF 4 + + +std::string generate_inp_data(string_data& data) { + std::string inp_data = ""; + for (auto& [b, v] : data) { + std::string tmp = std::bitset<64>(v).to_string(); + inp_data += tmp.substr(64-b, b); + } + assert(inp_data.size() == 128); + + std::string string_data; + string_data.reserve(16); + for (int i = 0; i < 128; i+=8) { + std::string substr = inp_data.substr(i, 8); + string_data.push_back( (uint8_t)std::stoi(substr.c_str(), 0, 2)); + } + + return string_data; +} + +string_data generate_string_data(uint8_t string_number) { + + srand((unsigned)time(0)); + string_data data; // + data.push_back({1, 0}); // idle chip + data.push_back({4, string_number}); // string number + + if (string_number == 1) { + data.push_back({2, 3}); // not_used + data.push_back({2, 1}); // p1 + data.push_back({12, 113}); // t_k + data.push_back({1, rand() & 1}); // x_vel_sign + data.push_back({23, 7122}); // x_vel_value + data.push_back({1, rand() & 1}); // x_accel_sign + data.push_back({4, 3}); // x_accel_value + data.push_back({1, rand() & 1}); // x_sign + data.push_back({26, 33554431}); // x_value + } else if (string_number == 2) { + data.push_back({3, 3}); // b_n + data.push_back({1, 1}); // p2 + data.push_back({7, 123}); // t_b + data.push_back({5, 31}); // not_used + data.push_back({1, rand() & 1}); // y_vel_sign + data.push_back({23, 7422}); // y_vel_value + data.push_back({1, rand() & 1}); // y_accel_sign + data.push_back({4, 3}); // y_accel_value + data.push_back({1, rand() & 1}); // y_sign + data.push_back({26, 67108863}); // y_value + } else if (string_number == 3) { + data.push_back({1, 0}); // p3 + data.push_back({1, 1}); // gamma_n_sign + data.push_back({10, 123}); // gamma_n_value + data.push_back({1, 0}); // not_used + data.push_back({2, 2}); // p + data.push_back({1, 1}); // l_n + data.push_back({1, rand() & 1}); // z_vel_sign + data.push_back({23, 1337}); // z_vel_value + data.push_back({1, rand() & 1}); // z_accel_sign + data.push_back({4, 9}); // z_accel_value + data.push_back({1, rand() & 1}); // z_sign + data.push_back({26, 100023}); // z_value + } else if (string_number == 4) { + data.push_back({1, rand() & 1}); // tau_n_sign + data.push_back({21, 197152}); // tau_n_value + data.push_back({1, rand() & 1}); // delta_tau_n_sign + data.push_back({4, 4}); // delta_tau_n_value + data.push_back({5, 0}); // e_n + data.push_back({14, 2}); // not_used_1 + data.push_back({1, 1}); // p4 + data.push_back({4, 9}); // f_t + data.push_back({3, 3}); // not_used_2 + data.push_back({11, 2047}); // n_t + data.push_back({5, 2}); // n + data.push_back({2, 1}); // m + } else if (string_number == 5) { + data.push_back({11, 2047}); // n_a + data.push_back({32, 4294767295}); // tau_c + data.push_back({1, 0}); // not_used_1 + data.push_back({5, 2}); // n_4 + data.push_back({22, 4114304}); // tau_gps + data.push_back({1, 0}); // l_n + } else { // non-immediate data is not parsed + data.push_back({64, rand()}); // data_1 + data.push_back({8, 6}); // data_2 + } + + data.push_back({8, rand() & 0xFF}); // hamming code + data.push_back({11, rand() & 0x7FF}); // pad + data.push_back({16, rand() & 0xFFFF}); // superframe + data.push_back({8, rand() & 0xFF}); // pad + data.push_back({8, rand() & 0xFF}); // frame + return data; +} + +TEST_CASE("parse_string_number_1"){ + string_data data = generate_string_data(1); + std::string inp_data = generate_inp_data(data); + + kaitai::kstream stream(inp_data); + glonass_t gl_string(&stream); + + REQUIRE(gl_string.idle_chip() == data[IDLE_CHIP_IDX].second); + REQUIRE(gl_string.string_number() == data[STRING_NUMBER_IDX].second); + REQUIRE(gl_string.hamming_code() == data[ST1_HC_OFF + HC_IDX].second); + REQUIRE(gl_string.pad_1() == data[ST1_HC_OFF + PAD1_IDX].second); + REQUIRE(gl_string.superframe_number() == data[ST1_HC_OFF + SUPERFRAME_IDX].second); + REQUIRE(gl_string.pad_2() == data[ST1_HC_OFF + PAD2_IDX].second); + REQUIRE(gl_string.frame_number() == data[ST1_HC_OFF + FRAME_IDX].second); + + kaitai::kstream str1(inp_data); + glonass_t str1_data(&str1); + glonass_t::string_1_t* s1 = static_cast(str1_data.data()); + + REQUIRE(s1->not_used() == data[ST1_NU_IDX].second); + REQUIRE(s1->p1() == data[ST1_P1_IDX].second); + REQUIRE(s1->t_k() == data[ST1_T_K_IDX].second); + + int mul = s1->x_vel_sign() ? (-1) : 1; + REQUIRE(s1->x_vel() == (data[ST1_X_VEL_V_IDX].second * mul)); + mul = s1->x_accel_sign() ? (-1) : 1; + REQUIRE(s1->x_accel() == (data[ST1_X_ACCEL_V_IDX].second * mul)); + mul = s1->x_sign() ? (-1) : 1; + REQUIRE(s1->x() == (data[ST1_X_V_IDX].second * mul)); +} + +TEST_CASE("parse_string_number_2"){ + string_data data = generate_string_data(2); + std::string inp_data = generate_inp_data(data); + + kaitai::kstream stream(inp_data); + glonass_t gl_string(&stream); + + REQUIRE(gl_string.idle_chip() == data[IDLE_CHIP_IDX].second); + REQUIRE(gl_string.string_number() == data[STRING_NUMBER_IDX].second); + REQUIRE(gl_string.hamming_code() == data[ST2_HC_OFF + HC_IDX].second); + REQUIRE(gl_string.pad_1() == data[ST2_HC_OFF + PAD1_IDX].second); + REQUIRE(gl_string.superframe_number() == data[ST2_HC_OFF + SUPERFRAME_IDX].second); + REQUIRE(gl_string.pad_2() == data[ST2_HC_OFF + PAD2_IDX].second); + REQUIRE(gl_string.frame_number() == data[ST2_HC_OFF + FRAME_IDX].second); + + kaitai::kstream str2(inp_data); + glonass_t str2_data(&str2); + glonass_t::string_2_t* s2 = static_cast(str2_data.data()); + + REQUIRE(s2->b_n() == data[ST2_BN_IDX].second); + REQUIRE(s2->not_used() == data[ST2_NU_IDX].second); + REQUIRE(s2->p2() == data[ST2_P2_IDX].second); + REQUIRE(s2->t_b() == data[ST2_TB_IDX].second); + int mul = s2->y_vel_sign() ? (-1) : 1; + REQUIRE(s2->y_vel() == (data[ST2_Y_VEL_V_IDX].second * mul)); + mul = s2->y_accel_sign() ? (-1) : 1; + REQUIRE(s2->y_accel() == (data[ST2_Y_ACCEL_V_IDX].second * mul)); + mul = s2->y_sign() ? (-1) : 1; + REQUIRE(s2->y() == (data[ST2_Y_V_IDX].second * mul)); +} + +TEST_CASE("parse_string_number_3"){ + string_data data = generate_string_data(3); + std::string inp_data = generate_inp_data(data); + + kaitai::kstream stream(inp_data); + glonass_t gl_string(&stream); + + REQUIRE(gl_string.idle_chip() == data[IDLE_CHIP_IDX].second); + REQUIRE(gl_string.string_number() == data[STRING_NUMBER_IDX].second); + REQUIRE(gl_string.hamming_code() == data[ST3_HC_OFF + HC_IDX].second); + REQUIRE(gl_string.pad_1() == data[ST3_HC_OFF + PAD1_IDX].second); + REQUIRE(gl_string.superframe_number() == data[ST3_HC_OFF + SUPERFRAME_IDX].second); + REQUIRE(gl_string.pad_2() == data[ST3_HC_OFF + PAD2_IDX].second); + REQUIRE(gl_string.frame_number() == data[ST3_HC_OFF + FRAME_IDX].second); + + kaitai::kstream str3(inp_data); + glonass_t str3_data(&str3); + glonass_t::string_3_t* s3 = static_cast(str3_data.data()); + + REQUIRE(s3->p3() == data[ST3_P3_IDX].second); + int mul = s3->gamma_n_sign() ? (-1) : 1; + REQUIRE(s3->gamma_n() == (data[ST3_GAMMA_N_V_IDX].second * mul)); + REQUIRE(s3->not_used() == data[ST3_NU_1_IDX].second); + REQUIRE(s3->p() == data[ST3_P_IDX].second); + REQUIRE(s3->l_n() == data[ST3_L_N_IDX].second); + mul = s3->z_vel_sign() ? (-1) : 1; + REQUIRE(s3->z_vel() == (data[ST3_Z_VEL_V_IDX].second * mul)); + mul = s3->z_accel_sign() ? (-1) : 1; + REQUIRE(s3->z_accel() == (data[ST3_Z_ACCEL_V_IDX].second * mul)); + mul = s3->z_sign() ? (-1) : 1; + REQUIRE(s3->z() == (data[ST3_Z_V_IDX].second * mul)); +} + +TEST_CASE("parse_string_number_4"){ + string_data data = generate_string_data(4); + std::string inp_data = generate_inp_data(data); + + kaitai::kstream stream(inp_data); + glonass_t gl_string(&stream); + + REQUIRE(gl_string.idle_chip() == data[IDLE_CHIP_IDX].second); + REQUIRE(gl_string.string_number() == data[STRING_NUMBER_IDX].second); + REQUIRE(gl_string.hamming_code() == data[ST4_HC_OFF + HC_IDX].second); + REQUIRE(gl_string.pad_1() == data[ST4_HC_OFF + PAD1_IDX].second); + REQUIRE(gl_string.superframe_number() == data[ST4_HC_OFF + SUPERFRAME_IDX].second); + REQUIRE(gl_string.pad_2() == data[ST4_HC_OFF + PAD2_IDX].second); + REQUIRE(gl_string.frame_number() == data[ST4_HC_OFF + FRAME_IDX].second); + + kaitai::kstream str4(inp_data); + glonass_t str4_data(&str4); + glonass_t::string_4_t* s4 = static_cast(str4_data.data()); + + int mul = s4->tau_n_sign() ? (-1) : 1; + REQUIRE(s4->tau_n() == (data[ST4_TAU_N_V_IDX].second * mul)); + mul = s4->delta_tau_n_sign() ? (-1) : 1; + REQUIRE(s4->delta_tau_n() == (data[ST4_DELTA_TAU_N_V_IDX].second * mul)); + REQUIRE(s4->e_n() == data[ST4_E_N_IDX].second); + REQUIRE(s4->not_used_1() == data[ST4_NU_1_IDX].second); + REQUIRE(s4->p4() == data[ST4_P4_IDX].second); + REQUIRE(s4->f_t() == data[ST4_F_T_IDX].second); + REQUIRE(s4->not_used_2() == data[ST4_NU_2_IDX].second); + REQUIRE(s4->n_t() == data[ST4_N_T_IDX].second); + REQUIRE(s4->n() == data[ST4_N_IDX].second); + REQUIRE(s4->m() == data[ST4_M_IDX].second); +} + +TEST_CASE("parse_string_number_5"){ + string_data data = generate_string_data(5); + std::string inp_data = generate_inp_data(data); + + kaitai::kstream stream(inp_data); + glonass_t gl_string(&stream); + + REQUIRE(gl_string.idle_chip() == data[IDLE_CHIP_IDX].second); + REQUIRE(gl_string.string_number() == data[STRING_NUMBER_IDX].second); + REQUIRE(gl_string.hamming_code() == data[ST5_HC_OFF + HC_IDX].second); + REQUIRE(gl_string.pad_1() == data[ST5_HC_OFF + PAD1_IDX].second); + REQUIRE(gl_string.superframe_number() == data[ST5_HC_OFF + SUPERFRAME_IDX].second); + REQUIRE(gl_string.pad_2() == data[ST5_HC_OFF + PAD2_IDX].second); + REQUIRE(gl_string.frame_number() == data[ST5_HC_OFF + FRAME_IDX].second); + + kaitai::kstream str5(inp_data); + glonass_t str5_data(&str5); + glonass_t::string_5_t* s5 = static_cast(str5_data.data()); + + REQUIRE(s5->n_a() == data[ST5_N_A_IDX].second); + REQUIRE(s5->tau_c() == data[ST5_TAU_C_IDX].second); + REQUIRE(s5->not_used() == data[ST5_NU_IDX].second); + REQUIRE(s5->n_4() == data[ST5_N_4_IDX].second); + REQUIRE(s5->tau_gps() == data[ST5_TAU_GPS_IDX].second); + REQUIRE(s5->l_n() == data[ST5_L_N_IDX].second); +} + +TEST_CASE("parse_string_number_NI"){ + string_data data = generate_string_data((rand() % 10) + 6); + std::string inp_data = generate_inp_data(data); + + kaitai::kstream stream(inp_data); + glonass_t gl_string(&stream); + + REQUIRE(gl_string.idle_chip() == data[IDLE_CHIP_IDX].second); + REQUIRE(gl_string.string_number() == data[STRING_NUMBER_IDX].second); + REQUIRE(gl_string.hamming_code() == data[ST6_HC_OFF + HC_IDX].second); + REQUIRE(gl_string.pad_1() == data[ST6_HC_OFF + PAD1_IDX].second); + REQUIRE(gl_string.superframe_number() == data[ST6_HC_OFF + SUPERFRAME_IDX].second); + REQUIRE(gl_string.pad_2() == data[ST6_HC_OFF + PAD2_IDX].second); + REQUIRE(gl_string.frame_number() == data[ST6_HC_OFF + FRAME_IDX].second); + + kaitai::kstream strni(inp_data); + glonass_t strni_data(&strni); + glonass_t::string_non_immediate_t* sni = static_cast(strni_data.data()); + + REQUIRE(sni->data_1() == data[ST6_DATA_1_IDX].second); + REQUIRE(sni->data_2() == data[ST6_DATA_2_IDX].second); +} diff --git a/selfdrive/locationd/test/test_glonass_runner.cc b/selfdrive/locationd/test/test_glonass_runner.cc new file mode 100644 index 0000000000..62bf7476a1 --- /dev/null +++ b/selfdrive/locationd/test/test_glonass_runner.cc @@ -0,0 +1,2 @@ +#define CATCH_CONFIG_MAIN +#include "catch2/catch.hpp" From 549e4d9636036af228d3d930d69e8dfd1f67dba6 Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Mon, 30 Jan 2023 17:40:37 -0700 Subject: [PATCH 231/484] Ubloxd: move gps parsing to function (#27122) move ublox gps parsing to function Co-authored-by: Kurt Nistelberger --- selfdrive/locationd/ublox_msg.cc | 210 ++++++++++++++++--------------- selfdrive/locationd/ublox_msg.h | 2 + 2 files changed, 110 insertions(+), 102 deletions(-) diff --git a/selfdrive/locationd/ublox_msg.cc b/selfdrive/locationd/ublox_msg.cc index 8bd9985170..4b31b0101e 100644 --- a/selfdrive/locationd/ublox_msg.cc +++ b/selfdrive/locationd/ublox_msg.cc @@ -147,123 +147,129 @@ kj::Array UbloxMsgParser::gen_nav_pvt(ubx_t::nav_pvt_t *msg) { return capnp::messageToFlatArray(msg_builder); } - -kj::Array UbloxMsgParser::gen_rxm_sfrbx(ubx_t::rxm_sfrbx_t *msg) { +kj::Array UbloxMsgParser::parse_gps_ephemeris(ubx_t::rxm_sfrbx_t *msg) { + // GPS subframes are packed into 10x 4 bytes, each containing 3 actual bytes + // We will first need to separate the data from the padding and parity auto body = *msg->body(); + assert(body.size() == 10); + + std::string subframe_data; + subframe_data.reserve(30); + for (uint32_t word : body) { + word = word >> 6; // TODO: Verify parity + subframe_data.push_back(word >> 16); + subframe_data.push_back(word >> 8); + subframe_data.push_back(word >> 0); + } - if (msg->gnss_id() == ubx_t::gnss_type_t::GNSS_TYPE_GPS) { - // GPS subframes are packed into 10x 4 bytes, each containing 3 actual bytes - // We will first need to separate the data from the padding and parity - assert(body.size() == 10); - - std::string subframe_data; - subframe_data.reserve(30); - for (uint32_t word : body) { - word = word >> 6; // TODO: Verify parity - subframe_data.push_back(word >> 16); - subframe_data.push_back(word >> 8); - subframe_data.push_back(word >> 0); + // Collect subframes in map and parse when we have all the parts + { + kaitai::kstream stream(subframe_data); + gps_t subframe(&stream); + int subframe_id = subframe.how()->subframe_id(); + int sv_id = msg->sv_id(); + uint64_t tow_counter = subframe.how()->tow_count(); + + bool clear_buffer = subframe_id == 1; + if (gps_sat_tow_count.count(sv_id) != 0) { + int64_t counter_diff = tow_counter - gps_sat_tow_count[sv_id]; + clear_buffer |= counter_diff != 1 && counter_diff != -100798; } + if (clear_buffer) gps_subframes[sv_id].clear(); - // Collect subframes in map and parse when we have all the parts + gps_subframes[sv_id][subframe_id] = subframe_data; + gps_sat_tow_count[sv_id] = tow_counter; + } + + if (gps_subframes[msg->sv_id()].size() == 5) { + MessageBuilder msg_builder; + auto eph = msg_builder.initEvent().initUbloxGnss().initEphemeris(); + eph.setSvId(msg->sv_id()); + + // Subframe 1 { - kaitai::kstream stream(subframe_data); + kaitai::kstream stream(gps_subframes[msg->sv_id()][1]); gps_t subframe(&stream); - int subframe_id = subframe.how()->subframe_id(); - int sv_id = msg->sv_id(); - uint64_t tow_counter = subframe.how()->tow_count(); - - bool clear_buffer = subframe_id == 1; - if (gps_sat_tow_count.count(sv_id) != 0) { - int64_t counter_diff = tow_counter - gps_sat_tow_count[sv_id]; - clear_buffer |= counter_diff != 1 && counter_diff != -100798; - } - if (clear_buffer) gps_subframes[sv_id].clear(); + gps_t::subframe_1_t* subframe_1 = static_cast(subframe.body()); + + eph.setGpsWeek(subframe_1->week_no()); + eph.setTgd(subframe_1->t_gd() * pow(2, -31)); + eph.setToc(subframe_1->t_oc() * pow(2, 4)); + eph.setAf2(subframe_1->af_2() * pow(2, -55)); + eph.setAf1(subframe_1->af_1() * pow(2, -43)); + eph.setAf0(subframe_1->af_0() * pow(2, -31)); + eph.setSvHealth(subframe_1->sv_health()); + } - gps_subframes[sv_id][subframe_id] = subframe_data; - gps_sat_tow_count[sv_id] = tow_counter; + // Subframe 2 + { + kaitai::kstream stream(gps_subframes[msg->sv_id()][2]); + gps_t subframe(&stream); + gps_t::subframe_2_t* subframe_2 = static_cast(subframe.body()); + + eph.setCrs(subframe_2->c_rs() * pow(2, -5)); + eph.setDeltaN(subframe_2->delta_n() * pow(2, -43) * gpsPi); + eph.setM0(subframe_2->m_0() * pow(2, -31) * gpsPi); + eph.setCuc(subframe_2->c_uc() * pow(2, -29)); + eph.setEcc(subframe_2->e() * pow(2, -33)); + eph.setCus(subframe_2->c_us() * pow(2, -29)); + eph.setA(pow(subframe_2->sqrt_a() * pow(2, -19), 2.0)); + eph.setToe(subframe_2->t_oe() * pow(2, 4)); } - if (gps_subframes[msg->sv_id()].size() == 5) { - MessageBuilder msg_builder; - auto eph = msg_builder.initEvent().initUbloxGnss().initEphemeris(); - eph.setSvId(msg->sv_id()); - - // Subframe 1 - { - kaitai::kstream stream(gps_subframes[msg->sv_id()][1]); - gps_t subframe(&stream); - gps_t::subframe_1_t* subframe_1 = static_cast(subframe.body()); - - eph.setGpsWeek(subframe_1->week_no()); - eph.setTgd(subframe_1->t_gd() * pow(2, -31)); - eph.setToc(subframe_1->t_oc() * pow(2, 4)); - eph.setAf2(subframe_1->af_2() * pow(2, -55)); - eph.setAf1(subframe_1->af_1() * pow(2, -43)); - eph.setAf0(subframe_1->af_0() * pow(2, -31)); - eph.setSvHealth(subframe_1->sv_health()); - } + // Subframe 3 + { + kaitai::kstream stream(gps_subframes[msg->sv_id()][3]); + gps_t subframe(&stream); + gps_t::subframe_3_t* subframe_3 = static_cast(subframe.body()); + + eph.setCic(subframe_3->c_ic() * pow(2, -29)); + eph.setOmega0(subframe_3->omega_0() * pow(2, -31) * gpsPi); + eph.setCis(subframe_3->c_is() * pow(2, -29)); + eph.setI0(subframe_3->i_0() * pow(2, -31) * gpsPi); + eph.setCrc(subframe_3->c_rc() * pow(2, -5)); + eph.setOmega(subframe_3->omega() * pow(2, -31) * gpsPi); + eph.setOmegaDot(subframe_3->omega_dot() * pow(2, -43) * gpsPi); + eph.setIode(subframe_3->iode()); + eph.setIDot(subframe_3->idot() * pow(2, -43) * gpsPi); + } - // Subframe 2 - { - kaitai::kstream stream(gps_subframes[msg->sv_id()][2]); - gps_t subframe(&stream); - gps_t::subframe_2_t* subframe_2 = static_cast(subframe.body()); - - eph.setCrs(subframe_2->c_rs() * pow(2, -5)); - eph.setDeltaN(subframe_2->delta_n() * pow(2, -43) * gpsPi); - eph.setM0(subframe_2->m_0() * pow(2, -31) * gpsPi); - eph.setCuc(subframe_2->c_uc() * pow(2, -29)); - eph.setEcc(subframe_2->e() * pow(2, -33)); - eph.setCus(subframe_2->c_us() * pow(2, -29)); - eph.setA(pow(subframe_2->sqrt_a() * pow(2, -19), 2.0)); - eph.setToe(subframe_2->t_oe() * pow(2, 4)); + // Subframe 4 + { + kaitai::kstream stream(gps_subframes[msg->sv_id()][4]); + gps_t subframe(&stream); + gps_t::subframe_4_t* subframe_4 = static_cast(subframe.body()); + + // This is page 18, why is the page id 56? + if (subframe_4->data_id() == 1 && subframe_4->page_id() == 56) { + auto iono = static_cast(subframe_4->body()); + double a0 = iono->a0() * pow(2, -30); + double a1 = iono->a1() * pow(2, -27); + double a2 = iono->a2() * pow(2, -24); + double a3 = iono->a3() * pow(2, -24); + eph.setIonoAlpha({a0, a1, a2, a3}); + + double b0 = iono->b0() * pow(2, 11); + double b1 = iono->b1() * pow(2, 14); + double b2 = iono->b2() * pow(2, 16); + double b3 = iono->b3() * pow(2, 16); + eph.setIonoBeta({b0, b1, b2, b3}); } + } - // Subframe 3 - { - kaitai::kstream stream(gps_subframes[msg->sv_id()][3]); - gps_t subframe(&stream); - gps_t::subframe_3_t* subframe_3 = static_cast(subframe.body()); - - eph.setCic(subframe_3->c_ic() * pow(2, -29)); - eph.setOmega0(subframe_3->omega_0() * pow(2, -31) * gpsPi); - eph.setCis(subframe_3->c_is() * pow(2, -29)); - eph.setI0(subframe_3->i_0() * pow(2, -31) * gpsPi); - eph.setCrc(subframe_3->c_rc() * pow(2, -5)); - eph.setOmega(subframe_3->omega() * pow(2, -31) * gpsPi); - eph.setOmegaDot(subframe_3->omega_dot() * pow(2, -43) * gpsPi); - eph.setIode(subframe_3->iode()); - eph.setIDot(subframe_3->idot() * pow(2, -43) * gpsPi); - } + return capnp::messageToFlatArray(msg_builder); + } + return kj::Array(); +} - // Subframe 4 - { - kaitai::kstream stream(gps_subframes[msg->sv_id()][4]); - gps_t subframe(&stream); - gps_t::subframe_4_t* subframe_4 = static_cast(subframe.body()); - - // This is page 18, why is the page id 56? - if (subframe_4->data_id() == 1 && subframe_4->page_id() == 56) { - auto iono = static_cast(subframe_4->body()); - double a0 = iono->a0() * pow(2, -30); - double a1 = iono->a1() * pow(2, -27); - double a2 = iono->a2() * pow(2, -24); - double a3 = iono->a3() * pow(2, -24); - eph.setIonoAlpha({a0, a1, a2, a3}); - - double b0 = iono->b0() * pow(2, 11); - double b1 = iono->b1() * pow(2, 14); - double b2 = iono->b2() * pow(2, 16); - double b3 = iono->b3() * pow(2, 16); - eph.setIonoBeta({b0, b1, b2, b3}); - } - } - return capnp::messageToFlatArray(msg_builder); - } +kj::Array UbloxMsgParser::gen_rxm_sfrbx(ubx_t::rxm_sfrbx_t *msg) { + switch (msg->gnss_id()) { + case ubx_t::gnss_type_t::GNSS_TYPE_GPS: + return parse_gps_ephemeris(msg); + default: + return kj::Array(); } - return kj::Array(); } kj::Array UbloxMsgParser::gen_rxm_rawx(ubx_t::rxm_rawx_t *msg) { diff --git a/selfdrive/locationd/ublox_msg.h b/selfdrive/locationd/ublox_msg.h index 919e9963f1..736e6d5171 100644 --- a/selfdrive/locationd/ublox_msg.h +++ b/selfdrive/locationd/ublox_msg.h @@ -102,6 +102,8 @@ class UbloxMsgParser { inline bool valid(); inline bool valid_so_far(); + kj::Array parse_gps_ephemeris(ubx_t::rxm_sfrbx_t *msg); + std::unordered_map> gps_subframes; std::unordered_map gps_sat_tow_count; From feba2f3fe566114325f8c9119540fb19cf401a4a Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Mon, 30 Jan 2023 17:41:42 -0800 Subject: [PATCH 232/484] fix micd input device (#27160) * try no device * unused * move sounddevice import --- system/micd.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/system/micd.py b/system/micd.py index a56140e3b9..97ba0c262e 100755 --- a/system/micd.py +++ b/system/micd.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -import sounddevice as sd import numpy as np from cereal import messaging @@ -84,11 +83,11 @@ class Mic: self.measurements = self.measurements[FFT_SAMPLES:] - def micd_thread(self, device=None): - if device is None: - device = "sysdefault" + def micd_thread(self): + # sounddevice must be imported after forking processes + import sounddevice as sd # pylint: disable=import-outside-toplevel - with sd.InputStream(device=device, channels=1, samplerate=SAMPLE_RATE, callback=self.callback) as stream: + with sd.InputStream(channels=1, samplerate=SAMPLE_RATE, callback=self.callback) as stream: cloudlog.info(f"micd stream started: {stream.samplerate=} {stream.channels=} {stream.dtype=} {stream.device=}") while True: self.update() From f3a9dac93e0d1a9058e94d60b48ae5a189841d6c Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Tue, 31 Jan 2023 02:42:38 +0100 Subject: [PATCH 233/484] cabana: add --dbc command line argument (#27132) --- tools/cabana/cabana.cc | 7 +++++++ tools/cabana/mainwin.cc | 5 +++++ tools/cabana/mainwin.h | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/tools/cabana/cabana.cc b/tools/cabana/cabana.cc index d9b6b5948b..e34e2d0205 100644 --- a/tools/cabana/cabana.cc +++ b/tools/cabana/cabana.cc @@ -24,6 +24,7 @@ int main(int argc, char *argv[]) { cmd_parser.addOption({"zmq", "the ip address on which to receive zmq messages", "zmq"}); cmd_parser.addOption({"data_dir", "local directory with routes", "data_dir"}); cmd_parser.addOption({"no-vipc", "do not output video"}); + cmd_parser.addOption({"dbc", "dbc file to open", "dbc"}); cmd_parser.process(app); const QStringList args = cmd_parser.positionalArguments(); if (args.empty() && !cmd_parser.isSet("demo") && !cmd_parser.isSet("stream")) { @@ -57,6 +58,12 @@ int main(int argc, char *argv[]) { } MainWindow w; + + // Load DBC + if (cmd_parser.isSet("dbc")) { + w.loadFile(cmd_parser.value("dbc")); + } + w.show(); return app.exec(); } diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index 3c2d46cbf2..94cf9840f7 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -243,6 +243,11 @@ void MainWindow::loadDBCFromClipboard() { } void MainWindow::loadDBCFromFingerprint() { + // Don't overwrite already loaded DBC + if (!dbc()->name().isEmpty()) { + return; + } + remindSaveChanges(); auto fingerprint = can->carFingerprint(); video_dock->setWindowTitle(tr("ROUTE: %1 FINGERPINT: %2").arg(can->routeName()).arg(fingerprint.isEmpty() ? tr("Unknown Car") : fingerprint)); diff --git a/tools/cabana/mainwin.h b/tools/cabana/mainwin.h index ba36d676e0..c26f6973c0 100644 --- a/tools/cabana/mainwin.h +++ b/tools/cabana/mainwin.h @@ -20,6 +20,7 @@ public: MainWindow(); void dockCharts(bool dock); void showStatusMessage(const QString &msg, int timeout = 0) { statusBar()->showMessage(msg, timeout); } + void loadFile(const QString &fn); public slots: void newFile(); @@ -40,7 +41,6 @@ signals: protected: void remindSaveChanges(); void saveFile(const QString &fn); - void loadFile(const QString &fn); void setCurrentFile(const QString &fn); void updateRecentFileActions(); void createActions(); From ceceb1c6ccfec5a47b4a290ee6f81dc443ce79cc Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 31 Jan 2023 11:23:36 +0800 Subject: [PATCH 234/484] replay: fix hang if started with a special segment and there is no INIT_DATA in events (#27107) * add events if allow or block is specified * add init_data --- tools/replay/replay.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/replay/replay.cc b/tools/replay/replay.cc index 998c93b938..5c90777bbc 100644 --- a/tools/replay/replay.cc +++ b/tools/replay/replay.cc @@ -25,6 +25,13 @@ Replay::Replay(QString route, QStringList allow, QStringList block, SubMaster *s s.push_back(it.name); } } + + if (!allow_list.empty()) { + // the following events are needed for replay to work properly. + allow_list.insert(cereal::Event::Which::INIT_DATA); + allow_list.insert(cereal::Event::Which::CAR_PARAMS); + } + qDebug() << "services " << s; qDebug() << "loading route " << route; From ef9deeb6eba3823982dadd485a3676441320226b Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Mon, 30 Jan 2023 22:37:54 -0800 Subject: [PATCH 235/484] Ford longitudinal control (#27161) ford long --- selfdrive/car/ford/carcontroller.py | 19 ++++++++++++++++++- selfdrive/car/ford/fordcan.py | 20 ++++++++++++++++++++ selfdrive/car/ford/values.py | 5 +++++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/selfdrive/car/ford/carcontroller.py b/selfdrive/car/ford/carcontroller.py index 06da87bf34..cfff37f6d5 100644 --- a/selfdrive/car/ford/carcontroller.py +++ b/selfdrive/car/ford/carcontroller.py @@ -2,7 +2,8 @@ from cereal import car from common.numpy_fast import clip from opendbc.can.packer import CANPacker from selfdrive.car import apply_std_steer_angle_limits -from selfdrive.car.ford.fordcan import create_acc_ui_msg, create_button_msg, create_lat_ctl_msg, create_lka_msg, create_lkas_ui_msg +from selfdrive.car.ford.fordcan import create_acc_command, create_acc_ui_msg, create_button_msg, create_lat_ctl_msg, \ + create_lka_msg, create_lkas_ui_msg from selfdrive.car.ford.values import CANBUS, CarControllerParams VisualAlert = car.CarControl.HUDControl.VisualAlert @@ -55,6 +56,22 @@ class CarController: can_sends.append(create_lka_msg(self.packer)) can_sends.append(create_lat_ctl_msg(self.packer, CC.latActive, 0., 0., -apply_curvature, 0.)) + ### longitudinal control ### + # send acc command at 50Hz + if self.CP.openpilotLongitudinalControl and (self.frame % CarControllerParams.ACC_CONTROL_STEP) == 0: + accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX) + + precharge_brake = accel < -0.1 + if accel > -0.5: + gas = accel + decel = False + else: + gas = -5.0 + decel = True + + can_sends.append(create_acc_command(self.packer, CC.longActive, gas, accel, precharge_brake, decel)) + + ### ui ### send_ui = (self.main_on_last != main_on) or (self.lkas_enabled_last != CC.latActive) or (self.steer_alert_last != steer_alert) diff --git a/selfdrive/car/ford/fordcan.py b/selfdrive/car/ford/fordcan.py index 9ddde80f87..d9c37426f8 100644 --- a/selfdrive/car/ford/fordcan.py +++ b/selfdrive/car/ford/fordcan.py @@ -55,6 +55,26 @@ def create_lat_ctl_msg(packer, lat_active: bool, path_offset: float, path_angle: return packer.make_can_msg("LateralMotionControl", CANBUS.main, values) +def create_acc_command(packer, long_active: bool, gas: float, accel: float, precharge_brake: bool, decel: bool): + """ + Creates a CAN message for the Ford ACC Command. + + This command can be used to enable ACC, to set the ACC gas/brake/decel values + and to disable ACC. + + Frequency is 50Hz. + """ + + values = { + "AccBrkTot_A_Rq": accel, # Brake total accel request: [-20|11.9449] m/s^2 + "Cmbb_B_Enbl": 1 if long_active else 0, # Enabled: 0=No, 1=Yes + "AccPrpl_A_Rq": gas, # Acceleration request: [-5|5.23] m/s^2 + "AccBrkPrchg_B_Rq": 1 if precharge_brake else 0, # Pre-charge brake request: 0=No, 1=Yes + "AccBrkDecel_B_Rq": 1 if decel else 0, # Deceleration request: 0=Inactive, 1=Active + } + return packer.make_can_msg("ACCDATA", CANBUS.main, values) + + def create_lkas_ui_msg(packer, main_on: bool, enabled: bool, steer_alert: bool, hud_control, stock_values: dict): """ Creates a CAN message for the Ford IPC IPMA/LKAS status. diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index b93be61448..7b075f1547 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -14,6 +14,8 @@ Ecu = car.CarParams.Ecu class CarControllerParams: # Messages: Lane_Assist_Data1, LateralMotionControl STEER_STEP = 5 + # Message: ACCDATA + ACC_CONTROL_STEP = 2 # Message: IPMA_Data LKAS_UI_STEP = 100 # Message: ACCDATA_3 @@ -29,6 +31,9 @@ class CarControllerParams: ANGLE_RATE_LIMIT_UP = AngleRateLimit(speed_bp=[5, 15, 25], angle_v=[0.005, 0.00056, 0.0002]) ANGLE_RATE_LIMIT_DOWN = AngleRateLimit(speed_bp=[5, 15, 25], angle_v=[0.008, 0.00089, 0.00032]) + ACCEL_MAX = 2.0 # m/s^s max acceleration + ACCEL_MIN = -3.5 # m/s^s max deceleration + def __init__(self, CP): pass From 4c2f8edb7783f4b5797c02f1bdc2008fbaf4e0c7 Mon Sep 17 00:00:00 2001 From: Anthony Rose <20302208+Cx01N@users.noreply.github.com> Date: Tue, 31 Jan 2023 02:23:45 -0500 Subject: [PATCH 236/484] Add Hyundai Ioniq 5 Fingerprint (#27137) * added 2022 ioniq 5 fingerprint * removed extra eps field * fixed eps value * removed cornerradar * Apply suggestions from code review --------- Co-authored-by: Shane Smiskol --- selfdrive/car/hyundai/values.py | 1 + 1 file changed, 1 insertion(+) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index c0cf23b50d..4e3dd484c8 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -1493,6 +1493,7 @@ FW_VERSIONS = { b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.06 99211-GI000 210813', b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.05 99211-GI010 220614', b'\xf1\x00NE1 MFC AT EUR RHD 1.00 1.01 99211-GI010 211007', + b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.01 99211-GI010 211007', ], }, CAR.TUCSON_4TH_GEN: { From 09cd0b4900d8aff1c9484c8848c9b5f7ff269594 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 31 Jan 2023 01:09:36 -0800 Subject: [PATCH 237/484] GM camera ACC: reliable relay open init (#27163) Reliable relay open --- selfdrive/car/gm/carcontroller.py | 2 +- selfdrive/car/gm/carstate.py | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/selfdrive/car/gm/carcontroller.py b/selfdrive/car/gm/carcontroller.py index b4a79d10a6..c5aae34249 100644 --- a/selfdrive/car/gm/carcontroller.py +++ b/selfdrive/car/gm/carcontroller.py @@ -63,7 +63,7 @@ class CarController: elif (self.frame - self.last_steer_frame) >= steer_step: # Initialize ASCMLKASteeringCmd counter using the camera until we get a msg on the bus if init_lka_counter: - self.lka_steering_cmd_counter = CS.camera_lka_steering_cmd_counter + 1 + self.lka_steering_cmd_counter = CS.pt_lka_steering_cmd_counter + 1 if CC.latActive: new_steer = int(round(actuators.steer * self.params.STEER_MAX)) diff --git a/selfdrive/car/gm/carstate.py b/selfdrive/car/gm/carstate.py index de0fd2eed6..cf6d2817ec 100644 --- a/selfdrive/car/gm/carstate.py +++ b/selfdrive/car/gm/carstate.py @@ -18,7 +18,7 @@ class CarState(CarStateBase): can_define = CANDefine(DBC[CP.carFingerprint]["pt"]) self.shifter_values = can_define.dv["ECMPRDNL2"]["PRNDL2"] self.loopback_lka_steering_cmd_updated = False - self.camera_lka_steering_cmd_counter = 0 + self.pt_lka_steering_cmd_counter = 0 self.buttons_counter = 0 def update(self, pt_cp, cam_cp, loopback_cp): @@ -33,7 +33,7 @@ class CarState(CarStateBase): # Variables used for avoiding LKAS faults self.loopback_lka_steering_cmd_updated = len(loopback_cp.vl_all["ASCMLKASteeringCmd"]["RollingCounter"]) > 0 if self.CP.networkLocation == NetworkLocation.fwdCamera: - self.camera_lka_steering_cmd_counter = cam_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"] + self.pt_lka_steering_cmd_counter = pt_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"] ret.wheelSpeeds = self.get_wheel_speeds( pt_cp.vl["EBCMWheelSpdFront"]["FLWheelSpd"], @@ -113,13 +113,11 @@ class CarState(CarStateBase): if CP.networkLocation == NetworkLocation.fwdCamera: signals += [ ("AEBCmdActive", "AEBCmd"), - ("RollingCounter", "ASCMLKASteeringCmd"), ("ACCSpeedSetpoint", "ASCMActiveCruiseControlStatus"), ("ACCCruiseState", "ASCMActiveCruiseControlStatus"), ] checks += [ ("AEBCmd", 10), - ("ASCMLKASteeringCmd", 10), ("ASCMActiveCruiseControlStatus", 25), ] @@ -180,6 +178,15 @@ class CarState(CarStateBase): ("ECMAcceleratorPos", 80), ] + # Used to read back last counter sent to PT by camera + if CP.networkLocation == NetworkLocation.fwdCamera: + signals += [ + ("RollingCounter", "ASCMLKASteeringCmd"), + ] + checks += [ + ("ASCMLKASteeringCmd", 0), + ] + if CP.transmissionType == TransmissionType.direct: signals.append(("RegenPaddle", "EBCMRegenPaddle")) checks.append(("EBCMRegenPaddle", 50)) From f17bca00ba6919bae309f0c68bad11b0f6b18607 Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Tue, 31 Jan 2023 17:17:21 +0100 Subject: [PATCH 238/484] panda.cc: fix possible heap overflow on wrong checksum (#27151) * panda.cc: fix possible heap overflow on wrong checksum * off by one --- selfdrive/boardd/panda.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/selfdrive/boardd/panda.cc b/selfdrive/boardd/panda.cc index 4bba070eee..647a0d9c78 100644 --- a/selfdrive/boardd/panda.cc +++ b/selfdrive/boardd/panda.cc @@ -236,6 +236,9 @@ void Panda::can_send(capnp::List::Reader can_data_list) { } bool Panda::can_receive(std::vector& out_vec) { + // Check if enough space left in buffer to store RECV_SIZE data + assert(receive_buffer_size + RECV_SIZE <= sizeof(receive_buffer)); + int recv = handle->bulk_read(0x81, &receive_buffer[receive_buffer_size], RECV_SIZE); if (!comms_healthy()) { return false; @@ -278,6 +281,7 @@ bool Panda::unpack_can_buffer(uint8_t *data, uint32_t &size, std::vector Date: Tue, 31 Jan 2023 13:47:41 -0500 Subject: [PATCH 239/484] VW MQB: Add FW for 2016 SEAT Leon (#27168) --- selfdrive/car/volkswagen/values.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py index bdb43c542a..5f0913e880 100755 --- a/selfdrive/car/volkswagen/values.py +++ b/selfdrive/car/volkswagen/values.py @@ -942,16 +942,19 @@ FW_VERSIONS = { b'\xf1\x8704L906021EL\xf1\x897542', b'\xf1\x8704L906026BP\xf1\x891198', b'\xf1\x8704L906026BP\xf1\x897608', + b'\xf1\x8704L906056CR\xf1\x892797', b'\xf1\x8705E906018AS\xf1\x899596', b'\xf1\x878V0906264H \xf1\x890005', ], (Ecu.transmission, 0x7e1, None): [ + b'\xf1\x870CW300041D \xf1\x891004', b'\xf1\x870CW300041G \xf1\x891003', b'\xf1\x870CW300050J \xf1\x891908', b'\xf1\x870D9300042M \xf1\x895016', ], (Ecu.srs, 0x715, None): [ b'\xf1\x873Q0959655AC\xf1\x890189\xf1\x82\r11110011110011021511110200', + b'\xf1\x873Q0959655AS\xf1\x890200\xf1\x82\r11110011110011021511110200', b'\xf1\x873Q0959655AS\xf1\x890200\xf1\x82\r12110012120012021612110200', b'\xf1\x873Q0959655BH\xf1\x890703\xf1\x82\x0e1312001313001305171311052900', b'\xf1\x873Q0959655CM\xf1\x890720\xf1\x82\0161312001313001305171311052900', @@ -965,6 +968,7 @@ FW_VERSIONS = { (Ecu.fwdRadar, 0x757, None): [ b'\xf1\x875Q0907572B \xf1\x890200\xf1\x82\00101', b'\xf1\x875Q0907572H \xf1\x890620', + b'\xf1\x875Q0907572K \xf1\x890402\xf1\x82\x0101', b'\xf1\x875Q0907572P \xf1\x890682', ], }, From 5ce7572b46dcd2b32f11967cc559402eab2ab5ca Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Tue, 31 Jan 2023 14:00:10 -0500 Subject: [PATCH 240/484] VW MNB: Volkswagen Crafter Mk2 (#26006) * VW MNB: Volkswagen Crafter Mk2 * Crafter has a min steer speed :( * add to non_tested_cars for now * autogratitude * Revert "autogratitude" This reverts commit 8f19085bfa3424a8a75d7c95f32be8ecb010f8f4. * autogratitude * regen docs --- RELEASES.md | 1 + docs/CARS.md | 7 +++++- selfdrive/car/tests/routes.py | 1 + selfdrive/car/torque_data/substitute.yaml | 1 + selfdrive/car/volkswagen/interface.py | 5 +++++ selfdrive/car/volkswagen/values.py | 27 ++++++++++++++++++++++- 6 files changed, 40 insertions(+), 2 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 1b5ef8f7a7..ddb4a036b0 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -8,6 +8,7 @@ Version 0.9.1 (2022-12-XX) * Hyundai Tucson 2022-23 support * Kia Sorento 2022-23 support thanks to sunnyhaibin! * Kia Sorento Plug-in Hybrid 2022 support thanks to sunnyhaibin! +* Volkswagen Crafter and MAN TGE 2017-23 support thanks to jyoung8607! Version 0.9.0 (2022-11-21) ======================== diff --git a/docs/CARS.md b/docs/CARS.md index 390d2bf349..3a102795c4 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,7 +4,7 @@ A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system. -# 224 Supported Cars +# 229 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness|Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| @@ -135,6 +135,8 @@ A supported vehicle is one that just works when you install a comma three. All s |Lexus|RX Hybrid 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| |Lexus|RX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| |Lexus|UX Hybrid 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|MAN|eTGE 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|MAN|TGE 2017-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| |Mazda|CX-5 2022-23|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Mazda|| |Mazda|CX-9 2021-23|All|Stock|0 mph|28 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Mazda|| |Nissan|Altima 2019-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Nissan B|| @@ -209,6 +211,8 @@ A supported vehicle is one that just works when you install a comma three. All s |Volkswagen|California 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| |Volkswagen|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| |Volkswagen|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Crafter 2017-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|e-Crafter 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| |Volkswagen|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| |Volkswagen|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| |Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| @@ -217,6 +221,7 @@ A supported vehicle is one that just works when you install a comma three. All s |Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| |Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| |Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Grand California 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| |Volkswagen|Jetta 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| |Volkswagen|Jetta GLI 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| |Volkswagen|Passat 2015-22[8](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| diff --git a/selfdrive/car/tests/routes.py b/selfdrive/car/tests/routes.py index d1b73afcf6..bcdb00fd60 100644 --- a/selfdrive/car/tests/routes.py +++ b/selfdrive/car/tests/routes.py @@ -26,6 +26,7 @@ non_tested_cars = [ HYUNDAI.GENESIS_G90, HYUNDAI.KIA_OPTIMA_H, HONDA.ODYSSEY_CHN, + VOLKSWAGEN.CRAFTER_MK2, # need a route from an ACC-equipped Crafter ] CarTestRoute = namedtuple('CarTestRoute', ['route', 'car_model', 'segment'], defaults=(None,)) diff --git a/selfdrive/car/torque_data/substitute.yaml b/selfdrive/car/torque_data/substitute.yaml index aeb2e6f280..56057dfae0 100644 --- a/selfdrive/car/torque_data/substitute.yaml +++ b/selfdrive/car/torque_data/substitute.yaml @@ -59,6 +59,7 @@ SKODA SCALA 1ST GEN: SKODA SUPERB 3RD GEN SKODA KODIAQ 1ST GEN: SKODA SUPERB 3RD GEN SKODA KAROQ 1ST GEN: SKODA SUPERB 3RD GEN SKODA KAMIQ 1ST GEN: SKODA SUPERB 3RD GEN +VOLKSWAGEN CRAFTER 2ND GEN: VOLKSWAGEN TIGUAN 2ND GEN VOLKSWAGEN T-ROC 1ST GEN: VOLKSWAGEN TIGUAN 2ND GEN VOLKSWAGEN T-CROSS 1ST GEN: VOLKSWAGEN TIGUAN 2ND GEN VOLKSWAGEN TOURAN 2ND GEN: VOLKSWAGEN TIGUAN 2ND GEN diff --git a/selfdrive/car/volkswagen/interface.py b/selfdrive/car/volkswagen/interface.py index da0ce25afa..b979a96d70 100644 --- a/selfdrive/car/volkswagen/interface.py +++ b/selfdrive/car/volkswagen/interface.py @@ -107,6 +107,11 @@ class CarInterface(CarInterfaceBase): ret.mass = 2011 + STD_CARGO_KG ret.wheelbase = 2.98 + elif candidate == CAR.CRAFTER_MK2: + ret.mass = 2100 + STD_CARGO_KG + ret.wheelbase = 3.64 # SWB, LWB is 4.49, TBD how to detect difference + ret.minSteerSpeed = 50 * CV.KPH_TO_MS + elif candidate == CAR.GOLF_MK7: ret.mass = 1397 + STD_CARGO_KG ret.wheelbase = 2.62 diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py index 5f0913e880..9f6dee2689 100755 --- a/selfdrive/car/volkswagen/values.py +++ b/selfdrive/car/volkswagen/values.py @@ -111,6 +111,7 @@ class CANBUS: class CAR: ARTEON_MK1 = "VOLKSWAGEN ARTEON 1ST GEN" # Chassis AN, Mk1 VW Arteon and variants ATLAS_MK1 = "VOLKSWAGEN ATLAS 1ST GEN" # Chassis CA, Mk1 VW Atlas and Atlas Cross Sport + CRAFTER_MK2 = "VOLKSWAGEN CRAFTER 2ND GEN" # Chassis SY/SZ, Mk2 VW Crafter, VW Grand California, MAN TGE GOLF_MK7 = "VOLKSWAGEN GOLF 7TH GEN" # Chassis 5G/AU/BA/BE, Mk7 VW Golf and variants JETTA_MK7 = "VOLKSWAGEN JETTA 7TH GEN" # Chassis BU, Mk7 VW Jetta PASSAT_MK8 = "VOLKSWAGEN PASSAT 8TH GEN" # Chassis 3G, Mk8 VW Passat and variants @@ -122,7 +123,7 @@ class CAR: TIGUAN_MK2 = "VOLKSWAGEN TIGUAN 2ND GEN" # Chassis AD/BW, Mk2 VW Tiguan and variants TOURAN_MK2 = "VOLKSWAGEN TOURAN 2ND GEN" # Chassis 1T, Mk2 VW Touran and variants TRANSPORTER_T61 = "VOLKSWAGEN TRANSPORTER T6.1" # Chassis 7H/7L, T6-facelift Transporter/Multivan/Caravelle/California - TROC_MK1 = "VOLKSWAGEN T-ROC 1ST GEN" # Chassis A1, Mk1 VW VW T-Roc and variants + TROC_MK1 = "VOLKSWAGEN T-ROC 1ST GEN" # Chassis A1, Mk1 VW T-Roc and variants AUDI_A3_MK3 = "AUDI A3 3RD GEN" # Chassis 8V/FF, Mk3 Audi A3 and variants AUDI_Q2_MK1 = "AUDI Q2 1ST GEN" # Chassis GA, Mk1 Audi Q2 (RoW) and Q2L (China only) AUDI_Q3_MK2 = "AUDI Q3 2ND GEN" # Chassis 8U/F3/FS, Mk2 Audi Q3 and variants @@ -184,6 +185,13 @@ CAR_INFO: Dict[str, Union[VWCarInfo, List[VWCarInfo]]] = { VWCarInfo("Volkswagen Teramont Cross Sport 2021-22"), VWCarInfo("Volkswagen Teramont X 2021-22"), ], + CAR.CRAFTER_MK2: [ + VWCarInfo("Volkswagen Crafter 2017-23", video_link="https://youtu.be/4100gLeabmo"), + VWCarInfo("Volkswagen e-Crafter 2018-23", video_link="https://youtu.be/4100gLeabmo"), + VWCarInfo("Volkswagen Grand California 2019-23", video_link="https://youtu.be/4100gLeabmo"), + VWCarInfo("MAN TGE 2017-23", video_link="https://youtu.be/4100gLeabmo"), + VWCarInfo("MAN eTGE 2020-23", video_link="https://youtu.be/4100gLeabmo"), + ], CAR.GOLF_MK7: [ VWCarInfo("Volkswagen e-Golf 2014-20"), VWCarInfo("Volkswagen Golf 2015-20"), @@ -352,6 +360,23 @@ FW_VERSIONS = { b'\xf1\x875Q0907572P \xf1\x890682', ], }, + CAR.CRAFTER_MK2: { + (Ecu.engine, 0x7e0, None): [ + b'\xf1\x8704L906056EK\xf1\x896391', + ], + # Only current upstreamed vehicle has a manual transmission + #(Ecu.transmission, 0x7e1, None): [ + #], + (Ecu.srs, 0x715, None): [ + b'\xf1\x873Q0959655BG\xf1\x890703\xf1\x82\x0e16120016130012051G1313052900', + ], + (Ecu.eps, 0x712, None): [ + b'\xf1\x872N0909143E \xf1\x897021\xf1\x82\x05163AZ306A2', + ], + (Ecu.fwdRadar, 0x757, None): [ + b'\xf1\x872Q0907572M \xf1\x890233', + ], + }, CAR.GOLF_MK7: { (Ecu.engine, 0x7e0, None): [ b'\xf1\x8704E906016A \xf1\x897697', From df5ccda7315e7767465c61ebdee5e9cb30832bc9 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Tue, 31 Jan 2023 11:06:03 -0800 Subject: [PATCH 241/484] Ford CAN FD (#27166) * add LateralMotionControl2 message * send LatCtl2 message on CAN FD cars --- selfdrive/car/ford/carcontroller.py | 14 ++++++--- selfdrive/car/ford/fordcan.py | 44 ++++++++++++++++++++++++++++- selfdrive/car/ford/values.py | 5 +++- 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/selfdrive/car/ford/carcontroller.py b/selfdrive/car/ford/carcontroller.py index cfff37f6d5..00fb461223 100644 --- a/selfdrive/car/ford/carcontroller.py +++ b/selfdrive/car/ford/carcontroller.py @@ -3,8 +3,8 @@ from common.numpy_fast import clip from opendbc.can.packer import CANPacker from selfdrive.car import apply_std_steer_angle_limits from selfdrive.car.ford.fordcan import create_acc_command, create_acc_ui_msg, create_button_msg, create_lat_ctl_msg, \ - create_lka_msg, create_lkas_ui_msg -from selfdrive.car.ford.values import CANBUS, CarControllerParams + create_lat_ctl2_msg, create_lka_msg, create_lkas_ui_msg +from selfdrive.car.ford.values import CANBUS, CANFD_CARS, CarControllerParams VisualAlert = car.CarControl.HUDControl.VisualAlert @@ -54,7 +54,14 @@ class CarController: self.apply_curvature_last = apply_curvature can_sends.append(create_lka_msg(self.packer)) - can_sends.append(create_lat_ctl_msg(self.packer, CC.latActive, 0., 0., -apply_curvature, 0.)) + + if self.CP.carFingerprint in CANFD_CARS: + # TODO: extended mode + mode = 1 if CC.latActive else 0 + counter = self.frame // CarControllerParams.STEER_STEP + can_sends.append(create_lat_ctl2_msg(self.packer, mode, 0., 0., -apply_curvature, 0., counter)) + else: + can_sends.append(create_lat_ctl_msg(self.packer, CC.latActive, 0., 0., -apply_curvature, 0.)) ### longitudinal control ### # send acc command at 50Hz @@ -71,7 +78,6 @@ class CarController: can_sends.append(create_acc_command(self.packer, CC.longActive, gas, accel, precharge_brake, decel)) - ### ui ### send_ui = (self.main_on_last != main_on) or (self.lkas_enabled_last != CC.latActive) or (self.steer_alert_last != steer_alert) diff --git a/selfdrive/car/ford/fordcan.py b/selfdrive/car/ford/fordcan.py index d9c37426f8..594d50f59f 100644 --- a/selfdrive/car/ford/fordcan.py +++ b/selfdrive/car/ford/fordcan.py @@ -4,6 +4,15 @@ from selfdrive.car.ford.values import CANBUS HUDControl = car.CarControl.HUDControl +def calculate_lat_ctl2_checksum(mode: int, counter: int, dat: bytearray): + checksum = mode + counter + checksum += dat[2] + ((dat[3] & 0xE0) >> 5) # curvature + checksum += dat[6] + ((dat[7] & 0xE0) >> 5) # curvature rate + checksum += (dat[3] & 0x1F) + ((dat[4] & 0xFC) >> 2) # path angle + checksum += (dat[4] & 0x3) + dat[5] # path offset + return 0xFF - (checksum & 0xFF) + + def create_lka_msg(packer): """ Creates an empty CAN message for the Ford LKA Command. @@ -16,7 +25,8 @@ def create_lka_msg(packer): return packer.make_can_msg("Lane_Assist_Data1", CANBUS.main, {}) -def create_lat_ctl_msg(packer, lat_active: bool, path_offset: float, path_angle: float, curvature: float, curvature_rate: float): +def create_lat_ctl_msg(packer, lat_active: bool, path_offset: float, path_angle: float, curvature: float, + curvature_rate: float): """ Creates a CAN message for the Ford TJA/LCA Command. @@ -55,6 +65,38 @@ def create_lat_ctl_msg(packer, lat_active: bool, path_offset: float, path_angle: return packer.make_can_msg("LateralMotionControl", CANBUS.main, values) +def create_lat_ctl2_msg(packer, mode: int, path_offset: float, path_angle: float, curvature: float, + curvature_rate: float, counter: int): + """ + Create a CAN message for the new Ford Lane Centering command. + + This message is used on the CAN FD platform and replaces the old LateralMotionControl message. It is similar but has + additional signals for a counter and checksum. + + Frequency is 20Hz. + """ + + values = { + "LatCtl_D2_Rq": mode, # Mode: 0=None, 1=PathFollowingLimitedMode, 2=PathFollowingExtendedMode, + # 3=SafeRampOut, 4-7=NotUsed [0|7] + "LatCtlRampType_D_Rq": 0, # 0=Slow, 1=Medium, 2=Fast, 3=Immediate [0|3] + "LatCtlPrecision_D_Rq": 1, # 0=Comfortable, 1=Precise, 2/3=NotUsed [0|3] + "LatCtlPathOffst_L_Actl": path_offset, # [-5.12|5.11] meter + "LatCtlPath_An_Actl": path_angle, # [-0.5|0.5235] radians + "LatCtlCurv_No_Actl": curvature, # [-0.02|0.02094] 1/meter + "LatCtlCrv_NoRate2_Actl": curvature_rate, # [-0.001024|0.001023] 1/meter^2 + "HandsOffCnfm_B_Rq": 0, # 0=Inactive, 1=Active [0|1] + "LatCtlPath_No_Cnt": counter, # [0|15] + "LatCtlPath_No_Cs": 0, # [0|255] + } + + # calculate checksum + dat = packer.make_can_msg("LateralMotionControl2", CANBUS.main, values)[2] + values["LatCtlPath_No_Cs"] = calculate_lat_ctl2_checksum(mode, counter, dat) + + return packer.make_can_msg("LateralMotionControl2", CANBUS.main, values) + + def create_acc_command(packer, long_active: bool, gas: float, accel: float, precharge_brake: bool, decel: bool): """ Creates a CAN message for the Ford ACC Command. diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index 7b075f1547..4cabdb11e1 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -1,7 +1,7 @@ from collections import defaultdict from dataclasses import dataclass from enum import Enum -from typing import Dict, List, Union +from typing import Dict, List, Set, Union from cereal import car from selfdrive.car import AngleRateLimit, dbc_dict @@ -52,6 +52,9 @@ class CAR: MAVERICK_MK1 = "FORD MAVERICK 1ST GEN" +CANFD_CARS: Set[str] = set() + + class RADAR: DELPHI_ESR = 'ford_fusion_2018_adas' DELPHI_MRR = 'FORD_CADS' From cf943940269ec1e400f820d869aabc45e1c472bb Mon Sep 17 00:00:00 2001 From: Sean Cox Date: Tue, 31 Jan 2023 15:02:05 -0500 Subject: [PATCH 242/484] Fix 2017 Genesis G80 car harness and shop link in docs (#27127) * Fix 2017 Genesis G80 car harness * Revert "Fix 2017 Genesis G80 car harness" This reverts commit 2d5a92b7f0972c7ab61b132dce6cfebe978781c9. * split and make G80 2017 have J harness * generate and change min enable speed * adjust min steer speed --------- Co-authored-by: Shane Smiskol --- docs/CARS.md | 5 +++-- selfdrive/car/hyundai/values.py | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index 3a102795c4..f4ad4b75fa 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,7 +4,7 @@ A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system. -# 229 Supported Cars +# 230 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness|Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| @@ -30,7 +30,8 @@ A supported vehicle is one that just works when you install a comma three. All s |comma|body|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|None|| |Genesis|G70 2018-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F|| |Genesis|G70 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F|| -|Genesis|G80 2017-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| +|Genesis|G80 2017|All|Stock|19 mph|37 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai J|| +|Genesis|G80 2018-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| |Genesis|G90 2017-18|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| |Genesis|GV60 (Advanced Trim) 2023[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|| |Genesis|GV60 (Performance Trim) 2023[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 4e3dd484c8..691b52c936 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -232,7 +232,10 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { CAR.GENESIS_G70: HyundaiCarInfo("Genesis G70 2018-19", "All", harness=Harness.hyundai_f), CAR.GENESIS_G70_2020: HyundaiCarInfo("Genesis G70 2020", "All", harness=Harness.hyundai_f), CAR.GENESIS_GV70_1ST_GEN: HyundaiCarInfo("Genesis GV70 2022-23", "All", harness=Harness.hyundai_l), - CAR.GENESIS_G80: HyundaiCarInfo("Genesis G80 2017-19", "All", harness=Harness.hyundai_h), + CAR.GENESIS_G80: [ + HyundaiCarInfo("Genesis G80 2017", "All", min_steer_speed=37 * CV.MPH_TO_MS, min_enable_speed=19 * CV.MPH_TO_MS, harness=Harness.hyundai_j), + HyundaiCarInfo("Genesis G80 2018-19", "All", harness=Harness.hyundai_h), + ], CAR.GENESIS_G90: HyundaiCarInfo("Genesis G90 2017-18", "All", harness=Harness.hyundai_c), } From f1caecff727fe4d53edc131b30b7a31c1fb4736c Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 31 Jan 2023 12:48:13 -0800 Subject: [PATCH 243/484] Genesis G80 2017: group with Hyundai Genesis (#27170) * Revert "Fix 2017 Genesis G80 car harness and shop link in docs (#27127)" This reverts commit cf943940269ec1e400f820d869aabc45e1c472bb. * The HGenesis became rebranded to G80 in 2017 (2018 model year). G80 2017 fingerprints as Hyundai Genesis, and we handle the min steer speed correctly https://en.wikipedia.org/wiki/Genesis_G80 https://en.wikipedia.org/wiki/Hyundai_Genesis * fix --- selfdrive/car/hyundai/values.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 691b52c936..1be612ad74 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -147,7 +147,10 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { ], CAR.ELANTRA_2021: HyundaiCarInfo("Hyundai Elantra 2021-23", video_link="https://youtu.be/_EdYQtV52-c", harness=Harness.hyundai_k), CAR.ELANTRA_HEV_2021: HyundaiCarInfo("Hyundai Elantra Hybrid 2021-23", video_link="https://youtu.be/_EdYQtV52-c", harness=Harness.hyundai_k), - CAR.HYUNDAI_GENESIS: HyundaiCarInfo("Hyundai Genesis 2015-16", min_enable_speed=19 * CV.MPH_TO_MS, harness=Harness.hyundai_j), # TODO: check 2015 packages + CAR.HYUNDAI_GENESIS: [ + HyundaiCarInfo("Hyundai Genesis 2015-16", min_enable_speed=19 * CV.MPH_TO_MS, harness=Harness.hyundai_j), # TODO: check 2015 packages + HyundaiCarInfo("Genesis G80 2017", "All", min_enable_speed=19 * CV.MPH_TO_MS, harness=Harness.hyundai_j), + ], CAR.IONIQ: HyundaiCarInfo("Hyundai Ioniq Hybrid 2017-19", harness=Harness.hyundai_c), CAR.IONIQ_HEV_2022: HyundaiCarInfo("Hyundai Ioniq Hybrid 2020-22", harness=Harness.hyundai_h), # TODO: confirm 2020-21 harness CAR.IONIQ_EV_LTD: HyundaiCarInfo("Hyundai Ioniq Electric 2019", harness=Harness.hyundai_c), @@ -232,10 +235,7 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { CAR.GENESIS_G70: HyundaiCarInfo("Genesis G70 2018-19", "All", harness=Harness.hyundai_f), CAR.GENESIS_G70_2020: HyundaiCarInfo("Genesis G70 2020", "All", harness=Harness.hyundai_f), CAR.GENESIS_GV70_1ST_GEN: HyundaiCarInfo("Genesis GV70 2022-23", "All", harness=Harness.hyundai_l), - CAR.GENESIS_G80: [ - HyundaiCarInfo("Genesis G80 2017", "All", min_steer_speed=37 * CV.MPH_TO_MS, min_enable_speed=19 * CV.MPH_TO_MS, harness=Harness.hyundai_j), - HyundaiCarInfo("Genesis G80 2018-19", "All", harness=Harness.hyundai_h), - ], + CAR.GENESIS_G80: HyundaiCarInfo("Genesis G80 2018-19", "All", harness=Harness.hyundai_h), CAR.GENESIS_G90: HyundaiCarInfo("Genesis G90 2017-18", "All", harness=Harness.hyundai_c), } From 49e955d321aff222665ac89b62d9de0b5d8395d2 Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Tue, 31 Jan 2023 22:08:07 +0100 Subject: [PATCH 244/484] cabana: dynamically switch between OpenGL (#27167) * cabana: dynamically switch between OpenGL * put back todo * only switch when x axis changes --- tools/cabana/chartswidget.cc | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 2edc89d857..923b6bbb59 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -358,11 +358,6 @@ void ChartView::setPlotAreaLeftPosition(int pos) { void ChartView::addSeries(const QString &msg_id, const Signal *sig) { QLineSeries *series = new QLineSeries(this); - // TODO: Due to a bug in CameraWidget the camera frames - // are drawn instead of the graphs on MacOS. Re-enable OpenGL when fixed -#ifndef __APPLE__ - series->setUseOpenGL(true); -#endif chart()->addSeries(series); series->attachAxis(axis_x); series->attachAxis(axis_y); @@ -476,17 +471,32 @@ void ChartView::updatePlot(double cur, double min, double max) { if (min != axis_x->min() || max != axis_x->max()) { axis_x->setRange(min, max); updateAxisY(); - } - // Show points when zoomed in enough - for (auto &s : sigs) { - auto begin = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->min(), [](auto &p, double x) { return p.x() < x; }); - auto end = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->max(), [](auto &p, double x) { return p.x() < x; }); + // Show points when zoomed in enough + for (auto &s : sigs) { + auto begin = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->min(), [](auto &p, double x) { return p.x() < x; }); + auto end = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->max(), [](auto &p, double x) { return p.x() < x; }); - int num_points = std::max(end - begin, 1); - int pixels_per_point = width() / num_points; + int num_points = std::max(end - begin, 1); + int pixels_per_point = width() / num_points; - s.series->setPointsVisible(pixels_per_point > 20); + s.series->setPointsVisible(pixels_per_point > 20); + + // TODO: On MacOS QChartWidget doesn't work with the OpenGL settings that CameraWidget needs. +#ifndef __APPLE + // OpenGL mode lacks certain features (such as showing points), only use when drawing many points + bool use_opengl = pixels_per_point < 1; + s.series->setUseOpenGL(use_opengl); + + // Qt doesn't properly apply device pixel ratio in OpenGL mode + QApplication* application = static_cast(QApplication::instance()); + float scale = use_opengl ? application->devicePixelRatio() : 1.0; + + QPen pen = s.series->pen(); + pen.setWidth(2.0 * scale); + s.series->setPen(pen); +#endif + } } scene()->invalidate({}, QGraphicsScene::ForegroundLayer); From 0402e949270219bfbd964d38916ed52917090169 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 1 Feb 2023 05:45:57 +0800 Subject: [PATCH 245/484] cabana: render light or dark icon based on system theme (#27155) --- tools/cabana/chartswidget.cc | 14 ++++++-------- tools/cabana/detailwidget.cc | 11 +++++------ tools/cabana/signaledit.cc | 8 +++----- tools/cabana/util.cc | 17 +++++++++++++++++ tools/cabana/util.h | 4 ++++ tools/cabana/videowidget.cc | 4 +--- 6 files changed, 36 insertions(+), 22 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 923b6bbb59..47fc5dc03b 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -13,8 +13,6 @@ #include #include -#include "selfdrive/ui/qt/util.h" - // ChartsWidget ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { @@ -24,7 +22,7 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { QToolBar *toolbar = new QToolBar(tr("Charts"), this); toolbar->setIconSize({16, 16}); - QAction *new_plot_btn = toolbar->addAction(bootstrapPixmap("file-plus"), ""); + QAction *new_plot_btn = toolbar->addAction(utils::icon("file-plus"), ""); new_plot_btn->setToolTip(tr("New Plot")); toolbar->addWidget(title_label = new QLabel()); title_label->setContentsMargins(0, 0, 12, 0); @@ -46,9 +44,9 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { range_slider->setPageStep(60); // 1 min toolbar->addWidget(range_slider); - reset_zoom_btn = toolbar->addAction(bootstrapPixmap("zoom-out"), ""); + reset_zoom_btn = toolbar->addAction(utils::icon("zoom-out"), ""); reset_zoom_btn->setToolTip(tr("Reset zoom (drag on chart to zoom X-Axis)")); - remove_all_btn = toolbar->addAction(bootstrapPixmap("x"), ""); + remove_all_btn = toolbar->addAction(utils::icon("x"), ""); remove_all_btn->setToolTip(tr("Remove all charts")); dock_btn = toolbar->addAction(""); main_layout->addWidget(toolbar); @@ -170,7 +168,7 @@ void ChartsWidget::setMaxChartRange(int value) { void ChartsWidget::updateToolBar() { range_lb->setText(QString(" %1:%2 ").arg(max_chart_range / 60, 2, 10, QLatin1Char('0')).arg(max_chart_range % 60, 2, 10, QLatin1Char('0'))); title_label->setText(tr("Charts: %1").arg(charts.size())); - dock_btn->setIcon(bootstrapPixmap(docking ? "arrow-up-right" : "arrow-down-left")); + dock_btn->setIcon(utils::icon(docking ? "arrow-up-right" : "arrow-down-left")); dock_btn->setToolTip(docking ? tr("Undock charts") : tr("Dock charts")); remove_all_btn->setEnabled(!charts.isEmpty()); reset_zoom_btn->setEnabled(is_zoomed); @@ -311,7 +309,7 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { chart->setMargins({20, 11, 11, 11}); QToolButton *remove_btn = new QToolButton(); - remove_btn->setIcon(bootstrapPixmap("x")); + remove_btn->setIcon(utils::icon("x")); remove_btn->setAutoRaise(true); remove_btn->setToolTip(tr("Remove Chart")); close_btn_proxy = new QGraphicsProxyWidget(chart); @@ -319,7 +317,7 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { close_btn_proxy->setZValue(chart->zValue() + 11); QToolButton *manage_btn = new QToolButton(); - manage_btn->setIcon(bootstrapPixmap("gear")); + manage_btn->setIcon(utils::icon("gear")); manage_btn->setAutoRaise(true); manage_btn->setToolTip(tr("Manage series")); manage_btn_proxy = new QGraphicsProxyWidget(chart); diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index 5e127966c1..3af0fa9fc3 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -6,7 +6,6 @@ #include #include -#include "selfdrive/ui/qt/util.h" #include "tools/cabana/commands.h" #include "tools/cabana/dbcmanager.h" #include "tools/cabana/streams/abstractstream.h" @@ -38,8 +37,8 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart name_label->setAlignment(Qt::AlignCenter); name_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); toolbar->addWidget(name_label); - toolbar->addAction(bootstrapPixmap("pencil"), "", this, &DetailWidget::editMsg)->setToolTip(tr("Edit Message")); - remove_msg_act = toolbar->addAction(bootstrapPixmap("x-lg"), "", this, &DetailWidget::removeMsg); + toolbar->addAction(utils::icon("pencil"), "", this, &DetailWidget::editMsg)->setToolTip(tr("Edit Message")); + remove_msg_act = toolbar->addAction(utils::icon("x-lg"), "", this, &DetailWidget::removeMsg); remove_msg_act->setToolTip(tr("Remove Message")); main_layout->addWidget(toolbar); @@ -63,8 +62,8 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart tab_widget = new QTabWidget(this); tab_widget->setTabPosition(QTabWidget::South); - tab_widget->addTab(splitter, bootstrapPixmap("file-earmark-ruled"), "&Msg"); - tab_widget->addTab(history_log = new LogsWidget(this), bootstrapPixmap("stopwatch"), "&Logs"); + tab_widget->addTab(splitter, utils::icon("file-earmark-ruled"), "&Msg"); + tab_widget->addTab(history_log = new LogsWidget(this), utils::icon("stopwatch"), "&Logs"); main_layout->addWidget(tab_widget); stacked_layout = new QStackedLayout(this); @@ -148,7 +147,7 @@ void DetailWidget::refresh() { if (!warnings.isEmpty()) { warning_label->setText(warnings.join('\n')); - warning_icon->setPixmap(bootstrapPixmap(msg ? "exclamation-triangle" : "info-circle")); + warning_icon->setPixmap(utils::icon(msg ? "exclamation-triangle" : "info-circle")); } warning_widget->setVisible(!warnings.isEmpty()); } diff --git a/tools/cabana/signaledit.cc b/tools/cabana/signaledit.cc index ceb7b7ba3f..7f31293c41 100644 --- a/tools/cabana/signaledit.cc +++ b/tools/cabana/signaledit.cc @@ -10,8 +10,6 @@ #include "tools/cabana/commands.h" -#include "selfdrive/ui/qt/util.h" - // SignalModel SignalModel::SignalModel(QObject *parent) : root(new Item), QAbstractItemModel(parent) { @@ -132,7 +130,7 @@ QVariant SignalModel::data(const QModelIndex &index, int role) const { if (item->type == Item::Endian) return item->sig->is_little_endian ? Qt::Checked : Qt::Unchecked; if (item->type == Item::Signed) return item->sig->is_signed ? Qt::Checked : Qt::Unchecked; } else if (role == Qt::DecorationRole && index.column() == 0 && item->type == Item::ExtraInfo) { - return bootstrapPixmap(item->parent->extra_expanded ? "chevron-compact-down" : "chevron-compact-up"); + return utils::icon(item->parent->extra_expanded ? "chevron-compact-down" : "chevron-compact-up"); } } return {}; @@ -331,7 +329,7 @@ SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts), hl->addWidget(filter_edit); hl->addStretch(1); auto collapse_btn = new QToolButton(); - collapse_btn->setIcon(bootstrapPixmap("dash-square")); + collapse_btn->setIcon(utils::icon("dash-square")); collapse_btn->setIconSize({12, 12}); collapse_btn->setAutoRaise(true); collapse_btn->setToolTip(tr("Collapse All")); @@ -375,7 +373,7 @@ void SignalView::setMessage(const QString &id) { void SignalView::rowsChanged() { auto create_btn = [](const QString &id, const QString &tooltip) { auto btn = new QToolButton(); - btn->setIcon(bootstrapPixmap(id)); + btn->setIcon(utils::icon(id)); btn->setToolTip(tooltip); btn->setAutoRaise(true); return btn; diff --git a/tools/cabana/util.cc b/tools/cabana/util.cc index 023e893e11..699aa7dc01 100644 --- a/tools/cabana/util.cc +++ b/tools/cabana/util.cc @@ -1,8 +1,11 @@ #include "tools/cabana/util.h" +#include #include #include +#include "selfdrive/ui/qt/util.h" + static QColor blend(QColor a, QColor b) { return QColor((a.red() + b.red()) / 2, (a.green() + b.green()) / 2, (a.blue() + b.blue()) / 2, (a.alpha() + b.alpha()) / 2); } @@ -100,3 +103,17 @@ QValidator::State NameValidator::validate(QString &input, int &pos) const { input.replace(' ', '_'); return QRegExpValidator::validate(input, pos); } + +namespace utils { +QPixmap icon(const QString &id) { + static bool dark_theme = QApplication::style()->standardPalette().color(QPalette::WindowText).value() > + QApplication::style()->standardPalette().color(QPalette::Background).value(); + QPixmap pm = bootstrapPixmap(id); + if (dark_theme) { + QPainter p(&pm); + p.setCompositionMode(QPainter::CompositionMode_SourceIn); + p.fillRect(pm.rect(), Qt::lightGray); + } + return pm; +} +} // namespace utils diff --git a/tools/cabana/util.h b/tools/cabana/util.h index 20c85af39e..a598726bb6 100644 --- a/tools/cabana/util.h +++ b/tools/cabana/util.h @@ -45,3 +45,7 @@ public: NameValidator(QObject *parent=nullptr); QValidator::State validate(QString &input, int &pos) const override; }; + +namespace utils { +QPixmap icon(const QString &id); +} diff --git a/tools/cabana/videowidget.cc b/tools/cabana/videowidget.cc index 445a21c755..d8d89a708c 100644 --- a/tools/cabana/videowidget.cc +++ b/tools/cabana/videowidget.cc @@ -13,8 +13,6 @@ #include #include -#include "selfdrive/ui/qt/util.h" - inline QString formatTime(int seconds) { return QDateTime::fromTime_t(seconds).toString(seconds > 60 * 60 ? "hh:mm:ss" : "mm:ss"); } @@ -130,7 +128,7 @@ void VideoWidget::updateState() { } void VideoWidget::updatePlayBtnState() { - play_btn->setIcon(bootstrapPixmap(can->isPaused() ? "play" : "pause")); + play_btn->setIcon(utils::icon(can->isPaused() ? "play" : "pause")); play_btn->setToolTip(can->isPaused() ? tr("Play") : tr("Pause")); } From 1ba590e04389aea21e7e54ad574dfcc91f7cd1e6 Mon Sep 17 00:00:00 2001 From: cydia2020 <12470297+cydia2020@users.noreply.github.com> Date: Wed, 1 Feb 2023 10:12:31 +1100 Subject: [PATCH 246/484] Toyota: make LKAS message 5hz (#26830) * Toyota: make LKAS message 5hz * update refs --------- Co-authored-by: Shane Smiskol --- selfdrive/car/toyota/carcontroller.py | 2 +- selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/car/toyota/carcontroller.py b/selfdrive/car/toyota/carcontroller.py index 6dbdd4b5d9..222cf70613 100644 --- a/selfdrive/car/toyota/carcontroller.py +++ b/selfdrive/car/toyota/carcontroller.py @@ -143,7 +143,7 @@ class CarController: # forcing the pcm to disengage causes a bad fault sound so play a good sound instead send_ui = True - if self.frame % 100 == 0 or send_ui: + if self.frame % 20 == 0 or send_ui: can_sends.append(create_ui_command(self.packer, steer_alert, pcm_cancel_cmd, hud_control.leftLaneVisible, hud_control.rightLaneVisible, hud_control.leftLaneDepart, hud_control.rightLaneDepart, CC.enabled, CS.lkas_hud)) diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 51de0f0013..b598524fd7 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -844c84eae269e74b246b039d2b33fd44e98a5b68 \ No newline at end of file +d795362593d935767c694c652ecb05ab80dd1914 \ No newline at end of file From ec4c553542b3e63e24bb2b08c0ed007c69356a59 Mon Sep 17 00:00:00 2001 From: AlexandreSato <66435071+AlexandreSato@users.noreply.github.com> Date: Tue, 31 Jan 2023 20:46:57 -0300 Subject: [PATCH 247/484] Toyota: add FW engine for Brazilian Corolla Hybrid (#26943) * Add miss FW engine for Toyota Corolla Hybrid Test route ` 4f50e44908cc46b8|2023-01-12--21-08-57 ` * braziliam toyota corolla hybrid 2023 is US affected by TSK? * add a new entry for south america * Revert "add a new entry for south america" This reverts commit 28454c04cc93dc241da3b004a7154e44313e85c9. * CARS.md is autogenerated! * delete CARS.md to fix merge conflict * space * i don't know if we know for sure not all international 2023's have TSK --------- Co-authored-by: Shane Smiskol --- docs/CARS.md | 3 ++- selfdrive/car/toyota/values.py | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/CARS.md b/docs/CARS.md index f4ad4b75fa..ea06176178 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,7 +4,7 @@ A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system. -# 230 Supported Cars +# 231 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness|Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| @@ -184,6 +184,7 @@ A supported vehicle is one that just works when you install a comma three. All s |Toyota|Corolla Cross Hybrid (Non-US only) 2020-22|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| |Toyota|Corolla Hatchback 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| |Toyota|Corolla Hybrid 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Toyota|Corolla Hybrid (Non-US only) 2020-23|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| |Toyota|Highlander 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| |Toyota|Highlander 2020-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| |Toyota|Highlander Hybrid 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index c413028a25..6c2b865982 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -125,6 +125,7 @@ CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = { ], CAR.COROLLAH_TSS2: [ ToyotaCarInfo("Toyota Corolla Hybrid 2020-22"), + ToyotaCarInfo("Toyota Corolla Hybrid (Non-US only) 2020-23", min_enable_speed=7.5), ToyotaCarInfo("Toyota Corolla Cross Hybrid (Non-US only) 2020-22", min_enable_speed=7.5), ToyotaCarInfo("Lexus UX Hybrid 2019-22"), ], @@ -841,6 +842,7 @@ FW_VERSIONS = { b'\x02896630A07000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', b'\x02896630A21000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', b'\x02896630ZJ5000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', + b'\x02896630ZK8000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', b'\x02896630ZN8000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', b'\x02896630ZQ3000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', b'\x02896630ZR2000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', From f37d35ed63e463ea0634885e0578f605958d5121 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 31 Jan 2023 15:48:40 -0800 Subject: [PATCH 248/484] Hyundai: add FW for Genesis and Genesis G80 (#27171) * add FW for Genesis and G80 * move to top --- selfdrive/car/hyundai/values.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 1be612ad74..1d531c2b87 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -367,6 +367,13 @@ FW_QUERY_CONFIG = FwQueryConfig( ) FW_VERSIONS = { + CAR.HYUNDAI_GENESIS: { + (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00DH LKAS 1.1 -150210', + b'\xf1\x00DH LKAS 1.4 -140110', + b'\xf1\x00DH LKAS 1.5 -140425', + ], + }, CAR.IONIQ: { (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00AEhe SCC H-CUP 1.01 1.01 96400-G2000 ', @@ -1031,6 +1038,25 @@ FW_VERSIONS = { b'\xf1\x81640H0051\x00\x00\x00\x00\x00\x00\x00\x00', ], }, + CAR.GENESIS_G80: { + (Ecu.fwdRadar, 0x7d0, None): [ + b'\xf1\x00DH__ SCC F-CUP 1.00 1.01 96400-B1120 ', + ], + (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00DH LKAS AT USA LHD 1.01 1.03 95895-B1500 180713', + b'\xf1\x00DH LKAS AT USA LHD 1.01 1.02 95895-B1500 170810', + b'\xf1\x00DH LKAS AT USA LHD 1.01 1.01 95895-B1500 161014', + ], + (Ecu.transmission, 0x7e1, None): [ + b'\xf1\x00bcsh8p54 E21\x00\x00\x00\x00\x00\x00\x00SDH0T33NH4\xd7O\x9e\xc9', + b'\xf1\x00bcsh8p54 E18\x00\x00\x00\x00\x00\x00\x00TDH0G38NH3:-\xa9n', + b'\xf1\x00bcsh8p54 E18\x00\x00\x00\x00\x00\x00\x00SDH0G38NH2j\x9dA\x1c', + b'\xf1\x00bcsh8p54 E18\x00\x00\x00\x00\x00\x00\x00SDH0T33NH3\x97\xe6\xbc\xb8', + ], + (Ecu.engine, 0x7e0, None): [ + b'\xf1\x81640F0051\x00\x00\x00\x00\x00\x00\x00\x00', + ], + }, CAR.GENESIS_G90: { (Ecu.transmission, 0x7e1, None): [b'\xf1\x87VDGMD15866192DD3x\x88x\x89wuFvvfUf\x88vWwgwwwvfVgx\x87o\xff\xbc^\xf1\x81E14\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcshcm49 E14\x00\x00\x00\x00\x00\x00\x00SHI0G50NB1tc5\xb7'], (Ecu.fwdRadar, 0x7d0, None): [b'\xf1\x00HI__ SCC F-CUP 1.00 1.01 96400-D2100 '], From 022ef679e6d67f0be82ddc19b595bc874d456138 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 31 Jan 2023 20:26:33 -0800 Subject: [PATCH 249/484] GM camera ACC: reliable relay closing (#27164) * Reliable relay open * Reliable relay close * ign in a loop * fixes * we need this * log * comment to remind me tmrw * ign fix * this makes it 2x more reliable, but messyyy * Revert "this makes it 2x more reliable, but messyyy" This reverts commit 03401dc4a705cfacbe5a7048d95dccb6fa80d57f. * revert non-related stuff * comments, spaces * a stands for indefinite article * not applicable for non-camera acc * something... * Revert "something..." This reverts commit de8a158488efd5ef257434fbc3c55aefff800cb7. * Update ref_commit --- selfdrive/car/gm/carcontroller.py | 9 +++++++-- selfdrive/car/gm/carstate.py | 4 ++++ selfdrive/test/process_replay/ref_commit | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/selfdrive/car/gm/carcontroller.py b/selfdrive/car/gm/carcontroller.py index c5aae34249..c25de41769 100644 --- a/selfdrive/car/gm/carcontroller.py +++ b/selfdrive/car/gm/carcontroller.py @@ -50,9 +50,14 @@ class CarController: # Steering (Active: 50Hz, inactive: 10Hz) # Attempt to sync with camera on startup at 50Hz, first few msgs are blocked - init_lka_counter = not self.sent_lka_steering_cmd and self.CP.networkLocation == NetworkLocation.fwdCamera + init_lka_counter = not self.sent_lka_steering_cmd + # Also send at 50Hz until we're in sync with camera so counters align when relay closes, preventing a fault + # openpilot can subtly drift, so this is activated throughout a drive to stay synced + out_of_sync = self.lka_steering_cmd_counter % 4 != (CS.camera_lka_steering_cmd_counter + 1) % 4 + sync_steer = (init_lka_counter or out_of_sync) and self.CP.networkLocation == NetworkLocation.fwdCamera + steer_step = self.params.INACTIVE_STEER_STEP - if CC.latActive or init_lka_counter: + if CC.latActive or sync_steer: steer_step = self.params.STEER_STEP # Avoid GM EPS faults when transmitting messages too close together: skip this transmit if we just received the diff --git a/selfdrive/car/gm/carstate.py b/selfdrive/car/gm/carstate.py index cf6d2817ec..8ea8ea8874 100644 --- a/selfdrive/car/gm/carstate.py +++ b/selfdrive/car/gm/carstate.py @@ -19,6 +19,7 @@ class CarState(CarStateBase): self.shifter_values = can_define.dv["ECMPRDNL2"]["PRNDL2"] self.loopback_lka_steering_cmd_updated = False self.pt_lka_steering_cmd_counter = 0 + self.camera_lka_steering_cmd_counter = 0 self.buttons_counter = 0 def update(self, pt_cp, cam_cp, loopback_cp): @@ -34,6 +35,7 @@ class CarState(CarStateBase): self.loopback_lka_steering_cmd_updated = len(loopback_cp.vl_all["ASCMLKASteeringCmd"]["RollingCounter"]) > 0 if self.CP.networkLocation == NetworkLocation.fwdCamera: self.pt_lka_steering_cmd_counter = pt_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"] + self.camera_lka_steering_cmd_counter = cam_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"] ret.wheelSpeeds = self.get_wheel_speeds( pt_cp.vl["EBCMWheelSpdFront"]["FLWheelSpd"], @@ -113,11 +115,13 @@ class CarState(CarStateBase): if CP.networkLocation == NetworkLocation.fwdCamera: signals += [ ("AEBCmdActive", "AEBCmd"), + ("RollingCounter", "ASCMLKASteeringCmd"), ("ACCSpeedSetpoint", "ASCMActiveCruiseControlStatus"), ("ACCCruiseState", "ASCMActiveCruiseControlStatus"), ] checks += [ ("AEBCmd", 10), + ("ASCMLKASteeringCmd", 10), ("ASCMActiveCruiseControlStatus", 25), ] diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index b598524fd7..b7cb6ceb33 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -d795362593d935767c694c652ecb05ab80dd1914 \ No newline at end of file +baef183c9602b702756e2fd0781b5d289b61d19a From 519b3c8847d5c054b130667977e181cc7e54e7fa Mon Sep 17 00:00:00 2001 From: Vivek Aithal Date: Wed, 1 Feb 2023 11:21:51 -0800 Subject: [PATCH 250/484] paramsd: Check if roll from the localizer is actually valid (#27105) * add roll_valid check, use localizer roll when it is valid * increase std to 1.5 * btter check * avoid numpy * update refs * update refs --- selfdrive/locationd/paramsd.py | 17 +++++++++++------ selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/selfdrive/locationd/paramsd.py b/selfdrive/locationd/paramsd.py index 7e7c2b091f..7e30b1e3a7 100755 --- a/selfdrive/locationd/paramsd.py +++ b/selfdrive/locationd/paramsd.py @@ -14,8 +14,9 @@ from system.swaglog import cloudlog MAX_ANGLE_OFFSET_DELTA = 20 * DT_MDL # Max 20 deg/s -ROLL_MAX_DELTA = np.radians(20.0) * DT_MDL # 20deg in 1 second is well within curvature limits +ROLL_MAX_DELTA = math.radians(20.0) * DT_MDL # 20deg in 1 second is well within curvature limits ROLL_MIN, ROLL_MAX = math.radians(-10), math.radians(10) +ROLL_STD_MAX = math.radians(1.5) LATERAL_ACC_SENSOR_THRESHOLD = 4.0 @@ -37,8 +38,7 @@ class ParamsLearner: self.yaw_rate_std = 0.0 self.roll = 0.0 self.steering_angle = 0.0 - - self.valid = True + self.roll_valid = False def handle_log(self, t, which, msg): if which == 'liveLocationKalman': @@ -47,8 +47,8 @@ class ParamsLearner: localizer_roll = msg.orientationNED.value[0] localizer_roll_std = np.radians(1) if np.isnan(msg.orientationNED.std[0]) else msg.orientationNED.std[0] - roll_valid = msg.orientationNED.valid and ROLL_MIN < localizer_roll < ROLL_MAX - if roll_valid: + self.roll_valid = (localizer_roll_std < ROLL_STD_MAX) and (ROLL_MIN < localizer_roll < ROLL_MAX) and msg.sensorsOK + if self.roll_valid: roll = localizer_roll # Experimentally found multiplier of 2 to be best trade-off between stability and accuracy or similar? roll_std = 2 * localizer_roll_std @@ -156,6 +156,7 @@ def main(sm=None, pm=None): learner = ParamsLearner(CP, params['steerRatio'], params['stiffnessFactor'], math.radians(params['angleOffsetAverageDeg'])) angle_offset_average = params['angleOffsetAverageDeg'] angle_offset = angle_offset_average + roll = 0.0 while True: sm.update() @@ -175,6 +176,8 @@ def main(sm=None, pm=None): angle_offset_average = clip(math.degrees(x[States.ANGLE_OFFSET]), angle_offset_average - MAX_ANGLE_OFFSET_DELTA, angle_offset_average + MAX_ANGLE_OFFSET_DELTA) angle_offset = clip(math.degrees(x[States.ANGLE_OFFSET] + x[States.ANGLE_OFFSET_FAST]), angle_offset - MAX_ANGLE_OFFSET_DELTA, angle_offset + MAX_ANGLE_OFFSET_DELTA) + roll = clip(float(x[States.ROAD_ROLL]), roll - ROLL_MAX_DELTA, roll + ROLL_MAX_DELTA) + roll_std = float(P[States.ROAD_ROLL]) # Account for the opposite signs of the yaw rates sensors_valid = bool(abs(learner.speed * (x[States.YAW_RATE] + learner.yaw_rate)) < LATERAL_ACC_SENSOR_THRESHOLD) @@ -185,12 +188,14 @@ def main(sm=None, pm=None): liveParameters.sensorValid = sensors_valid liveParameters.steerRatio = float(x[States.STEER_RATIO]) liveParameters.stiffnessFactor = float(x[States.STIFFNESS]) - liveParameters.roll = float(x[States.ROAD_ROLL]) + liveParameters.roll = roll liveParameters.angleOffsetAverageDeg = angle_offset_average liveParameters.angleOffsetDeg = angle_offset liveParameters.valid = all(( abs(liveParameters.angleOffsetAverageDeg) < 10.0, abs(liveParameters.angleOffsetDeg) < 10.0, + abs(liveParameters.roll) < ROLL_MAX, + roll_std < ROLL_STD_MAX, 0.2 <= liveParameters.stiffnessFactor <= 5.0, min_sr <= liveParameters.steerRatio <= max_sr, )) diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index b7cb6ceb33..959c213a03 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -baef183c9602b702756e2fd0781b5d289b61d19a +9ef210f7e473fa46dd43337b5f09eeabebc694b7 \ No newline at end of file From 02c751f4830b87793530732b5ac820ac48978ba9 Mon Sep 17 00:00:00 2001 From: Sean Cox Date: Wed, 1 Feb 2023 16:01:08 -0500 Subject: [PATCH 251/484] Hyundai: remove HYUNDAI_GENESIS from legacy steer max blacklist (#27176) --- selfdrive/car/hyundai/values.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 1d531c2b87..6bd4789552 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -35,7 +35,7 @@ class CarControllerParams: # To determine the limit for your car, find the maximum value that the stock LKAS will request. # If the max stock LKAS request is <384, add your car to this list. - elif CP.carFingerprint in (CAR.GENESIS_G80, CAR.GENESIS_G90, CAR.ELANTRA, CAR.HYUNDAI_GENESIS, CAR.IONIQ, + elif CP.carFingerprint in (CAR.GENESIS_G80, CAR.GENESIS_G90, CAR.ELANTRA, CAR.IONIQ, CAR.IONIQ_EV_LTD, CAR.SANTA_FE_PHEV_2022, CAR.SONATA_LF, CAR.KIA_FORTE, CAR.KIA_NIRO_PHEV, CAR.KIA_OPTIMA_H, CAR.KIA_SORENTO): self.STEER_MAX = 255 From b740bdc99c213a73628081b9f07b559420072d99 Mon Sep 17 00:00:00 2001 From: Jason Wen <47793918+sunnyhaibin@users.noreply.github.com> Date: Wed, 1 Feb 2023 16:01:26 -0500 Subject: [PATCH 252/484] Hyundai: Enable radar tracks for Santa Fe 2019 (#27175) --- selfdrive/debug/hyundai_enable_radar_points.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/selfdrive/debug/hyundai_enable_radar_points.py b/selfdrive/debug/hyundai_enable_radar_points.py index ac7e7102d0..07ce5ebddb 100755 --- a/selfdrive/debug/hyundai_enable_radar_points.py +++ b/selfdrive/debug/hyundai_enable_radar_points.py @@ -52,6 +52,10 @@ SUPPORTED_FW_VERSIONS = { b'IK__ SCC F-CUP 1.00 1.02 96400-G9100\x18\x07\x06\x17\x12 ': ConfigValues( default_config=b"\x00\x00\x00\x01\x00\x00", tracks_enabled=b"\x00\x00\x00\x01\x00\x01"), + # 2019 SANTA FE + b"TM__ SCC F-CUP 1.00 1.00 99110-S1210\x19\x01%\x168 ": ConfigValues( + default_config=b"\x00\x00\x00\x01\x00\x00", + tracks_enabled=b"\x00\x00\x00\x01\x00\x01"), } if __name__ == "__main__": From ef0e8a312768e710e060e846e34c14255ffce028 Mon Sep 17 00:00:00 2001 From: Rewat S <76684800+taperec@users.noreply.github.com> Date: Thu, 2 Feb 2023 05:17:06 +0700 Subject: [PATCH 253/484] Multilang: add missing Thai translations (#27148) * Multilang: add missing Thai translations * Multilang: update Thai translations * Multilang: add missing Thai translations --- selfdrive/ui/translations/main_th.ts | 97 ++++++++++++++++++++++------ 1 file changed, 78 insertions(+), 19 deletions(-) diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/main_th.ts index 0de0ba5f9a..ce394ecb97 100644 --- a/selfdrive/ui/translations/main_th.ts +++ b/selfdrive/ui/translations/main_th.ts @@ -238,6 +238,14 @@ Disengage to Power Off ยกเลิกระบบช่วยขับเพื่อปิดเครื่อง + + Reset + รีเซ็ต + + + Review + ทบทวน + DriveStats @@ -273,6 +281,17 @@ กำลังเปิดกล้อง + + ExperimentalModeButton + + EXPERIMENTAL MODE ON + คุณกำลังใช้โหมดทดลอง + + + CHILL MODE ON + คุณกำลังใช้โหมดชิล + + InputDialog @@ -463,6 +482,17 @@ location set จดจำ connect.comma.ai โดยการเพิ่มไปยังหน้าจอโฮม เพื่อใช้งานเหมือนเป็นแอปพลิเคชัน + + ParamControl + + Enable + เปิดใช้งาน + + + Cancel + ยกเลิก + + PrimeAdWidget @@ -585,13 +615,6 @@ location set ไม่สามารถเมานต์พาร์ติชั่นข้อมูล กดยืนยันเพื่อรีเซ็ตอุปกรณ์ของคุณ - - RichTextDialog - - Ok - ตกลง - - SettingsWindow @@ -850,6 +873,10 @@ location set Select a branch เลือก Branch + + Uninstall + ถอนการติดตั้ง + SshControl @@ -974,29 +1001,57 @@ location set Show map on left side when in split screen view. แสดงแผนที่ด้านซ้ายของหน้าจอเมื่ออยู่ในโหมดแบ่งหน้าจอ - - 🌮 End-to-end longitudinal (extremely alpha) 🌮 - 🌮 ควบคุมเร่ง/เบรคแบบ End-to-end (อยู่ขั้นพัฒนา) 🌮 - Experimental openpilot Longitudinal Control ทดลองใช้ระบบควบคุมการเร่ง/เบรคโดย openpilot - <b>WARNING: openpilot longitudinal control is experimental for this car and will disable AEB.</b> - <b>คำเตือน: การควบคุมการเร่ง/เบรคโดย openpilot สำหรับรถคันนี้ยังอยู่ในขั้นทดลอง และระบบเบรคฉุกเฉินอัตโนมัติ (AEB) จะถูกปิด</b> + Experimental Mode + โหมดทดลอง - Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would. Super experimental. - ให้ openpilot ควบคุมการเร่ง/เบรคแบบ end-to-end โดย openpilot จะขับอย่างที่มนุษย์คิด ระบบยังอยู่ในขั้นทดลอง + WARNING: openpilot longitudinal control is experimental for this car and will disable Automatic Emergency Braking (AEB). + คำเตือน: การควบคุมการเร่ง/เบรคโดย openpilot สำหรับรถคันนี้ยังอยู่ในขั้นพัฒนา และระบบเบรคฉุกเฉินอัตโนมัติ (AEB) จะถูกปิด - openpilot longitudinal control is not currently available for this car. - ขณะนี้ยังไม่มีระบบควบคุมการเร่ง/เบรคโดย openpilot สำหรับรถคันนี้ + On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when using experimental openpilot longitudinal control. + โดยปกติสำหรับรถคันนี้ openpilot จะควบคุมการเร่ง/เบรคด้วยระบบ ACC จากโรงงาน แทนการควยคุมโดย openpilot เปิดสวิตซ์นี้เพื่อให้ openpilot ควบคุมการเร่ง/เบรค แนะนำให้เปิดโหมดทดลองเมื่อต้องการให้ openpilot ควบคุมการเร่ง/เบรค ซึ่งอยู่ในขั้นพัฒนา - Enable experimental longitudinal control to enable this. - เปิดใช้งานระบบควบคุมการเร่ง/เบรคขั้นทดลอง เพื่อเปิดใช้งานสิ่งนี้ + openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: + โดยปกติ openpilot จะขับใน<b>โหมดชิล</b> เปิดโหมดทดลองเพื่อใช้<b>ความสามารถในขั้นพัฒนา</b> ซึ่งยังไม่พร้อมสำหรับโหมดชิล ความสามารถในขั้นพัฒนามีดังนี้: + + + 🌮 End-to-End Longitudinal Control 🌮 + 🌮 ควบคุมเร่ง/เบรคแบบ End-to-End 🌮 + + + Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. + ให้ openpilot ควบคุมการเร่ง/เบรค โดย openpilot จะขับอย่างที่มนุษย์คิด รวมถึงการหยุดที่ไฟแดง และป้ายหยุดรถ เนื่องจาก openpilot จะกำหนดความเร็วในการขับด้วยตัวเอง การตั้งความเร็วจะเป็นเพียงการกำหนดความเร็วสูงสูดเท่านั้น ความสามารถนี้ยังอยู่ในขั้นพัฒนา อาจเกิดข้อผิดพลาดขึ้นได้ + + + New Driving Visualization + การแสดงภาพการขับขี่แบบใหม่ + + + The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. + การแสดงภาพการขับขี่จะเปลี่ยนไปใช้กล้องมุมกว้างที่หันหน้าไปทางถนนเมื่ออยู่ในความเร็วต่ำ เพื่อแสดงภาพการเลี้ยวที่ดีขึ้น โลโก้โหมดการทดลองจะแสดงที่มุมบนขวาด้วย + + + Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. + ขณะนี้โหมดทดลองไม่สามารถใช้งานได้ในรถคันนี้ เนื่องจากเปิดใช้ระบบควบคุมการเร่ง/เบรคของรถที่ติดตั้งจากโรงงานอยู่ + + + openpilot longitudinal control may come in a future update. + ระบบควบคุมการเร่ง/เบรคโดย openpilot อาจมาในการอัปเดตในอนาคต + + + An experimental version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + เวอร์ชันทดลองของระบบควบคุมการเร่ง/เบรคโดย openpilot สามารถทดสอบได้พร้อมกับโหมดการทดลอง บน branch ที่กำลังพัฒนา + + + Enable experimental longitudinal control to allow Experimental mode. + เปิดระบบควบคุมการเร่ง/เบรคขั้นพัฒนาโดย openpilot เพื่อเปิดใช้งานโหมดทดลอง @@ -1052,5 +1107,9 @@ location set Forget Wi-Fi Network "%1"? เลิกใช้เครือข่าย Wi-Fi "%1"? + + Forget + เลิกใช้ + From 3d98cb72c08ec5d9b9ac5645ea9effecadd6bc38 Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Wed, 1 Feb 2023 15:19:40 -0700 Subject: [PATCH 254/484] Ubloxd: gps add iodc check (#27162) * gps add iodc check * add test * simplify * update ref --------- Co-authored-by: Kurt Nistelberger --- .../locationd/test/test_ublox_processing.py | 38 +++++++++++++- selfdrive/locationd/ublox_msg.cc | 52 +++++++------------ selfdrive/locationd/ublox_msg.h | 1 - selfdrive/test/process_replay/ref_commit | 2 +- 4 files changed, 55 insertions(+), 38 deletions(-) diff --git a/selfdrive/locationd/test/test_ublox_processing.py b/selfdrive/locationd/test/test_ublox_processing.py index 7aa588d43e..cd4ce0de04 100755 --- a/selfdrive/locationd/test/test_ublox_processing.py +++ b/selfdrive/locationd/test/test_ublox_processing.py @@ -1,5 +1,5 @@ import unittest - +import time import numpy as np from laika import AstroDog @@ -8,7 +8,8 @@ from laika.raw_gnss import correct_measurements, process_measurements, read_raw_ from laika.opt import calc_pos_fix from selfdrive.test.openpilotci import get_url from tools.lib.logreader import LogReader - +from selfdrive.test.helpers import with_processes +import cereal.messaging as messaging def get_gnss_measurements(log_reader): gnss_measurements = [] @@ -21,6 +22,12 @@ def get_gnss_measurements(log_reader): gnss_measurements.append(read_raw_ublox(report)) return gnss_measurements +def get_ublox_raw(log_reader): + ublox_raw = [] + for msg in log_reader: + if msg.which() == "ubloxRaw": + ublox_raw.append(msg) + return ublox_raw class TestUbloxProcessing(unittest.TestCase): NUM_TEST_PROCESS_MEAS = 10 @@ -30,6 +37,10 @@ class TestUbloxProcessing(unittest.TestCase): lr = LogReader(get_url("4cf7a6ad03080c90|2021-09-29--13-46-36", 0)) cls.gnss_measurements = get_gnss_measurements(lr) + # test gps ephemeris continuity check (drive has ephemeris issues with cutover data) + lr = LogReader(get_url("37b6542f3211019a|2023-01-15--23-45-10", 14)) + cls.ublox_raw = get_ublox_raw(lr) + def test_read_ublox_raw(self): count_gps = 0 count_glonass = 0 @@ -76,6 +87,29 @@ class TestUbloxProcessing(unittest.TestCase): self.assertEqual(count_processed_measurements, 69) self.assertEqual(count_corrected_measurements, 69) + @with_processes(['ubloxd']) + def test_ublox_gps_cutover(self): + time.sleep(2) + ugs = messaging.sub_sock("ubloxGnss", timeout=0.1) + ur_pm = messaging.PubMaster(['ubloxRaw']) + + def replay_segment(): + rcv_msgs = [] + for msg in self.ublox_raw: + ur_pm.send(msg.which(), msg.as_builder()) + time.sleep(0.01) + rcv_msgs += messaging.drain_sock(ugs) + + time.sleep(0.1) + rcv_msgs += messaging.drain_sock(ugs) + return rcv_msgs + + # replay twice to enforce cutover data on rewind + rcv_msgs = replay_segment() + rcv_msgs += replay_segment() + + ephems_cnt = sum(m.ubloxGnss.which() == 'ephemeris' for m in rcv_msgs) + self.assertEqual(ephems_cnt, 15) if __name__ == "__main__": unittest.main() diff --git a/selfdrive/locationd/ublox_msg.cc b/selfdrive/locationd/ublox_msg.cc index 4b31b0101e..127c7e56b6 100644 --- a/selfdrive/locationd/ublox_msg.cc +++ b/selfdrive/locationd/ublox_msg.cc @@ -166,26 +166,25 @@ kj::Array UbloxMsgParser::parse_gps_ephemeris(ubx_t::rxm_sfrbx_t *m { kaitai::kstream stream(subframe_data); gps_t subframe(&stream); - int subframe_id = subframe.how()->subframe_id(); - int sv_id = msg->sv_id(); - uint64_t tow_counter = subframe.how()->tow_count(); - bool clear_buffer = subframe_id == 1; - if (gps_sat_tow_count.count(sv_id) != 0) { - int64_t counter_diff = tow_counter - gps_sat_tow_count[sv_id]; - clear_buffer |= counter_diff != 1 && counter_diff != -100798; + int subframe_id = subframe.how()->subframe_id(); + if (subframe_id > 3) { + // dont parse almanac subframes + return kj::Array(); } - if (clear_buffer) gps_subframes[sv_id].clear(); - - gps_subframes[sv_id][subframe_id] = subframe_data; - gps_sat_tow_count[sv_id] = tow_counter; + gps_subframes[msg->sv_id()][subframe_id] = subframe_data; } - if (gps_subframes[msg->sv_id()].size() == 5) { + // publish if subframes 1-3 have been collected + if (gps_subframes[msg->sv_id()].size() == 3) { MessageBuilder msg_builder; auto eph = msg_builder.initEvent().initUbloxGnss().initEphemeris(); eph.setSvId(msg->sv_id()); + int iode_s2 = 0; + int iode_s3 = 0; + int iodc_lsb = 0; + // Subframe 1 { kaitai::kstream stream(gps_subframes[msg->sv_id()][1]); @@ -199,6 +198,7 @@ kj::Array UbloxMsgParser::parse_gps_ephemeris(ubx_t::rxm_sfrbx_t *m eph.setAf1(subframe_1->af_1() * pow(2, -43)); eph.setAf0(subframe_1->af_0() * pow(2, -31)); eph.setSvHealth(subframe_1->sv_health()); + iodc_lsb = subframe_1->iodc_lsb(); } // Subframe 2 @@ -215,6 +215,7 @@ kj::Array UbloxMsgParser::parse_gps_ephemeris(ubx_t::rxm_sfrbx_t *m eph.setCus(subframe_2->c_us() * pow(2, -29)); eph.setA(pow(subframe_2->sqrt_a() * pow(2, -19), 2.0)); eph.setToe(subframe_2->t_oe() * pow(2, 4)); + iode_s2 = subframe_2->iode(); } // Subframe 3 @@ -232,31 +233,14 @@ kj::Array UbloxMsgParser::parse_gps_ephemeris(ubx_t::rxm_sfrbx_t *m eph.setOmegaDot(subframe_3->omega_dot() * pow(2, -43) * gpsPi); eph.setIode(subframe_3->iode()); eph.setIDot(subframe_3->idot() * pow(2, -43) * gpsPi); + iode_s3 = subframe_3->iode(); } - // Subframe 4 - { - kaitai::kstream stream(gps_subframes[msg->sv_id()][4]); - gps_t subframe(&stream); - gps_t::subframe_4_t* subframe_4 = static_cast(subframe.body()); - - // This is page 18, why is the page id 56? - if (subframe_4->data_id() == 1 && subframe_4->page_id() == 56) { - auto iono = static_cast(subframe_4->body()); - double a0 = iono->a0() * pow(2, -30); - double a1 = iono->a1() * pow(2, -27); - double a2 = iono->a2() * pow(2, -24); - double a3 = iono->a3() * pow(2, -24); - eph.setIonoAlpha({a0, a1, a2, a3}); - - double b0 = iono->b0() * pow(2, 11); - double b1 = iono->b1() * pow(2, 14); - double b2 = iono->b2() * pow(2, 16); - double b3 = iono->b3() * pow(2, 16); - eph.setIonoBeta({b0, b1, b2, b3}); - } + gps_subframes[msg->sv_id()].clear(); + if (iodc_lsb != iode_s2 || iodc_lsb != iode_s3) { + // data set cutover, reject ephemeris + return kj::Array(); } - return capnp::messageToFlatArray(msg_builder); } return kj::Array(); diff --git a/selfdrive/locationd/ublox_msg.h b/selfdrive/locationd/ublox_msg.h index 736e6d5171..f031313168 100644 --- a/selfdrive/locationd/ublox_msg.h +++ b/selfdrive/locationd/ublox_msg.h @@ -105,7 +105,6 @@ class UbloxMsgParser { kj::Array parse_gps_ephemeris(ubx_t::rxm_sfrbx_t *msg); std::unordered_map> gps_subframes; - std::unordered_map gps_sat_tow_count; size_t bytes_in_parse_buf = 0; uint8_t msg_parse_buf[ublox::UBLOX_HEADER_SIZE + ublox::UBLOX_MAX_MSG_SIZE]; diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 959c213a03..a6c8aa682d 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -9ef210f7e473fa46dd43337b5f09eeabebc694b7 \ No newline at end of file +e825c953316277a342284a1f10b8dab88e95fc31 \ No newline at end of file From 83af2182b457692532ecc1426304782af1f9887d Mon Sep 17 00:00:00 2001 From: mrquell <48170600+mrquell@users.noreply.github.com> Date: Wed, 1 Feb 2023 20:04:48 -0500 Subject: [PATCH 255/484] Support for Kia Niro PHEV 2020 (#27158) * Update values.py * Update values.py Added support for Kia Niro PHEV 2020 * Update values.py Added support for Kia Niro PHEV 2020 * Update values.py Changed the fingerprint to be 4 seperate IDs * Apply suggestions from code review * generate * gen docs --------- Co-authored-by: Shane Smiskol --- docs/CARS.md | 3 ++- selfdrive/car/hyundai/values.py | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index ea06176178..9a33278d3b 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,7 +4,7 @@ A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system. -# 231 Supported Cars +# 232 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness|Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| @@ -107,6 +107,7 @@ A supported vehicle is one that just works when you install a comma three. All s |Kia|Niro Hybrid 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F|| |Kia|Niro Hybrid 2022|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| |Kia|Niro Plug-in Hybrid 2018-19|All|openpilot available[1](#footnotes)|10 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| +|Kia|Niro Plug-in Hybrid 2020|All|openpilot available[1](#footnotes)|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai D|| |Kia|Optima 2017|Advanced Smart Cruise Control|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai B|| |Kia|Optima 2019-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai G|| |Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|| diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 6bd4789552..b355da6cac 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -198,7 +198,10 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { HyundaiCarInfo("Kia Niro EV 2021", "All", "https://www.youtube.com/watch?v=lT7zcG6ZpGo", harness=Harness.hyundai_c), HyundaiCarInfo("Kia Niro EV 2022", "All", "https://www.youtube.com/watch?v=lT7zcG6ZpGo", harness=Harness.hyundai_h), ], - CAR.KIA_NIRO_PHEV: HyundaiCarInfo("Kia Niro Plug-in Hybrid 2018-19", "All", min_enable_speed=10. * CV.MPH_TO_MS, harness=Harness.hyundai_c), + CAR.KIA_NIRO_PHEV: [ + HyundaiCarInfo("Kia Niro Plug-in Hybrid 2018-19", "All", min_enable_speed=10. * CV.MPH_TO_MS, harness=Harness.hyundai_c), + HyundaiCarInfo("Kia Niro Plug-in Hybrid 2020", "All", harness=Harness.hyundai_d), + ], CAR.KIA_NIRO_HEV_2021: [ HyundaiCarInfo("Kia Niro Hybrid 2021", harness=Harness.hyundai_f), # TODO: could be hyundai_d, verify HyundaiCarInfo("Kia Niro Hybrid 2022", harness=Harness.hyundai_h), @@ -1231,22 +1234,27 @@ FW_VERSIONS = { (Ecu.engine, 0x7e0, None): [ b'\xf1\x816H6F4051\x00\x00\x00\x00\x00\x00\x00\x00', b'\xf1\x816H6D1051\x00\x00\x00\x00\x00\x00\x00\x00', + b'\xf1\x816H6F6051\x00\x00\x00\x00\x00\x00\x00\x00', ], (Ecu.transmission, 0x7e1, None): [ b"\xf1\x816U3J2051\x00\x00\xf1\x006U3H0_C2\x00\x006U3J2051\x00\x00PDE0G16NS2\xf4'\\\x91", b'\xf1\x816U3J2051\x00\x00\xf1\x006U3H0_C2\x00\x006U3J2051\x00\x00PDE0G16NS2\x00\x00\x00\x00', b'\xf1\x816U3H3051\x00\x00\xf1\x006U3H0_C2\x00\x006U3H3051\x00\x00PDE0G16NS1\x00\x00\x00\x00', b'\xf1\x816U3H3051\x00\x00\xf1\x006U3H0_C2\x00\x006U3H3051\x00\x00PDE0G16NS1\x13\xcd\x88\x92', + b'\xf1\x006U3H1_C2\x00\x006U3J9051\x00\x00PDE0G16NL2&[\xc3\x01', ], (Ecu.eps, 0x7D4, None): [ b'\xf1\x00DE MDPS C 1.00 1.09 56310G5301\x00 4DEHC109', + b'\xf1\x00DE MDPS C 1.00 1.01 56310G5520\x00 4DEPC101', ], (Ecu.fwdCamera, 0x7C4, None): [ b'\xf1\x00DEP MFC AT USA LHD 1.00 1.01 95740-G5010 170424', b'\xf1\x00DEP MFC AT USA LHD 1.00 1.00 95740-G5010 170117', + b'\xf1\x00DEP MFC AT USA LHD 1.00 1.05 99211-G5000 190826', ], (Ecu.fwdRadar, 0x7D0, None): [ b'\xf1\x00DEhe SCC H-CUP 1.01 1.02 96400-G5100 ', + b'\xf1\x00DEhe SCC F-CUP 1.00 1.02 99110-G5100 ', ], }, CAR.KIA_NIRO_HEV_2021: { From 0ee53532be30c740a77f20c39b672f7b0ebb755b Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Wed, 1 Feb 2023 17:33:08 -0800 Subject: [PATCH 256/484] fix pigeond device reset --- selfdrive/sensord/pigeond.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/sensord/pigeond.py b/selfdrive/sensord/pigeond.py index bb53dd1bd4..9d0a62bd3b 100755 --- a/selfdrive/sensord/pigeond.py +++ b/selfdrive/sensord/pigeond.py @@ -123,7 +123,7 @@ class TTYPigeon(): init_baudrate(self) # clear configuration - self.send_with_ack(b"\xb5\x62\x06\x09\x0d\x00\x00\x00\x1f\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x17\x71\x5b") + self.send_with_ack(b"\xb5\x62\x06\x09\x0d\x00\x1f\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17\x71\xd7") # clear flash memory (almanac backup) self.send_with_ack(b"\xB5\x62\x09\x14\x04\x00\x01\x00\x00\x00\x22\xf0") From 2a6de71cc0d369314d098a91baa9984f01b05faa Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 1 Feb 2023 18:38:00 -0800 Subject: [PATCH 257/484] get_present_ecus: check available pandas (#27181) * add num_pandas to get_present_ecus * always bugged me * move this to function signature --- selfdrive/car/car_helpers.py | 2 +- selfdrive/car/ecu_addrs.py | 2 +- selfdrive/car/fw_versions.py | 8 ++++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/selfdrive/car/car_helpers.py b/selfdrive/car/car_helpers.py index 88e8c72153..e326209096 100644 --- a/selfdrive/car/car_helpers.py +++ b/selfdrive/car/car_helpers.py @@ -99,7 +99,7 @@ def fingerprint(logcan, sendcan, num_pandas): else: cloudlog.warning("Getting VIN & FW versions") vin_rx_addr, vin = get_vin(logcan, sendcan, bus) - ecu_rx_addrs = get_present_ecus(logcan, sendcan) + ecu_rx_addrs = get_present_ecus(logcan, sendcan, num_pandas=num_pandas) car_fw = get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, num_pandas=num_pandas) cached = False diff --git a/selfdrive/car/ecu_addrs.py b/selfdrive/car/ecu_addrs.py index 9f6ace2b5f..e5d550fac8 100755 --- a/selfdrive/car/ecu_addrs.py +++ b/selfdrive/car/ecu_addrs.py @@ -87,5 +87,5 @@ if __name__ == "__main__": for addr, subaddr, bus in ecu_addrs: msg = f" 0x{hex(addr)}" if subaddr is not None: - msg += f" (sub-address: 0x{hex(subaddr)})" + msg += f" (sub-address: {hex(subaddr)})" print(msg) diff --git a/selfdrive/car/fw_versions.py b/selfdrive/car/fw_versions.py index f4d92ab960..0db260abbd 100755 --- a/selfdrive/car/fw_versions.py +++ b/selfdrive/car/fw_versions.py @@ -146,12 +146,16 @@ def match_fw_to_car(fw_versions, allow_exact=True, allow_fuzzy=True): return True, set() -def get_present_ecus(logcan, sendcan): +def get_present_ecus(logcan, sendcan, num_pandas=1) -> Set[Tuple[int, Optional[int], int]]: queries = list() parallel_queries = list() responses = set() for brand, r in REQUESTS: + # Skip query if no panda available + if r.bus > num_pandas * 4 - 1: + continue + for brand_versions in VERSIONS[brand].values(): for ecu_type, addr, sub_addr in brand_versions: # Only query ecus in whitelist if whitelist is not empty @@ -171,7 +175,7 @@ def get_present_ecus(logcan, sendcan): queries.insert(0, parallel_queries) - ecu_responses: Set[Tuple[int, Optional[int], int]] = set() + ecu_responses = set() for query in queries: ecu_responses.update(get_ecu_addrs(logcan, sendcan, set(query), responses, timeout=0.1)) return ecu_responses From 0eeb69a5d0f2009a38bf4ebdf18e0e143ff7d5f2 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 1 Feb 2023 19:50:48 -0800 Subject: [PATCH 258/484] boardd: enable fingerprinting with both multiplexed modes (#27159) Co-authored-by: Shane Smiskol --- common/params.cc | 1 + selfdrive/boardd/boardd.cc | 15 ++++++--------- selfdrive/car/car_helpers.py | 5 ++++- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/common/params.cc b/common/params.cc index 35ceecfdc3..7dd0fbcb41 100644 --- a/common/params.cc +++ b/common/params.cc @@ -111,6 +111,7 @@ std::unordered_map keys = { {"DoReboot", CLEAR_ON_MANAGER_START}, {"DoShutdown", CLEAR_ON_MANAGER_START}, {"DoUninstall", CLEAR_ON_MANAGER_START}, + {"FirmwareObdQueryDone", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON}, {"ForcePowerDown", CLEAR_ON_MANAGER_START}, {"GitBranch", PERSISTENT}, {"GitCommit", PERSISTENT}, diff --git a/selfdrive/boardd/boardd.cc b/selfdrive/boardd/boardd.cc index 5ef49d4b22..0473d3488c 100644 --- a/selfdrive/boardd/boardd.cc +++ b/selfdrive/boardd/boardd.cc @@ -106,6 +106,8 @@ void sync_time(Panda *panda, SyncTimeDir dir) { bool safety_setter_thread(std::vector pandas) { LOGD("Starting safety setter thread"); + Params p; + // there should be at least one panda connected if (pandas.size() == 0) { return false; @@ -117,25 +119,20 @@ bool safety_setter_thread(std::vector pandas) { pandas[i]->set_safety_model(cereal::CarParams::SafetyModel::ELM327, safety_param); } - Params p = Params(); - - // wait for VIN to be read + // wait for FW query at OBD port to finish while (true) { if (do_exit || !check_all_connected(pandas) || !ignition) { return false; } - std::string value_vin = p.get("CarVin"); - if (value_vin.size() > 0) { - // sanity check VIN format - assert(value_vin.size() == 17); - LOGW("got CarVin %s", value_vin.c_str()); + if (p.getBool("FirmwareObdQueryDone")) { + LOGW("finished FW query at OBD port"); break; } util::sleep_for(20); } - // set to ELM327 for ECU knockouts + // set to ELM327 to finish fingerprinting and for potential ECU knockouts for (Panda *panda : pandas) { panda->set_safety_model(cereal::CarParams::SafetyModel::ELM327, 1U); } diff --git a/selfdrive/car/car_helpers.py b/selfdrive/car/car_helpers.py index e326209096..4ccce979d3 100644 --- a/selfdrive/car/car_helpers.py +++ b/selfdrive/car/car_helpers.py @@ -113,7 +113,10 @@ def fingerprint(logcan, sendcan, num_pandas): cloudlog.event("Malformed VIN", vin=vin, error=True) vin = VIN_UNKNOWN cloudlog.warning("VIN %s", vin) - Params().put("CarVin", vin) + + params = Params() + params.put("CarVin", vin) + params.put_bool("FirmwareObdQueryDone", True) finger = gen_empty_fingerprint() candidate_cars = {i: all_legacy_fingerprint_cars() for i in [0, 1]} # attempt fingerprint on both bus 0 and 1 From e07865fe4dbe5855fd2d5a53f6770a416bbe2ee3 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 1 Feb 2023 20:11:40 -0800 Subject: [PATCH 259/484] boardd: update loopback test with new param --- selfdrive/boardd/tests/test_boardd_loopback.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/boardd/tests/test_boardd_loopback.py b/selfdrive/boardd/tests/test_boardd_loopback.py index e9bbcb4586..b8ebbd88a3 100755 --- a/selfdrive/boardd/tests/test_boardd_loopback.py +++ b/selfdrive/boardd/tests/test_boardd_loopback.py @@ -51,7 +51,7 @@ class TestBoardd(unittest.TestCase): cp.safetyConfigs = [safety_config]*num_pandas params = Params() - params.put("CarVin", b"0"*17) + params.put_bool("FirmwareObdQueryDone", True) params.put_bool("ControlsReady", True) params.put("CarParams", cp.to_bytes()) From 647e81f9bbab53439dea4b418c532c21afbbb50a Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 3 Feb 2023 02:23:21 +0800 Subject: [PATCH 260/484] cabana: show series colors in tooltip (#27186) show series colors in tooltip --- tools/cabana/chartswidget.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 47fc5dc03b..b49b20f2cc 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -652,10 +652,10 @@ void ChartView::mouseMoveEvent(QMouseEvent *ev) { auto value_pos = chart()->mapToPosition(*it); if (value_pos.x() > track_pt.x()) track_pt = value_pos; } - text_list.push_back(QString(" %1 : %2 ").arg(sigs.size() > 1 ? s.sig->name.c_str() : "Value").arg(value)); + text_list.push_back(QString("%2: %3").arg(s.series->color().name(), s.sig->name.c_str(), value)); } if (track_pt.x() == 0) track_pt = ev->pos(); - QString text = QString("
 Time: %1  
%2
") + QString text = QString("%1
%2") .arg(chart()->mapToValue(track_pt).x(), 0, 'f', 3) .arg(text_list.join("
")); QPoint pt((int)track_pt.x() + 20, plot_area.top() - 20); From 24f4ada36d21a8ec841b2d866b1dc142baae8eb2 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 2 Feb 2023 10:25:20 -0800 Subject: [PATCH 261/484] updated: fix brief notification of update for same version (#27182) --- common/params.cc | 2 +- selfdrive/updated.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/common/params.cc b/common/params.cc index 7dd0fbcb41..db5e5e700d 100644 --- a/common/params.cc +++ b/common/params.cc @@ -171,7 +171,7 @@ std::unordered_map keys = { {"Timezone", PERSISTENT}, {"TrainingVersion", PERSISTENT}, {"UbloxAvailable", PERSISTENT}, - {"UpdateAvailable", CLEAR_ON_MANAGER_START}, + {"UpdateAvailable", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON}, {"UpdateFailedCount", CLEAR_ON_MANAGER_START}, {"UpdaterState", CLEAR_ON_MANAGER_START}, {"UpdaterFetchAvailable", CLEAR_ON_MANAGER_START}, diff --git a/selfdrive/updated.py b/selfdrive/updated.py index 9da2a05a11..2cb7d1c13b 100755 --- a/selfdrive/updated.py +++ b/selfdrive/updated.py @@ -423,6 +423,9 @@ def main() -> None: wait_helper = WaitTimeHelper() wait_helper.only_check_for_update = True + # invalidate old finalized update + set_consistent_flag(False) + # Run the update loop while True: wait_helper.ready_event.clear() From 6fa9b555a435cc9639193cca516b78a22189356c Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 2 Feb 2023 11:38:09 -0800 Subject: [PATCH 262/484] rename carState.radarOffCan to radarUnavailable (#27187) * rename carState.radarOffCan to radarUnavailable * bump cereal --- cereal | 2 +- selfdrive/car/body/interface.py | 2 +- selfdrive/car/chrysler/interface.py | 2 +- selfdrive/car/ford/interface.py | 2 +- selfdrive/car/gm/carcontroller.py | 2 +- selfdrive/car/gm/interface.py | 4 ++-- selfdrive/car/gm/radar_interface.py | 2 +- selfdrive/car/honda/interface.py | 2 +- selfdrive/car/honda/radar_interface.py | 2 +- selfdrive/car/hyundai/interface.py | 2 +- selfdrive/car/hyundai/radar_interface.py | 2 +- selfdrive/car/mazda/interface.py | 2 +- selfdrive/car/nissan/interface.py | 2 +- selfdrive/car/subaru/interface.py | 2 +- selfdrive/car/tests/test_car_interfaces.py | 2 +- selfdrive/car/volkswagen/interface.py | 2 +- selfdrive/test/process_replay/process_replay.py | 2 +- 17 files changed, 18 insertions(+), 18 deletions(-) diff --git a/cereal b/cereal index b27131e72f..bdbac40160 160000 --- a/cereal +++ b/cereal @@ -1 +1 @@ -Subproject commit b27131e72f987f3bd0fcd28a42f686b583709e1f +Subproject commit bdbac40160a550c88e481a3bf700a1016901bd9b diff --git a/selfdrive/car/body/interface.py b/selfdrive/car/body/interface.py index bc5b36e2ee..a90c4cfb87 100644 --- a/selfdrive/car/body/interface.py +++ b/selfdrive/car/body/interface.py @@ -24,7 +24,7 @@ class CarInterface(CarInterfaceBase): ret.wheelSpeedFactor = SPEED_FROM_RPM ret.centerToFront = ret.wheelbase * 0.44 - ret.radarOffCan = True + ret.radarUnavailable = True ret.openpilotLongitudinalControl = True ret.steerControlType = car.CarParams.SteerControlType.angle diff --git a/selfdrive/car/chrysler/interface.py b/selfdrive/car/chrysler/interface.py index c8504d6fb1..99f1692daa 100755 --- a/selfdrive/car/chrysler/interface.py +++ b/selfdrive/car/chrysler/interface.py @@ -12,7 +12,7 @@ class CarInterface(CarInterfaceBase): ret.carName = "chrysler" ret.dashcamOnly = candidate in RAM_HD - ret.radarOffCan = DBC[candidate]['radar'] is None + ret.radarUnavailable = DBC[candidate]['radar'] is None ret.steerActuatorDelay = 0.1 ret.steerLimitTimer = 0.4 diff --git a/selfdrive/car/ford/interface.py b/selfdrive/car/ford/interface.py index 7c6a8ecbe4..8ce30e5d68 100644 --- a/selfdrive/car/ford/interface.py +++ b/selfdrive/car/ford/interface.py @@ -18,7 +18,7 @@ class CarInterface(CarInterfaceBase): # These cars are dashcam only until the port is finished ret.dashcamOnly = True - ret.radarOffCan = True + ret.radarUnavailable = True ret.steerControlType = car.CarParams.SteerControlType.angle ret.steerActuatorDelay = 0.2 ret.steerLimitTimer = 1.0 diff --git a/selfdrive/car/gm/carcontroller.py b/selfdrive/car/gm/carcontroller.py index c25de41769..56f6ff5a31 100644 --- a/selfdrive/car/gm/carcontroller.py +++ b/selfdrive/car/gm/carcontroller.py @@ -114,7 +114,7 @@ class CarController: # Radar needs to know current speed and yaw rate (50hz), # and that ADAS is alive (10hz) - if not self.CP.radarOffCan: + if not self.CP.radarUnavailable: tt = self.frame * DT_CTRL time_and_headlights_step = 10 if self.frame % time_and_headlights_step == 0: diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index 856dcbaae5..3c2a12ef86 100755 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -63,7 +63,7 @@ class CarInterface(CarInterfaceBase): if candidate in CAMERA_ACC_CAR: ret.experimentalLongitudinalAvailable = True ret.networkLocation = NetworkLocation.fwdCamera - ret.radarOffCan = True # no radar + ret.radarUnavailable = True # no radar ret.pcmCruise = True ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM ret.minEnableSpeed = 5 * CV.KPH_TO_MS @@ -85,7 +85,7 @@ class CarInterface(CarInterfaceBase): else: # ASCM, OBD-II harness ret.openpilotLongitudinalControl = True ret.networkLocation = NetworkLocation.gateway - ret.radarOffCan = False + ret.radarUnavailable = False ret.pcmCruise = False # stock non-adaptive cruise control is kept off # supports stop and go, but initial engage must (conservatively) be above 18mph ret.minEnableSpeed = 18 * CV.MPH_TO_MS diff --git a/selfdrive/car/gm/radar_interface.py b/selfdrive/car/gm/radar_interface.py index 5cb211812d..b86a85f915 100755 --- a/selfdrive/car/gm/radar_interface.py +++ b/selfdrive/car/gm/radar_interface.py @@ -36,7 +36,7 @@ class RadarInterface(RadarInterfaceBase): def __init__(self, CP): super().__init__(CP) - self.rcp = None if CP.radarOffCan else create_radar_can_parser(CP.carFingerprint) + self.rcp = None if CP.radarUnavailable else create_radar_can_parser(CP.carFingerprint) self.trigger_msg = LAST_RADAR_MSG self.updated_messages = set() diff --git a/selfdrive/car/honda/interface.py b/selfdrive/car/honda/interface.py index da921ccda7..98243d81dd 100755 --- a/selfdrive/car/honda/interface.py +++ b/selfdrive/car/honda/interface.py @@ -34,7 +34,7 @@ class CarInterface(CarInterfaceBase): if candidate in HONDA_BOSCH: ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hondaBosch)] - ret.radarOffCan = True + ret.radarUnavailable = True if candidate not in HONDA_BOSCH_RADARLESS: # Disable the radar and let openpilot control longitudinal diff --git a/selfdrive/car/honda/radar_interface.py b/selfdrive/car/honda/radar_interface.py index 629ab01d4c..660be4c449 100755 --- a/selfdrive/car/honda/radar_interface.py +++ b/selfdrive/car/honda/radar_interface.py @@ -21,7 +21,7 @@ class RadarInterface(RadarInterfaceBase): self.track_id = 0 self.radar_fault = False self.radar_wrong_config = False - self.radar_off_can = CP.radarOffCan + self.radar_off_can = CP.radarUnavailable self.radar_ts = CP.radarTimeStep self.delay = int(round(0.1 / CP.radarTimeStep)) # 0.1s delay of radar diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py index 6d6d9833df..030c3f1abc 100644 --- a/selfdrive/car/hyundai/interface.py +++ b/selfdrive/car/hyundai/interface.py @@ -20,7 +20,7 @@ class CarInterface(CarInterfaceBase): @staticmethod def _get_params(ret, candidate, fingerprint, car_fw, experimental_long): ret.carName = "hyundai" - ret.radarOffCan = RADAR_START_ADDR not in fingerprint[1] or DBC[ret.carFingerprint]["radar"] is None + ret.radarUnavailable = RADAR_START_ADDR not in fingerprint[1] or DBC[ret.carFingerprint]["radar"] is None # These cars have been put into dashcam only due to both a lack of users and test coverage. # These cars likely still work fine. Once a user confirms each car works and a test route is diff --git a/selfdrive/car/hyundai/radar_interface.py b/selfdrive/car/hyundai/radar_interface.py index 0d22611fb5..4ecca542b5 100644 --- a/selfdrive/car/hyundai/radar_interface.py +++ b/selfdrive/car/hyundai/radar_interface.py @@ -37,7 +37,7 @@ class RadarInterface(RadarInterfaceBase): self.trigger_msg = RADAR_START_ADDR + RADAR_MSG_COUNT - 1 self.track_id = 0 - self.radar_off_can = CP.radarOffCan + self.radar_off_can = CP.radarUnavailable self.rcp = get_radar_can_parser(CP) def update(self, can_strings): diff --git a/selfdrive/car/mazda/interface.py b/selfdrive/car/mazda/interface.py index fdd2439ff9..65444ff6e0 100755 --- a/selfdrive/car/mazda/interface.py +++ b/selfdrive/car/mazda/interface.py @@ -14,7 +14,7 @@ class CarInterface(CarInterfaceBase): def _get_params(ret, candidate, fingerprint, car_fw, experimental_long): ret.carName = "mazda" ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.mazda)] - ret.radarOffCan = True + ret.radarUnavailable = True ret.dashcamOnly = candidate not in (CAR.CX5_2022, CAR.CX9_2021) diff --git a/selfdrive/car/nissan/interface.py b/selfdrive/car/nissan/interface.py index 386e859089..2e769cf662 100644 --- a/selfdrive/car/nissan/interface.py +++ b/selfdrive/car/nissan/interface.py @@ -19,7 +19,7 @@ class CarInterface(CarInterfaceBase): ret.steerRatio = 17 ret.steerControlType = car.CarParams.SteerControlType.angle - ret.radarOffCan = True + ret.radarUnavailable = True if candidate in (CAR.ROGUE, CAR.XTRAIL): ret.mass = 1610 + STD_CARGO_KG diff --git a/selfdrive/car/subaru/interface.py b/selfdrive/car/subaru/interface.py index 22468801ec..f94333baab 100644 --- a/selfdrive/car/subaru/interface.py +++ b/selfdrive/car/subaru/interface.py @@ -11,7 +11,7 @@ class CarInterface(CarInterfaceBase): @staticmethod def _get_params(ret, candidate, fingerprint, car_fw, experimental_long): ret.carName = "subaru" - ret.radarOffCan = True + ret.radarUnavailable = True ret.dashcamOnly = candidate in PREGLOBAL_CARS ret.autoResumeSng = False diff --git a/selfdrive/car/tests/test_car_interfaces.py b/selfdrive/car/tests/test_car_interfaces.py index deb0454b9c..24c995a2d0 100755 --- a/selfdrive/car/tests/test_car_interfaces.py +++ b/selfdrive/car/tests/test_car_interfaces.py @@ -76,7 +76,7 @@ class TestCarInterfaces(unittest.TestCase): # Run radar interface once radar_interface.update([]) - if not car_params.radarOffCan and radar_interface.rcp is not None and \ + if not car_params.radarUnavailable and radar_interface.rcp is not None and \ hasattr(radar_interface, '_update') and hasattr(radar_interface, 'trigger_msg'): radar_interface._update([radar_interface.trigger_msg]) diff --git a/selfdrive/car/volkswagen/interface.py b/selfdrive/car/volkswagen/interface.py index b979a96d70..1e620b4f73 100644 --- a/selfdrive/car/volkswagen/interface.py +++ b/selfdrive/car/volkswagen/interface.py @@ -23,7 +23,7 @@ class CarInterface(CarInterfaceBase): @staticmethod def _get_params(ret, candidate, fingerprint, car_fw, experimental_long): ret.carName = "volkswagen" - ret.radarOffCan = True + ret.radarUnavailable = True use_off_car_defaults = len(fingerprint[0]) == 0 # Pick sensible carParams during offline doc generation/CI jobs diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 606dcad9c4..b531cb3430 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -204,7 +204,7 @@ def controlsd_rcv_callback(msg, CP, cfg, fsm): def radar_rcv_callback(msg, CP, cfg, fsm): if msg.which() != "can": return [], False - elif CP.radarOffCan: + elif CP.radarUnavailable: return ["radarState", "liveTracks"], True radar_msgs = {"honda": [0x445], "toyota": [0x19f, 0x22f], "gm": [0x474], From 4fd15096979675379b7f99320bb6fe187f9bcf6b Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Thu, 2 Feb 2023 12:48:57 -0800 Subject: [PATCH 263/484] Ford: restore radar DBC, check radarUnavailable (#27188) --- selfdrive/car/ford/radar_interface.py | 2 +- selfdrive/car/ford/values.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/car/ford/radar_interface.py b/selfdrive/car/ford/radar_interface.py index c942703002..ee4efb311d 100644 --- a/selfdrive/car/ford/radar_interface.py +++ b/selfdrive/car/ford/radar_interface.py @@ -47,7 +47,7 @@ class RadarInterface(RadarInterfaceBase): self.updated_messages = set() self.track_id = 0 self.radar = DBC[CP.carFingerprint]['radar'] - if self.radar is None: + if self.radar is None or CP.radarUnavailable: self.rcp = None elif self.radar == RADAR.DELPHI_ESR: self.rcp = _create_delphi_esr_radar_can_parser() diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index 4cabdb11e1..3723fe1d4a 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -60,7 +60,7 @@ class RADAR: DELPHI_MRR = 'FORD_CADS' -DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict("ford_lincoln_base_pt", None)) +DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict("ford_lincoln_base_pt", RADAR.DELPHI_MRR)) @dataclass From cb50244a89fde385f55b2f6ad3ecd83c9b8f5c3a Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 2 Feb 2023 13:03:53 -0800 Subject: [PATCH 264/484] Bump total scons nodes --- selfdrive/manager/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/manager/build.py b/selfdrive/manager/build.py index c8a7d41539..5b69e3dca7 100755 --- a/selfdrive/manager/build.py +++ b/selfdrive/manager/build.py @@ -15,7 +15,7 @@ from system.version import is_dirty MAX_CACHE_SIZE = 4e9 if "CI" in os.environ else 2e9 CACHE_DIR = Path("/data/scons_cache" if AGNOS else "/tmp/scons_cache") -TOTAL_SCONS_NODES = 2395 +TOTAL_SCONS_NODES = 2460 MAX_BUILD_PROGRESS = 100 PREBUILT = os.path.exists(os.path.join(BASEDIR, 'prebuilt')) From 6b04ead6dd8fc89540c9d0000a2aa262c6b420e1 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 3 Feb 2023 05:14:36 +0800 Subject: [PATCH 265/484] cabana: remove the hardcoded width for columns (#27185) --- tools/cabana/messageswidget.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index 0526ff141f..9011ce0a4d 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -26,9 +26,6 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { table_widget->setSelectionMode(QAbstractItemView::SingleSelection); table_widget->setSortingEnabled(true); table_widget->sortByColumn(0, Qt::AscendingOrder); - table_widget->setColumnWidth(0, 250); - table_widget->setColumnWidth(1, 80); - table_widget->setColumnWidth(2, 80); table_widget->horizontalHeader()->setStretchLastSection(true); table_widget->verticalHeader()->hide(); From 628b80de3d1ac9b5556fcd4c60818d99b4b76af9 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 3 Feb 2023 05:14:53 +0800 Subject: [PATCH 266/484] cabana: improve line marker (#27189) --- tools/cabana/chartswidget.cc | 42 ++++++++++++++++++++---------------- tools/cabana/chartswidget.h | 2 +- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index b49b20f2cc..ca56b5810e 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -589,7 +589,7 @@ qreal ChartView::niceNumber(qreal x, bool ceiling) { } void ChartView::leaveEvent(QEvent *event) { - track_pt = {0, 0}; + track_pts.clear(); scene()->update(); QChartView::leaveEvent(event); } @@ -639,27 +639,26 @@ void ChartView::mouseMoveEvent(QMouseEvent *ev) { auto rubber = findChild(); bool is_zooming = rubber && rubber->isVisible(); const auto plot_area = chart()->plotArea(); - track_pt = {0, 0}; + track_pts.clear(); if (!is_zooming && plot_area.contains(ev->pos())) { + track_pts.resize(sigs.size()); QStringList text_list; const double sec = chart()->mapToValue(ev->pos()).x(); - for (auto &s : sigs) { + for (int i = 0; i < sigs.size(); ++i) { QString value = "--"; // use reverse iterator to find last item <= sec. - auto it = std::lower_bound(s.vals.rbegin(), s.vals.rend(), sec, [](auto &p, double x) { return p.x() > x; }); - if (it != s.vals.rend() && it->x() >= axis_x->min()) { + auto it = std::lower_bound(sigs[i].vals.rbegin(), sigs[i].vals.rend(), sec, [](auto &p, double x) { return p.x() > x; }); + if (it != sigs[i].vals.rend() && it->x() >= axis_x->min()) { value = QString::number(it->y()); - auto value_pos = chart()->mapToPosition(*it); - if (value_pos.x() > track_pt.x()) track_pt = value_pos; + track_pts[i] = chart()->mapToPosition(*it); } - text_list.push_back(QString("%2: %3").arg(s.series->color().name(), s.sig->name.c_str(), value)); + text_list.push_back(QString("%2: %3").arg(sigs[i].series->color().name(), sigs[i].sig->name.c_str(), value)); } - if (track_pt.x() == 0) track_pt = ev->pos(); - QString text = QString("%1
%2") - .arg(chart()->mapToValue(track_pt).x(), 0, 'f', 3) - .arg(text_list.join("
")); - QPoint pt((int)track_pt.x() + 20, plot_area.top() - 20); - QToolTip::showText(mapToGlobal(pt), text, this, plot_area.toRect()); + auto max = std::max_element(track_pts.begin(), track_pts.end(), [](auto &a, auto &b) { return a.x() < b.x(); }); + auto pt = (max == track_pts.end()) ? ev->pos() : *max; + text_list.push_front(QString::number(chart()->mapToValue(pt).x(), 'f', 3)); + QPointF tooltip_pt(pt.x() + 12, plot_area.top() - 20); + QToolTip::showText(mapToGlobal(tooltip_pt.toPoint()), pt.isNull() ? "" : text_list.join("
"), this, plot_area.toRect()); scene()->update(); } else { QToolTip::hideText(); @@ -712,11 +711,18 @@ void ChartView::drawForeground(QPainter *painter, const QRectF &rect) { qreal y2 = chart()->plotArea().bottom() + 2; painter->setPen(QPen(chart()->titleBrush().color(), 2)); painter->drawLine(QPointF{x, y1}, QPointF{x, y2}); - if (!track_pt.isNull()) { + + auto max = std::max_element(track_pts.begin(), track_pts.end(), [](auto &a, auto &b) { return a.x() < b.x(); }); + if (max != track_pts.end() && !max->isNull()) { painter->setPen(QPen(Qt::darkGray, 1, Qt::DashLine)); - painter->drawLine(QPointF{track_pt.x(), y1}, QPointF{track_pt.x(), y2}); - painter->setBrush(Qt::darkGray); - painter->drawEllipse(track_pt, 5, 5); + painter->drawLine(QPointF{max->x(), y1}, QPointF{max->x(), y2}); + painter->setPen(Qt::NoPen); + for (int i = 0; i < track_pts.size(); ++i) { + if (!track_pts[i].isNull() && i < sigs.size()) { + painter->setBrush(sigs[i].series->color().darker(125)); + painter->drawEllipse(track_pts[i], 5.5, 5.5); + } + } } } diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index d5572756c1..41b2ddffcc 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -73,7 +73,7 @@ private: QValueAxis *axis_x; QValueAxis *axis_y; - QPointF track_pt; + QVector track_pts; QGraphicsProxyWidget *close_btn_proxy; QGraphicsProxyWidget *manage_btn_proxy; QList sigs; From 9ece098098d571224ebf0a5b75e069416ca9d7f3 Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Thu, 2 Feb 2023 22:15:11 +0100 Subject: [PATCH 267/484] cabana: do not round time when seeking from chartswidget (#27141) * cabana: do not round time when seeking from chartswidget * prevent zooming/seeking past end of route --- tools/cabana/chartswidget.cc | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index ca56b5810e..f0a81fe65d 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -617,14 +617,21 @@ void ChartView::mouseReleaseEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton && rubber && rubber->isVisible()) { rubber->hide(); QRectF rect = rubber->geometry().normalized(); - double min = std::floor(chart()->mapToValue(rect.topLeft()).x() * 10.0) / 10.0; - double max = std::floor(chart()->mapToValue(rect.bottomRight()).x() * 10.0) / 10.0; + double min = chart()->mapToValue(rect.topLeft()).x(); + double max = chart()->mapToValue(rect.bottomRight()).x(); + + // Prevent zooming/seeking past the end of the route + min = std::clamp(min, can->routeStartTime(), can->routeStartTime() + can->totalSeconds()); + max = std::clamp(max, can->routeStartTime(), can->routeStartTime() + can->totalSeconds()); + + double min_rounded = std::floor(min * 10.0) / 10.0; + double max_rounded = std::floor(max * 10.0) / 10.0; if (rubber->width() <= 0) { // no rubber dragged, seek to mouse position can->seekTo(min); - } else if ((max - min) >= 0.5) { + } else if ((max_rounded - min_rounded) >= 0.5) { // zoom in if selected range is greater than 0.5s - emit zoomIn(min, max); + emit zoomIn(min_rounded, max_rounded); } event->accept(); } else if (!can->liveStreaming() && event->button() == Qt::RightButton) { From e3c202b4a2d79f6843aa54ec9a1ef81e39da2731 Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Thu, 2 Feb 2023 22:16:09 +0100 Subject: [PATCH 268/484] cabana: add button to suppress highlighted bytes (#27131) --- tools/cabana/historylog.cc | 8 ++-- tools/cabana/historylog.h | 2 +- tools/cabana/messageswidget.cc | 65 ++++++++++++++++++++++++-- tools/cabana/messageswidget.h | 10 +++- tools/cabana/streams/abstractstream.cc | 6 ++- tools/cabana/streams/abstractstream.h | 1 + tools/cabana/util.cc | 7 ++- tools/cabana/util.h | 13 +++--- 8 files changed, 91 insertions(+), 21 deletions(-) diff --git a/tools/cabana/historylog.cc b/tools/cabana/historylog.cc index 830390f4a8..ab5c6cbbe0 100644 --- a/tools/cabana/historylog.cc +++ b/tools/cabana/historylog.cc @@ -17,7 +17,7 @@ QVariant HistoryLogModel::data(const QModelIndex &index, int role) const { } return show_signals ? QString::number(m.sig_values[index.column() - 1]) : toHex(m.data); } else if (role == Qt::UserRole && index.column() == 1 && !show_signals) { - return HexColors::toVariantList(m.colors); + return ChangeTracker::toVariantList(m.colors); } return {}; } @@ -142,7 +142,8 @@ std::deque HistoryLogModel::fetchData(uint64_t from_ti auto msgs = fetchData(first, events->rend(), min_time); if (update_colors && min_time > 0) { for (auto it = msgs.rbegin(); it != msgs.rend(); ++it) { - it->colors = hex_colors.compute(it->data, it->mono_time / (double)1e9, freq); + hex_colors.compute(it->data, it->mono_time / (double)1e9, freq); + it->colors = hex_colors.colors; } } return msgs; @@ -152,7 +153,8 @@ std::deque HistoryLogModel::fetchData(uint64_t from_ti auto msgs = fetchData(first, events->end(), 0); if (update_colors) { for (auto it = msgs.rbegin(); it != msgs.rend(); ++it) { - it->colors = hex_colors.compute(it->data, it->mono_time / (double)1e9, freq); + hex_colors.compute(it->data, it->mono_time / (double)1e9, freq); + it->colors = hex_colors.colors; } } return msgs; diff --git a/tools/cabana/historylog.h b/tools/cabana/historylog.h index bc2e0e3914..2458fc1c31 100644 --- a/tools/cabana/historylog.h +++ b/tools/cabana/historylog.h @@ -53,7 +53,7 @@ public: std::deque fetchData(uint64_t from_time, uint64_t min_time = 0); QString msg_id; - HexColors hex_colors; + ChangeTracker hex_colors; bool has_more_data = true; const int batch_size = 50; int filter_sig_idx = -1; diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index 9011ce0a4d..64f56ae73f 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -1,10 +1,12 @@ #include "tools/cabana/messageswidget.h" +#include #include +#include #include -#include #include -#include +#include +#include #include "tools/cabana/dbcmanager.h" @@ -28,9 +30,16 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { table_widget->sortByColumn(0, Qt::AscendingOrder); table_widget->horizontalHeader()->setStretchLastSection(true); table_widget->verticalHeader()->hide(); - main_layout->addWidget(table_widget); + // suppress + QHBoxLayout *suppress_layout = new QHBoxLayout(); + suppress_add = new QPushButton("Suppress Highlighted"); + suppress_clear = new QPushButton(); + suppress_layout->addWidget(suppress_add); + suppress_layout->addWidget(suppress_clear); + main_layout->addLayout(suppress_layout); + // signals/slots QObject::connect(filter, &QLineEdit::textChanged, model, &MessageListModel::setFilterString); QObject::connect(can, &AbstractStream::msgsReceived, model, &MessageListModel::msgsReceived); @@ -46,6 +55,16 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { } } }); + QObject::connect(suppress_add, &QPushButton::clicked, [=]() { + model->suppress(); + updateSuppressedButtons(); + }); + QObject::connect(suppress_clear, &QPushButton::clicked, [=]() { + model->clearSuppress(); + updateSuppressedButtons(); + }); + + updateSuppressedButtons(); } void MessagesWidget::selectMessage(const QString &msg_id) { @@ -54,6 +73,17 @@ void MessagesWidget::selectMessage(const QString &msg_id) { } } +void MessagesWidget::updateSuppressedButtons() { + if (model->suppressed_bytes.empty()) { + suppress_clear->setEnabled(false); + suppress_clear->setText("Clear Suppressed"); + } else { + suppress_clear->setEnabled(true); + suppress_clear->setText(QString("Clear Suppressed (%1)").arg(model->suppressed_bytes.size())); + } +} + + // MessageListModel QVariant MessageListModel::headerData(int section, Qt::Orientation orientation, int role) const { @@ -75,7 +105,16 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const { case 4: return toHex(can_data.dat); } } else if (role == Qt::UserRole && index.column() == 4) { - return HexColors::toVariantList(can_data.colors); + QList colors; + for (int i = 0; i < can_data.dat.size(); i++){ + if (suppressed_bytes.contains({id, i})) { + colors.append(QColor(255, 255, 255, 0)); + } else { + colors.append(can_data.colors[i]); + } + } + return colors; + } return {}; } @@ -157,3 +196,21 @@ void MessageListModel::sort(int column, Qt::SortOrder order) { sortMessages(); } } + +void MessageListModel::suppress() { + const double cur_ts = can->currentSec(); + + for (auto &id : msgs) { + auto &can_data = can->lastMessage(id); + for (int i = 0; i < can_data.dat.size(); i++) { + const double dt = cur_ts - can_data.last_change_t[i]; + if (dt < 2.0) { + suppressed_bytes.insert({id, i}); + } + } + } +} + +void MessageListModel::clearSuppress() { + suppressed_bytes.clear(); +} diff --git a/tools/cabana/messageswidget.h b/tools/cabana/messageswidget.h index 69fdd26170..1927faa646 100644 --- a/tools/cabana/messageswidget.h +++ b/tools/cabana/messageswidget.h @@ -2,8 +2,9 @@ #include #include -#include +#include #include +#include #include "tools/cabana/streams/abstractstream.h" @@ -20,7 +21,10 @@ public: void setFilterString(const QString &string); void msgsReceived(const QHash *new_msgs = nullptr); void sortMessages(); + void suppress(); + void clearSuppress(); QStringList msgs; + QSet> suppressed_bytes; private: QString filter_str; @@ -36,6 +40,7 @@ public: void selectMessage(const QString &message_id); QByteArray saveHeaderState() const { return table_widget->horizontalHeader()->saveState(); } bool restoreHeaderState(const QByteArray &state) const { return table_widget->horizontalHeader()->restoreState(state); } + void updateSuppressedButtons(); signals: void msgSelectionChanged(const QString &message_id); @@ -44,4 +49,7 @@ protected: QTableView *table_widget; QString current_msg_id; MessageListModel *model; + QPushButton *suppress_add; + QPushButton *suppress_clear; + }; diff --git a/tools/cabana/streams/abstractstream.cc b/tools/cabana/streams/abstractstream.cc index b188c41826..308e5556c6 100644 --- a/tools/cabana/streams/abstractstream.cc +++ b/tools/cabana/streams/abstractstream.cc @@ -19,7 +19,7 @@ void AbstractStream::process(QHash *messages) { bool AbstractStream::updateEvent(const Event *event) { static std::unique_ptr new_msgs = std::make_unique>(); - static QHash hex_colors; + static QHash change_trackers; static double prev_update_ts = 0; if (event->which == cereal::Event::Which::CAN) { @@ -42,7 +42,9 @@ bool AbstractStream::updateEvent(const Event *event) { if (double delta = (current_sec - counters_begin_sec); delta > 0) { data.freq = data.count / delta; } - data.colors = hex_colors[id].compute(data.dat, data.ts, data.freq); + change_trackers[id].compute(data.dat, data.ts, data.freq); + data.colors = change_trackers[id].colors; + data.last_change_t = change_trackers[id].last_change_t; } double ts = millis_since_boot(); diff --git a/tools/cabana/streams/abstractstream.h b/tools/cabana/streams/abstractstream.h index 9a0187da5e..e16e11a14b 100644 --- a/tools/cabana/streams/abstractstream.h +++ b/tools/cabana/streams/abstractstream.h @@ -17,6 +17,7 @@ struct CanData { uint32_t freq = 0; QByteArray dat; QVector colors; + QVector last_change_t; }; class AbstractStream : public QObject { diff --git a/tools/cabana/util.cc b/tools/cabana/util.cc index 699aa7dc01..f9d19dfda3 100644 --- a/tools/cabana/util.cc +++ b/tools/cabana/util.cc @@ -10,7 +10,7 @@ static QColor blend(QColor a, QColor b) { return QColor((a.red() + b.red()) / 2, (a.green() + b.green()) / 2, (a.blue() + b.blue()) / 2, (a.alpha() + b.alpha()) / 2); } -const QVector &HexColors::compute(const QByteArray &dat, double ts, uint32_t freq) { +void ChangeTracker::compute(const QByteArray &dat, double ts, uint32_t freq) { if (prev_dat.size() != dat.size()) { colors.resize(dat.size()); last_change_t.resize(dat.size()); @@ -45,16 +45,15 @@ const QVector &HexColors::compute(const QByteArray &dat, double ts, uint } prev_dat = dat; - return colors; } -void HexColors::clear() { +void ChangeTracker::clear() { prev_dat.clear(); last_change_t.clear(); colors.clear(); } -QList HexColors::toVariantList(const QVector &colors) { +QList ChangeTracker::toVariantList(const QVector &colors) { QList ret; ret.reserve(colors.size()); for (auto &c : colors) ret.append(c); diff --git a/tools/cabana/util.h b/tools/cabana/util.h index a598726bb6..b76652f4b0 100644 --- a/tools/cabana/util.h +++ b/tools/cabana/util.h @@ -7,19 +7,20 @@ #include #include -class HexColors { +class ChangeTracker { public: - const QVector &compute(const QByteArray &dat, double ts, uint32_t freq); - static QList toVariantList(const QVector &colors); - void clear(); + void compute(const QByteArray &dat, double ts, uint32_t freq); + static QList toVariantList(const QVector &colors); + void clear(); + + QVector last_change_t; + QVector colors; private: const int periodic_threshold = 10; const int start_alpha = 128; const float fade_time = 2.0; QByteArray prev_dat; - QVector last_change_t; - QVector colors; }; class MessageBytesDelegate : public QStyledItemDelegate { From 53396076dfaa857da735d9943fc7e33cefdadf69 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 3 Feb 2023 05:16:32 +0800 Subject: [PATCH 269/484] cabana: add capability for switching between line and scatter plots (#27169) * add capability for switching between line and scatter plots * Update tools/cabana/chartswidget.cc Co-authored-by: Willem Melching * setUserOpengl in createSeries * update series title * set marker size by pixels_per_point * sync menu state * cleanup * set default series type in settings dlg * remove qdebug --------- Co-authored-by: Willem Melching --- tools/cabana/chartswidget.cc | 84 +++++++++++++++++++++++++++++------- tools/cabana/chartswidget.h | 8 +++- tools/cabana/settings.cc | 8 ++++ tools/cabana/settings.h | 2 + 4 files changed, 86 insertions(+), 16 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index f0a81fe65d..e49d28db44 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -178,6 +179,7 @@ void ChartsWidget::settingChanged() { range_slider->setRange(1, settings.max_cached_minutes * 60); for (auto c : charts) { c->setFixedHeight(settings.chart_height); + c->setSeriesType(settings.chart_series_type == 0 ? QAbstractSeries::SeriesTypeLine : QAbstractSeries::SeriesTypeScatter); } } @@ -297,6 +299,8 @@ bool ChartsWidget::eventFilter(QObject *obj, QEvent *event) { // ChartView ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { + series_type = settings.chart_series_type == 0 ? QAbstractSeries::SeriesTypeLine : QAbstractSeries::SeriesTypeScatter; + QChart *chart = new QChart(); chart->setBackgroundRoundness(0); axis_x = new QValueAxis(this); @@ -317,9 +321,20 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { close_btn_proxy->setZValue(chart->zValue() + 11); QToolButton *manage_btn = new QToolButton(); + manage_btn->setToolButtonStyle(Qt::ToolButtonIconOnly); manage_btn->setIcon(utils::icon("gear")); manage_btn->setAutoRaise(true); - manage_btn->setToolTip(tr("Manage series")); + QMenu *menu = new QMenu(this); + line_series_action = menu->addAction(tr("Line"), [this]() { setSeriesType(QAbstractSeries::SeriesTypeLine); }); + line_series_action->setCheckable(true); + line_series_action->setChecked(series_type == QAbstractSeries::SeriesTypeLine); + scatter_series_action = menu->addAction(tr("Scatter"), [this]() { setSeriesType(QAbstractSeries::SeriesTypeScatter); }); + scatter_series_action->setCheckable(true); + scatter_series_action->setChecked(series_type == QAbstractSeries::SeriesTypeScatter); + menu->addSeparator(); + menu->addAction(tr("Manage series"), this, &ChartView::manageSeries); + manage_btn->setMenu(menu); + manage_btn->setPopupMode(QToolButton::InstantPopup); manage_btn_proxy = new QGraphicsProxyWidget(chart); manage_btn_proxy->setWidget(manage_btn); manage_btn_proxy->setZValue(chart->zValue() + 11); @@ -334,7 +349,6 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { QObject::connect(dbc(), &DBCManager::msgRemoved, this, &ChartView::msgRemoved); QObject::connect(dbc(), &DBCManager::msgUpdated, this, &ChartView::msgUpdated); QObject::connect(remove_btn, &QToolButton::clicked, this, &ChartView::remove); - QObject::connect(manage_btn, &QToolButton::clicked, this, &ChartView::manageSeries); } qreal ChartView::getYAsixLabelWidth() const { @@ -354,8 +368,7 @@ void ChartView::setPlotAreaLeftPosition(int pos) { } void ChartView::addSeries(const QString &msg_id, const Signal *sig) { - QLineSeries *series = new QLineSeries(this); - + QXYSeries *series = createSeries(series_type); chart()->addSeries(series); series->attachAxis(axis_x); series->attachAxis(axis_y); @@ -478,22 +491,26 @@ void ChartView::updatePlot(double cur, double min, double max) { int num_points = std::max(end - begin, 1); int pixels_per_point = width() / num_points; - s.series->setPointsVisible(pixels_per_point > 20); + if (series_type == QAbstractSeries::SeriesTypeScatter) { + ((QScatterSeries *)s.series)->setMarkerSize(std::clamp(pixels_per_point / 3, 1, 8)); + } else { + s.series->setPointsVisible(pixels_per_point > 20); - // TODO: On MacOS QChartWidget doesn't work with the OpenGL settings that CameraWidget needs. + // TODO: On MacOS QChartWidget doesn't work with the OpenGL settings that CameraWidget needs. #ifndef __APPLE - // OpenGL mode lacks certain features (such as showing points), only use when drawing many points - bool use_opengl = pixels_per_point < 1; - s.series->setUseOpenGL(use_opengl); + // OpenGL mode lacks certain features (such as showing points), only use when drawing many points + bool use_opengl = pixels_per_point < 1; + s.series->setUseOpenGL(use_opengl); - // Qt doesn't properly apply device pixel ratio in OpenGL mode - QApplication* application = static_cast(QApplication::instance()); - float scale = use_opengl ? application->devicePixelRatio() : 1.0; + // Qt doesn't properly apply device pixel ratio in OpenGL mode + QApplication *application = static_cast(QApplication::instance()); + float scale = use_opengl ? application->devicePixelRatio() : 1.0; - QPen pen = s.series->pen(); - pen.setWidth(2.0 * scale); - s.series->setPen(pen); + QPen pen = s.series->pen(); + pen.setWidth(2.0 * scale); + s.series->setPen(pen); #endif + } } } @@ -733,6 +750,43 @@ void ChartView::drawForeground(QPainter *painter, const QRectF &rect) { } } +QXYSeries *ChartView::createSeries(QAbstractSeries::SeriesType type) { + QXYSeries *series = nullptr; + if (type == QAbstractSeries::SeriesTypeLine) { + series = new QLineSeries(this); + } else { + series = new QScatterSeries(this); + } + // TODO: Due to a bug in CameraWidget the camera frames + // are drawn instead of the graphs on MacOS. Re-enable OpenGL when fixed +#ifndef __APPLE__ + series->setUseOpenGL(true); +#endif + return series; +} + +void ChartView::setSeriesType(QAbstractSeries::SeriesType type) { + if (type != series_type) { + series_type = type; + line_series_action->setChecked(type == QAbstractSeries::SeriesTypeLine); + scatter_series_action->setChecked(type == QAbstractSeries::SeriesTypeScatter); + + for (auto &s : sigs) { + chart()->removeSeries(s.series); + s.series->deleteLater(); + } + for (auto &s : sigs) { + auto series = createSeries(series_type); + chart()->addSeries(series); + series->attachAxis(axis_x); + series->attachAxis(axis_y); + series->replace(s.vals); + s.series = series; + } + updateTitle(); + } +} + // SeriesSelector SeriesSelector::SeriesSelector(QWidget *parent) { diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index 41b2ddffcc..9b2afd45a9 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "tools/cabana/dbcmanager.h" @@ -30,13 +31,14 @@ public: void updatePlot(double cur, double min, double max); void setPlotAreaLeftPosition(int pos); qreal getYAsixLabelWidth() const; + void setSeriesType(QAbstractSeries::SeriesType type); struct SigItem { QString msg_id; uint8_t source = 0; uint32_t address = 0; const Signal *sig = nullptr; - QLineSeries *series = nullptr; + QXYSeries *series = nullptr; QVector vals; uint64_t last_value_mono_time = 0; }; @@ -70,6 +72,7 @@ private: void drawForeground(QPainter *painter, const QRectF &rect) override; void applyNiceNumbers(qreal min, qreal max); qreal niceNumber(qreal x, bool ceiling); + QXYSeries *createSeries(QAbstractSeries::SeriesType type); QValueAxis *axis_x; QValueAxis *axis_y; @@ -79,6 +82,9 @@ private: QList sigs; double cur_sec = 0; const QString mime_type = "application/x-cabanachartview"; + QAbstractSeries::SeriesType series_type = QAbstractSeries::SeriesTypeLine; + QAction *line_series_action; + QAction *scatter_series_action; }; class ChartsWidget : public QWidget { diff --git a/tools/cabana/settings.cc b/tools/cabana/settings.cc index 78969c76c3..22c7a941ab 100644 --- a/tools/cabana/settings.cc +++ b/tools/cabana/settings.cc @@ -25,6 +25,7 @@ void Settings::save() { s.setValue("video_splitter_state", video_splitter_state); s.setValue("recent_files", recent_files); s.setValue("message_header_state", message_header_state); + s.setValue("chart_series_type", chart_series_type); } void Settings::load() { @@ -40,6 +41,7 @@ void Settings::load() { video_splitter_state = s.value("video_splitter_state").toByteArray(); recent_files = s.value("recent_files").toStringList(); message_header_state = s.value("message_header_state").toByteArray(); + chart_series_type = s.value("chart_series_type", 0).toInt(); } // SettingsDlg @@ -60,6 +62,11 @@ SettingsDlg::SettingsDlg(QWidget *parent) : QDialog(parent) { cached_minutes->setValue(settings.max_cached_minutes); form_layout->addRow(tr("Max Cached Minutes"), cached_minutes); + chart_series_type = new QComboBox(this); + chart_series_type->addItems({tr("Line"), tr("Scatter")}); + chart_series_type->setCurrentIndex(settings.chart_series_type); + form_layout->addRow(tr("Chart Default Series Type"), chart_series_type); + chart_height = new QSpinBox(this); chart_height->setRange(100, 500); chart_height->setSingleStep(10); @@ -77,6 +84,7 @@ SettingsDlg::SettingsDlg(QWidget *parent) : QDialog(parent) { void SettingsDlg::save() { settings.fps = fps->value(); settings.max_cached_minutes = cached_minutes->value(); + settings.chart_series_type = chart_series_type->currentIndex(); settings.chart_height = chart_height->value(); settings.save(); accept(); diff --git a/tools/cabana/settings.h b/tools/cabana/settings.h index 56c6a992ae..7abad81c29 100644 --- a/tools/cabana/settings.h +++ b/tools/cabana/settings.h @@ -18,6 +18,7 @@ public: int chart_height = 200; int chart_column_count = 1; int chart_range = 3 * 60; // e minutes + int chart_series_type = 0; QString last_dir; QByteArray geometry; QByteArray video_splitter_state; @@ -38,6 +39,7 @@ public: QSpinBox *fps; QSpinBox *cached_minutes; QSpinBox *chart_height; + QComboBox *chart_series_type; }; extern Settings settings; From 444df54a70c8d0fb31c1a2474053c862e4efb61b Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 2 Feb 2023 13:46:44 -0800 Subject: [PATCH 270/484] Chrysler: disable radar parsing (#27190) * Chrysler: disable radar parsing * update refs --- selfdrive/car/chrysler/interface.py | 4 ++-- selfdrive/car/chrysler/radar_interface.py | 3 ++- selfdrive/test/process_replay/ref_commit | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/selfdrive/car/chrysler/interface.py b/selfdrive/car/chrysler/interface.py index 99f1692daa..da086105e3 100755 --- a/selfdrive/car/chrysler/interface.py +++ b/selfdrive/car/chrysler/interface.py @@ -2,7 +2,7 @@ from cereal import car from panda import Panda from selfdrive.car import STD_CARGO_KG, get_safety_config -from selfdrive.car.chrysler.values import CAR, DBC, RAM_HD, RAM_DT, RAM_CARS, ChryslerFlags +from selfdrive.car.chrysler.values import CAR, RAM_HD, RAM_DT, RAM_CARS, ChryslerFlags from selfdrive.car.interfaces import CarInterfaceBase @@ -12,7 +12,7 @@ class CarInterface(CarInterfaceBase): ret.carName = "chrysler" ret.dashcamOnly = candidate in RAM_HD - ret.radarUnavailable = DBC[candidate]['radar'] is None + ret.radarUnavailable = True # DBC[candidate]['radar'] is None ret.steerActuatorDelay = 0.1 ret.steerLimitTimer = 0.4 diff --git a/selfdrive/car/chrysler/radar_interface.py b/selfdrive/car/chrysler/radar_interface.py index 348e3c3632..0ab8c10b44 100755 --- a/selfdrive/car/chrysler/radar_interface.py +++ b/selfdrive/car/chrysler/radar_interface.py @@ -45,12 +45,13 @@ def _address_to_track(address): class RadarInterface(RadarInterfaceBase): def __init__(self, CP): super().__init__(CP) + self.CP = CP self.rcp = _create_radar_can_parser(CP.carFingerprint) self.updated_messages = set() self.trigger_msg = LAST_MSG def update(self, can_strings): - if self.rcp is None: + if self.rcp is None or self.CP.radarUnavailable: return super().update(None) vls = self.rcp.update_strings(can_strings) diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index a6c8aa682d..c49ed64c65 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -e825c953316277a342284a1f10b8dab88e95fc31 \ No newline at end of file +78f00e30719a6af9644b2cbd0c76d2e652e95ccd \ No newline at end of file From 20fb2b9bb3f06187665928eda6e740db29b45e5b Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Thu, 2 Feb 2023 14:28:45 -0800 Subject: [PATCH 271/484] ui: tap experimental mode icon to toggle (#27064) * ui: tap experimental mode icon to toggle After experimental mode has been enabled for the first time (confirmed), it can be toggled by tapping the engage-ability/experimental mode icon in the upper right. Closes #27002 * replace with QPushButton * fixes * cleanup --- selfdrive/ui/qt/onroad.cc | 72 ++++++++++++++++++++++++++++++++------- selfdrive/ui/qt/onroad.h | 29 ++++++++++++---- 2 files changed, 81 insertions(+), 20 deletions(-) diff --git a/selfdrive/ui/qt/onroad.cc b/selfdrive/ui/qt/onroad.cc index 1b4b880fbf..7a4b4240c3 100644 --- a/selfdrive/ui/qt/onroad.cc +++ b/selfdrive/ui/qt/onroad.cc @@ -175,11 +175,62 @@ void OnroadAlerts::paintEvent(QPaintEvent *event) { } +ExperimentalButton::ExperimentalButton(QWidget *parent) : QPushButton(parent) { + setVisible(false); + setFixedSize(btn_size, btn_size); + setCheckable(true); + + params = Params(); + engage_img = loadPixmap("../assets/img_chffr_wheel.png", {img_size, img_size}); + experimental_img = loadPixmap("../assets/img_experimental.svg", {img_size, img_size}); + + QObject::connect(this, &QPushButton::toggled, [=](bool checked) { + params.putBool("ExperimentalMode", checked); + }); +} + +void ExperimentalButton::updateState(const UIState &s) { + const SubMaster &sm = *(s.sm); + + // button is "visible" if engageable or enabled + const auto cs = sm["controlsState"].getControlsState(); + setVisible(cs.getEngageable() || cs.getEnabled()); + + // button is "checked" if experimental mode is enabled + setChecked(sm["controlsState"].getControlsState().getExperimentalMode()); + + // disable button when experimental mode is not available, or has not been confirmed for the first time + const auto cp = sm["carParams"].getCarParams(); + const bool experimental_mode_available = cp.getExperimentalLongitudinalAvailable() ? params.getBool("ExperimentalLongitudinalEnabled") : cp.getOpenpilotLongitudinalControl(); + setEnabled(params.getBool("ExperimentalModeConfirmed") && experimental_mode_available); +} + +void ExperimentalButton::paintEvent(QPaintEvent *event) { + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + + QPoint center(btn_size / 2, btn_size / 2); + QPixmap img = isChecked() ? experimental_img : engage_img; + + p.setOpacity(1.0); + p.setPen(Qt::NoPen); + p.setBrush(QColor(0, 0, 0, 166)); + p.drawEllipse(center, btn_size / 2, btn_size / 2); + p.setOpacity(isDown() ? 0.8 : 1.0); + p.drawPixmap((btn_size - img_size) / 2, (btn_size - img_size) / 2, img); +} + + AnnotatedCameraWidget::AnnotatedCameraWidget(VisionStreamType type, QWidget* parent) : fps_filter(UI_FREQ, 3, 1. / UI_FREQ), CameraWidget("camerad", type, true, parent) { pm = std::make_unique>({"uiDebug"}); - engage_img = loadPixmap("../assets/img_chffr_wheel.png", {img_size, img_size}); - experimental_img = loadPixmap("../assets/img_experimental.svg", {img_size - 5, img_size - 5}); + QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setMargin(bdr_s); + main_layout->setSpacing(0); + + experimental_btn = new ExperimentalButton(this); + main_layout->addWidget(experimental_btn, 0, Qt::AlignTop | Qt::AlignRight); + dm_img = loadPixmap("../assets/img_driver_face.png", {img_size, img_size}); } @@ -227,9 +278,11 @@ void AnnotatedCameraWidget::updateState(const UIState &s) { setProperty("hideDM", cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE); setProperty("status", s.status); - // update engageability and DM icons at 2Hz + // update engageability/experimental mode button + experimental_btn->updateState(s); + + // update DM icons at 2Hz if (sm.frame % (UI_FREQ / 2) == 0) { - setProperty("engageable", cs.getEngageable() || cs.getEnabled()); setProperty("dmActive", sm["driverMonitoringState"].getDriverMonitoringState().getIsActiveMode()); setProperty("rightHandDM", sm["driverMonitoringState"].getDriverMonitoringState().getIsRHD()); } @@ -382,16 +435,9 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) { configFont(p, "Inter", 66, "Regular"); drawText(p, rect().center().x(), 290, speedUnit, 200); - // engage-ability icon - if (engageable) { - SubMaster &sm = *(uiState()->sm); - drawIcon(p, rect().right() - radius / 2 - bdr_s * 2, radius / 2 + int(bdr_s * 1.5), - sm["controlsState"].getControlsState().getExperimentalMode() ? experimental_img : engage_img, blackColor(166), 1.0); - } - // dm icon if (!hideDM) { - int dm_icon_x = rightHandDM ? rect().right() - radius / 2 - (bdr_s * 2) : radius / 2 + (bdr_s * 2); + int dm_icon_x = rightHandDM ? rect().right() - btn_size / 2 - (bdr_s * 2) : btn_size / 2 + (bdr_s * 2); drawIcon(p, dm_icon_x, rect().bottom() - footer_h / 2, dm_img, blackColor(70), dmActive ? 1.0 : 0.2); } @@ -414,7 +460,7 @@ void AnnotatedCameraWidget::drawIcon(QPainter &p, int x, int y, QPixmap &img, QB p.setOpacity(1.0); // bg dictates opacity of ellipse p.setPen(Qt::NoPen); p.setBrush(bg); - p.drawEllipse(x - radius / 2, y - radius / 2, radius, radius); + p.drawEllipse(x - btn_size / 2, y - btn_size / 2, btn_size, btn_size); p.setOpacity(opacity); p.drawPixmap(x - img.size().width() / 2, y - img.size().height() / 2, img); } diff --git a/selfdrive/ui/qt/onroad.h b/selfdrive/ui/qt/onroad.h index 3dbb05b674..73c2e03795 100644 --- a/selfdrive/ui/qt/onroad.h +++ b/selfdrive/ui/qt/onroad.h @@ -1,11 +1,16 @@ #pragma once +#include #include #include #include "common/util.h" -#include "selfdrive/ui/qt/widgets/cameraview.h" #include "selfdrive/ui/ui.h" +#include "selfdrive/ui/qt/widgets/cameraview.h" + + +const int btn_size = 192; +const int img_size = (btn_size / 4) * 3; // ***** onroad widgets ***** @@ -24,6 +29,21 @@ private: Alert alert = {}; }; +class ExperimentalButton : public QPushButton { + Q_OBJECT + +public: + explicit ExperimentalButton(QWidget *parent = 0); + void updateState(const UIState &s); + +private: + void paintEvent(QPaintEvent *event) override; + + Params params; + QPixmap engage_img; + QPixmap experimental_img; +}; + // container window for the NVG UI class AnnotatedCameraWidget : public CameraWidget { Q_OBJECT @@ -36,7 +56,6 @@ class AnnotatedCameraWidget : public CameraWidget { Q_PROPERTY(bool has_us_speed_limit MEMBER has_us_speed_limit); Q_PROPERTY(bool is_metric MEMBER is_metric); - Q_PROPERTY(bool engageable MEMBER engageable); Q_PROPERTY(bool dmActive MEMBER dmActive); Q_PROPERTY(bool hideDM MEMBER hideDM); Q_PROPERTY(bool rightHandDM MEMBER rightHandDM); @@ -50,18 +69,14 @@ private: void drawIcon(QPainter &p, int x, int y, QPixmap &img, QBrush bg, float opacity); void drawText(QPainter &p, int x, int y, const QString &text, int alpha = 255); - QPixmap engage_img; - QPixmap experimental_img; + ExperimentalButton *experimental_btn; QPixmap dm_img; - const int radius = 192; - const int img_size = (radius / 2) * 1.5; float speed; QString speedUnit; float setSpeed; float speedLimit; bool is_cruise_set = false; bool is_metric = false; - bool engageable = false; bool dmActive = false; bool hideDM = false; bool rightHandDM = false; From 2ecdb2857e863cb29831f6a9bd4ca3359dce63af Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 2 Feb 2023 15:30:52 -0800 Subject: [PATCH 272/484] loggerd: rotate if segment length is >20% longer than expected (#27193) * loggerd: rotate if segment length is >20% expected * add that back * comment --------- Co-authored-by: Comma Device --- selfdrive/loggerd/loggerd.cc | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/selfdrive/loggerd/loggerd.cc b/selfdrive/loggerd/loggerd.cc index 9beb3c3bf1..e09cdfaa9e 100644 --- a/selfdrive/loggerd/loggerd.cc +++ b/selfdrive/loggerd/loggerd.cc @@ -24,15 +24,25 @@ void logger_rotate(LoggerdState *s) { } void rotate_if_needed(LoggerdState *s) { - if (s->ready_to_rotate == s->max_waiting) { - logger_rotate(s); - } + // all encoders ready, trigger rotation + bool all_ready = s->ready_to_rotate == s->max_waiting; + // fallback logic to prevent extremely long segments in the case of camera, encoder, etc. malfunctions + bool timed_out = false; double tms = millis_since_boot(); - if ((tms - s->last_rotate_tms) > SEGMENT_LENGTH * 1000 && - (tms - s->last_camera_seen_tms) > NO_CAMERA_PATIENCE && - !LOGGERD_TEST) { - LOGW("no camera packet seen. auto rotating"); + double seg_length_secs = (tms - s->last_rotate_tms) / 1000.; + if ((seg_length_secs > SEGMENT_LENGTH) && !LOGGERD_TEST) { + // TODO: might be nice to put these reasons in the sentinel + if ((tms - s->last_camera_seen_tms) > NO_CAMERA_PATIENCE) { + timed_out = true; + LOGE("no camera packets seen. auto rotating"); + } else if (seg_length_secs > SEGMENT_LENGTH*1.2) { + timed_out = true; + LOGE("segment too long. auto rotating"); + } + } + + if (all_ready || timed_out) { logger_rotate(s); } } From b3b35b23fab832781bcf4f34d218ecc0b07c67fb Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 2 Feb 2023 17:12:13 -0800 Subject: [PATCH 273/484] controlsd: don't show steer saturated if recently overriding (#27191) * controlsd: don't show steer saturated if recently overriding * 2s * update refs --- selfdrive/controls/controlsd.py | 46 +++++++++++++----------- selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 21d8fe2d66..fff6bcf576 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -173,6 +173,7 @@ class Controls: self.cruise_mismatch_counter = 0 self.can_rcv_timeout_counter = 0 self.last_blinker_frame = 0 + self.last_steering_pressed_frame = 0 self.distance_traveled = 0 self.last_functional_fan_frame = 0 self.events_prev = [] @@ -623,29 +624,34 @@ class Controls: lac_log.output = actuators.steer lac_log.saturated = abs(actuators.steer) >= 0.9 + if CS.steeringPressed: + self.last_steering_pressed_frame = self.sm.frame + recent_steer_pressed = (self.sm.frame - self.last_steering_pressed_frame)*DT_CTRL < 2.0 + # Send a "steering required alert" if saturation count has reached the limit - if lac_log.active and not CS.steeringPressed and self.CP.lateralTuning.which() == 'torque' and not self.joystick_mode: - undershooting = abs(lac_log.desiredLateralAccel) / abs(1e-3 + lac_log.actualLateralAccel) > 1.2 - turning = abs(lac_log.desiredLateralAccel) > 1.0 - good_speed = CS.vEgo > 5 - max_torque = abs(self.last_actuators.steer) > 0.99 - if undershooting and turning and good_speed and max_torque: - self.events.add(EventName.steerSaturated) - elif lac_log.active and lac_log.saturated: - dpath_points = lat_plan.dPathPoints - if len(dpath_points): - # Check if we deviated from the path - # TODO use desired vs actual curvature - if self.CP.steerControlType == car.CarParams.SteerControlType.angle: - steering_value = actuators.steeringAngleDeg - else: - steering_value = actuators.steer + if lac_log.active and not recent_steer_pressed: + if self.CP.lateralTuning.which() == 'torque' and not self.joystick_mode: + undershooting = abs(lac_log.desiredLateralAccel) / abs(1e-3 + lac_log.actualLateralAccel) > 1.2 + turning = abs(lac_log.desiredLateralAccel) > 1.0 + good_speed = CS.vEgo > 5 + max_torque = abs(self.last_actuators.steer) > 0.99 + if undershooting and turning and good_speed and max_torque: + lac_log.active and self.events.add(EventName.steerSaturated) + elif lac_log.saturated: + dpath_points = lat_plan.dPathPoints + if len(dpath_points): + # Check if we deviated from the path + # TODO use desired vs actual curvature + if self.CP.steerControlType == car.CarParams.SteerControlType.angle: + steering_value = actuators.steeringAngleDeg + else: + steering_value = actuators.steer - left_deviation = steering_value > 0 and dpath_points[0] < -0.20 - right_deviation = steering_value < 0 and dpath_points[0] > 0.20 + left_deviation = steering_value > 0 and dpath_points[0] < -0.20 + right_deviation = steering_value < 0 and dpath_points[0] > 0.20 - if left_deviation or right_deviation: - self.events.add(EventName.steerSaturated) + if left_deviation or right_deviation: + self.events.add(EventName.steerSaturated) # Ensure no NaNs/Infs for p in ACTUATOR_FIELDS: diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index c49ed64c65..c787f4be2d 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -78f00e30719a6af9644b2cbd0c76d2e652e95ccd \ No newline at end of file +21b29c8a9eb9acd63e91cbda55657afb266a07f9 \ No newline at end of file From 4e27a7f22a94ee5d8afc0476a6781f7e19b6fcf0 Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Thu, 2 Feb 2023 20:29:45 -0700 Subject: [PATCH 274/484] Glonass support, ubloxd add ephemeris parsing (#27119) * add glonass kaitai parsing * add kaita generated files * remove glonass from build * add string non immediate type * fix kaitai bug * cleanUp * add patch file * fix scons order * make lookup const * remove comments * rename * add to release files * add signs * final ublox parsing * bump cereal * update ref --------- Co-authored-by: Kurt Nistelberger --- selfdrive/locationd/ublox_msg.cc | 152 ++++++++++++++++++++++- selfdrive/locationd/ublox_msg.h | 12 +- selfdrive/test/process_replay/ref_commit | 2 +- 3 files changed, 162 insertions(+), 4 deletions(-) diff --git a/selfdrive/locationd/ublox_msg.cc b/selfdrive/locationd/ublox_msg.cc index 127c7e56b6..3ff768ba06 100644 --- a/selfdrive/locationd/ublox_msg.cc +++ b/selfdrive/locationd/ublox_msg.cc @@ -65,6 +65,21 @@ inline bool UbloxMsgParser::valid_so_far() { return true; } +inline uint16_t UbloxMsgParser::get_glonass_year(uint8_t N4, uint16_t Nt) { + // convert time to year (conversion from A3.1.3) + int J = 0; + if (1 <= Nt && Nt <= 366) { + J = 1; + } else if (367 <= Nt && Nt <= 731) { + J = 2; + } else if (732 <= Nt && Nt <= 1096) { + J = 3; + } else if (1097 <= Nt && Nt <= 1461) { + J = 4; + } + uint16_t year = 1996 + 4*(N4 -1) + (J - 1); + return year; +} bool UbloxMsgParser::add_data(const uint8_t *incoming_data, uint32_t incoming_data_len, size_t &bytes_consumed) { int needed = needed_bytes(); @@ -103,9 +118,9 @@ std::pair> UbloxMsgParser::gen_msg() { switch (ubx_message.msg_type()) { case 0x0107: return {"gpsLocationExternal", gen_nav_pvt(static_cast(body))}; - case 0x0213: + case 0x0213: // UBX-RXM-SFRB (Broadcast Navigation Data Subframe) return {"ubloxGnss", gen_rxm_sfrbx(static_cast(body))}; - case 0x0215: + case 0x0215: // UBX-RXM-RAW (Multi-GNSS Raw Measurement Data) return {"ubloxGnss", gen_rxm_rawx(static_cast(body))}; case 0x0a09: return {"ubloxGnss", gen_mon_hw(static_cast(body))}; @@ -246,11 +261,144 @@ kj::Array UbloxMsgParser::parse_gps_ephemeris(ubx_t::rxm_sfrbx_t *m return kj::Array(); } +kj::Array UbloxMsgParser::parse_glonass_ephemeris(ubx_t::rxm_sfrbx_t *msg) { + if (msg->sv_id() == 255) { + // data can be decoded before identifying the SV number, in this case 255 + // is returned, which means "unknown" (ublox p32) + return kj::Array(); + } + + auto body = *msg->body(); + assert(body.size() == 4); + { + std::string string_data; + string_data.reserve(16); + for (uint32_t word : body) { + for (int i = 3; i >= 0; i--) + string_data.push_back(word >> 8*i); + } + + kaitai::kstream stream(string_data); + glonass_t gl_string(&stream); + + int string_number = gl_string.string_number(); + if (string_number > 5 || gl_string.idle_chip()) { + // dont parse non immediate data, idle_chip == 0 + return kj::Array(); + } + + // immediate data is the same within one superframe + if (glonass_superframes[msg->sv_id()] != gl_string.superframe_number()) { + glonass_strings[msg->sv_id()].clear(); + glonass_superframes[msg->sv_id()] = gl_string.superframe_number(); + } + glonass_strings[msg->sv_id()][string_number] = string_data; + } + + // publish if strings 1-5 have been collected + if (glonass_strings[msg->sv_id()].size() != 5) { + return kj::Array(); + } + + MessageBuilder msg_builder; + auto eph = msg_builder.initEvent().initUbloxGnss().initGlonassEphemeris(); + eph.setSvId(msg->sv_id()); + uint16_t current_day = 0; + + // string number 1 + { + kaitai::kstream stream(glonass_strings[msg->sv_id()][1]); + glonass_t gl_stream(&stream); + glonass_t::string_1_t* data = static_cast(gl_stream.data()); + + eph.setP1(data->p1()); + eph.setTk(data->t_k()); + eph.setXVel(data->x_vel() * pow(2, -20)); + eph.setXAccel(data->x_accel() * pow(2, -30)); + eph.setX(data->x() * pow(2, -11)); + } + + // string number 2 + { + kaitai::kstream stream(glonass_strings[msg->sv_id()][2]); + glonass_t gl_stream(&stream); + glonass_t::string_2_t* data = static_cast(gl_stream.data()); + + eph.setSvHealth(data->b_n()>>2); // MSB indicates health + eph.setP2(data->p2()); + eph.setTb(data->t_b()); + eph.setYVel(data->y_vel() * pow(2, -20)); + eph.setYAccel(data->y_accel() * pow(2, -30)); + eph.setY(data->y() * pow(2, -11)); + } + + // string number 3 + { + kaitai::kstream stream(glonass_strings[msg->sv_id()][3]); + glonass_t gl_stream(&stream); + glonass_t::string_3_t* data = static_cast(gl_stream.data()); + + eph.setP3(data->p3()); + eph.setGammaN(data->gamma_n() * pow(2, -40)); + eph.setSvHealth(eph.getSvHealth() | data->l_n()); + eph.setZVel(data->z_vel() * pow(2, -20)); + eph.setZAccel(data->z_accel() * pow(2, -30)); + eph.setZ(data->z() * pow(2, -11)); + } + + // string number 4 + { + kaitai::kstream stream(glonass_strings[msg->sv_id()][4]); + glonass_t gl_stream(&stream); + glonass_t::string_4_t* data = static_cast(gl_stream.data()); + + current_day = data->n_t(); + eph.setTauN(data->tau_n() * pow(2, -30)); + eph.setDeltaTauN(data->delta_tau_n() * pow(2, -30)); + eph.setAge(data->e_n()); + eph.setP4(data->p4()); + eph.setSvURA(glonass_URA_lookup.at(data->f_t())); + if (msg->sv_id() != data->n()) { + LOGE("SV_ID != SLOT_NUMBER: %d %d", msg->sv_id(), data->n()) + } + eph.setSvType(data->m()); + } + + // string number 5 + { + kaitai::kstream stream(glonass_strings[msg->sv_id()][5]); + glonass_t gl_stream(&stream); + glonass_t::string_5_t* data = static_cast(gl_stream.data()); + + // string5 parsing is only needed to get the year, this can be removed and + // the year can be fetched later in laika (note rollovers and leap year) + uint8_t n_4 = data->n_4(); + uint16_t year = get_glonass_year(n_4, current_day); + + uint16_t last_leap_year = 1996 + 4*(n_4-1); + uint16_t days_till_this_year = (year - last_leap_year)*365; + if (days_till_this_year != 0) { + days_till_this_year++; + } + + eph.setYear(year); + eph.setDayInYear(current_day - days_till_this_year); + eph.setHour((eph.getTk()>>7) & 0x1F); + eph.setMinute((eph.getTk()>>1) & 0x3F); + eph.setSecond((eph.getTk() & 0x1) * 30); + } + + glonass_strings[msg->sv_id()].clear(); + return capnp::messageToFlatArray(msg_builder); +} + kj::Array UbloxMsgParser::gen_rxm_sfrbx(ubx_t::rxm_sfrbx_t *msg) { switch (msg->gnss_id()) { case ubx_t::gnss_type_t::GNSS_TYPE_GPS: return parse_gps_ephemeris(msg); + case ubx_t::gnss_type_t::GNSS_TYPE_GLONASS: + return parse_glonass_ephemeris(msg); default: return kj::Array(); } diff --git a/selfdrive/locationd/ublox_msg.h b/selfdrive/locationd/ublox_msg.h index f031313168..6988f20b74 100644 --- a/selfdrive/locationd/ublox_msg.h +++ b/selfdrive/locationd/ublox_msg.h @@ -10,6 +10,7 @@ #include "cereal/messaging/messaging.h" #include "common/util.h" #include "selfdrive/locationd/generated/gps.h" +#include "selfdrive/locationd/generated/glonass.h" #include "selfdrive/locationd/generated/ubx.h" using namespace std::string_literals; @@ -101,13 +102,22 @@ class UbloxMsgParser { inline bool valid_cheksum(); inline bool valid(); inline bool valid_so_far(); + inline uint16_t get_glonass_year(uint8_t N4, uint16_t Nt); kj::Array parse_gps_ephemeris(ubx_t::rxm_sfrbx_t *msg); + kj::Array parse_glonass_ephemeris(ubx_t::rxm_sfrbx_t *msg); std::unordered_map> gps_subframes; size_t bytes_in_parse_buf = 0; uint8_t msg_parse_buf[ublox::UBLOX_HEADER_SIZE + ublox::UBLOX_MAX_MSG_SIZE]; -}; + // user range accuracy in meters + const std::unordered_map glonass_URA_lookup = + {{ 0, 1}, { 1, 2}, { 2, 2.5}, { 3, 4}, { 4, 5}, {5, 7}, + { 6, 10}, { 7, 12}, { 8, 14}, { 9, 16}, {10, 32}, + {11, 64}, {12, 128}, {13, 256}, {14, 512}, {15, 1024}}; + std::unordered_map> glonass_strings; + std::unordered_map glonass_superframes; +}; diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index c787f4be2d..68675e7007 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -21b29c8a9eb9acd63e91cbda55657afb266a07f9 \ No newline at end of file +2beed04e654cdf84fac5842869f38c8cd55e9867 \ No newline at end of file From 04a7808590ac55a5e3dea6080b6a24aae78d9277 Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Thu, 2 Feb 2023 21:16:28 -0700 Subject: [PATCH 275/484] glonass support laikad (#27173) * add glonass kaitai parsing * add kaita generated files * remove glonass from build * add string non immediate type * fix kaitai bug * cleanUp * add patch file * fix scons order * make lookup const * remove comments * add to release files * init * add laika * remove printf * laikad add glonass ephem parsing * add signs * final ublox parsing * rev gps * bump subs * address comments * disable internet for laikad --------- Co-authored-by: Kurt Nistelberger --- laika_repo | 2 +- selfdrive/locationd/laikad.py | 20 +++++++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/laika_repo b/laika_repo index 2e5c7b8a85..b30afe44d2 160000 --- a/laika_repo +++ b/laika_repo @@ -1 +1 @@ -Subproject commit 2e5c7b8a85cbaebda7fa715fe53d5e6ecbd62b0a +Subproject commit b30afe44d283072b6ee0ea17f290cca5d002baf7 diff --git a/selfdrive/locationd/laikad.py b/selfdrive/locationd/laikad.py index b8d86c63fd..e85cd0ad42 100755 --- a/selfdrive/locationd/laikad.py +++ b/selfdrive/locationd/laikad.py @@ -17,7 +17,7 @@ from common.params import Params, put_nonblocking from laika import AstroDog from laika.constants import SECS_IN_HR, SECS_IN_MIN from laika.downloader import DownloadFailed -from laika.ephemeris import Ephemeris, EphemerisType, convert_ublox_ephem, parse_qcom_ephem +from laika.ephemeris import Ephemeris, EphemerisType, convert_ublox_gps_ephem, convert_ublox_glonass_ephem, parse_qcom_ephem from laika.gps_time import GPSTime from laika.helpers import ConstellationId from laika.raw_gnss import GNSSMeasurement, correct_measurements, process_measurements, read_raw_ublox, read_raw_qcom @@ -139,17 +139,22 @@ class Laikad: if self.use_qcom: return gnss_msg.which() == 'drSvPoly' else: - return gnss_msg.which() == 'ephemeris' + return gnss_msg.which() in ('ephemeris', 'glonassEphemeris') def read_ephemeris(self, gnss_msg): - # TODO this only works on GLONASS if self.use_qcom: # TODO this is not robust to gps week rollover if self.gps_week is None: return ephem = parse_qcom_ephem(gnss_msg.drSvPoly, self.gps_week) else: - ephem = convert_ublox_ephem(gnss_msg.ephemeris) + if gnss_msg.which() == 'ephemeris': + ephem = convert_ublox_gps_ephem(gnss_msg.ephemeris) + elif gnss_msg.which() == 'glonassEphemeris': + ephem = convert_ublox_glonass_ephem(gnss_msg.glonassEphemeris) + else: + cloudlog.error(f"Unsupported ephemeris type: {gnss_msg.which()}") + return self.astro_dog.add_navs({ephem.prn: [ephem]}) self.cache_ephemeris(t=ephem.epoch) @@ -410,8 +415,13 @@ def main(sm=None, pm=None, qc=None): if pm is None: pm = messaging.PubMaster(['gnssMeasurements']) + # disable until set as main gps source, to better analyze startup time + use_internet = False #"LAIKAD_NO_INTERNET" not in os.environ + replay = "REPLAY" in os.environ - use_internet = "LAIKAD_NO_INTERNET" not in os.environ + if replay or "CI" in os.environ: + use_internet = True + laikad = Laikad(save_ephemeris=not replay, auto_fetch_navs=use_internet, use_qcom=use_qcom) while True: From 41304db1e661fa6593d777f0f17dfd60e6f909c8 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 3 Feb 2023 12:39:16 +0800 Subject: [PATCH 276/484] cabana: syc the last messages after seeking (#27149) * fix wrong last message time * update last messages after seekto * cleanup * remove src,address from CanData * merge master merge master --- tools/cabana/historylog.cc | 6 +-- tools/cabana/messageswidget.cc | 7 +-- tools/cabana/streams/abstractstream.cc | 64 +++++++++++++++++++------- tools/cabana/streams/abstractstream.h | 10 ++-- tools/cabana/streams/replaystream.cc | 6 --- tools/cabana/streams/replaystream.h | 2 +- tools/replay/replay.cc | 2 +- 7 files changed, 62 insertions(+), 35 deletions(-) diff --git a/tools/cabana/historylog.cc b/tools/cabana/historylog.cc index ab5c6cbbe0..95bd94e76b 100644 --- a/tools/cabana/historylog.cc +++ b/tools/cabana/historylog.cc @@ -79,7 +79,7 @@ void HistoryLogModel::setFilter(int sig_idx, const QString &value, std::function void HistoryLogModel::updateState() { if (!msg_id.isEmpty()) { - uint64_t current_time = (can->currentSec() + can->routeStartTime()) * 1e9; + uint64_t current_time = (can->lastMessage(msg_id).ts + can->routeStartTime()) * 1e9 + 1; auto new_msgs = dynamic_mode ? fetchData(current_time, last_fetch_time) : fetchData(0); if ((has_more_data = !new_msgs.empty())) { beginInsertRows({}, 0, new_msgs.size() - 1); @@ -109,7 +109,7 @@ std::deque HistoryLogModel::fetchData(InputIt first, I for (auto it = first; it != last && (*it)->mono_time > min_time; ++it) { if ((*it)->which == cereal::Event::Which::CAN) { for (const auto &c : (*it)->event.getCan()) { - if (src == c.getSrc() && address == c.getAddress()) { + if (address == c.getAddress() && src == c.getSrc()) { const auto dat = c.getDat(); for (int i = 0; i < sigs.size(); ++i) { values[i] = get_raw_value((uint8_t *)dat.begin(), dat.size(), *(sigs[i])); @@ -140,7 +140,7 @@ std::deque HistoryLogModel::fetchData(uint64_t from_ti if (dynamic_mode) { auto first = std::upper_bound(events->rbegin(), events->rend(), from_time, [=](uint64_t ts, auto &e) { return e->mono_time < ts; }); auto msgs = fetchData(first, events->rend(), min_time); - if (update_colors && min_time > 0) { + if (update_colors && (min_time > 0 || messages.empty())) { for (auto it = msgs.rbegin(); it != msgs.rend(); ++it) { hex_colors.compute(it->data, it->mono_time / (double)1e9, freq); it->colors = hex_colors.colors; diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index 64f56ae73f..9d0fc23e4d 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -106,11 +106,12 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const { } } else if (role == Qt::UserRole && index.column() == 4) { QList colors; + colors.reserve(can_data.dat.size()); for (int i = 0; i < can_data.dat.size(); i++){ if (suppressed_bytes.contains({id, i})) { colors.append(QColor(255, 255, 255, 0)); } else { - colors.append(can_data.colors[i]); + colors.append(i < can_data.colors.size() ? can_data.colors[i] : QColor(255, 255, 255, 0)); } } return colors; @@ -152,8 +153,8 @@ void MessageListModel::sortMessages() { }); } else if (sort_column == 1) { std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) { - auto ll = std::tuple{can->lastMessage(l).src, can->lastMessage(l).address, l}; - auto rr = std::tuple{can->lastMessage(r).src, can->lastMessage(r).address, r}; + auto ll = DBCManager::parseId(l); + auto rr = DBCManager::parseId(r); return sort_order == Qt::AscendingOrder ? ll < rr : ll > rr; }); } else if (sort_column == 2) { diff --git a/tools/cabana/streams/abstractstream.cc b/tools/cabana/streams/abstractstream.cc index 308e5556c6..8a77e0eb39 100644 --- a/tools/cabana/streams/abstractstream.cc +++ b/tools/cabana/streams/abstractstream.cc @@ -4,7 +4,9 @@ AbstractStream *can = nullptr; AbstractStream::AbstractStream(QObject *parent, bool is_live_streaming) : is_live_streaming(is_live_streaming), QObject(parent) { can = this; + new_msgs = std::make_unique>(); QObject::connect(this, &AbstractStream::received, this, &AbstractStream::process, Qt::QueuedConnection); + QObject::connect(this, &AbstractStream::seekedTo, this, &AbstractStream::updateLastMsgsTo); } void AbstractStream::process(QHash *messages) { @@ -18,30 +20,17 @@ void AbstractStream::process(QHash *messages) { } bool AbstractStream::updateEvent(const Event *event) { - static std::unique_ptr new_msgs = std::make_unique>(); - static QHash change_trackers; static double prev_update_ts = 0; if (event->which == cereal::Event::Which::CAN) { - double current_sec = currentSec(); - if (counters_begin_sec == 0 || counters_begin_sec >= current_sec) { - new_msgs->clear(); - counters.clear(); - counters_begin_sec = current_sec; - } - - auto can_events = event->event.getCan(); - for (const auto &c : can_events) { + double current_sec = event->mono_time / 1e9 - routeStartTime(); + for (const auto &c : event->event.getCan()) { QString id = QString("%1:%2").arg(c.getSrc()).arg(c.getAddress(), 1, 16); CanData &data = (*new_msgs)[id]; data.ts = current_sec; - data.src = c.getSrc(); - data.address = c.getAddress(); data.dat = QByteArray((char *)c.getDat().begin(), c.getDat().size()); data.count = ++counters[id]; - if (double delta = (current_sec - counters_begin_sec); delta > 0) { - data.freq = data.count / delta; - } + data.freq = data.count / std::max(1.0, current_sec); change_trackers[id].compute(data.dat, data.ts, data.freq); data.colors = change_trackers[id].colors; data.last_change_t = change_trackers[id].last_change_t; @@ -60,3 +49,46 @@ bool AbstractStream::updateEvent(const Event *event) { } return true; } + +const CanData &AbstractStream::lastMessage(const QString &id) { + static CanData empty_data; + auto it = can_msgs.find(id); + return it != can_msgs.end() ? it.value() : empty_data; +} + +void AbstractStream::updateLastMsgsTo(double sec) { + QHash, CanData> last_msgs; // Much faster than QHash + last_msgs.reserve(can_msgs.size()); + double route_start_time = routeStartTime(); + uint64_t last_ts = (sec + route_start_time) * 1e9; + auto last = std::upper_bound(events()->rbegin(), events()->rend(), last_ts, [](uint64_t ts, auto &e) { return e->mono_time < ts; }); + for (auto it = last; it != events()->rend(); ++it) { + if ((*it)->which == cereal::Event::Which::CAN) { + for (const auto &c : (*it)->event.getCan()) { + auto &m = last_msgs[{c.getSrc(), c.getAddress()}]; + if (++m.count == 1) { + m.ts = ((*it)->mono_time / 1e9) - route_start_time; + m.dat = QByteArray((char *)c.getDat().begin(), c.getDat().size()); + m.colors = QVector(m.dat.size(), QColor(0, 0, 0, 0)); + m.last_change_t = QVector(m.dat.size(), m.ts); + } else { + m.freq = m.count / std::max(1.0, m.ts); + } + } + } + } + + // it is thread safe to update data here. + // updateEvent will not be called before replayStream::seekedTo return. + new_msgs->clear(); + change_trackers.clear(); + counters.clear(); + can_msgs.clear(); + for (auto it = last_msgs.cbegin(); it != last_msgs.cend(); ++it) { + QString msg_id = QString("%1:%2").arg(it.key().first).arg(it.key().second, 1, 16); + can_msgs[msg_id] = it.value(); + counters[msg_id] = it.value().count; + } + emit updated(); + emit msgsReceived(&can_msgs); +} diff --git a/tools/cabana/streams/abstractstream.h b/tools/cabana/streams/abstractstream.h index e16e11a14b..0dbb3d96a6 100644 --- a/tools/cabana/streams/abstractstream.h +++ b/tools/cabana/streams/abstractstream.h @@ -11,8 +11,6 @@ struct CanData { double ts = 0.; - uint8_t src = 0; - uint32_t address = 0; uint32_t count = 0; uint32_t freq = 0; QByteArray dat; @@ -34,7 +32,7 @@ public: virtual double routeStartTime() const { return 0; } virtual double currentSec() const = 0; virtual QDateTime currentDateTime() const { return {}; } - virtual const CanData &lastMessage(const QString &id) { return can_msgs[id]; } + virtual const CanData &lastMessage(const QString &id); virtual VisionStreamType visionStreamType() const { return VISION_STREAM_ROAD; } virtual const Route *route() const { return nullptr; } virtual const std::vector *events() const = 0; @@ -54,16 +52,18 @@ signals: void received(QHash *); public: - QMap can_msgs; + QHash can_msgs; protected: void process(QHash *); bool updateEvent(const Event *event); + void updateLastMsgsTo(double sec); bool is_live_streaming = false; - std::atomic counters_begin_sec = 0; std::atomic processing = false; QHash counters; + std::unique_ptr> new_msgs; + QHash change_trackers; }; // A global pointer referring to the unique AbstractStream object diff --git a/tools/cabana/streams/replaystream.cc b/tools/cabana/streams/replaystream.cc index fd58f7a409..72c4a13048 100644 --- a/tools/cabana/streams/replaystream.cc +++ b/tools/cabana/streams/replaystream.cc @@ -42,12 +42,6 @@ bool ReplayStream::eventFilter(const Event *event) { return true; } -void ReplayStream::seekTo(double ts) { - replay->seekTo(std::max(double(0), ts), false); - counters_begin_sec = 0; - emit updated(); -} - void ReplayStream::pause(bool pause) { replay->pause(pause); emit(pause ? paused() : resume()); diff --git a/tools/cabana/streams/replaystream.h b/tools/cabana/streams/replaystream.h index 1688915212..a9a74e33b5 100644 --- a/tools/cabana/streams/replaystream.h +++ b/tools/cabana/streams/replaystream.h @@ -12,7 +12,7 @@ public: ~ReplayStream(); bool loadRoute(const QString &route, const QString &data_dir, uint32_t replay_flags = REPLAY_FLAG_NONE); bool eventFilter(const Event *event); - void seekTo(double ts) override; + void seekTo(double ts) override { replay->seekTo(std::max(double(0), ts), false); }; inline QString routeName() const override { return replay->route()->name(); } inline QString carFingerprint() const override { return replay->carFingerprint().c_str(); } inline VisionStreamType visionStreamType() const override { return replay->hasFlag(REPLAY_FLAG_ECAM) ? VISION_STREAM_WIDE_ROAD : VISION_STREAM_ROAD; } diff --git a/tools/replay/replay.cc b/tools/replay/replay.cc index 5c90777bbc..178b116a87 100644 --- a/tools/replay/replay.cc +++ b/tools/replay/replay.cc @@ -114,9 +114,9 @@ void Replay::seekTo(double seconds, bool relative) { rInfo("seeking to %d s, segment %d", (int)seconds, seg); current_segment_ = seg; cur_mono_time_ = route_start_ts_ + seconds * 1e9; + emit seekedTo(seconds); return isSegmentMerged(seg); }); - emit seekedTo(seconds); queueSegment(); } From b1e744987f88641577139061a6444b6357f5773a Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Thu, 2 Feb 2023 22:00:49 -0800 Subject: [PATCH 277/484] Ford: add Lincoln Aviator 2021 to Ford Explorer platform (#27095) * add Lincoln Aviator 2021 to Ford Explorer platform Based on the Ford Explorer, the Aviator has very similar firmware versions. Add these to the Explorer platform and create a new CarInfo for the Aviator. f0709d2bc6ca451f|2022-12-10--12-36-59--0 VIN: 5LM5J7XC8MGL09541 * Ford: new gear shifter signal to support Aviator * probably don't need these * don't need to pass None here, it defaults to None * will rename in DBC * bump opendbc * bump process replay ref --- opendbc | 2 +- selfdrive/car/ford/carstate.py | 6 +++--- selfdrive/car/ford/values.py | 8 +++++++- selfdrive/car/interfaces.py | 18 +++++++++--------- selfdrive/test/process_replay/ref_commit | 2 +- 5 files changed, 21 insertions(+), 15 deletions(-) diff --git a/opendbc b/opendbc index d585a9bf29..0b7439f551 160000 --- a/opendbc +++ b/opendbc @@ -1 +1 @@ -Subproject commit d585a9bf2908b2c83bf02b567b9e1f5bfc587a01 +Subproject commit 0b7439f551168f202fb033b7e3e177048687e726 diff --git a/selfdrive/car/ford/carstate.py b/selfdrive/car/ford/carstate.py index 215900fef6..f97225e9f2 100644 --- a/selfdrive/car/ford/carstate.py +++ b/selfdrive/car/ford/carstate.py @@ -14,7 +14,7 @@ class CarState(CarStateBase): super().__init__(CP) can_define = CANDefine(DBC[CP.carFingerprint]["pt"]) if CP.transmissionType == TransmissionType.automatic: - self.shifter_values = can_define.dv["Gear_Shift_by_Wire_FD1"]["TrnGear_D_RqDrv"] + self.shifter_values = can_define.dv["Gear_Shift_by_Wire_FD1"]["TrnRng_D_RqGsm"] def update(self, cp, cp_cam): ret = car.CarState.new_message() @@ -51,7 +51,7 @@ class CarState(CarStateBase): # gear if self.CP.transmissionType == TransmissionType.automatic: - gear = self.shifter_values.get(cp.vl["Gear_Shift_by_Wire_FD1"]["TrnGear_D_RqDrv"], None) + gear = self.shifter_values.get(cp.vl["Gear_Shift_by_Wire_FD1"]["TrnRng_D_RqGsm"]) ret.gearShifter = self.parse_gear_shifter(gear) elif self.CP.transmissionType == TransmissionType.manual: ret.clutchPressed = cp.vl["Engine_Clutch_Data"]["CluPdlPos_Pc_Meas"] > 0 @@ -171,7 +171,7 @@ class CarState(CarStateBase): if CP.transmissionType == TransmissionType.automatic: signals += [ - ("TrnGear_D_RqDrv", "Gear_Shift_by_Wire_FD1"), # GWM transmission gear position + ("TrnRng_D_RqGsm", "Gear_Shift_by_Wire_FD1"), # GWM transmission gear position ] checks += [ ("Gear_Shift_by_Wire_FD1", 10), diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index 3723fe1d4a..526b74b16c 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -75,7 +75,10 @@ CAR_INFO: Dict[str, Union[CarInfo, List[CarInfo]]] = { FordCarInfo("Ford Escape 2020-21"), FordCarInfo("Ford Kuga 2020-21", "Driver Assistance Pack"), ], - CAR.EXPLORER_MK6: FordCarInfo("Ford Explorer 2020-22"), + CAR.EXPLORER_MK6: [ + FordCarInfo("Ford Explorer 2020-22"), + FordCarInfo("Lincoln Aviator 2021", "Co-Pilot360 Plus"), + ], CAR.FOCUS_MK4: FordCarInfo("Ford Focus EU 2019", "Driver Assistance Pack"), CAR.MAVERICK_MK1: FordCarInfo("Ford Maverick 2022", "Co-Pilot360 Assist"), } @@ -165,13 +168,16 @@ FW_VERSIONS = { (Ecu.fwdCamera, 0x706, None): [ b'LB5T-14F397-AE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'LB5T-14F397-AF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + b'LC5T-14F397-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', ], (Ecu.engine, 0x7E0, None): [ b'LB5A-14C204-EAC\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'MB5A-14C204-MD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + b'MB5A-14C204-RC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'NB5A-14C204-HB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', ], (Ecu.shiftByWire, 0x732, None): [ + b'L1MP-14C561-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'L1MP-14G395-AD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'L1MP-14G395-AE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'L1MP-14G395-JB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', diff --git a/selfdrive/car/interfaces.py b/selfdrive/car/interfaces.py index 7192f5252c..e75067da7e 100644 --- a/selfdrive/car/interfaces.py +++ b/selfdrive/car/interfaces.py @@ -401,15 +401,15 @@ class CarStateBase(ABC): return GearShifter.unknown d: Dict[str, car.CarState.GearShifter] = { - 'P': GearShifter.park, 'PARK': GearShifter.park, - 'R': GearShifter.reverse, 'REVERSE': GearShifter.reverse, - 'N': GearShifter.neutral, 'NEUTRAL': GearShifter.neutral, - 'E': GearShifter.eco, 'ECO': GearShifter.eco, - 'T': GearShifter.manumatic, 'MANUAL': GearShifter.manumatic, - 'D': GearShifter.drive, 'DRIVE': GearShifter.drive, - 'S': GearShifter.sport, 'SPORT': GearShifter.sport, - 'L': GearShifter.low, 'LOW': GearShifter.low, - 'B': GearShifter.brake, 'BRAKE': GearShifter.brake, + 'P': GearShifter.park, 'PARK': GearShifter.park, + 'R': GearShifter.reverse, 'REVERSE': GearShifter.reverse, + 'N': GearShifter.neutral, 'NEUTRAL': GearShifter.neutral, + 'E': GearShifter.eco, 'ECO': GearShifter.eco, + 'T': GearShifter.manumatic, 'MANUAL': GearShifter.manumatic, + 'D': GearShifter.drive, 'DRIVE': GearShifter.drive, + 'S': GearShifter.sport, 'SPORT': GearShifter.sport, + 'L': GearShifter.low, 'LOW': GearShifter.low, + 'B': GearShifter.brake, 'BRAKE': GearShifter.brake, } return d.get(gear.upper(), GearShifter.unknown) diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 68675e7007..2c66c7075c 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -2beed04e654cdf84fac5842869f38c8cd55e9867 \ No newline at end of file +7c2a98f4a94b6dded6a90d8913ab85a63069c186 \ No newline at end of file From 1816b499c4c55461797d41b8418faeb58d5f7292 Mon Sep 17 00:00:00 2001 From: Jason Wen <47793918+sunnyhaibin@users.noreply.github.com> Date: Fri, 3 Feb 2023 01:58:29 -0500 Subject: [PATCH 278/484] HKG: Car Port for Kia K5 Hybrid 2020 (#26947) * HKG: Car Port for Kia K5 Hybrid 2020 * Add test route (Experimental Mode) * No abs * remove extra trans * move up --------- Co-authored-by: Shane Smiskol --- RELEASES.md | 1 + docs/CARS.md | 3 ++- selfdrive/car/hyundai/carcontroller.py | 3 ++- selfdrive/car/hyundai/hyundaican.py | 2 +- selfdrive/car/hyundai/interface.py | 2 +- selfdrive/car/hyundai/values.py | 28 +++++++++++++++++++---- selfdrive/car/tests/routes.py | 1 + selfdrive/car/torque_data/substitute.yaml | 1 + 8 files changed, 33 insertions(+), 8 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index ddb4a036b0..9103fbc4b5 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -6,6 +6,7 @@ Version 0.9.1 (2022-12-XX) * Chevrolet Bolt EV 2022-23 support thanks to JasonJShuler! * Genesis GV60 2023 support thanks to sunnyhaibin! * Hyundai Tucson 2022-23 support +* Kia K5 Hybrid 2020 support thanks to sunnyhaibin! * Kia Sorento 2022-23 support thanks to sunnyhaibin! * Kia Sorento Plug-in Hybrid 2022 support thanks to sunnyhaibin! * Volkswagen Crafter and MAN TGE 2017-23 support thanks to jyoung8607! diff --git a/docs/CARS.md b/docs/CARS.md index 9a33278d3b..45873c4924 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,7 +4,7 @@ A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system. -# 232 Supported Cars +# 233 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness|Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| @@ -100,6 +100,7 @@ A supported vehicle is one that just works when you install a comma three. All s |Kia|EV6 (without HDA II) 2022[5](#footnotes)|Highway Driving Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| |Kia|Forte 2019-21|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai G|| |Kia|K5 2021-22|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|| +|Kia|K5 Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|| |Kia|Niro EV 2019|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| |Kia|Niro EV 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F|| |Kia|Niro EV 2021|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| diff --git a/selfdrive/car/hyundai/carcontroller.py b/selfdrive/car/hyundai/carcontroller.py index b81c5e3f7d..6f043fddbb 100644 --- a/selfdrive/car/hyundai/carcontroller.py +++ b/selfdrive/car/hyundai/carcontroller.py @@ -181,7 +181,8 @@ class CarController: if self.frame % 5 == 0 and self.car_fingerprint in (CAR.SONATA, CAR.PALISADE, CAR.IONIQ, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV_2021, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KIA_CEED, CAR.KIA_SELTOS, CAR.KONA_EV, CAR.KONA_EV_2022, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.SANTA_FE_2022, - CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.GENESIS_G70_2020, CAR.SANTA_FE_PHEV_2022, CAR.KIA_STINGER_2022): + CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.GENESIS_G70_2020, CAR.SANTA_FE_PHEV_2022, + CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020): can_sends.append(hyundaican.create_lfahda_mfc(self.packer, CC.enabled)) # 5 Hz ACC options diff --git a/selfdrive/car/hyundai/hyundaican.py b/selfdrive/car/hyundai/hyundaican.py index c2ffffbf22..858f3d0876 100644 --- a/selfdrive/car/hyundai/hyundaican.py +++ b/selfdrive/car/hyundai/hyundaican.py @@ -21,7 +21,7 @@ def create_lkas11(packer, frame, car_fingerprint, apply_steer, steer_req, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KIA_SELTOS, CAR.ELANTRA_2021, CAR.GENESIS_G70_2020, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_EV, CAR.KONA_HEV, CAR.KONA_EV_2022, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, - CAR.SANTA_FE_PHEV_2022, CAR.KIA_STINGER_2022): + CAR.SANTA_FE_PHEV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020): values["CF_Lkas_LdwsActivemode"] = int(left_lane) + (int(right_lane) << 1) values["CF_Lkas_LdwsOpt_USM"] = 2 diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py index 030c3f1abc..c708e2b78e 100644 --- a/selfdrive/car/hyundai/interface.py +++ b/selfdrive/car/hyundai/interface.py @@ -166,7 +166,7 @@ class CarInterface(CarInterfaceBase): ret.wheelbase = 2.65 ret.steerRatio = 13.75 tire_stiffness_factor = 0.5 - elif candidate == CAR.KIA_K5_2021: + elif candidate in (CAR.KIA_K5_2021, CAR.KIA_K5_HEV_2020): ret.mass = 3228. * CV.LB_TO_KG ret.wheelbase = 2.85 ret.steerRatio = 13.27 # 2021 Kia K5 Steering Ratio (all trims) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index b355da6cac..1a0c12d2e0 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -96,6 +96,7 @@ class CAR: # Kia KIA_FORTE = "KIA FORTE E 2018 & GT 2021" KIA_K5_2021 = "KIA K5 2021" + KIA_K5_HEV_2020 = "KIA K5 HYBRID 2020" KIA_NIRO_EV = "KIA NIRO EV 2020" KIA_NIRO_PHEV = "KIA NIRO HYBRID 2019" KIA_NIRO_HEV_2021 = "KIA NIRO HYBRID 2021" @@ -192,6 +193,7 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { # Kia CAR.KIA_FORTE: HyundaiCarInfo("Kia Forte 2019-21", harness=Harness.hyundai_g), CAR.KIA_K5_2021: HyundaiCarInfo("Kia K5 2021-22", harness=Harness.hyundai_a), + CAR.KIA_K5_HEV_2020: HyundaiCarInfo("Kia K5 Hybrid 2020", harness=Harness.hyundai_a), CAR.KIA_NIRO_EV: [ HyundaiCarInfo("Kia Niro EV 2019", "All", "https://www.youtube.com/watch?v=lT7zcG6ZpGo", harness=Harness.hyundai_h), HyundaiCarInfo("Kia Niro EV 2020", "All", "https://www.youtube.com/watch?v=lT7zcG6ZpGo", harness=Harness.hyundai_f), @@ -1148,6 +1150,23 @@ FW_VERSIONS = { b'\xf1\x00bcsh8p54 U913\x00\x00\x00\x00\x00\x00TDL4T16NB05\x94t\x18', ], }, + CAR.KIA_K5_HEV_2020: { + (Ecu.fwdRadar, 0x7D0, None): [ + b'\xf1\x00DLhe SCC FHCUP 1.00 1.02 99110-L7000 ', + ], + (Ecu.eps, 0x7D4, None): [ + b'\xf1\x00DL3 MDPS C 1.00 1.02 56310-L7000 4DLHC102', + ], + (Ecu.fwdCamera, 0x7C4, None): [ + b'\xf1\x00DL3HMFC AT KOR LHD 1.00 1.02 99210-L2000 200309', + ], + (Ecu.engine, 0x7E0, None): [ + b'\xf1\x87391162JLA0', + ], + (Ecu.transmission, 0x7E1, None): [ + b'\xf1\x00PSBG2323 E08\x00\x00\x00\x00\x00\x00\x00TDL2H20KA2\xe3\xc6cz', + ], + }, CAR.KONA_EV: { (Ecu.abs, 0x7D1, None): [ b'\xf1\x00OS IEB \r 105\x18\t\x18 58520-K4000', @@ -1608,7 +1627,7 @@ FW_VERSIONS = { } CHECKSUM = { - "crc8": [CAR.SANTA_FE, CAR.SONATA, CAR.PALISADE, CAR.KIA_SELTOS, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022], + "crc8": [CAR.SANTA_FE, CAR.SONATA, CAR.PALISADE, CAR.KIA_SELTOS, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.KIA_K5_HEV_2020], "6B": [CAR.KIA_SORENTO, CAR.HYUNDAI_GENESIS], } @@ -1616,10 +1635,10 @@ FEATURES = { # which message has the gear "use_cluster_gears": {CAR.ELANTRA, CAR.KONA}, "use_tcu_gears": {CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.SONATA_LF, CAR.VELOSTER, CAR.TUCSON}, - "use_elect_gears": {CAR.KIA_NIRO_EV, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_OPTIMA_H, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.IONIQ, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.KONA_EV_2022}, + "use_elect_gears": {CAR.KIA_NIRO_EV, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_OPTIMA_H, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.IONIQ, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.KONA_EV_2022, CAR.KIA_K5_HEV_2020}, # these cars use the FCA11 message for the AEB and FCW signals, all others use SCC12 - "use_fca": {CAR.SONATA, CAR.SONATA_HYBRID, CAR.ELANTRA, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.KIA_STINGER, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_FORTE, CAR.KIA_NIRO_EV, CAR.PALISADE, CAR.GENESIS_G70, CAR.GENESIS_G70_2020, CAR.KONA, CAR.SANTA_FE, CAR.KIA_SELTOS, CAR.KONA_HEV, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.TUCSON, CAR.KONA_EV_2022, CAR.KIA_STINGER_2022}, + "use_fca": {CAR.SONATA, CAR.SONATA_HYBRID, CAR.ELANTRA, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.KIA_STINGER, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_FORTE, CAR.KIA_NIRO_EV, CAR.PALISADE, CAR.GENESIS_G70, CAR.GENESIS_G70_2020, CAR.KONA, CAR.SANTA_FE, CAR.KIA_SELTOS, CAR.KONA_HEV, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.TUCSON, CAR.KONA_EV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020}, } CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.TUCSON_4TH_GEN, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, CAR.SANTA_CRUZ_1ST_GEN, CAR.KIA_SPORTAGE_5TH_GEN, CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.GENESIS_GV60_EV_1ST_GEN, CAR.KIA_SORENTO_4TH_GEN} @@ -1630,7 +1649,7 @@ CANFD_RADAR_SCC_CAR = {CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, C # The camera does SCC on these cars, rather than the radar CAMERA_SCC_CAR = {CAR.KONA_EV_2022, } -HYBRID_CAR = {CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN} # these cars use a different gas signal +HYBRID_CAR = {CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.KIA_K5_HEV_2020} # these cars use a different gas signal EV_CAR = {CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.KIA_NIRO_EV, CAR.KONA_EV_2022, CAR.KIA_EV6, CAR.IONIQ_5, CAR.GENESIS_GV60_EV_1ST_GEN} # these cars require a special panda safety mode due to missing counters and checksums in the messages @@ -1655,6 +1674,7 @@ DBC = { CAR.IONIQ_HEV_2022: dbc_dict('hyundai_kia_generic', None), CAR.KIA_FORTE: dbc_dict('hyundai_kia_generic', None), CAR.KIA_K5_2021: dbc_dict('hyundai_kia_generic', None), + CAR.KIA_K5_HEV_2020: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'), CAR.KIA_NIRO_EV: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'), CAR.KIA_NIRO_PHEV: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'), CAR.KIA_NIRO_HEV_2021: dbc_dict('hyundai_kia_generic', None), diff --git a/selfdrive/car/tests/routes.py b/selfdrive/car/tests/routes.py index bcdb00fd60..ce38eeb0de 100644 --- a/selfdrive/car/tests/routes.py +++ b/selfdrive/car/tests/routes.py @@ -123,6 +123,7 @@ routes = [ CarTestRoute("d545129f3ca90f28|2022-10-19--09-22-54", HYUNDAI.KIA_EV6), # HDA2 CarTestRoute("68d6a96e703c00c9|2022-09-10--16-09-39", HYUNDAI.KIA_EV6), # HDA1 CarTestRoute("007d5e4ad9f86d13|2021-09-30--15-09-23", HYUNDAI.KIA_K5_2021), + CarTestRoute("c58dfc9fc16590e0|2023-01-14--13-51-48", HYUNDAI.KIA_K5_HEV_2020), CarTestRoute("50c6c9b85fd1ff03|2020-10-26--17-56-06", HYUNDAI.KIA_NIRO_EV), CarTestRoute("173219cf50acdd7b|2021-07-05--10-27-41", HYUNDAI.KIA_NIRO_PHEV), CarTestRoute("34a875f29f69841a|2021-07-29--13-02-09", HYUNDAI.KIA_NIRO_HEV_2021), diff --git a/selfdrive/car/torque_data/substitute.yaml b/selfdrive/car/torque_data/substitute.yaml index 56057dfae0..1ac43efc39 100644 --- a/selfdrive/car/torque_data/substitute.yaml +++ b/selfdrive/car/torque_data/substitute.yaml @@ -37,6 +37,7 @@ HYUNDAI ELANTRA HYBRID 2021: HYUNDAI SONATA 2020 HYUNDAI TUCSON 2019: HYUNDAI SANTA FE 2019 HYUNDAI TUCSON 4TH GEN: HYUNDAI TUCSON HYBRID 4TH GEN HYUNDAI SANTA FE 2022: HYUNDAI SANTA FE HYBRID 2022 +KIA K5 HYBRID 2020: KIA K5 2021 KIA STINGER 2022: KIA STINGER GT2 2018 GENESIS G90 2017: GENESIS G70 2018 GENESIS G80 2017: GENESIS G70 2018 From bd67d0088783d52262f6796060a53a31e393333b Mon Sep 17 00:00:00 2001 From: Jason Wen <47793918+sunnyhaibin@users.noreply.github.com> Date: Fri, 3 Feb 2023 02:08:38 -0500 Subject: [PATCH 279/484] Hyundai: Add FW Versions for 2020 Ioniq Plug-in Hybrid (#27196) * Hyundai: Add FW Versions for 2020 Ioniq Plug-in Hybrid * Aha! * Another one * Update selfdrive/car/hyundai/values.py * missing a radar --------- Co-authored-by: Shane Smiskol --- selfdrive/car/hyundai/values.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 1a0c12d2e0..9c1efa812d 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -416,17 +416,18 @@ FW_VERSIONS = { }, CAR.IONIQ_PHEV: { (Ecu.fwdRadar, 0x7d0, None): [ - b'\xf1\000AEhe SCC FHCUP 1.00 1.02 99110-G2100 ', + b'\xf1\x00AEhe SCC FHCUP 1.00 1.02 99110-G2100 ', b'\xf1\x00AEhe SCC F-CUP 1.00 1.00 99110-G2200 ', b'\xf1\x00AEhe SCC F-CUP 1.00 1.00 99110-G2600 ', + b'\xf1\x00AEhe SCC F-CUP 1.00 1.02 99110-G2100 ', ], (Ecu.eps, 0x7d4, None): [ - b'\xf1\000AE MDPS C 1.00 1.01 56310/G2510 4APHC101', + b'\xf1\x00AE MDPS C 1.00 1.01 56310/G2510 4APHC101', b'\xf1\x00AE MDPS C 1.00 1.01 56310/G2560 4APHC101', b'\xf1\x00AE MDPS C 1.00 1.01 56310G2510\x00 4APHC101', ], (Ecu.fwdCamera, 0x7c4, None): [ - b'\xf1\000AEP MFC AT USA LHD 1.00 1.01 95740-G2600 190819', + b'\xf1\x00AEP MFC AT USA LHD 1.00 1.01 95740-G2600 190819', b'\xf1\x00AEP MFC AT EUR RHD 1.00 1.01 95740-G2600 190819', b'\xf1\x00AEP MFC AT USA LHD 1.00 1.00 95740-G2700 201027', ], @@ -439,6 +440,7 @@ FW_VERSIONS = { b'\xf1\x816U3J8051\x00\x00\xf1\x006U3H1_C2\x00\x006U3J8051\x00\x00PAETG16UL0\x00\x00\x00\x00', b'\xf1\x816U3J9051\x00\x00\xf1\x006U3H1_C2\x00\x006U3J9051\x00\x00PAE0G16NL2\xad\xeb\xabt', b'\xf1\x816U3J9051\x00\x00\xf1\x006U3H1_C2\x00\x006U3J9051\x00\x00PAE0G16NL2\x00\x00\x00\x00', + b'\xf1\x006U3H1_C2\x00\x006U3J9051\x00\x00PAE0G16NL0\x00\x00\x00\x00', ], }, CAR.IONIQ_EV_2020: { From 603e03116e4ef8dac20f0b957c38369f6cad9c7f Mon Sep 17 00:00:00 2001 From: jayvin100 <34394301+jayvin100@users.noreply.github.com> Date: Fri, 3 Feb 2023 15:16:43 +0800 Subject: [PATCH 280/484] Add support for Malaysian Civic 2022 (#27126) Co-authored-by: Shane Smiskol --- selfdrive/car/honda/values.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/selfdrive/car/honda/values.py b/selfdrive/car/honda/values.py index 07eed0cfc2..3d1c006aaa 100644 --- a/selfdrive/car/honda/values.py +++ b/selfdrive/car/honda/values.py @@ -1440,6 +1440,7 @@ FW_VERSIONS = { (Ecu.eps, 0x18DA30F1, None): [ b'39990-T39-A130\x00\x00', b'39990-T43-J020\x00\x00', + b'39990-T24-T120\x00\x00', ], (Ecu.gateway, 0x18DAEFF1, None): [ b'38897-T20-A020\x00\x00', @@ -1447,11 +1448,13 @@ FW_VERSIONS = { b'38897-T21-A010\x00\x00', b'38897-T20-A210\x00\x00', b'38897-T20-A310\x00\x00', + b'38897-T24-Z120\x00\x00', ], (Ecu.srs, 0x18DA53F1, None): [ b'77959-T20-A970\x00\x00', b'77959-T47-A940\x00\x00', b'77959-T47-A950\x00\x00', + b'77959-T20-M820\x00\x00', ], (Ecu.combinationMeter, 0x18DA60F1, None): [ b'78108-T21-A220\x00\x00', @@ -1459,22 +1462,26 @@ FW_VERSIONS = { b'78108-T23-A110\x00\x00', b'78108-T21-A230\x00\x00', b'78108-T22-A020\x00\x00', + b'78108-T21-MB10\x00\x00', ], (Ecu.fwdRadar, 0x18dab0f1, None): [ b'36161-T20-A070\x00\x00', b'36161-T20-A080\x00\x00', b'36161-T20-A060\x00\x00', b'36161-T47-A070\x00\x00', + b'36161-T24-T070\x00\x00', ], (Ecu.vsa, 0x18DA28F1, None): [ b'57114-T20-AB40\x00\x00', b'57114-T43-JB30\x00\x00', + b'57114-T24-TB30\x00\x00', ], (Ecu.transmission, 0x18da1ef1, None): [ b'28101-65D-A020\x00\x00', b'28101-65D-A120\x00\x00', b'28101-65H-A020\x00\x00', b'28101-65H-A120\x00\x00', + b'28101-65J-N010\x00\x00', ], (Ecu.programmedFuelInjection, 0x18da10f1, None): [ b'37805-64L-A540\x00\x00', @@ -1482,6 +1489,7 @@ FW_VERSIONS = { b'37805-64S-A720\x00\x00', b'37805-64A-A540\x00\x00', b'37805-64A-A620\x00\x00', + b'37805-64D-P510\x00\x00', ], }, } From a8b43a44c56600d5a003ef1314873a509c165363 Mon Sep 17 00:00:00 2001 From: Jacob Pfeifer Date: Fri, 3 Feb 2023 02:25:51 -0500 Subject: [PATCH 281/484] 2020 Hyundai Santa Fe fingerprint update (#27135) * add new transmission fwVersion for 2020 hyundai santa fe * use shorter trans, add missing fw * duplicate --------- Co-authored-by: Shane Smiskol --- selfdrive/car/hyundai/values.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 9c1efa812d..4c8e924c49 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -686,7 +686,7 @@ FW_VERSIONS = { (Ecu.abs, 0x7d1, None): [ b'\xf1\x00TM ESC \r 100\x18\x031 58910-S2650', b'\xf1\x00TM ESC \r 103\x18\x11\x08 58910-S2650', - b'\xf1\x00TM ESC \r 104\x19\a\b 58910-S2650', + b'\xf1\x00TM ESC \r 104\x19\x07\x08 58910-S2650', b'\xf1\x00TM ESC \x02 100\x18\x030 58910-S2600', b'\xf1\x00TM ESC \x02 102\x18\x07\x01 58910-S2600', b'\xf1\x00TM ESC \x02 103\x18\x11\x07 58910-S2600', @@ -708,6 +708,7 @@ FW_VERSIONS = { b'\xf1\x00TM MFC AT USA LHD 1.00 1.00 99211-S2000 180409', ], (Ecu.transmission, 0x7e1, None): [ + b'\xf1\x006W351_C2\x00\x006W3E1051\x00\x00TTM4T20NS5\x00\x00\x00\x00', b'\xf1\x87LBJSGA7082574HG0\x87www\x98\x88\x88\x88\x99\xaa\xb9\x9afw\x86gx\x99\xa7\x89co\xf8\xffvU_\xffR\xaf\xf1\x816W3C2051\x00\x00\xf1\x006W351_C2\x00\x006W3C2051\x00\x00TTM2T20NS1\x00\xa6\xe0\x91', b'\xf1\x87LBKSGA0458404HG0vfvg\x87www\x89\x99\xa8\x99y\xaa\xa7\x9ax\x88\xa7\x88t_\xf9\xff\x86w\x8f\xff\x15x\xf1\x816W3C2051\x00\x00\xf1\x006W351_C2\x00\x006W3C2051\x00\x00TTM2T20NS1\x00\x00\x00\x00', b'\xf1\x87LDJUEA6010814HG1\x87w\x87x\x86gvw\x88\x88\x98\x88gw\x86wx\x88\x97\x88\x85o\xf8\xff\x86f_\xff\xd37\xf1\x816W3C2051\x00\x00\xf1\x006W351_C2\x00\x006W3C2051\x00\x00TTM4T20NS0\xf8\x19\x92g', From ef95f921d1e3a5f8566ed4820b8ae8ca9b03609e Mon Sep 17 00:00:00 2001 From: Iamz Date: Fri, 3 Feb 2023 14:35:22 +0700 Subject: [PATCH 282/484] Added new fingerprint for Toyota Corolla Cross GR Sport (#26693) Co-authored-by: Shane Smiskol --- selfdrive/car/toyota/values.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index 6c2b865982..490c94db75 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -848,6 +848,7 @@ FW_VERSIONS = { b'\x02896630ZR2000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', b'\x02896630ZT8000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', b'\x02896630ZT9000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', + b'\x02896630ZZ0000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', b'\x028966312K6000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', b'\x028966312L0000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', b'\x028966312Q3000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', @@ -860,6 +861,7 @@ FW_VERSIONS = { b'8965B12361\x00\x00\x00\x00\x00\x00', b'8965B12451\x00\x00\x00\x00\x00\x00', b'8965B16011\x00\x00\x00\x00\x00\x00', + b'8965B16101\x00\x00\x00\x00\x00\x00', b'8965B76012\x00\x00\x00\x00\x00\x00', b'8965B76050\x00\x00\x00\x00\x00\x00', b'\x018965B12350\x00\x00\x00\x00\x00\x00', @@ -907,6 +909,7 @@ FW_VERSIONS = { b'\x028646F1202100\x00\x00\x00\x008646G2601400\x00\x00\x00\x00', b'\x028646F1202200\x00\x00\x00\x008646G2601500\x00\x00\x00\x00', b'\x028646F1601100\x00\x00\x00\x008646G2601400\x00\x00\x00\x00', + b'\x028646F1601200\x00\x00\x00\x008646G2601400\x00\x00\x00\x00', b"\x028646F1601300\x00\x00\x00\x008646G2601400\x00\x00\x00\x00", b'\x028646F4203400\x00\x00\x00\x008646G2601200\x00\x00\x00\x00', b'\x028646F76020C0\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', From 1ace481fa85287cbfb1799bea9d2fd325eaf1ce7 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 4 Feb 2023 02:50:39 +0800 Subject: [PATCH 283/484] cabana: elide long signal names (#27202) elide long signal names --- tools/cabana/signaledit.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/cabana/signaledit.cc b/tools/cabana/signaledit.cc index 7f31293c41..b871ffa21a 100644 --- a/tools/cabana/signaledit.cc +++ b/tools/cabana/signaledit.cc @@ -292,7 +292,10 @@ void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op font.setBold(true); painter->setFont(font); painter->setPen((option.state & QStyle::State_Selected ? option.palette.highlightedText() : option.palette.text()).color()); - painter->drawText(option.rect.adjusted(rc.width() + 9, 0, 0, 0), option.displayAlignment, index.data(Qt::DisplayRole).toString()); + QString text = index.data(Qt::DisplayRole).toString(); + QRect text_rect = option.rect.adjusted(rc.width() + 9, 0, 0, 0); + text = painter->fontMetrics().elidedText(text, Qt::ElideRight, text_rect.width()); + painter->drawText(text_rect, option.displayAlignment, text); painter->restore(); } else { QStyledItemDelegate::paint(painter, option, index); From bd66f77a3b5f59c1b0f72a4a655f0ca883b35f11 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 4 Feb 2023 03:40:35 +0800 Subject: [PATCH 284/484] cabana: fix chart buttons occasionally did not respond to click. (#27198) * fix btn not responding on click * change icon from gear to list(menu) --- tools/cabana/chartswidget.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index e49d28db44..1b8ce657a6 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -322,7 +322,7 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { QToolButton *manage_btn = new QToolButton(); manage_btn->setToolButtonStyle(Qt::ToolButtonIconOnly); - manage_btn->setIcon(utils::icon("gear")); + manage_btn->setIcon(utils::icon("list")); manage_btn->setAutoRaise(true); QMenu *menu = new QMenu(this); line_series_action = menu->addAction(tr("Line"), [this]() { setSeriesType(QAbstractSeries::SeriesTypeLine); }); @@ -613,7 +613,7 @@ void ChartView::leaveEvent(QEvent *event) { void ChartView::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton && !chart()->plotArea().contains(event->pos()) && - !manage_btn_proxy->widget()->underMouse() && !close_btn_proxy->widget()->underMouse()) { + !manage_btn_proxy->geometry().contains(event->pos()) && !close_btn_proxy->geometry().contains(event->pos())) { QMimeData *mimeData = new QMimeData; mimeData->setData(mime_type, QByteArray::number((qulonglong)this)); QDrag *drag = new QDrag(this); From 858f185fd28444b5ca72038b5e284a5699704917 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 3 Feb 2023 12:46:14 -0800 Subject: [PATCH 285/484] Hyundai: update Hyundai Genesis tuning (#27197) --- selfdrive/car/torque_data/params.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/car/torque_data/params.yaml b/selfdrive/car/torque_data/params.yaml index 397b29525d..7da508a156 100644 --- a/selfdrive/car/torque_data/params.yaml +++ b/selfdrive/car/torque_data/params.yaml @@ -25,7 +25,7 @@ HONDA ODYSSEY 2018: [1.8774809275211801, 0.8394431662987996, 0.2096978613792822] HONDA PILOT 2017: [1.7262026201812795, 0.9470005614967523, 0.21351430733218763] HONDA RIDGELINE 2017: [1.4146525028237624, 0.7356572861629564, 0.23307177552211328] HYUNDAI ELANTRA 2021: [3.169, 2.1259108157250735, 0.0819] -HYUNDAI GENESIS 2015-2016: [1.8466226943929824, 1.5552063647830634, 0.0984484465421171] +HYUNDAI GENESIS 2015-2016: [2.7807965280270794, 2.325, 0.0984484465421171] HYUNDAI IONIQ 5 2022: [3.172929, 2.713050, 0.096019] HYUNDAI IONIQ ELECTRIC LIMITED 2019: [1.7662975472852054, 1.613755614526594, 0.17087579756306276] HYUNDAI IONIQ PHEV 2020: [3.2928700076638537, 2.1193482926455656, 0.12463700961468778] From 29d9d037590d2db33b7d49c44120d83307ec6568 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 4 Feb 2023 04:47:26 +0800 Subject: [PATCH 286/484] cabana: add dialog to open route from remote or local (#27183) * add OpenRouteDialog * cleanup * failed_to_load * fix load * clear message list and stream after open new route * show message * remove all tabs and charts after open * use textEdited * check route format * cleanup loadRoute --- tools/cabana/SConscript | 2 +- tools/cabana/cabana.cc | 23 +++++++--- tools/cabana/chartswidget.cc | 4 +- tools/cabana/chartswidget.h | 2 +- tools/cabana/detailwidget.cc | 10 ++++ tools/cabana/detailwidget.h | 1 + tools/cabana/mainwin.cc | 17 +++++++ tools/cabana/mainwin.h | 1 + tools/cabana/messageswidget.cc | 26 +++++++++-- tools/cabana/messageswidget.h | 4 ++ tools/cabana/route.cc | 68 ++++++++++++++++++++++++++++ tools/cabana/route.h | 19 ++++++++ tools/cabana/settings.cc | 2 + tools/cabana/settings.h | 1 + tools/cabana/streams/replaystream.cc | 12 ++--- tools/cabana/streams/replaystream.h | 7 +-- 16 files changed, 174 insertions(+), 25 deletions(-) create mode 100644 tools/cabana/route.cc create mode 100644 tools/cabana/route.h diff --git a/tools/cabana/SConscript b/tools/cabana/SConscript index 0c9ad14973..a9922ba9be 100644 --- a/tools/cabana/SConscript +++ b/tools/cabana/SConscript @@ -21,7 +21,7 @@ cabana_env = qt_env.Clone() prev_moc_path = cabana_env['QT_MOCHPREFIX'] cabana_env['QT_MOCHPREFIX'] = os.path.dirname(prev_moc_path) + '/cabana/moc_' cabana_lib = cabana_env.Library("cabana_lib", ['mainwin.cc', 'streams/livestream.cc', 'streams/abstractstream.cc', 'streams/replaystream.cc', 'binaryview.cc', 'chartswidget.cc', 'historylog.cc', 'videowidget.cc', 'signaledit.cc', 'dbcmanager.cc', - 'commands.cc', 'messageswidget.cc', 'settings.cc', 'util.cc', 'detailwidget.cc', 'tools/findsimilarbits.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) + 'commands.cc', 'messageswidget.cc', 'route.cc', 'settings.cc', 'util.cc', 'detailwidget.cc', 'tools/findsimilarbits.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) cabana_env.Program('_cabana', ['cabana.cc', cabana_lib, asset_obj], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) if arch == "Darwin": diff --git a/tools/cabana/cabana.cc b/tools/cabana/cabana.cc index e34e2d0205..e028e383c2 100644 --- a/tools/cabana/cabana.cc +++ b/tools/cabana/cabana.cc @@ -4,6 +4,7 @@ #include "common/prefix.h" #include "selfdrive/ui/qt/util.h" #include "tools/cabana/mainwin.h" +#include "tools/cabana/route.h" #include "tools/cabana/streams/livestream.h" #include "tools/cabana/streams/replaystream.h" @@ -26,10 +27,6 @@ int main(int argc, char *argv[]) { cmd_parser.addOption({"no-vipc", "do not output video"}); cmd_parser.addOption({"dbc", "dbc file to open", "dbc"}); cmd_parser.process(app); - const QStringList args = cmd_parser.positionalArguments(); - if (args.empty() && !cmd_parser.isSet("demo") && !cmd_parser.isSet("stream")) { - cmd_parser.showHelp(); - } std::unique_ptr op_prefix; std::unique_ptr stream; @@ -41,7 +38,6 @@ int main(int argc, char *argv[]) { #ifndef __APPLE__ op_prefix.reset(new OpenpilotPrefix()); #endif - const QString route = args.empty() ? DEMO_ROUTE : args.first(); uint32_t replay_flags = REPLAY_FLAG_NONE; if (cmd_parser.isSet("ecam")) { replay_flags |= REPLAY_FLAG_ECAM; @@ -50,9 +46,22 @@ int main(int argc, char *argv[]) { } else if (cmd_parser.isSet("no-vipc")) { replay_flags |= REPLAY_FLAG_NO_VIPC; } - auto replay_stream = new ReplayStream(&app); + + const QStringList args = cmd_parser.positionalArguments(); + QString route; + if (args.size() > 0) { + route = args.first(); + } else if (cmd_parser.isSet("demo")) { + route = DEMO_ROUTE; + } + + auto replay_stream = new ReplayStream(replay_flags, &app); stream.reset(replay_stream); - if (!replay_stream->loadRoute(route, cmd_parser.value("data_dir"), replay_flags)) { + if (route.isEmpty()) { + if (OpenRouteDialog dlg(nullptr); !dlg.exec()) { + return 0; + } + } else if (!replay_stream->loadRoute(route, cmd_parser.value("data_dir"))) { return 0; } } diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 1b8ce657a6..eb14e5a044 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -137,7 +137,7 @@ void ChartsWidget::updateState() { if (!is_zoomed) { double pos = (cur_sec - display_range.first) / max_chart_range; if (pos < 0 || pos > 0.8) { - const double min_event_sec = (can->events()->front()->mono_time / (double)1e9) - can->routeStartTime(); + const double min_event_sec = can->events()->empty() ? 0 : (can->events()->front()->mono_time / (double)1e9 - can->routeStartTime()); display_range.first = std::floor(std::max(min_event_sec, cur_sec - max_chart_range * 0.2)); } display_range.second = std::floor(display_range.first + max_chart_range); @@ -157,7 +157,7 @@ void ChartsWidget::updateState() { void ChartsWidget::setMaxChartRange(int value) { max_chart_range = settings.chart_range = value; double current_sec = can->currentSec(); - const double min_event_sec = (can->events()->front()->mono_time / (double)1e9) - can->routeStartTime(); + const double min_event_sec = can->events()->empty() ? 0 : (can->events()->front()->mono_time / (double)1e9 - can->routeStartTime()); // keep current_sec's pos double pos = (current_sec - display_range.first) / (display_range.second - display_range.first); display_range.first = std::floor(std::max(min_event_sec, current_sec - max_chart_range * (1.0 - pos))); diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index 9b2afd45a9..15da8e53ad 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -97,6 +97,7 @@ public: public slots: void setColumnCount(int n); + void removeAll(); signals: void dock(bool floating); @@ -114,7 +115,6 @@ private: void zoomIn(double min, double max); void zoomReset(); void updateToolBar(); - void removeAll(); void setMaxChartRange(int value); void updateLayout(); void settingChanged(); diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index 3af0fa9fc3..46f7a148f4 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -104,6 +104,16 @@ void DetailWidget::showTabBarContextMenu(const QPoint &pt) { } } +void DetailWidget::removeAll() { + msg_id = ""; + tabbar->blockSignals(true); + while (tabbar->count() > 0) { + tabbar->removeTab(0); + } + tabbar->blockSignals(false); + stacked_layout->setCurrentIndex(0); +} + void DetailWidget::setMessage(const QString &message_id) { msg_id = message_id; int index = tabbar->count() - 1; diff --git a/tools/cabana/detailwidget.h b/tools/cabana/detailwidget.h index 3a3f3adf0e..a62e23f226 100644 --- a/tools/cabana/detailwidget.h +++ b/tools/cabana/detailwidget.h @@ -30,6 +30,7 @@ public: DetailWidget(ChartsWidget *charts, QWidget *parent); void setMessage(const QString &message_id); void refresh(); + void removeAll(); QSize minimumSizeHint() const override { return binary_view->minimumSizeHint(); } private: diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index 94cf9840f7..dfd38e267d 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -15,6 +15,7 @@ #include #include "tools/cabana/commands.h" +#include "tools/cabana/route.h" static MainWindow *main_win = nullptr; void qLogMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { @@ -66,6 +67,11 @@ MainWindow::MainWindow() : QMainWindow() { void MainWindow::createActions() { QMenu *file_menu = menuBar()->addMenu(tr("&File")); + if (!can->liveStreaming()) { + file_menu->addAction(tr("Open Route..."), this, &MainWindow::openRoute); + file_menu->addSeparator(); + } + file_menu->addAction(tr("New DBC File"), this, &MainWindow::newFile)->setShortcuts(QKeySequence::New); file_menu->addAction(tr("Open DBC File..."), this, &MainWindow::openFile)->setShortcuts(QKeySequence::Open); @@ -185,6 +191,17 @@ void MainWindow::DBCFileChanged() { setWindowFilePath(QString("%1").arg(dbc()->name())); } +void MainWindow::openRoute() { + OpenRouteDialog dlg(this); + if (dlg.exec()) { + detail_widget->removeAll(); + charts_widget->removeAll(); + statusBar()->showMessage(tr("Route %1 loaded").arg(can->routeName()), 2000); + } else if (dlg.failedToLoad()) { + close(); + } +} + void MainWindow::newFile() { remindSaveChanges(); dbc()->open("untitled.dbc", ""); diff --git a/tools/cabana/mainwin.h b/tools/cabana/mainwin.h index c26f6973c0..5e627df58b 100644 --- a/tools/cabana/mainwin.h +++ b/tools/cabana/mainwin.h @@ -23,6 +23,7 @@ public: void loadFile(const QString &fn); public slots: + void openRoute(); void newFile(); void openFile(); void openRecentFile(); diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index 9d0fc23e4d..359fe10f50 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -14,7 +13,7 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { QVBoxLayout *main_layout = new QVBoxLayout(this); // message filter - QLineEdit *filter = new QLineEdit(this); + filter = new QLineEdit(this); filter->setClearButtonEnabled(true); filter->setPlaceholderText(tr("filter messages")); main_layout->addWidget(filter); @@ -41,8 +40,9 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { main_layout->addLayout(suppress_layout); // signals/slots - QObject::connect(filter, &QLineEdit::textChanged, model, &MessageListModel::setFilterString); + QObject::connect(filter, &QLineEdit::textEdited, model, &MessageListModel::setFilterString); QObject::connect(can, &AbstractStream::msgsReceived, model, &MessageListModel::msgsReceived); + QObject::connect(can, &AbstractStream::streamStarted, this, &MessagesWidget::reset); QObject::connect(dbc(), &DBCManager::DBCFileChanged, model, &MessageListModel::sortMessages); QObject::connect(dbc(), &DBCManager::msgUpdated, model, &MessageListModel::sortMessages); QObject::connect(dbc(), &DBCManager::msgRemoved, model, &MessageListModel::sortMessages); @@ -83,6 +83,13 @@ void MessagesWidget::updateSuppressedButtons() { } } +void MessagesWidget::reset() { + model->reset(); + filter->clear(); + current_msg_id = ""; + updateSuppressedButtons(); +} + // MessageListModel @@ -175,10 +182,11 @@ void MessageListModel::sortMessages() { void MessageListModel::msgsReceived(const QHash *new_msgs) { int prev_row_count = msgs.size(); - if (filter_str.isEmpty() && msgs.size() != can->can_msgs.size()) { + bool update_all = new_msgs->size() == can->can_msgs.size(); + if (update_all || (filter_str.isEmpty() && msgs.size() != can->can_msgs.size())) { msgs = can->can_msgs.keys(); } - if (msgs.size() != prev_row_count) { + if (update_all || msgs.size() != prev_row_count) { sortMessages(); return; } @@ -215,3 +223,11 @@ void MessageListModel::suppress() { void MessageListModel::clearSuppress() { suppressed_bytes.clear(); } + +void MessageListModel::reset() { + beginResetModel(); + filter_str = ""; + msgs.clear(); + clearSuppress(); + endResetModel(); +} diff --git a/tools/cabana/messageswidget.h b/tools/cabana/messageswidget.h index 1927faa646..81ee36cd6f 100644 --- a/tools/cabana/messageswidget.h +++ b/tools/cabana/messageswidget.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -23,6 +24,7 @@ public: void sortMessages(); void suppress(); void clearSuppress(); + void reset(); QStringList msgs; QSet> suppressed_bytes; @@ -41,6 +43,7 @@ public: QByteArray saveHeaderState() const { return table_widget->horizontalHeader()->saveState(); } bool restoreHeaderState(const QByteArray &state) const { return table_widget->horizontalHeader()->restoreState(state); } void updateSuppressedButtons(); + void reset(); signals: void msgSelectionChanged(const QString &message_id); @@ -48,6 +51,7 @@ signals: protected: QTableView *table_widget; QString current_msg_id; + QLineEdit *filter; MessageListModel *model; QPushButton *suppress_add; QPushButton *suppress_clear; diff --git a/tools/cabana/route.cc b/tools/cabana/route.cc new file mode 100644 index 0000000000..ab322cdf90 --- /dev/null +++ b/tools/cabana/route.cc @@ -0,0 +1,68 @@ +#include "tools/cabana/route.h" + +#include +#include +#include +#include +#include +#include + +#include "tools/cabana/streams/replaystream.h" + +OpenRouteDialog::OpenRouteDialog(QWidget *parent) : QDialog(parent) { + // TODO: get route list from api.comma.ai + QHBoxLayout *edit_layout = new QHBoxLayout; + edit_layout->addWidget(new QLabel(tr("Route:"))); + edit_layout->addWidget(route_edit = new QLineEdit(this)); + route_edit->setPlaceholderText(tr("Enter remote route name or click browse to select a local route")); + auto file_btn = new QPushButton(tr("Browse..."), this); + edit_layout->addWidget(file_btn); + + btn_box = new QDialogButtonBox(QDialogButtonBox::Open | QDialogButtonBox::Cancel); + btn_box->button(QDialogButtonBox::Open)->setEnabled(false); + + QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->addStretch(0); + main_layout->addLayout(edit_layout); + main_layout->addStretch(0); + main_layout->addWidget(btn_box); + setMinimumSize({550, 120}); + + QObject::connect(btn_box, &QDialogButtonBox::accepted, this, &OpenRouteDialog::loadRoute); + QObject::connect(btn_box, &QDialogButtonBox::rejected, this, &QDialog::reject); + QObject::connect(route_edit, &QLineEdit::textChanged, [this]() { + btn_box->button(QDialogButtonBox::Open)->setEnabled(!route_edit->text().isEmpty()); + }); + QObject::connect(file_btn, &QPushButton::clicked, [=]() { + QString dir = QFileDialog::getExistingDirectory(this, tr("Open Local Route"), settings.last_route_dir); + if (!dir.isEmpty()) { + route_edit->setText(dir); + settings.last_route_dir = QFileInfo(dir).absolutePath(); + } + }); +} + +void OpenRouteDialog::loadRoute() { + btn_box->setEnabled(false); + + QString route = route_edit->text(); + QString data_dir; + if (int idx = route.lastIndexOf('/'); idx != -1) { + data_dir = route.mid(0, idx + 1); + route = route.mid(idx + 1); + } + + bool is_valid_format = Route::parseRoute(route).str.size() > 0; + if (!is_valid_format) { + QMessageBox::warning(nullptr, tr("Warning"), tr("Invalid route format: '%1'").arg(route)); + } else { + failed_to_load = !dynamic_cast(can)->loadRoute(route, data_dir); + if (failed_to_load) { + QMessageBox::warning(nullptr, tr("Warning"), tr("Failed to load route: '%1'").arg(route)); + } else { + accept(); + } + } + + btn_box->setEnabled(true); +} diff --git a/tools/cabana/route.h b/tools/cabana/route.h new file mode 100644 index 0000000000..ceda71d585 --- /dev/null +++ b/tools/cabana/route.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include + +class OpenRouteDialog : public QDialog { + Q_OBJECT + +public: + OpenRouteDialog(QWidget *parent); + void loadRoute(); + inline bool failedToLoad() const { return failed_to_load; } + +private: + QLineEdit *route_edit; + QDialogButtonBox *btn_box; + bool failed_to_load = false; +}; diff --git a/tools/cabana/settings.cc b/tools/cabana/settings.cc index 22c7a941ab..6cbd16cabf 100644 --- a/tools/cabana/settings.cc +++ b/tools/cabana/settings.cc @@ -20,6 +20,7 @@ void Settings::save() { s.setValue("chart_range", chart_range); s.setValue("chart_column_count", chart_column_count); s.setValue("last_dir", last_dir); + s.setValue("last_route_dir", last_route_dir); s.setValue("window_state", window_state); s.setValue("geometry", geometry); s.setValue("video_splitter_state", video_splitter_state); @@ -36,6 +37,7 @@ void Settings::load() { chart_range = s.value("chart_range", 3 * 60).toInt(); chart_column_count = s.value("chart_column_count", 1).toInt(); last_dir = s.value("last_dir", QDir::homePath()).toString(); + last_route_dir = s.value("last_route_dir", QDir::homePath()).toString(); window_state = s.value("window_state").toByteArray(); geometry = s.value("geometry").toByteArray(); video_splitter_state = s.value("video_splitter_state").toByteArray(); diff --git a/tools/cabana/settings.h b/tools/cabana/settings.h index 7abad81c29..a302d20077 100644 --- a/tools/cabana/settings.h +++ b/tools/cabana/settings.h @@ -20,6 +20,7 @@ public: int chart_range = 3 * 60; // e minutes int chart_series_type = 0; QString last_dir; + QString last_route_dir; QByteArray geometry; QByteArray video_splitter_state; QByteArray window_state; diff --git a/tools/cabana/streams/replaystream.cc b/tools/cabana/streams/replaystream.cc index 72c4a13048..b768b94327 100644 --- a/tools/cabana/streams/replaystream.cc +++ b/tools/cabana/streams/replaystream.cc @@ -2,7 +2,7 @@ #include "tools/cabana/dbcmanager.h" -ReplayStream::ReplayStream(QObject *parent) : AbstractStream(parent, false) { +ReplayStream::ReplayStream(uint32_t replay_flags, QObject *parent) : replay_flags(replay_flags), AbstractStream(parent, false) { QObject::connect(&settings, &Settings::changed, [this]() { if (replay) replay->setSegmentCacheLimit(settings.max_cached_minutes); }); @@ -16,13 +16,13 @@ static bool event_filter(const Event *e, void *opaque) { return ((ReplayStream *)opaque)->eventFilter(e); } -bool ReplayStream::loadRoute(const QString &route, const QString &data_dir, uint32_t replay_flags) { - replay = new Replay(route, {"can", "roadEncodeIdx", "wideRoadEncodeIdx", "carParams"}, {}, nullptr, replay_flags, data_dir, this); +bool ReplayStream::loadRoute(const QString &route, const QString &data_dir) { + replay.reset(new Replay(route, {"can", "roadEncodeIdx", "wideRoadEncodeIdx", "carParams"}, {}, nullptr, replay_flags, data_dir, this)); replay->setSegmentCacheLimit(settings.max_cached_minutes); replay->installEventFilter(event_filter, this); - QObject::connect(replay, &Replay::seekedTo, this, &AbstractStream::seekedTo); - QObject::connect(replay, &Replay::segmentsMerged, this, &AbstractStream::eventsMerged); - QObject::connect(replay, &Replay::streamStarted, this, &AbstractStream::streamStarted); + QObject::connect(replay.get(), &Replay::seekedTo, this, &AbstractStream::seekedTo); + QObject::connect(replay.get(), &Replay::segmentsMerged, this, &AbstractStream::eventsMerged); + QObject::connect(replay.get(), &Replay::streamStarted, this, &AbstractStream::streamStarted); if (replay->load()) { const auto &segments = replay->route()->segments(); if (std::none_of(segments.begin(), segments.end(), [](auto &s) { return s.second.rlog.length() > 0; })) { diff --git a/tools/cabana/streams/replaystream.h b/tools/cabana/streams/replaystream.h index a9a74e33b5..69fb738ab8 100644 --- a/tools/cabana/streams/replaystream.h +++ b/tools/cabana/streams/replaystream.h @@ -8,9 +8,9 @@ class ReplayStream : public AbstractStream { Q_OBJECT public: - ReplayStream(QObject *parent); + ReplayStream(uint32_t replay_flags, QObject *parent); ~ReplayStream(); - bool loadRoute(const QString &route, const QString &data_dir, uint32_t replay_flags = REPLAY_FLAG_NONE); + bool loadRoute(const QString &route, const QString &data_dir); bool eventFilter(const Event *event); void seekTo(double ts) override { replay->seekTo(std::max(double(0), ts), false); }; inline QString routeName() const override { return replay->route()->name(); } @@ -28,5 +28,6 @@ public: inline const std::vector> getTimeline() override { return replay->getTimeline(); } private: - Replay *replay = nullptr; + std::unique_ptr replay = nullptr; + uint32_t replay_flags = REPLAY_FLAG_NONE; }; From d6516059bd757b1e23c94808187b2dbc38786ca8 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 4 Feb 2023 05:03:01 +0800 Subject: [PATCH 287/484] cabana: fix incorrect point size after adding new series to chart or changing series type (#27199) * update pointers after create series * cleanup --- tools/cabana/chartswidget.cc | 51 ++++++++++++++++++++---------------- tools/cabana/chartswidget.h | 1 + 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index eb14e5a044..54cacf56cd 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -376,6 +376,7 @@ void ChartView::addSeries(const QString &msg_id, const Signal *sig) { sigs.push_back({.msg_id = msg_id, .address = address, .source = source, .sig = sig, .series = series}); updateTitle(); updateSeries(sig); + updateSeriesPoints(); emit seriesAdded(msg_id, sig); } @@ -482,39 +483,42 @@ void ChartView::updatePlot(double cur, double min, double max) { if (min != axis_x->min() || max != axis_x->max()) { axis_x->setRange(min, max); updateAxisY(); + updateSeriesPoints(); + } - // Show points when zoomed in enough - for (auto &s : sigs) { - auto begin = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->min(), [](auto &p, double x) { return p.x() < x; }); - auto end = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->max(), [](auto &p, double x) { return p.x() < x; }); + scene()->invalidate({}, QGraphicsScene::ForegroundLayer); +} - int num_points = std::max(end - begin, 1); - int pixels_per_point = width() / num_points; +void ChartView::updateSeriesPoints() { + // Show points when zoomed in enough + for (auto &s : sigs) { + auto begin = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->min(), [](auto &p, double x) { return p.x() < x; }); + auto end = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->max(), [](auto &p, double x) { return p.x() < x; }); - if (series_type == QAbstractSeries::SeriesTypeScatter) { - ((QScatterSeries *)s.series)->setMarkerSize(std::clamp(pixels_per_point / 3, 1, 8)); - } else { - s.series->setPointsVisible(pixels_per_point > 20); + int num_points = std::max(end - begin, 1); + int pixels_per_point = width() / num_points; - // TODO: On MacOS QChartWidget doesn't work with the OpenGL settings that CameraWidget needs. + if (series_type == QAbstractSeries::SeriesTypeScatter) { + ((QScatterSeries *)s.series)->setMarkerSize(std::clamp(pixels_per_point / 3, 1, 8)); + } else { + s.series->setPointsVisible(pixels_per_point > 20); + + // TODO: On MacOS QChartWidget doesn't work with the OpenGL settings that CameraWidget needs. #ifndef __APPLE - // OpenGL mode lacks certain features (such as showing points), only use when drawing many points - bool use_opengl = pixels_per_point < 1; - s.series->setUseOpenGL(use_opengl); + // OpenGL mode lacks certain features (such as showing points), only use when drawing many points + bool use_opengl = pixels_per_point < 1; + s.series->setUseOpenGL(use_opengl); - // Qt doesn't properly apply device pixel ratio in OpenGL mode - QApplication *application = static_cast(QApplication::instance()); - float scale = use_opengl ? application->devicePixelRatio() : 1.0; + // Qt doesn't properly apply device pixel ratio in OpenGL mode + QApplication *application = static_cast(QApplication::instance()); + float scale = use_opengl ? application->devicePixelRatio() : 1.0; - QPen pen = s.series->pen(); - pen.setWidth(2.0 * scale); - s.series->setPen(pen); + QPen pen = s.series->pen(); + pen.setWidth(2.0 * scale); + s.series->setPen(pen); #endif - } } } - - scene()->invalidate({}, QGraphicsScene::ForegroundLayer); } void ChartView::updateSeries(const Signal *sig, const std::vector *events, bool clear) { @@ -783,6 +787,7 @@ void ChartView::setSeriesType(QAbstractSeries::SeriesType type) { series->replace(s.vals); s.series = series; } + updateSeriesPoints(); updateTitle(); } } diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index 15da8e53ad..8c50993971 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -73,6 +73,7 @@ private: void applyNiceNumbers(qreal min, qreal max); qreal niceNumber(qreal x, bool ceiling); QXYSeries *createSeries(QAbstractSeries::SeriesType type); + void updateSeriesPoints(); QValueAxis *axis_x; QValueAxis *axis_y; From 74187463da9f93c6841a6394b0b91a5451ef4765 Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Fri, 3 Feb 2023 14:45:36 -0700 Subject: [PATCH 288/484] laikad: add laikad startup tests (#27207) * add laikad startup test * move debug files to xx * add laikad startup and ephemeris test --------- Co-authored-by: Kurt Nistelberger --- tools/gpstest/test_laikad.py | 105 +++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 tools/gpstest/test_laikad.py diff --git a/tools/gpstest/test_laikad.py b/tools/gpstest/test_laikad.py new file mode 100644 index 0000000000..613ac991b1 --- /dev/null +++ b/tools/gpstest/test_laikad.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +import os +import time +import unittest + +import cereal.messaging as messaging +import selfdrive.sensord.pigeond as pd + +from common.params import Params +from system.hardware import TICI +from selfdrive.manager.process_config import managed_processes +from selfdrive.test.helpers import with_processes + + +def wait_for_location(sm, timeout, con=10): + cons_meas = 0 + start_time = time.monotonic() + while (time.monotonic() - start_time) < timeout: + sm.update() + if not sm.updated["gnssMeasurements"]: + continue + + msg = sm["gnssMeasurements"] + cons_meas = (cons_meas + 1) if 'positionECEF' in msg.to_dict() else 0 + if cons_meas >= con: + return True + return False + + +class TestLaikad(unittest.TestCase): + @classmethod + def setUpClass(self): + if not TICI: + raise unittest.SkipTest + + ublox_available = Params().get_bool("UbloxAvailable") + if not ublox_available: + raise unittest.SkipTest + + def setUp(self): + # ensure laikad cold start + Params().remove("LaikadEphemeris") + os.environ["LAIKAD_NO_INTERNET"] = "1" + managed_processes['laikad'].start() + + def tearDown(self): + managed_processes['laikad'].stop() + + + @with_processes(['pigeond', 'ubloxd']) + def test_laikad_cold_start(self): + time.sleep(5) + + start_time = time.monotonic() + sm = messaging.SubMaster(["gnssMeasurements"]) + + success = wait_for_location(sm, 60*2, con=10) + duration = time.monotonic() - start_time + + assert success, "Waiting for location timed out (2min)!" + assert duration < 60, f"Received Location {duration}!" + + + @with_processes(['ubloxd']) + def test_laikad_ublox_reset_start(self): + time.sleep(2) + + pigeon, pm = pd.create_pigeon() + pd.init_baudrate(pigeon) + assert pigeon.reset_device(), "Could not reset device!" + + laikad_sock = messaging.sub_sock("gnssMeasurements", timeout=0.1) + ublox_gnss_sock = messaging.sub_sock("ubloxGnss", timeout=0.1) + + pd.init_baudrate(pigeon) + pd.initialize_pigeon(pigeon) + pd.run_receiving(pigeon, pm, 180) + + ublox_msgs = messaging.drain_sock(ublox_gnss_sock) + laikad_msgs = messaging.drain_sock(laikad_sock) + + gps_ephem_cnt = 0 + glonass_ephem_cnt = 0 + for um in ublox_msgs: + if um.ubloxGnss.which() == 'ephemeris': + gps_ephem_cnt += 1 + elif um.ubloxGnss.which() == 'glonassEphemeris': + glonass_ephem_cnt += 1 + + assert gps_ephem_cnt > 0, "NO gps ephemeris collected!" + assert glonass_ephem_cnt > 0, "NO glonass ephemeris collected!" + + pos_meas = 0 + duration = -1 + for lm in laikad_msgs: + pos_meas = (pos_meas + 1) if 'positionECEF' in lm.gnssMeasurements.to_dict() else 0 + if pos_meas > 5: + duration = (lm.logMonoTime - laikad_msgs[0].logMonoTime)*1e-9 + break + + assert pos_meas > 5, "NOT enough positions at end of read!" + assert duration < 120, "Laikad took too long to get a Position!" + +if __name__ == "__main__": + unittest.main() From 9a42929b0c7bfa259f361b17ec22dfd34e389fec Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Fri, 3 Feb 2023 15:22:33 -0700 Subject: [PATCH 289/484] navd: check duration_typical before using (#27184) check duration_typical Co-authored-by: Kurt Nistelberger --- selfdrive/navd/navd.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/selfdrive/navd/navd.py b/selfdrive/navd/navd.py index 81e3bdb3df..7af911ab2a 100755 --- a/selfdrive/navd/navd.py +++ b/selfdrive/navd/navd.py @@ -226,7 +226,11 @@ class RouteEngine: remaining = 1.0 - along_geometry / max(step['distance'], 1) total_distance = step['distance'] * remaining total_time = step['duration'] * remaining - total_time_typical = step['duration_typical'] * remaining + + if step['duration_typical'] is None: + total_time_typical = total_time + else: + total_time_typical = step['duration_typical'] * remaining # Add up totals for future steps for i in range(self.step_idx + 1, len(self.route)): From 73a8d6b95185da3b49129143160b99d83e5a753d Mon Sep 17 00:00:00 2001 From: pbkompasz <47194071+pbkompasz@users.noreply.github.com> Date: Sat, 4 Feb 2023 01:30:59 +0200 Subject: [PATCH 290/484] docs: Add cabana to tools (#27208) --- docs/overview.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/overview.rst b/docs/overview.rst index cda51ba3d4..cc4c582155 100644 --- a/docs/overview.rst +++ b/docs/overview.rst @@ -77,3 +77,4 @@ tools Simulator tools/ssh/README.md Webcam + tools/cabana/README.md From 90108c0c28082b9129703b3a67e68180362e3dba Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sun, 5 Feb 2023 01:42:39 +0800 Subject: [PATCH 291/484] cabana: better window title for live streaming (#27214) better title for live streaming --- tools/cabana/mainwin.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index dfd38e267d..b24c4e6dc4 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -267,7 +267,11 @@ void MainWindow::loadDBCFromFingerprint() { remindSaveChanges(); auto fingerprint = can->carFingerprint(); - video_dock->setWindowTitle(tr("ROUTE: %1 FINGERPINT: %2").arg(can->routeName()).arg(fingerprint.isEmpty() ? tr("Unknown Car") : fingerprint)); + if (can->liveStreaming()) { + video_dock->setWindowTitle(can->routeName()); + } else { + video_dock->setWindowTitle(tr("ROUTE: %1 FINGERPINT: %2").arg(can->routeName()).arg(fingerprint.isEmpty() ? tr("Unknown Car") : fingerprint)); + } if (!fingerprint.isEmpty()) { auto dbc_name = fingerprint_to_dbc[fingerprint]; if (dbc_name != QJsonValue::Undefined) { From 2d64f99b016401a8b81d275aac4a3305798c3171 Mon Sep 17 00:00:00 2001 From: Erich Moraga <33645296+ErichMoraga@users.noreply.github.com> Date: Sat, 4 Feb 2023 17:10:55 -0600 Subject: [PATCH 292/484] Add missing HIGHLANDER_TSS2 engine f/w (#27220) @Arnold Jagt#4866 2023 Highlander LE (ICE) DongleID/route b75bae9b9bde6210|2023-02-04--17-44-15 --- selfdrive/car/toyota/values.py | 1 + 1 file changed, 1 insertion(+) diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index 490c94db75..1b30fe43e9 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -1012,6 +1012,7 @@ FW_VERSIONS = { b'\x01896630ED9100\x00\x00\x00\x00', b'\x01896630EE1000\x00\x00\x00\x00', b'\x01896630EE1100\x00\x00\x00\x00', + b'\x01896630EG3000\x00\x00\x00\x00', b'\x01896630EG5000\x00\x00\x00\x00', ], (Ecu.fwdRadar, 0x750, 0xf): [ From d662f6d289aa8689045d4cf5fa017bcf32d249a2 Mon Sep 17 00:00:00 2001 From: Erich Moraga <33645296+ErichMoraga@users.noreply.github.com> Date: Sat, 4 Feb 2023 17:11:08 -0600 Subject: [PATCH 293/484] Add sveral missing LEXUS_ESH_TSS2 firmwares (#27219) * Add sveral missing LEXUS_ESH_TSS2 firmwares @salmankali#7352 2023 Lexus ES 300h DongleID/route c010a8cd3af884f5|2023-02-04--03-27-12 * docs.py gen'd CARS.md w/ 2023 Lexus ES Hybrid update --- docs/CARS.md | 2 +- selfdrive/car/toyota/values.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index 45873c4924..c48698b84f 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -124,7 +124,7 @@ A supported vehicle is one that just works when you install a comma three. All s |Lexus|CT Hybrid 2017-18|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| |Lexus|ES 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| |Lexus|ES Hybrid 2017-18|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Lexus|ES Hybrid 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| +|Lexus|ES Hybrid 2019-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| |Lexus|IS 2017-19|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| |Lexus|NX 2018-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| |Lexus|NX 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index 1b30fe43e9..6cedef414b 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -162,7 +162,7 @@ CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = { CAR.LEXUS_CTH: ToyotaCarInfo("Lexus CT Hybrid 2017-18", "Lexus Safety System+"), CAR.LEXUS_ESH: ToyotaCarInfo("Lexus ES Hybrid 2017-18", "Lexus Safety System+"), CAR.LEXUS_ES_TSS2: ToyotaCarInfo("Lexus ES 2019-22"), - CAR.LEXUS_ESH_TSS2: ToyotaCarInfo("Lexus ES Hybrid 2019-22", video_link="https://youtu.be/BZ29osRVJeg?t=12"), + CAR.LEXUS_ESH_TSS2: ToyotaCarInfo("Lexus ES Hybrid 2019-23", video_link="https://youtu.be/BZ29osRVJeg?t=12"), CAR.LEXUS_IS: ToyotaCarInfo("Lexus IS 2017-19"), CAR.LEXUS_NX: ToyotaCarInfo("Lexus NX 2018-19"), CAR.LEXUS_NXH: ToyotaCarInfo("Lexus NX Hybrid 2018-19"), @@ -1592,12 +1592,14 @@ FW_VERSIONS = { b'\x028966333V4000\x00\x00\x00\x00897CF3305001\x00\x00\x00\x00', b'\x02896633T09000\x00\x00\x00\x00897CF3307001\x00\x00\x00\x00', b'\x01896633T38000\x00\x00\x00\x00', + b'\x01896633T58000\x00\x00\x00\x00', ], (Ecu.abs, 0x7b0, None): [ b'F152633423\x00\x00\x00\x00\x00\x00', b'F152633680\x00\x00\x00\x00\x00\x00', b'F152633681\x00\x00\x00\x00\x00\x00', b'F152633F50\x00\x00\x00\x00\x00\x00', + b'F152633F51\x00\x00\x00\x00\x00\x00', ], (Ecu.eps, 0x7a1, None): [ b'8965B33252\x00\x00\x00\x00\x00\x00', @@ -1613,6 +1615,7 @@ FW_VERSIONS = { b'\x018821F6201300\x00\x00\x00\x00', ], (Ecu.fwdCamera, 0x750, 0x6d): [ + b'\x028646F0610000\x00\x00\x00\x008646G3304000\x00\x00\x00\x00', b'\x028646F33030D0\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', b'\x028646F3303100\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', b'\x028646F3303200\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', From 784bef20ce1686f8ebbb2b17b3e806599921b8ed Mon Sep 17 00:00:00 2001 From: Erich Moraga <33645296+ErichMoraga@users.noreply.github.com> Date: Sat, 4 Feb 2023 17:24:37 -0600 Subject: [PATCH 294/484] Add missing AVALONH_TSS2 fwdRadar f/w (#27215) `@shankapotamous#6637` 2022 Avalon Hybrid DongleID/route 301800decccd6669|2023-02-04--10-09-55 --- selfdrive/car/toyota/values.py | 1 + 1 file changed, 1 insertion(+) diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index 6cedef414b..3046814fac 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -348,6 +348,7 @@ FW_VERSIONS = { ], (Ecu.fwdRadar, 0x750, 0xf): [ b'\x018821F6201200\x00\x00\x00\x00', + b'\x018821F6201300\x00\x00\x00\x00', ], (Ecu.fwdCamera, 0x750, 0x6d): [ b'\x028646F4104100\x00\x00\x00\x008646G5301200\x00\x00\x00\x00', From 2cb0f599628f65d4fdb9dc3d84bdcc0865c33224 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sun, 5 Feb 2023 07:25:06 +0800 Subject: [PATCH 295/484] cabana: delay starting live streaming thread (#27213) --- tools/cabana/streams/livestream.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tools/cabana/streams/livestream.cc b/tools/cabana/streams/livestream.cc index 3edd2a7758..b2fc7ea4a6 100644 --- a/tools/cabana/streams/livestream.cc +++ b/tools/cabana/streams/livestream.cc @@ -8,7 +8,7 @@ LiveStream::LiveStream(QObject *parent, QString address) : zmq_address(address), stream_thread = new QThread(this); QObject::connect(stream_thread, &QThread::started, [=]() { streamThread(); }); QObject::connect(stream_thread, &QThread::finished, stream_thread, &QThread::deleteLater); - stream_thread->start(); + QTimer::singleShot(0, [this]() { stream_thread->start(); }); } LiveStream::~LiveStream() { @@ -45,9 +45,6 @@ void LiveStream::streamThread() { } void LiveStream::handleEvent(Event *evt) { - std::lock_guard lk(lock); - can_events.push_back(evt); - current_ts = evt->mono_time; if (start_ts == 0 || current_ts < start_ts) { if (current_ts < start_ts) { @@ -57,6 +54,8 @@ void LiveStream::handleEvent(Event *evt) { emit streamStarted(); } + std::lock_guard lk(lock); + can_events.push_back(evt); if (!pause_) { if (speed_ < 1 && last_update_ts > 0) { auto it = std::upper_bound(can_events.begin(), can_events.end(), last_update_event_ts, [](uint64_t ts, auto &e) { From 9b054af8b6943ffea8d26a4dadb6cfe8fbefe990 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 6 Feb 2023 04:35:15 +0800 Subject: [PATCH 296/484] cabana: improve align charts (#27217) * improve align charts * set range on need * cleanup * remove unused include --- tools/cabana/chartswidget.cc | 120 +++++++++++++++++------------------ tools/cabana/chartswidget.h | 19 +++--- 2 files changed, 69 insertions(+), 70 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 54cacf56cd..87a0e8abd0 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -68,10 +68,6 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { charts_scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); main_layout->addWidget(charts_scroll); - align_charts_timer = new QTimer(this); - align_charts_timer->setSingleShot(true); - align_charts_timer->callOnTimeout(this, &ChartsWidget::alignCharts); - // init settings use_dark_theme = QApplication::style()->standardPalette().color(QPalette::WindowText).value() > QApplication::style()->standardPalette().color(QPalette::Background).value(); @@ -125,9 +121,9 @@ void ChartsWidget::zoomReset() { void ChartsWidget::updateState() { if (charts.isEmpty()) return; + const auto events = can->events(); if (can->liveStreaming()) { // appends incoming events to the end of series - const auto events = can->events(); for (auto c : charts) { c->updateSeries(nullptr, events, false); } @@ -135,33 +131,30 @@ void ChartsWidget::updateState() { const double cur_sec = can->currentSec(); if (!is_zoomed) { - double pos = (cur_sec - display_range.first) / max_chart_range; + double pos = (cur_sec - display_range.first) / std::max(1.0, (display_range.second - display_range.first)); if (pos < 0 || pos > 0.8) { - const double min_event_sec = can->events()->empty() ? 0 : (can->events()->front()->mono_time / (double)1e9 - can->routeStartTime()); - display_range.first = std::floor(std::max(min_event_sec, cur_sec - max_chart_range * 0.2)); + display_range.first = std::max(0.0, cur_sec - max_chart_range * 0.1); } - display_range.second = std::floor(display_range.first + max_chart_range); + double max_event_sec = events->empty() ? 0 : (events->back()->mono_time / 1e9 - can->routeStartTime()); + double max_sec = std::min(std::floor(display_range.first + max_chart_range), max_event_sec); + display_range.first = std::max(0.0, max_sec - max_chart_range); + display_range.second = display_range.first + max_chart_range; } else if (cur_sec < zoomed_range.first || cur_sec >= zoomed_range.second) { // loop in zoommed range can->seekTo(zoomed_range.first); } - setUpdatesEnabled(false); + charts_layout->parentWidget()->setUpdatesEnabled(false); const auto &range = is_zoomed ? zoomed_range : display_range; for (auto c : charts) { c->updatePlot(cur_sec, range.first, range.second); } - setUpdatesEnabled(true); + alignCharts(); + charts_layout->parentWidget()->setUpdatesEnabled(true); } void ChartsWidget::setMaxChartRange(int value) { max_chart_range = settings.chart_range = value; - double current_sec = can->currentSec(); - const double min_event_sec = can->events()->empty() ? 0 : (can->events()->front()->mono_time / (double)1e9 - can->routeStartTime()); - // keep current_sec's pos - double pos = (current_sec - display_range.first) / (display_range.second - display_range.first); - display_range.first = std::floor(std::max(min_event_sec, current_sec - max_chart_range * (1.0 - pos))); - display_range.second = std::floor(display_range.first + max_chart_range); updateToolBar(); updateState(); } @@ -200,7 +193,6 @@ ChartView *ChartsWidget::createChart() { QObject::connect(chart, &ChartView::zoomReset, this, &ChartsWidget::zoomReset); QObject::connect(chart, &ChartView::seriesRemoved, this, &ChartsWidget::seriesChanged); QObject::connect(chart, &ChartView::seriesAdded, this, &ChartsWidget::seriesChanged); - QObject::connect(chart, &ChartView::axisYUpdated, [this]() { align_charts_timer->start(100); }); charts.push_back(chart); updateLayout(); return chart; @@ -242,6 +234,7 @@ void ChartsWidget::updateLayout() { for (int i = 0; i < charts.size(); ++i) { charts_layout->addWidget(charts[charts.size() - i - 1], i / n, i % n); } + alignCharts(true); } void ChartsWidget::resizeEvent(QResizeEvent *event) { @@ -278,13 +271,16 @@ void ChartsWidget::removeAll() { emit seriesChanged(); } -void ChartsWidget::alignCharts() { +void ChartsWidget::alignCharts(bool force) { int plot_left = 0; for (auto c : charts) { - plot_left = qMax((qreal)plot_left, c->getYAsixLabelWidth()); + plot_left = std::max(plot_left, c->y_label_width); } - for (auto c : charts) { - c->setPlotAreaLeftPosition(plot_left); + plot_left = std::max((plot_left / 10) * 10 + 10, 50); + if (std::exchange(align_to, plot_left) != align_to || force) { + for (auto c : charts) { + c->updatePlotArea(align_to); + } } } @@ -302,15 +298,20 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { series_type = settings.chart_series_type == 0 ? QAbstractSeries::SeriesTypeLine : QAbstractSeries::SeriesTypeScatter; QChart *chart = new QChart(); - chart->setBackgroundRoundness(0); + chart->setBackgroundVisible(false); axis_x = new QValueAxis(this); axis_y = new QValueAxis(this); + axis_y->setLabelFormat("%.1f"); chart->addAxis(axis_x, Qt::AlignBottom); chart->addAxis(axis_y, Qt::AlignLeft); chart->legend()->layout()->setContentsMargins(0, 0, 40, 0); chart->legend()->setShowToolTips(true); - chart->layout()->setContentsMargins(0, 0, 0, 0); - chart->setMargins({20, 11, 11, 11}); + chart->setMargins({0, 0, 0, 0}); + + background = new QGraphicsRectItem(chart); + background->setBrush(Qt::white); + background->setPen(Qt::NoPen); + background->setZValue(chart->zValue() - 1); QToolButton *remove_btn = new QToolButton(); remove_btn->setIcon(utils::icon("x")); @@ -351,22 +352,6 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { QObject::connect(remove_btn, &QToolButton::clicked, this, &ChartView::remove); } -qreal ChartView::getYAsixLabelWidth() const { - if (axis_y->max() <= axis_y->min() || axis_y->tickCount() <= 1) { - return 0; - } - QFontMetrics fm(axis_y->labelsFont()); - int n = qMax(int(-qFloor(std::log10((axis_y->max() - axis_y->min()) / (axis_y->tickCount() - 1)))), 0) + 1; - return qMax(fm.width(QString::number(axis_y->min(), 'f', n)), fm.width(QString::number(axis_y->max(), 'f', n))) + 20; -} - -void ChartView::setPlotAreaLeftPosition(int pos) { - if (std::ceil(chart()->plotArea().left()) != pos) { - const float left_margin = chart()->margins().left() + pos - chart()->plotArea().left(); - chart()->setMargins(QMargins(left_margin, 11, 11, 11)); - } -} - void ChartView::addSeries(const QString &msg_id, const Signal *sig) { QXYSeries *series = createSeries(series_type); chart()->addSeries(series); @@ -467,11 +452,21 @@ void ChartView::manageSeries() { void ChartView::resizeEvent(QResizeEvent *event) { QChartView::resizeEvent(event); + updatePlotArea(); int x = event->size().width() - close_btn_proxy->size().width() - 11; close_btn_proxy->setPos(x, 8); manage_btn_proxy->setPos(x - manage_btn_proxy->size().width() - 5, 8); } +void ChartView::updatePlotArea(int left) { + align_to = left > 0 ? left : align_to; + QRect r = rect(); + background->setRect(r); + chart()->legend()->setGeometry(QRect(r.left(), r.top(), r.width(), 45)); + chart()->setPlotArea(QRect(align_to, r.top() + 45, r.width() - align_to - 22, r.height() - 80)); + chart()->layout()->invalidate(); +} + void ChartView::updateTitle() { for (auto &s : sigs) { s.series->setName(QString("%1 %2 %3").arg(s.sig->name.c_str()).arg(msgName(s.msg_id)).arg(s.msg_id)); @@ -536,7 +531,7 @@ void ChartView::updateSeries(const Signal *sig, const std::vector *even for (auto it = begin; it != events->end(); ++it) { if ((*it)->which == cereal::Event::Which::CAN) { for (const auto &c : (*it)->event.getCan()) { - if (s.source == c.getSrc() && s.address == c.getAddress()) { + if (s.address == c.getAddress() && s.source == c.getSrc()) { auto dat = c.getDat(); double value = get_raw_value((uint8_t *)dat.begin(), dat.size(), *s.sig); double ts = ((*it)->mono_time / (double)1e9) - route_start_time; // seconds @@ -549,46 +544,47 @@ void ChartView::updateSeries(const Signal *sig, const std::vector *even s.last_value_mono_time = events->back()->mono_time; } s.series->replace(s.vals); - updateAxisY(); } } + updateAxisY(); } // auto zoom on yaxis void ChartView::updateAxisY() { if (sigs.isEmpty()) return; - double min_y = std::numeric_limits::max(); - double max_y = std::numeric_limits::lowest(); + double min = std::numeric_limits::max(); + double max = std::numeric_limits::lowest(); for (auto &s : sigs) { - auto begin = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->min(), [](auto &p, double x) { return p.x() < x; }); - for (auto it = begin; it != s.vals.end() && it->x() <= axis_x->max(); ++it) { - if (it->y() < min_y) min_y = it->y(); - if (it->y() > max_y) max_y = it->y(); + auto first = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->min(), [](auto &p, double x) { return p.x() < x; }); + auto last = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->max(), [](auto &p, double x) { return p.x() < x; }); + for (auto it = first; it != last; ++it) { + if (it->y() < min) min = it->y(); + if (it->y() > max) max = it->y(); } } + if (min == std::numeric_limits::max()) min = 0; + if (max == std::numeric_limits::lowest()) max = 0; - if (min_y == std::numeric_limits::max()) min_y = 0; - if (max_y == std::numeric_limits::lowest()) max_y = 0; - if (std::abs(max_y - min_y) < 1e-3) { - applyNiceNumbers(min_y - 1, max_y + 1); - } else { - double range = max_y - min_y; - applyNiceNumbers(min_y - range * 0.05, max_y + range * 0.05); + double delta = std::abs(max - min) < 1e-3 ? 1 : (max - min) * 0.05; + auto [min_y, max_y, tick_count] = getNiceAxisNumbers(min - delta, max + delta, axis_y->tickCount()); + if (min_y != axis_y->min() || max_y != axis_y->max()) { + axis_y->setRange(min_y, max_y); + axis_y->setTickCount(tick_count); + + QFontMetrics fm(axis_y->labelsFont()); + int n = qMax(int(-qFloor(std::log10((max_y - min_y) / (tick_count - 1)))), 0) + 1; + y_label_width = qMax(fm.width(QString::number(min_y, 'f', n)), fm.width(QString::number(max_y, 'f', n))) + 20; // left margin 20 } - emit axisYUpdated(); } -void ChartView::applyNiceNumbers(qreal min, qreal max) { - int tick_count = axis_y->tickCount(); +std::tuple ChartView::getNiceAxisNumbers(qreal min, qreal max, int tick_count) { qreal range = niceNumber((max - min), true); // range with ceiling qreal step = niceNumber(range / (tick_count - 1), false); min = qFloor(min / step); max = qCeil(max / step); tick_count = int(max - min) + 1; - axis_y->setRange(min * step, max * step); - axis_y->setTickCount(tick_count); - axis_y->setLabelFormat("%.1f"); + return {min * step, max * step, tick_count}; } // nice numbers can be expressed as form of 1*10^n, 2* 10^n or 5*10^n diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index 8c50993971..f0f9e5cd9c 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -18,6 +17,8 @@ using namespace QtCharts; +const int CHART_MIN_WIDTH = 300; + class ChartView : public QChartView { Q_OBJECT @@ -29,9 +30,8 @@ public: bool hasSeries(const QString &msg_id, const Signal *sig) const; void updateSeries(const Signal *sig = nullptr, const std::vector *events = nullptr, bool clear = true); void updatePlot(double cur, double min, double max); - void setPlotAreaLeftPosition(int pos); - qreal getYAsixLabelWidth() const; void setSeriesType(QAbstractSeries::SeriesType type); + void updatePlotArea(int left = 0); struct SigItem { QString msg_id; @@ -49,7 +49,6 @@ signals: void zoomIn(double min, double max); void zoomReset(); void remove(); - void axisYUpdated(); private slots: void msgRemoved(uint32_t address); @@ -67,25 +66,30 @@ private: void dropEvent(QDropEvent *event) override; void leaveEvent(QEvent *event) override; void resizeEvent(QResizeEvent *event) override; + QSize sizeHint() const override { return {CHART_MIN_WIDTH, settings.chart_height}; } void updateAxisY(); void updateTitle(); void drawForeground(QPainter *painter, const QRectF &rect) override; - void applyNiceNumbers(qreal min, qreal max); + std::tuple getNiceAxisNumbers(qreal min, qreal max, int tick_count); qreal niceNumber(qreal x, bool ceiling); QXYSeries *createSeries(QAbstractSeries::SeriesType type); void updateSeriesPoints(); + int y_label_width = 50; + int align_to = 0; QValueAxis *axis_x; QValueAxis *axis_y; QVector track_pts; QGraphicsProxyWidget *close_btn_proxy; QGraphicsProxyWidget *manage_btn_proxy; + QGraphicsRectItem *background; QList sigs; double cur_sec = 0; const QString mime_type = "application/x-cabanachartview"; QAbstractSeries::SeriesType series_type = QAbstractSeries::SeriesTypeLine; QAction *line_series_action; QAction *scatter_series_action; + friend class ChartsWidget; }; class ChartsWidget : public QWidget { @@ -107,7 +111,7 @@ signals: private: void resizeEvent(QResizeEvent *event) override; - void alignCharts(); + void alignCharts(bool force = false); void newChart(); ChartView * createChart(); void removeChart(ChartView *chart); @@ -129,7 +133,6 @@ private: QAction *dock_btn; QAction *reset_zoom_btn; QAction *remove_all_btn; - QTimer *align_charts_timer; QGridLayout *charts_layout; QList charts; uint32_t max_chart_range = 0; @@ -141,7 +144,7 @@ private: QAction *columns_cb_action; QComboBox *columns_cb; int column_count = 1; - const int CHART_MIN_WIDTH = 300; + int align_to = 0; }; class SeriesSelector : public QDialog { From 54f3ca43bb72f697a5a2fc90d3c26f7301a48cd0 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 6 Feb 2023 04:35:30 +0800 Subject: [PATCH 297/484] cabana: only relayout charts when needed (#27210) --- tools/cabana/chartswidget.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 87a0e8abd0..8591d67d51 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -231,8 +231,12 @@ void ChartsWidget::updateLayout() { columns_cb_action->setVisible(show_column_cb); n = std::min(column_count, n); - for (int i = 0; i < charts.size(); ++i) { - charts_layout->addWidget(charts[charts.size() - i - 1], i / n, i % n); + if (charts.size() != charts_layout->count() || n != charts_layout->columnCount()) { + charts_layout->parentWidget()->setUpdatesEnabled(false); + for (int i = 0; i < charts.size(); ++i) { + charts_layout->addWidget(charts[charts.size() - i - 1], i / n, i % n); + } + QTimer::singleShot(0, [this]() { charts_layout->parentWidget()->setUpdatesEnabled(true); }); } alignCharts(true); } From 4117986520e539aa2a6288538c940728ccb89884 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 6 Feb 2023 06:50:32 +0800 Subject: [PATCH 298/484] cabana: fix incorrect clamp (#27218) * fix incorrect clamp * fix type error --- tools/cabana/chartswidget.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 8591d67d51..c6f5c277eb 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -642,8 +642,8 @@ void ChartView::mouseReleaseEvent(QMouseEvent *event) { double max = chart()->mapToValue(rect.bottomRight()).x(); // Prevent zooming/seeking past the end of the route - min = std::clamp(min, can->routeStartTime(), can->routeStartTime() + can->totalSeconds()); - max = std::clamp(max, can->routeStartTime(), can->routeStartTime() + can->totalSeconds()); + min = std::clamp(min, 0., can->totalSeconds()); + max = std::clamp(max, 0., can->totalSeconds()); double min_rounded = std::floor(min * 10.0) / 10.0; double max_rounded = std::floor(max * 10.0) / 10.0; From f5f14068173fbf8664e682e5b02270ecbc79abdb Mon Sep 17 00:00:00 2001 From: pbkompasz <47194071+pbkompasz@users.noreply.github.com> Date: Mon, 6 Feb 2023 00:52:28 +0200 Subject: [PATCH 299/484] docs: update cabana usage in README (#27225) * Add ecam * Update README.md --------- Co-authored-by: Adeeb Shihadeh --- tools/cabana/README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/cabana/README.md b/tools/cabana/README.md index dd131880a6..db247c39c5 100644 --- a/tools/cabana/README.md +++ b/tools/cabana/README.md @@ -8,13 +8,18 @@ Cabana is a tool developed to view raw CAN data. One use for this is creating an ```bash $ ./cabana -h -Usage: ./cabana [options] route +Usage: ./_cabana [options] route Options: -h, --help Displays this help. --demo use a demo route instead of providing your own --qcam load qcamera + --ecam load wide road camera + --stream read can messages from live streaming + --zmq the ip address on which to receive zmq messages --data_dir local directory with routes + --no-vipc do not output video + --dbc dbc file to open Arguments: route the drive to replay. find your drives at From 0374d4817d4730fb76adcee07252adf39759dd25 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 5 Feb 2023 15:45:01 -0800 Subject: [PATCH 300/484] setup pre-built master-ci (#27205) * try this * add as release branch * set the name * build on master-ci --- Jenkinsfile | 18 ++++++++++++++++-- release/build_release.sh | 22 +++++++++++++--------- system/version.py | 2 +- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 1841c68bba..30c045e419 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -13,6 +13,8 @@ export GIT_COMMIT=${env.GIT_COMMIT} export AZURE_TOKEN='${env.AZURE_TOKEN}' export MAPBOX_TOKEN='${env.MAPBOX_TOKEN}' +export GIT_SSH_COMMAND="ssh -i /data/gitkey" + source ~/.bash_profile if [ -f /TICI ]; then source /etc/profile @@ -56,14 +58,26 @@ pipeline { } stages { - stage('build release3') { + stage('build release3-staging') { agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } } when { branch 'devel-staging' } steps { phone_steps("tici-needs-can", [ - ["build release3-staging & dashcam3-staging", "PUSH=1 $SOURCE_DIR/release/build_release.sh"], + ["build release3-staging & dashcam3-staging", "RELEASE_BRANCH=release3-staging DASHCAM_BRANCH=dashcam3-staging $SOURCE_DIR/release/build_release.sh"], + ]) + } + } + + stage('build nightly') { + agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } } + when { + branch 'master-ci' + } + steps { + phone_steps("tici-needs-can", [ + ["build nightly", "RELEASE_BRANCH=nightly $SOURCE_DIR/release/build_release.sh"], ]) } } diff --git a/release/build_release.sh b/release/build_release.sh index 80106eefb2..60f81fce06 100755 --- a/release/build_release.sh +++ b/release/build_release.sh @@ -11,15 +11,19 @@ SOURCE_DIR="$(git rev-parse --show-toplevel)" if [ -f /TICI ]; then FILES_SRC="release/files_tici" - RELEASE_BRANCH=release3-staging - DASHCAM_BRANCH=dashcam3-staging else - exit 0 + echo "no release files set" + exit 1 +fi + +if [ -z "$RELEASE_BRANCH" ]; then + echo "RELEASE_BRANCH is not set" + exit 1 fi + # set git identity source $DIR/identity.sh -export GIT_SSH_COMMAND="ssh -i /data/gitkey" echo "[-] Setting up repo T=$SECONDS" rm -rf $BUILD_DIR @@ -27,7 +31,6 @@ mkdir -p $BUILD_DIR cd $BUILD_DIR git init git remote add origin git@github.com:commaai/openpilot.git -git fetch origin $RELEASE_BRANCH git checkout --orphan $RELEASE_BRANCH # do the files copy @@ -48,7 +51,6 @@ echo "#define COMMA_VERSION \"$VERSION-release\"" > common/version.h echo "[-] committing version $VERSION T=$SECONDS" git add -f . git commit -a -m "openpilot v$VERSION release" -git branch --set-upstream-to=origin/$RELEASE_BRANCH # Build panda firmware pushd panda/ @@ -105,10 +107,12 @@ RELEASE=1 selfdrive/test/test_onroad.py selfdrive/car/tests/test_car_interfaces.py rm -rf $TEST_FILES -if [ ! -z "$PUSH" ]; then - echo "[-] pushing T=$SECONDS" - git push -f origin $RELEASE_BRANCH +if [ ! -z "$RELEASE_BRANCH" ]; then + echo "[-] pushing release T=$SECONDS" + git push -f origin $RELEASE_BRANCH:$RELEASE_BRANCH +fi +if [ ! -z "$DASHCAM_BRANCH" ]; then # Create dashcam git rm selfdrive/car/*/carcontroller.py git commit -m "create dashcam release from release" diff --git a/system/version.py b/system/version.py index 6031531556..55f80afa35 100644 --- a/system/version.py +++ b/system/version.py @@ -7,7 +7,7 @@ from functools import lru_cache from common.basedir import BASEDIR from system.swaglog import cloudlog -RELEASE_BRANCHES = ['release3-staging', 'dashcam3-staging', 'release3', 'dashcam3'] +RELEASE_BRANCHES = ['release3-staging', 'dashcam3-staging', 'release3', 'dashcam3', 'nightly'] TESTED_BRANCHES = RELEASE_BRANCHES + ['devel', 'devel-staging'] training_version: bytes = b"0.2.0" From 1ecda86e51ad3dfefa87a9cebd28370d57ed55fb Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 7 Feb 2023 02:17:58 +0800 Subject: [PATCH 301/484] cabana: concurrently update chart series (#27230) --- tools/cabana/chartswidget.cc | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index c6f5c277eb..34498364c6 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -529,20 +529,38 @@ void ChartView::updateSeries(const Signal *sig, const std::vector *even s.vals.reserve(settings.max_cached_minutes * 60 * 100); // [n]seconds * 100hz s.last_value_mono_time = 0; } - double route_start_time = can->routeStartTime(); + + struct Chunk { + std::vector::const_iterator first, second; + QVector vals; + }; + // split into one minitue chunks + QVector chunks; Event begin_event(cereal::Event::Which::INIT_DATA, s.last_value_mono_time); auto begin = std::upper_bound(events->begin(), events->end(), &begin_event, Event::lessThan()); - for (auto it = begin; it != events->end(); ++it) { - if ((*it)->which == cereal::Event::Which::CAN) { - for (const auto &c : (*it)->event.getCan()) { - if (s.address == c.getAddress() && s.source == c.getSrc()) { - auto dat = c.getDat(); - double value = get_raw_value((uint8_t *)dat.begin(), dat.size(), *s.sig); - double ts = ((*it)->mono_time / (double)1e9) - route_start_time; // seconds - s.vals.push_back({ts, value}); + for (auto it = begin, second = begin; it != events->end(); it = second) { + second = std::lower_bound(it, events->end(), (*it)->mono_time + 1e9 * 60, [](auto &e, uint64_t ts) { return e->mono_time < ts; }); + chunks.push_back({it, second}); + } + + QtConcurrent::blockingMap(chunks, [&](Chunk &chunk) { + chunk.vals.reserve(60 * 100); // 100 hz + double route_start_time = can->routeStartTime(); + for (auto it = chunk.first; it != chunk.second; ++it) { + if ((*it)->which == cereal::Event::Which::CAN) { + for (const auto &c : (*it)->event.getCan()) { + if (s.address == c.getAddress() && s.source == c.getSrc()) { + auto dat = c.getDat(); + double value = get_raw_value((uint8_t *)dat.begin(), dat.size(), *s.sig); + double ts = ((*it)->mono_time / (double)1e9) - route_start_time; // seconds + chunk.vals.push_back({ts, value}); + } } } } + }); + for (auto &c : chunks) { + s.vals.append(c.vals); } if (events->size()) { s.last_value_mono_time = events->back()->mono_time; From f9de6a0dd876352dacc843ef2b28fb2456771239 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 7 Feb 2023 02:18:44 +0800 Subject: [PATCH 302/484] cabana: click on legend to show/hide series (#27231) * click on legend to show/hide series * update axis y * add move icon --- tools/cabana/chartswidget.cc | 27 ++++++++++++++++++++++++--- tools/cabana/chartswidget.h | 4 ++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 34498364c6..845649dfc1 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -317,6 +317,9 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { background->setPen(Qt::NoPen); background->setZValue(chart->zValue() - 1); + move_icon = new QGraphicsPixmapItem(utils::icon("grip-horizontal"), chart); + move_icon->setToolTip(tr("Drag and drop to combine charts")); + QToolButton *remove_btn = new QToolButton(); remove_btn->setIcon(utils::icon("x")); remove_btn->setAutoRaise(true); @@ -460,6 +463,7 @@ void ChartView::resizeEvent(QResizeEvent *event) { int x = event->size().width() - close_btn_proxy->size().width() - 11; close_btn_proxy->setPos(x, 8); manage_btn_proxy->setPos(x - manage_btn_proxy->size().width() - 5, 8); + move_icon->setPos(11, 8); } void ChartView::updatePlotArea(int left) { @@ -472,8 +476,12 @@ void ChartView::updatePlotArea(int left) { } void ChartView::updateTitle() { + for (QLegendMarker *marker : chart()->legend()->markers()) { + QObject::connect(marker, &QLegendMarker::clicked, this, &ChartView::handleMarkerClicked, Qt::UniqueConnection); + } for (auto &s : sigs) { - s.series->setName(QString("%1 %2 %3").arg(s.sig->name.c_str()).arg(msgName(s.msg_id)).arg(s.msg_id)); + auto decoration = s.series->isVisible() ? "none" : "line-through"; + s.series->setName(QString("%2%3 %4").arg(decoration).arg(s.sig->name.c_str()).arg(msgName(s.msg_id)).arg(s.msg_id)); } } @@ -578,6 +586,8 @@ void ChartView::updateAxisY() { double min = std::numeric_limits::max(); double max = std::numeric_limits::lowest(); for (auto &s : sigs) { + if (!s.series->isVisible()) continue; + auto first = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->min(), [](auto &p, double x) { return p.x() < x; }); auto last = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->max(), [](auto &p, double x) { return p.x() < x; }); for (auto it = first; it != last; ++it) { @@ -634,8 +644,7 @@ void ChartView::leaveEvent(QEvent *event) { } void ChartView::mousePressEvent(QMouseEvent *event) { - if (event->button() == Qt::LeftButton && !chart()->plotArea().contains(event->pos()) && - !manage_btn_proxy->geometry().contains(event->pos()) && !close_btn_proxy->geometry().contains(event->pos())) { + if (event->button() == Qt::LeftButton && move_icon->sceneBoundingRect().contains(event->pos())) { QMimeData *mimeData = new QMimeData; mimeData->setData(mime_type, QByteArray::number((qulonglong)this)); QDrag *drag = new QDrag(this); @@ -810,6 +819,18 @@ void ChartView::setSeriesType(QAbstractSeries::SeriesType type) { } } +void ChartView::handleMarkerClicked() { + auto marker = qobject_cast(sender()); + Q_ASSERT(marker); + if (sigs.size() > 1) { + auto series = marker->series(); + series->setVisible(!series->isVisible()); + marker->setVisible(true); + updateAxisY(); + updateTitle(); + } +} + // SeriesSelector SeriesSelector::SeriesSelector(QWidget *parent) { diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index f0f9e5cd9c..77f04534a4 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -5,9 +5,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -56,6 +58,7 @@ private slots: void signalUpdated(const Signal *sig); void signalRemoved(const Signal *sig); void manageSeries(); + void handleMarkerClicked(); private: QList::iterator removeItem(const QList::iterator &it); @@ -80,6 +83,7 @@ private: QValueAxis *axis_x; QValueAxis *axis_y; QVector track_pts; + QGraphicsPixmapItem *move_icon; QGraphicsProxyWidget *close_btn_proxy; QGraphicsProxyWidget *manage_btn_proxy; QGraphicsRectItem *background; From 68bcdaaff7f9050402d983f460f2c85866b4ca32 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Mon, 6 Feb 2023 10:40:45 -0800 Subject: [PATCH 303/484] settings: highlight nightly in branch switcher (#27227) --- selfdrive/ui/qt/offroad/software_settings.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/qt/offroad/software_settings.cc b/selfdrive/ui/qt/offroad/software_settings.cc index 12d62e63fb..6db6a6cdfe 100644 --- a/selfdrive/ui/qt/offroad/software_settings.cc +++ b/selfdrive/ui/qt/offroad/software_settings.cc @@ -54,7 +54,7 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) { connect(targetBranchBtn, &ButtonControl::clicked, [=]() { auto current = params.get("GitBranch"); QStringList branches = QString::fromStdString(params.get("UpdaterAvailableBranches")).split(","); - for (QString b : {current.c_str(), "devel-staging", "devel", "master-ci", "master"}) { + for (QString b : {current.c_str(), "devel-staging", "devel", "nightly", "master-ci", "master"}) { auto i = branches.indexOf(b); if (i >= 0) { branches.removeAt(i); From 9a51275cd246bcbf53da764495a944af8a0b51bd Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Mon, 6 Feb 2023 15:43:25 -0800 Subject: [PATCH 304/484] setup: fix button label on ethernet (#27235) * setup: continue without wi-fi on ethernet * switch around --- selfdrive/ui/qt/setup/setup.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/qt/setup/setup.cc b/selfdrive/ui/qt/setup/setup.cc index 69dafcf741..f1ffabf6c6 100644 --- a/selfdrive/ui/qt/setup/setup.cc +++ b/selfdrive/ui/qt/setup/setup.cc @@ -178,8 +178,8 @@ QWidget * Setup::network_setup() { QObject::connect(request, &HttpRequest::requestDone, [=](const QString &, bool success) { cont->setEnabled(success); if (success) { - const bool cell = networking->wifi->currentNetworkType() == NetworkType::CELL; - cont->setText(cell ? tr("Continue without Wi-Fi") : tr("Continue")); + const bool wifi = networking->wifi->currentNetworkType() == NetworkType::WIFI; + cont->setText(wifi ? tr("Continue") : tr("Continue without Wi-Fi")); } else { cont->setText(tr("Waiting for internet")); } From 1a99fd36101fbe4cbe9853b1df5dfe6c59fea5c4 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 7 Feb 2023 08:39:16 +0800 Subject: [PATCH 305/484] cabana: fixed wrong plot area after changed column count (#27228) --- tools/cabana/chartswidget.cc | 33 +++++++++++++++++---------------- tools/cabana/chartswidget.h | 9 +++++---- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 845649dfc1..a271df9dd7 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -149,7 +149,6 @@ void ChartsWidget::updateState() { for (auto c : charts) { c->updatePlot(cur_sec, range.first, range.second); } - alignCharts(); charts_layout->parentWidget()->setUpdatesEnabled(true); } @@ -186,13 +185,14 @@ ChartView *ChartsWidget::createChart() { auto chart = new ChartView(this); chart->setFixedHeight(settings.chart_height); chart->setMinimumWidth(CHART_MIN_WIDTH); - chart->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + chart->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); chart->chart()->setTheme(use_dark_theme ? QChart::QChart::ChartThemeDark : QChart::ChartThemeLight); QObject::connect(chart, &ChartView::remove, [=]() { removeChart(chart); }); QObject::connect(chart, &ChartView::zoomIn, this, &ChartsWidget::zoomIn); QObject::connect(chart, &ChartView::zoomReset, this, &ChartsWidget::zoomReset); QObject::connect(chart, &ChartView::seriesRemoved, this, &ChartsWidget::seriesChanged); QObject::connect(chart, &ChartView::seriesAdded, this, &ChartsWidget::seriesChanged); + QObject::connect(chart, &ChartView::axisYLabelWidthChanged, this, &ChartsWidget::alignCharts); charts.push_back(chart); updateLayout(); return chart; @@ -231,14 +231,14 @@ void ChartsWidget::updateLayout() { columns_cb_action->setVisible(show_column_cb); n = std::min(column_count, n); - if (charts.size() != charts_layout->count() || n != charts_layout->columnCount()) { + if (charts.size() != charts_layout->count() || n != current_column_count) { + current_column_count = n; charts_layout->parentWidget()->setUpdatesEnabled(false); for (int i = 0; i < charts.size(); ++i) { charts_layout->addWidget(charts[charts.size() - i - 1], i / n, i % n); } QTimer::singleShot(0, [this]() { charts_layout->parentWidget()->setUpdatesEnabled(true); }); } - alignCharts(true); } void ChartsWidget::resizeEvent(QResizeEvent *event) { @@ -275,16 +275,14 @@ void ChartsWidget::removeAll() { emit seriesChanged(); } -void ChartsWidget::alignCharts(bool force) { +void ChartsWidget::alignCharts() { int plot_left = 0; for (auto c : charts) { plot_left = std::max(plot_left, c->y_label_width); } plot_left = std::max((plot_left / 10) * 10 + 10, 50); - if (std::exchange(align_to, plot_left) != align_to || force) { - for (auto c : charts) { - c->updatePlotArea(align_to); - } + for (auto c : charts) { + c->updatePlotArea(plot_left); } } @@ -459,7 +457,7 @@ void ChartView::manageSeries() { void ChartView::resizeEvent(QResizeEvent *event) { QChartView::resizeEvent(event); - updatePlotArea(); + updatePlotArea(align_to); int x = event->size().width() - close_btn_proxy->size().width() - 11; close_btn_proxy->setPos(x, 8); manage_btn_proxy->setPos(x - manage_btn_proxy->size().width() - 5, 8); @@ -467,12 +465,14 @@ void ChartView::resizeEvent(QResizeEvent *event) { } void ChartView::updatePlotArea(int left) { - align_to = left > 0 ? left : align_to; QRect r = rect(); - background->setRect(r); - chart()->legend()->setGeometry(QRect(r.left(), r.top(), r.width(), 45)); - chart()->setPlotArea(QRect(align_to, r.top() + 45, r.width() - align_to - 22, r.height() - 80)); - chart()->layout()->invalidate(); + if (align_to != left || r != background->rect()) { + align_to = left; + background->setRect(r); + chart()->legend()->setGeometry(QRect(r.left(), r.top(), r.width(), 45)); + chart()->setPlotArea(QRect(align_to, r.top() + 45, r.width() - align_to - 22, r.height() - 80)); + chart()->layout()->invalidate(); + } } void ChartView::updateTitle() { @@ -600,13 +600,14 @@ void ChartView::updateAxisY() { double delta = std::abs(max - min) < 1e-3 ? 1 : (max - min) * 0.05; auto [min_y, max_y, tick_count] = getNiceAxisNumbers(min - delta, max + delta, axis_y->tickCount()); - if (min_y != axis_y->min() || max_y != axis_y->max()) { + if (min_y != axis_y->min() || max_y != axis_y->max() || y_label_width == 0) { axis_y->setRange(min_y, max_y); axis_y->setTickCount(tick_count); QFontMetrics fm(axis_y->labelsFont()); int n = qMax(int(-qFloor(std::log10((max_y - min_y) / (tick_count - 1)))), 0) + 1; y_label_width = qMax(fm.width(QString::number(min_y, 'f', n)), fm.width(QString::number(max_y, 'f', n))) + 20; // left margin 20 + emit axisYLabelWidthChanged(y_label_width); } } diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index 77f04534a4..66ef98991f 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -33,7 +33,7 @@ public: void updateSeries(const Signal *sig = nullptr, const std::vector *events = nullptr, bool clear = true); void updatePlot(double cur, double min, double max); void setSeriesType(QAbstractSeries::SeriesType type); - void updatePlotArea(int left = 0); + void updatePlotArea(int left); struct SigItem { QString msg_id; @@ -51,6 +51,7 @@ signals: void zoomIn(double min, double max); void zoomReset(); void remove(); + void axisYLabelWidthChanged(int w); private slots: void msgRemoved(uint32_t address); @@ -78,7 +79,7 @@ private: QXYSeries *createSeries(QAbstractSeries::SeriesType type); void updateSeriesPoints(); - int y_label_width = 50; + int y_label_width = 0; int align_to = 0; QValueAxis *axis_x; QValueAxis *axis_y; @@ -115,7 +116,7 @@ signals: private: void resizeEvent(QResizeEvent *event) override; - void alignCharts(bool force = false); + void alignCharts(); void newChart(); ChartView * createChart(); void removeChart(ChartView *chart); @@ -148,7 +149,7 @@ private: QAction *columns_cb_action; QComboBox *columns_cb; int column_count = 1; - int align_to = 0; + int current_column_count = 0; }; class SeriesSelector : public QDialog { From b462463fbfdc3f4336206bf12b2f07e35473ac3a Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Mon, 6 Feb 2023 17:09:13 -0800 Subject: [PATCH 306/484] installer: do not translate git progress strings (#27238) --- selfdrive/ui/installer/installer.cc | 6 +++--- selfdrive/ui/translations/main_de.ts | 12 ------------ selfdrive/ui/translations/main_ja.ts | 12 ------------ selfdrive/ui/translations/main_ko.ts | 12 ------------ selfdrive/ui/translations/main_pt-BR.ts | 12 ------------ selfdrive/ui/translations/main_zh-CHS.ts | 12 ------------ selfdrive/ui/translations/main_zh-CHT.ts | 12 ------------ 7 files changed, 3 insertions(+), 75 deletions(-) diff --git a/selfdrive/ui/installer/installer.cc b/selfdrive/ui/installer/installer.cc index 7d8bbf74e0..1af72c04df 100644 --- a/selfdrive/ui/installer/installer.cc +++ b/selfdrive/ui/installer/installer.cc @@ -141,9 +141,9 @@ void Installer::cachedFetch(const QString &cache) { void Installer::readProgress() { const QVector> stages = { // prefix, weight in percentage - {tr("Receiving objects: "), 91}, - {tr("Resolving deltas: "), 2}, - {tr("Updating files: "), 7}, + {"Receiving objects: ", 91}, + {"Resolving deltas: ", 2}, + {"Updating files: ", 7}, }; auto line = QString(proc.readAllStandardError()); diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index 2606df5efe..0bf91bf440 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -312,18 +312,6 @@ Installing... Installiere... - - Receiving objects: - Empfange Objekte: - - - Resolving deltas: - Unterschiede verarbeiten: - - - Updating files: - Dateien aktualisieren: - MapETA diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index 16f35dffaa..15f803dd99 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -311,18 +311,6 @@ Installing... インストールしています... - - Receiving objects: - オブジェクトをダウンロードしています: - - - Resolving deltas: - デルタを解決しています: - - - Updating files: - ファイルを更新しています: - MapETA diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index f50d2d9b3f..d0bc63c9e3 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -311,18 +311,6 @@ Installing... 설치중... - - Receiving objects: - 수신중: - - - Resolving deltas: - 델타병합: - - - Updating files: - 파일갱신: - MapETA diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index 445de72fc3..75807eebad 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -312,18 +312,6 @@ Installing... Instalando... - - Receiving objects: - Recebendo objetos: - - - Resolving deltas: - Resolvendo deltas: - - - Updating files: - Atualizando arquivos: - MapETA diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index 792a918770..ac96d9c711 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -311,18 +311,6 @@ Installing... 正在安装…… - - Receiving objects: - 正在接收: - - - Resolving deltas: - 正在处理: - - - Updating files: - 正在更新文件: - MapETA diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index e8e2ab6bd3..8ccf028f67 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -311,18 +311,6 @@ Installing... 安裝中… - - Receiving objects: - 接收對象: - - - Resolving deltas: - 分析差異: - - - Updating files: - 更新檔案: - MapETA From 8f6521ded3c06b52f0d3c1389e4077be3f184329 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 7 Feb 2023 10:57:37 +0800 Subject: [PATCH 307/484] cabana: improve charts toolbar (#27233) * improve toolbar * add text --- tools/cabana/chartswidget.cc | 19 +++++++++++-------- tools/cabana/chartswidget.h | 2 ++ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index a271df9dd7..d482b71cb3 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -22,6 +22,7 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { // toolbar QToolBar *toolbar = new QToolBar(tr("Charts"), this); toolbar->setIconSize({16, 16}); + toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); QAction *new_plot_btn = toolbar->addAction(utils::icon("file-plus"), ""); new_plot_btn->setToolTip(tr("New Plot")); @@ -36,17 +37,16 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { stretch_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); toolbar->addWidget(stretch_label); - toolbar->addWidget(new QLabel(tr("Range:"))); - toolbar->addWidget(range_lb = new QLabel(this)); + range_lb_action = toolbar->addWidget(range_lb = new QLabel(this)); range_slider = new QSlider(Qt::Horizontal, this); range_slider->setToolTip(tr("Set the chart range")); range_slider->setRange(1, settings.max_cached_minutes * 60); range_slider->setSingleStep(1); range_slider->setPageStep(60); // 1 min - toolbar->addWidget(range_slider); + range_slider_action = toolbar->addWidget(range_slider); reset_zoom_btn = toolbar->addAction(utils::icon("zoom-out"), ""); - reset_zoom_btn->setToolTip(tr("Reset zoom (drag on chart to zoom X-Axis)")); + reset_zoom_btn->setToolTip(tr("Reset zoom")); remove_all_btn = toolbar->addAction(utils::icon("x"), ""); remove_all_btn->setToolTip(tr("Remove all charts")); dock_btn = toolbar->addAction(""); @@ -159,12 +159,15 @@ void ChartsWidget::setMaxChartRange(int value) { } void ChartsWidget::updateToolBar() { - range_lb->setText(QString(" %1:%2 ").arg(max_chart_range / 60, 2, 10, QLatin1Char('0')).arg(max_chart_range % 60, 2, 10, QLatin1Char('0'))); title_label->setText(tr("Charts: %1").arg(charts.size())); - dock_btn->setIcon(utils::icon(docking ? "arrow-up-right" : "arrow-down-left")); - dock_btn->setToolTip(docking ? tr("Undock charts") : tr("Dock charts")); + range_lb->setText(QString("Range: %1:%2 ").arg(max_chart_range / 60, 2, 10, QLatin1Char('0')).arg(max_chart_range % 60, 2, 10, QLatin1Char('0'))); + range_lb_action->setVisible(!is_zoomed); + range_slider_action->setVisible(!is_zoomed); + reset_zoom_btn->setVisible(is_zoomed); + reset_zoom_btn->setText(is_zoomed ? tr("Zoomin: %1-%2").arg(zoomed_range.first, 0, 'f', 2).arg(zoomed_range.second, 0, 'f', 2) : ""); remove_all_btn->setEnabled(!charts.isEmpty()); - reset_zoom_btn->setEnabled(is_zoomed); + dock_btn->setIcon(utils::icon(docking ? "arrow-up-right-square" : "arrow-down-left-square")); + dock_btn->setToolTip(docking ? tr("Undock charts") : tr("Dock charts")); } void ChartsWidget::settingChanged() { diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index 66ef98991f..dc92be89f9 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -134,6 +134,8 @@ private: QLabel *title_label; QLabel *range_lb; QSlider *range_slider; + QAction *range_lb_action; + QAction *range_slider_action; bool docking = true; QAction *dock_btn; QAction *reset_zoom_btn; From ae99a6d15d363ac01f970f266dd7bc7a13ae7366 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 7 Feb 2023 10:58:24 +0800 Subject: [PATCH 308/484] cabana: paint points in drawForeground instead of switching opengl mode (#27239) --- tools/cabana/chartswidget.cc | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index d482b71cb3..a5abb8dc64 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -503,7 +503,7 @@ void ChartView::updateSeriesPoints() { // Show points when zoomed in enough for (auto &s : sigs) { auto begin = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->min(), [](auto &p, double x) { return p.x() < x; }); - auto end = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->max(), [](auto &p, double x) { return p.x() < x; }); + auto end = std::lower_bound(begin, s.vals.end(), axis_x->max(), [](auto &p, double x) { return p.x() < x; }); int num_points = std::max(end - begin, 1); int pixels_per_point = width() / num_points; @@ -512,21 +512,6 @@ void ChartView::updateSeriesPoints() { ((QScatterSeries *)s.series)->setMarkerSize(std::clamp(pixels_per_point / 3, 1, 8)); } else { s.series->setPointsVisible(pixels_per_point > 20); - - // TODO: On MacOS QChartWidget doesn't work with the OpenGL settings that CameraWidget needs. -#ifndef __APPLE - // OpenGL mode lacks certain features (such as showing points), only use when drawing many points - bool use_opengl = pixels_per_point < 1; - s.series->setUseOpenGL(use_opengl); - - // Qt doesn't properly apply device pixel ratio in OpenGL mode - QApplication *application = static_cast(QApplication::instance()); - float scale = use_opengl ? application->devicePixelRatio() : 1.0; - - QPen pen = s.series->pen(); - pen.setWidth(2.0 * scale); - s.series->setPen(pen); -#endif } } } @@ -592,7 +577,7 @@ void ChartView::updateAxisY() { if (!s.series->isVisible()) continue; auto first = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->min(), [](auto &p, double x) { return p.x() < x; }); - auto last = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->max(), [](auto &p, double x) { return p.x() < x; }); + auto last = std::lower_bound(first, s.vals.end(), axis_x->max(), [](auto &p, double x) { return p.x() < x; }); for (auto it = first; it != last; ++it) { if (it->y() < min) min = it->y(); if (it->y() > max) max = it->y(); @@ -783,6 +768,19 @@ void ChartView::drawForeground(QPainter *painter, const QRectF &rect) { } } } + + // paint points. OpenGL mode lacks certain features (such as showing points) + painter->setPen(Qt::NoPen); + for (auto &s : sigs) { + if (s.series->useOpenGL() && s.series->isVisible() && s.series->pointsVisible()) { + auto first = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->min(), [](auto &p, double x) { return p.x() < x; }); + auto last = std::lower_bound(first, s.vals.end(), axis_x->max(), [](auto &p, double x) { return p.x() < x; }); + for (auto it = first; it != last; ++it) { + painter->setBrush(s.series->color()); + painter->drawEllipse(chart()->mapToPosition(*it), 4, 4); + } + } + } } QXYSeries *ChartView::createSeries(QAbstractSeries::SeriesType type) { From 6fb5661abba34971e547941e0f6a6625bef3b75a Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Mon, 6 Feb 2023 20:02:30 -0800 Subject: [PATCH 309/484] setup: add serial to request headers (#27237) * setup: add dongle ID to request headers * rename openpilot * fix str * hardware: get_serial * set serial instead * fix typo * static * fixes --- selfdrive/ui/qt/setup/setup.cc | 5 +++++ system/hardware/base.h | 2 ++ system/hardware/tici/hardware.h | 17 +++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/selfdrive/ui/qt/setup/setup.cc b/selfdrive/ui/qt/setup/setup.cc index f1ffabf6c6..231c4c2ad6 100644 --- a/selfdrive/ui/qt/setup/setup.cc +++ b/selfdrive/ui/qt/setup/setup.cc @@ -29,6 +29,9 @@ void Setup::download(QString url) { auto version = util::read_file("/VERSION"); + struct curl_slist *list = NULL; + list = curl_slist_append(list, ("X-openpilot-serial: " + Hardware::get_serial()).c_str()); + char tmpfile[] = "/tmp/installer_XXXXXX"; FILE *fp = fdopen(mkstemp(tmpfile), "w"); @@ -38,6 +41,7 @@ void Setup::download(QString url) { curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_USERAGENT, (USER_AGENT + version).c_str()); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); int ret = curl_easy_perform(curl); @@ -50,6 +54,7 @@ void Setup::download(QString url) { emit finished(false); } + curl_slist_free_all(list); curl_easy_cleanup(curl); fclose(fp); } diff --git a/system/hardware/base.h b/system/hardware/base.h index f6e0b42d73..6cfc1d8743 100644 --- a/system/hardware/base.h +++ b/system/hardware/base.h @@ -16,6 +16,8 @@ public: static int get_voltage() { return 0; }; static int get_current() { return 0; }; + static std::string get_serial() { return "cccccc"; } + static void reboot() {} static void poweroff() {} static void set_brightness(int percent) {} diff --git a/system/hardware/tici/hardware.h b/system/hardware/tici/hardware.h index 521a257627..5f6fb2dc50 100644 --- a/system/hardware/tici/hardware.h +++ b/system/hardware/tici/hardware.h @@ -21,6 +21,23 @@ public: static int get_voltage() { return std::atoi(util::read_file("/sys/class/hwmon/hwmon1/in1_input").c_str()); }; static int get_current() { return std::atoi(util::read_file("/sys/class/hwmon/hwmon1/curr1_input").c_str()); }; + static std::string get_serial() { + static std::string serial(""); + if (serial.empty()) { + std::ifstream stream("/proc/cmdline"); + std::string cmdline; + std::getline(stream, cmdline); + + auto start = cmdline.find("serialno="); + if (start == std::string::npos) { + serial = "cccccc"; + } else { + auto end = cmdline.find(" ", start + 9); + serial = cmdline.substr(start + 9, end - start - 9); + } + } + return serial; + } static void reboot() { std::system("sudo reboot"); }; static void poweroff() { std::system("sudo poweroff"); }; From 55ad7ee3cb50256bdc16c6777e2432bcf9f702eb Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 7 Feb 2023 13:14:07 +0800 Subject: [PATCH 310/484] cabana: remove toolbutton padding (#27240) * remove toolbutton padding * fix incorrect checked state in setSeriesType --- tools/cabana/chartswidget.cc | 18 +++++++++--------- tools/cabana/chartswidget.h | 4 +++- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index a5abb8dc64..3cd2c80326 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -10,7 +10,6 @@ #include #include #include -#include #include #include @@ -22,7 +21,6 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { // toolbar QToolBar *toolbar = new QToolBar(tr("Charts"), this); toolbar->setIconSize({16, 16}); - toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); QAction *new_plot_btn = toolbar->addAction(utils::icon("file-plus"), ""); new_plot_btn->setToolTip(tr("New Plot")); @@ -45,8 +43,11 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { range_slider->setPageStep(60); // 1 min range_slider_action = toolbar->addWidget(range_slider); - reset_zoom_btn = toolbar->addAction(utils::icon("zoom-out"), ""); + reset_zoom_action = toolbar->addWidget(reset_zoom_btn = new QToolButton()); + reset_zoom_btn->setIcon(utils::icon("zoom-out")); reset_zoom_btn->setToolTip(tr("Reset zoom")); + reset_zoom_btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + remove_all_btn = toolbar->addAction(utils::icon("x"), ""); remove_all_btn->setToolTip(tr("Remove all charts")); dock_btn = toolbar->addAction(""); @@ -84,7 +85,7 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { QObject::connect(range_slider, &QSlider::valueChanged, this, &ChartsWidget::setMaxChartRange); QObject::connect(new_plot_btn, &QAction::triggered, this, &ChartsWidget::newChart); QObject::connect(remove_all_btn, &QAction::triggered, this, &ChartsWidget::removeAll); - QObject::connect(reset_zoom_btn, &QAction::triggered, this, &ChartsWidget::zoomReset); + QObject::connect(reset_zoom_btn, &QToolButton::clicked, this, &ChartsWidget::zoomReset); QObject::connect(columns_cb, SIGNAL(activated(int)), SLOT(setColumnCount(int))); QObject::connect(&settings, &Settings::changed, this, &ChartsWidget::settingChanged); QObject::connect(dock_btn, &QAction::triggered, [this]() { @@ -163,8 +164,8 @@ void ChartsWidget::updateToolBar() { range_lb->setText(QString("Range: %1:%2 ").arg(max_chart_range / 60, 2, 10, QLatin1Char('0')).arg(max_chart_range % 60, 2, 10, QLatin1Char('0'))); range_lb_action->setVisible(!is_zoomed); range_slider_action->setVisible(!is_zoomed); - reset_zoom_btn->setVisible(is_zoomed); - reset_zoom_btn->setText(is_zoomed ? tr("Zoomin: %1-%2").arg(zoomed_range.first, 0, 'f', 2).arg(zoomed_range.second, 0, 'f', 2) : ""); + reset_zoom_action->setVisible(is_zoomed); + reset_zoom_btn->setText(is_zoomed ? tr("Zoomin: %1-%2").arg(zoomed_range.first, 0, 'f', 1).arg(zoomed_range.second, 0, 'f', 1) : ""); remove_all_btn->setEnabled(!charts.isEmpty()); dock_btn->setIcon(utils::icon(docking ? "arrow-up-right-square" : "arrow-down-left-square")); dock_btn->setToolTip(docking ? tr("Undock charts") : tr("Dock charts")); @@ -799,11 +800,10 @@ QXYSeries *ChartView::createSeries(QAbstractSeries::SeriesType type) { } void ChartView::setSeriesType(QAbstractSeries::SeriesType type) { + line_series_action->setChecked(type == QAbstractSeries::SeriesTypeLine); + scatter_series_action->setChecked(type == QAbstractSeries::SeriesTypeScatter); if (type != series_type) { series_type = type; - line_series_action->setChecked(type == QAbstractSeries::SeriesTypeLine); - scatter_series_action->setChecked(type == QAbstractSeries::SeriesTypeScatter); - for (auto &s : sigs) { chart()->removeSeries(s.series); s.series->deleteLater(); diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index dc92be89f9..3e32dc069f 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -138,7 +139,8 @@ private: QAction *range_slider_action; bool docking = true; QAction *dock_btn; - QAction *reset_zoom_btn; + QAction *reset_zoom_action; + QToolButton *reset_zoom_btn; QAction *remove_all_btn; QGridLayout *charts_layout; QList charts; From 074e74ad08e199f189d4d84f73b2a9df1061eb97 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Tue, 7 Feb 2023 11:37:03 -0800 Subject: [PATCH 311/484] setup: check installer is elf (#27241) --- selfdrive/ui/qt/setup/setup.cc | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/qt/setup/setup.cc b/selfdrive/ui/qt/setup/setup.cc index 231c4c2ad6..0617214f08 100644 --- a/selfdrive/ui/qt/setup/setup.cc +++ b/selfdrive/ui/qt/setup/setup.cc @@ -20,6 +20,17 @@ const std::string USER_AGENT = "AGNOSSetup-"; const QString DASHCAM_URL = "https://dashcam.comma.ai"; +bool is_elf(char *fname) { + FILE *fp = fopen(fname, "rb"); + if (fp == NULL) { + return false; + } + char buf[4]; + size_t n = fread(buf, 1, 4, fp); + fclose(fp); + return n == 4 && buf[0] == 0x7f && buf[1] == 'E' && buf[2] == 'L' && buf[3] == 'F'; +} + void Setup::download(QString url) { CURL *curl = curl_easy_init(); if (!curl) { @@ -44,10 +55,9 @@ void Setup::download(QString url) { curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); int ret = curl_easy_perform(curl); - long res_status = 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &res_status); - if (ret == CURLE_OK && res_status == 200) { + if (ret == CURLE_OK && res_status == 200 && is_elf(tmpfile)) { rename(tmpfile, "/tmp/installer"); emit finished(true); } else { From 1a0a6387409203e1a2611eb35882de7a931ca703 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Tue, 7 Feb 2023 11:37:25 -0800 Subject: [PATCH 312/484] setup: replace software selection widget with input dialog (#27236) * setup: replace software selection widget with input dialog * revert https://github.com/commaai/openpilot/pull/21617/commits/3f7047651c31b209c9c4a0b8ff7bd408aca3f9e5 --- selfdrive/ui/qt/setup/setup.cc | 116 +++-------------------- selfdrive/ui/qt/setup/setup.h | 1 - selfdrive/ui/translations/main_de.ts | 12 --- selfdrive/ui/translations/main_ja.ts | 12 --- selfdrive/ui/translations/main_ko.ts | 12 --- selfdrive/ui/translations/main_pt-BR.ts | 12 --- selfdrive/ui/translations/main_zh-CHS.ts | 12 --- selfdrive/ui/translations/main_zh-CHT.ts | 12 --- 8 files changed, 14 insertions(+), 175 deletions(-) diff --git a/selfdrive/ui/qt/setup/setup.cc b/selfdrive/ui/qt/setup/setup.cc index 0617214f08..392be68a12 100644 --- a/selfdrive/ui/qt/setup/setup.cc +++ b/selfdrive/ui/qt/setup/setup.cc @@ -185,7 +185,20 @@ QWidget * Setup::network_setup() { QPushButton *cont = new QPushButton(); cont->setObjectName("navBtn"); cont->setProperty("primary", true); - QObject::connect(cont, &QPushButton::clicked, this, &Setup::nextPage); + QObject::connect(cont, &QPushButton::clicked, [=]() { + auto w = currentWidget(); + QTimer::singleShot(0, [=]() { + setCurrentWidget(downloading_widget); + }); + QString url = InputDialog::getText(tr("Enter URL"), this, tr("for Custom Software")); + if (!url.isEmpty()) { + QTimer::singleShot(1000, this, [=]() { + download(url); + }); + } else { + setCurrentWidget(w); + } + }); blayout->addWidget(cont); // setup timer for testing internet connection @@ -212,106 +225,6 @@ QWidget * Setup::network_setup() { return widget; } -QWidget * radio_button(QString title, QButtonGroup *group) { - QPushButton *btn = new QPushButton(title); - btn->setCheckable(true); - group->addButton(btn); - btn->setStyleSheet(R"( - QPushButton { - height: 230; - padding-left: 100px; - padding-right: 100px; - text-align: left; - font-size: 80px; - font-weight: 400; - border-radius: 10px; - background-color: #4F4F4F; - } - QPushButton:checked { - background-color: #465BEA; - } - )"); - - // checkmark icon - QPixmap pix(":/img_circled_check.svg"); - btn->setIcon(pix); - btn->setIconSize(QSize(0, 0)); - btn->setLayoutDirection(Qt::RightToLeft); - QObject::connect(btn, &QPushButton::toggled, [=](bool checked) { - btn->setIconSize(checked ? QSize(104, 104) : QSize(0, 0)); - }); - return btn; -} - -QWidget * Setup::software_selection() { - QWidget *widget = new QWidget(); - QVBoxLayout *main_layout = new QVBoxLayout(widget); - main_layout->setContentsMargins(55, 50, 55, 50); - main_layout->setSpacing(0); - - // title - QLabel *title = new QLabel(tr("Choose Software to Install")); - title->setStyleSheet("font-size: 90px; font-weight: 500;"); - main_layout->addWidget(title, 0, Qt::AlignLeft | Qt::AlignTop); - - main_layout->addSpacing(50); - - // dashcam + custom radio buttons - QButtonGroup *group = new QButtonGroup(widget); - group->setExclusive(true); - - QWidget *dashcam = radio_button(tr("Dashcam"), group); - main_layout->addWidget(dashcam); - - main_layout->addSpacing(30); - - QWidget *custom = radio_button(tr("Custom Software"), group); - main_layout->addWidget(custom); - - main_layout->addStretch(); - - // back + continue buttons - QHBoxLayout *blayout = new QHBoxLayout; - main_layout->addLayout(blayout); - blayout->setSpacing(50); - - QPushButton *back = new QPushButton(tr("Back")); - back->setObjectName("navBtn"); - QObject::connect(back, &QPushButton::clicked, this, &Setup::prevPage); - blayout->addWidget(back); - - QPushButton *cont = new QPushButton(tr("Continue")); - cont->setObjectName("navBtn"); - cont->setEnabled(false); - cont->setProperty("primary", true); - blayout->addWidget(cont); - - QObject::connect(cont, &QPushButton::clicked, [=]() { - auto w = currentWidget(); - QTimer::singleShot(0, [=]() { - setCurrentWidget(downloading_widget); - }); - QString url = DASHCAM_URL; - if (group->checkedButton() != dashcam) { - url = InputDialog::getText(tr("Enter URL"), this, tr("for Custom Software")); - } - if (!url.isEmpty()) { - QTimer::singleShot(1000, this, [=]() { - download(url); - }); - } else { - setCurrentWidget(w); - } - }); - - connect(group, QOverload::of(&QButtonGroup::buttonClicked), [=](QAbstractButton *btn) { - btn->setChecked(true); - cont->setEnabled(true); - }); - - return widget; -} - QWidget * Setup::downloading() { QWidget *widget = new QWidget(); QVBoxLayout *main_layout = new QVBoxLayout(widget); @@ -387,7 +300,6 @@ Setup::Setup(QWidget *parent) : QStackedWidget(parent) { addWidget(getting_started()); addWidget(network_setup()); - addWidget(software_selection()); downloading_widget = downloading(); addWidget(downloading_widget); diff --git a/selfdrive/ui/qt/setup/setup.h b/selfdrive/ui/qt/setup/setup.h index 8027e8bd4f..f990b5a6cb 100644 --- a/selfdrive/ui/qt/setup/setup.h +++ b/selfdrive/ui/qt/setup/setup.h @@ -14,7 +14,6 @@ private: QWidget *low_voltage(); QWidget *getting_started(); QWidget *network_setup(); - QWidget *software_selection(); QWidget *downloading(); QWidget *download_failed(); diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index 0bf91bf440..b40812af7c 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -674,18 +674,6 @@ location set Waiting for internet Auf Internet warten - - Choose Software to Install - Software zum installieren auswählen - - - Dashcam - Dashcam - - - Custom Software - Spezifische Software - Enter URL URL eingeben diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index 15f803dd99..ed08a52df5 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -672,18 +672,6 @@ location set Waiting for internet インターネット接続を待機中 - - Choose Software to Install - インストールするソフトウェアを選択してください - - - Dashcam - ドライブレコーダー - - - Custom Software - カスタムソフトウェア - Enter URL URL を入力 diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index d0bc63c9e3..7b742a5048 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -672,18 +672,6 @@ location set Waiting for internet 네트워크 접속을 기다립니다 - - Choose Software to Install - 설치할 소프트웨어를 선택하세요 - - - Dashcam - Dashcam - - - Custom Software - Custom Software - Enter URL URL 입력 diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index 75807eebad..a25c51de3c 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -676,18 +676,6 @@ trabalho definido Waiting for internet Esperando pela internet - - Choose Software to Install - Escolher Software para Instalar - - - Dashcam - Dashcam - - - Custom Software - Sofware Customizado - Enter URL Preencher URL diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index ac96d9c711..68c1bb766f 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -670,18 +670,6 @@ location set Waiting for internet 等待网络连接 - - Choose Software to Install - 选择要安装的软件 - - - Dashcam - Dashcam(行车记录仪) - - - Custom Software - 自定义软件 - Enter URL 输入网址 diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index 8ccf028f67..71315e118f 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -672,18 +672,6 @@ location set Waiting for internet 連接至網路中 - - Choose Software to Install - 選擇要安裝的軟體 - - - Dashcam - 行車記錄器 - - - Custom Software - 定制的軟體 - Enter URL 輸入網址 From 01bdf382c4bd8a30444c0bde3b737a95f5813f42 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 7 Feb 2023 12:52:50 -0800 Subject: [PATCH 313/484] CI: temporarily remove timeouts --- .github/workflows/prebuilt.yaml | 1 - .github/workflows/release.yaml | 1 - .github/workflows/selfdrive_tests.yaml | 20 -------------------- .github/workflows/tools_tests.yaml | 4 ---- 4 files changed, 26 deletions(-) diff --git a/.github/workflows/prebuilt.yaml b/.github/workflows/prebuilt.yaml index c8b4c51e38..1fd093e2ac 100644 --- a/.github/workflows/prebuilt.yaml +++ b/.github/workflows/prebuilt.yaml @@ -16,7 +16,6 @@ jobs: build_prebuilt: name: build prebuilt runs-on: ubuntu-20.04 - timeout-minutes: 60 if: github.repository == 'commaai/openpilot' env: IMAGE_NAME: openpilot-prebuilt diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 8df89dcc38..7f83f61e0a 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -8,7 +8,6 @@ jobs: build_masterci: name: build master-ci runs-on: ubuntu-20.04 - timeout-minutes: 60 if: github.repository == 'commaai/openpilot' steps: - name: Wait for green check mark diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index d5be221303..834b2141d0 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -32,7 +32,6 @@ jobs: build_release: name: build release runs-on: ubuntu-20.04 - timeout-minutes: 30 env: STRIPPED_DIR: /tmp/releasepilot steps: @@ -66,7 +65,6 @@ jobs: build_all: name: build all runs-on: ubuntu-20.04 - timeout-minutes: 30 steps: - uses: actions/checkout@v3 with: @@ -84,7 +82,6 @@ jobs: #build_mac: # name: build macos # runs-on: macos-latest - # timeout-minutes: 60 # steps: # - uses: actions/checkout@v3 # with: @@ -141,7 +138,6 @@ jobs: docker_push: name: docker push runs-on: ubuntu-20.04 - timeout-minutes: 22 if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/openpilot' needs: static_analysis # hack to ensure slow tests run first since this and static_analysis are fast steps: @@ -150,14 +146,12 @@ jobs: submodules: true - name: Build Docker image run: eval "$BUILD" - timeout-minutes: 13 - name: Push to container registry run: | $DOCKER_LOGIN docker push $DOCKER_REGISTRY/$BASE_IMAGE:latest - name: Build CL Docker image run: eval "$BUILD_CL" - timeout-minutes: 4 - name: Push to container registry run: | $DOCKER_LOGIN @@ -166,7 +160,6 @@ jobs: static_analysis: name: static analysis runs-on: ubuntu-20.04 - timeout-minutes: 20 steps: - uses: actions/checkout@v3 with: @@ -174,13 +167,11 @@ jobs: - name: Build Docker image run: eval "$BUILD" - name: pre-commit - timeout-minutes: 5 run: ${{ env.RUN }} "pre-commit run --all" valgrind: name: valgrind runs-on: ubuntu-20.04 - timeout-minutes: 20 steps: - uses: actions/checkout@v3 with: @@ -198,7 +189,6 @@ jobs: unit_tests: name: unit tests runs-on: ubuntu-20.04 - timeout-minutes: 30 steps: - uses: actions/checkout@v3 with: @@ -207,7 +197,6 @@ jobs: - name: Build openpilot run: ${{ env.RUN }} "scons -j$(nproc)" - name: Run unit tests - timeout-minutes: 15 run: | ${{ env.RUN }} "export SKIP_LONG_TESTS=1 && \ $UNIT_TEST common && \ @@ -242,7 +231,6 @@ jobs: process_replay: name: process replay runs-on: ubuntu-20.04 - timeout-minutes: 25 steps: - uses: actions/checkout@v3 with: @@ -280,7 +268,6 @@ jobs: test_modeld: name: model tests runs-on: ubuntu-20.04 - timeout-minutes: 20 steps: - uses: actions/checkout@v3 with: @@ -293,12 +280,10 @@ jobs: run: | ${{ env.RUN }} "scons -j$(nproc)" - name: Run model replay with ONNX - timeout-minutes: 2 run: | ${{ env.RUN_CL }} "ONNXCPU=1 CI=1 NO_NAV=1 coverage run selfdrive/test/process_replay/model_replay.py && \ coverage xml" - name: Run unit tests - timeout-minutes: 5 run: | ${{ env.RUN_CL }} "$UNIT_TEST selfdrive/modeld && \ coverage xml" @@ -308,7 +293,6 @@ jobs: test_longitudinal: name: longitudinal runs-on: ubuntu-20.04 - timeout-minutes: 20 steps: - uses: actions/checkout@v3 with: @@ -323,7 +307,6 @@ jobs: cd selfdrive/test/longitudinal_maneuvers && \ coverage run ./test_longitudinal.py && \ coverage xml" - timeout-minutes: 2 - name: "Upload coverage to Codecov" uses: codecov/codecov-action@v2 - uses: actions/upload-artifact@v2 @@ -336,7 +319,6 @@ jobs: test_cars: name: cars runs-on: ubuntu-20.04 - timeout-minutes: 20 strategy: fail-fast: false matrix: @@ -355,7 +337,6 @@ jobs: - name: Build openpilot run: ${{ env.RUN }} "scons -j$(nproc)" - name: Test car models - timeout-minutes: 12 run: | ${{ env.RUN }} "coverage run -m pytest selfdrive/car/tests/test_models.py && \ coverage xml && \ @@ -369,7 +350,6 @@ jobs: car_docs_diff: name: PR comments runs-on: ubuntu-20.04 - timeout-minutes: 20 if: github.event_name == 'pull_request' steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/tools_tests.yaml b/.github/workflows/tools_tests.yaml index 94cc3c2580..3b32d3c790 100644 --- a/.github/workflows/tools_tests.yaml +++ b/.github/workflows/tools_tests.yaml @@ -30,7 +30,6 @@ jobs: plotjuggler: name: plotjuggler runs-on: ubuntu-20.04 - timeout-minutes: 20 steps: - uses: actions/checkout@v3 with: @@ -38,7 +37,6 @@ jobs: - name: Build Docker image run: eval "$BUILD" - name: Unit test - timeout-minutes: 2 run: | ${{ env.RUN }} "scons -j$(nproc) --directory=/tmp/openpilot/cereal && \ apt-get update && \ @@ -49,7 +47,6 @@ jobs: simulator: name: simulator runs-on: ubuntu-20.04 - timeout-minutes: 30 env: IMAGE_NAME: openpilot-sim if: github.repository == 'commaai/openpilot' @@ -74,7 +71,6 @@ jobs: docs: name: build docs runs-on: ubuntu-20.04 - timeout-minutes: 25 steps: - uses: actions/checkout@v3 with: From 9af82c090e1bcb0d76b4c2468d538c75b6e9c650 Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Tue, 7 Feb 2023 15:01:47 -0800 Subject: [PATCH 314/484] ublox_day_fix --- selfdrive/locationd/ublox_msg.cc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/selfdrive/locationd/ublox_msg.cc b/selfdrive/locationd/ublox_msg.cc index 3ff768ba06..33ac2fe088 100644 --- a/selfdrive/locationd/ublox_msg.cc +++ b/selfdrive/locationd/ublox_msg.cc @@ -358,9 +358,6 @@ kj::Array UbloxMsgParser::parse_glonass_ephemeris(ubx_t::rxm_sfrbx_ eph.setAge(data->e_n()); eph.setP4(data->p4()); eph.setSvURA(glonass_URA_lookup.at(data->f_t())); - if (msg->sv_id() != data->n()) { - LOGE("SV_ID != SLOT_NUMBER: %d %d", msg->sv_id(), data->n()) - } eph.setSvType(data->m()); } @@ -374,6 +371,13 @@ kj::Array UbloxMsgParser::parse_glonass_ephemeris(ubx_t::rxm_sfrbx_ // the year can be fetched later in laika (note rollovers and leap year) uint8_t n_4 = data->n_4(); uint16_t year = get_glonass_year(n_4, current_day); + if (current_day > 1461) { + // impossible day within last 4 year, reject ephemeris + // TODO: check if this can be detected via hamming code + LOGE("INVALID DATA: current day out of range: %d, %d", current_day, n_4); + glonass_strings[msg->sv_id()].clear(); + return kj::Array(); + } uint16_t last_leap_year = 1996 + 4*(n_4-1); uint16_t days_till_this_year = (year - last_leap_year)*365; From 4f6fbdf590fdcd9e1327cb42905fa17542d64f73 Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Tue, 7 Feb 2023 15:02:21 -0800 Subject: [PATCH 315/484] Revert "ublox_day_fix" This reverts commit 9af82c090e1bcb0d76b4c2468d538c75b6e9c650. --- selfdrive/locationd/ublox_msg.cc | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/selfdrive/locationd/ublox_msg.cc b/selfdrive/locationd/ublox_msg.cc index 33ac2fe088..3ff768ba06 100644 --- a/selfdrive/locationd/ublox_msg.cc +++ b/selfdrive/locationd/ublox_msg.cc @@ -358,6 +358,9 @@ kj::Array UbloxMsgParser::parse_glonass_ephemeris(ubx_t::rxm_sfrbx_ eph.setAge(data->e_n()); eph.setP4(data->p4()); eph.setSvURA(glonass_URA_lookup.at(data->f_t())); + if (msg->sv_id() != data->n()) { + LOGE("SV_ID != SLOT_NUMBER: %d %d", msg->sv_id(), data->n()) + } eph.setSvType(data->m()); } @@ -371,13 +374,6 @@ kj::Array UbloxMsgParser::parse_glonass_ephemeris(ubx_t::rxm_sfrbx_ // the year can be fetched later in laika (note rollovers and leap year) uint8_t n_4 = data->n_4(); uint16_t year = get_glonass_year(n_4, current_day); - if (current_day > 1461) { - // impossible day within last 4 year, reject ephemeris - // TODO: check if this can be detected via hamming code - LOGE("INVALID DATA: current day out of range: %d, %d", current_day, n_4); - glonass_strings[msg->sv_id()].clear(); - return kj::Array(); - } uint16_t last_leap_year = 1996 + 4*(n_4-1); uint16_t days_till_this_year = (year - last_leap_year)*365; From 6330cdd88513d9dad3e5ab00811c4a5223924423 Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Tue, 7 Feb 2023 18:49:06 -0700 Subject: [PATCH 316/484] GPS: add tow to gps ephemeris (#27246) * add tow to gps ephemeris * bump cereal * update refs --------- Co-authored-by: Kurt Nistelberger --- cereal | 2 +- selfdrive/locationd/ublox_msg.cc | 1 + selfdrive/test/process_replay/ref_commit | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cereal b/cereal index bdbac40160..fa3e77b7c8 160000 --- a/cereal +++ b/cereal @@ -1 +1 @@ -Subproject commit bdbac40160a550c88e481a3bf700a1016901bd9b +Subproject commit fa3e77b7c8eee8752f19427b34adcb1ae5c70ec5 diff --git a/selfdrive/locationd/ublox_msg.cc b/selfdrive/locationd/ublox_msg.cc index 3ff768ba06..877ace9a06 100644 --- a/selfdrive/locationd/ublox_msg.cc +++ b/selfdrive/locationd/ublox_msg.cc @@ -213,6 +213,7 @@ kj::Array UbloxMsgParser::parse_gps_ephemeris(ubx_t::rxm_sfrbx_t *m eph.setAf1(subframe_1->af_1() * pow(2, -43)); eph.setAf0(subframe_1->af_0() * pow(2, -31)); eph.setSvHealth(subframe_1->sv_health()); + eph.setTowCount(subframe.how()->tow_count()); iodc_lsb = subframe_1->iodc_lsb(); } diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 2c66c7075c..f98caa80a7 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -7c2a98f4a94b6dded6a90d8913ab85a63069c186 \ No newline at end of file +d862b2c7d383d859db30d6c1cc665c29c7a2a9f3 \ No newline at end of file From 5a0cbc35a544f448e8db1e1578de7607b39a3e76 Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Tue, 7 Feb 2023 18:49:51 -0700 Subject: [PATCH 317/484] Ubloxd: glonass ephem parsing (#27248) temp fix Co-authored-by: Kurt Nistelberger --- selfdrive/locationd/ublox_msg.cc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/selfdrive/locationd/ublox_msg.cc b/selfdrive/locationd/ublox_msg.cc index 877ace9a06..8fa721a091 100644 --- a/selfdrive/locationd/ublox_msg.cc +++ b/selfdrive/locationd/ublox_msg.cc @@ -359,9 +359,6 @@ kj::Array UbloxMsgParser::parse_glonass_ephemeris(ubx_t::rxm_sfrbx_ eph.setAge(data->e_n()); eph.setP4(data->p4()); eph.setSvURA(glonass_URA_lookup.at(data->f_t())); - if (msg->sv_id() != data->n()) { - LOGE("SV_ID != SLOT_NUMBER: %d %d", msg->sv_id(), data->n()) - } eph.setSvType(data->m()); } @@ -375,6 +372,13 @@ kj::Array UbloxMsgParser::parse_glonass_ephemeris(ubx_t::rxm_sfrbx_ // the year can be fetched later in laika (note rollovers and leap year) uint8_t n_4 = data->n_4(); uint16_t year = get_glonass_year(n_4, current_day); + if (current_day > 1461) { + // impossible day within last 4 year, reject ephemeris + // TODO: check if this can be detected via hamming code + LOGE("INVALID DATA: current day out of range: %d, %d", current_day, n_4); + glonass_strings[msg->sv_id()].clear(); + return kj::Array(); + } uint16_t last_leap_year = 1996 + 4*(n_4-1); uint16_t days_till_this_year = (year - last_leap_year)*365; From 9799359ffdc56c379955a892a92961f559f4bc19 Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Tue, 7 Feb 2023 17:54:23 -0800 Subject: [PATCH 318/484] bump laika --- laika_repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/laika_repo b/laika_repo index b30afe44d2..278b44ba8c 160000 --- a/laika_repo +++ b/laika_repo @@ -1 +1 @@ -Subproject commit b30afe44d283072b6ee0ea17f290cca5d002baf7 +Subproject commit 278b44ba8c2dec28adc5b30cce13dabc195f15fc From 4d05b5b02a706eac246c6f5b545ecfc7513a6525 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 7 Feb 2023 20:11:36 -0800 Subject: [PATCH 319/484] Bump panda (#27249) * bumppanda * bumptomaster * bumppandaagain --- panda | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda b/panda index e7f36a2992..82151aaffe 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit e7f36a2992b353d7d66fbb697d166d4de8718c99 +Subproject commit 82151aaffefbb460887d09f4f5e29092d3b669ba From fe64e853639e00d6d2cc8910d66b9b8c6f56c808 Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Tue, 7 Feb 2023 21:25:46 -0800 Subject: [PATCH 320/484] readd slot number logging --- selfdrive/locationd/ublox_msg.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/selfdrive/locationd/ublox_msg.cc b/selfdrive/locationd/ublox_msg.cc index 8fa721a091..b746989466 100644 --- a/selfdrive/locationd/ublox_msg.cc +++ b/selfdrive/locationd/ublox_msg.cc @@ -359,6 +359,9 @@ kj::Array UbloxMsgParser::parse_glonass_ephemeris(ubx_t::rxm_sfrbx_ eph.setAge(data->e_n()); eph.setP4(data->p4()); eph.setSvURA(glonass_URA_lookup.at(data->f_t())); + if (msg->sv_id() != data->n()) { + LOGE("SV_ID != SLOT_NUMBER: %d %d", msg->sv_id(), data->n()) + } eph.setSvType(data->m()); } From 583057aa413f533e761ce869abd69f2fedef39b9 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 8 Feb 2023 13:46:59 +0800 Subject: [PATCH 321/484] cabana: add space between signal and msg name (#27252) --- tools/cabana/chartswidget.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 3cd2c80326..f3982bb7a2 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -485,7 +485,7 @@ void ChartView::updateTitle() { } for (auto &s : sigs) { auto decoration = s.series->isVisible() ? "none" : "line-through"; - s.series->setName(QString("%2%3 %4").arg(decoration).arg(s.sig->name.c_str()).arg(msgName(s.msg_id)).arg(s.msg_id)); + s.series->setName(QString("%2 %3 %4").arg(decoration, s.sig->name.c_str(), msgName(s.msg_id), s.msg_id)); } } From 86d86fa9dafc823a9cccd9c06ba995cd510d5535 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 7 Feb 2023 23:38:05 -0800 Subject: [PATCH 322/484] GM: reduce steering message rate (#27201) * Lower active rate and raise limits to compensate * bump panda * bump panda * Update ref_commit * bump panda to master --- panda | 2 +- selfdrive/car/gm/values.py | 6 +++--- selfdrive/test/process_replay/ref_commit | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/panda b/panda index 82151aaffe..7d27eb10fb 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 82151aaffefbb460887d09f4f5e29092d3b669ba +Subproject commit 7d27eb10fb32095e73f60c59a123cae08cc53719 diff --git a/selfdrive/car/gm/values.py b/selfdrive/car/gm/values.py index e633ec5f62..7628d8420b 100644 --- a/selfdrive/car/gm/values.py +++ b/selfdrive/car/gm/values.py @@ -11,10 +11,10 @@ Ecu = car.CarParams.Ecu class CarControllerParams: STEER_MAX = 300 # GM limit is 3Nm. Used by carcontroller to generate LKA output - STEER_STEP = 2 # Active control frames per command (50hz) + STEER_STEP = 3 # Active control frames per command (~33hz) INACTIVE_STEER_STEP = 10 # Inactive control frames per command (10hz) - STEER_DELTA_UP = 7 # Delta rates require review due to observed EPS weakness - STEER_DELTA_DOWN = 17 + STEER_DELTA_UP = 10 # Delta rates require review due to observed EPS weakness + STEER_DELTA_DOWN = 25 STEER_DRIVER_ALLOWANCE = 50 STEER_DRIVER_MULTIPLIER = 4 STEER_DRIVER_FACTOR = 100 diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index f98caa80a7..170beafe88 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -d862b2c7d383d859db30d6c1cc665c29c7a2a9f3 \ No newline at end of file +a6b17ab903022b42677abe2968f3c6e50c331980 From 5b9f2c3fcd09409211f518b9074afa046a1915d6 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 8 Feb 2023 00:09:49 -0800 Subject: [PATCH 323/484] GM: refactor steering control code (#27251) * hopefully easier to understand? * space * no confusing elif, move comment to relevant part * better --- selfdrive/car/gm/carcontroller.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/selfdrive/car/gm/carcontroller.py b/selfdrive/car/gm/carcontroller.py index 56f6ff5a31..affbcdc0fa 100644 --- a/selfdrive/car/gm/carcontroller.py +++ b/selfdrive/car/gm/carcontroller.py @@ -49,25 +49,26 @@ class CarController: can_sends = [] # Steering (Active: 50Hz, inactive: 10Hz) - # Attempt to sync with camera on startup at 50Hz, first few msgs are blocked - init_lka_counter = not self.sent_lka_steering_cmd - # Also send at 50Hz until we're in sync with camera so counters align when relay closes, preventing a fault - # openpilot can subtly drift, so this is activated throughout a drive to stay synced - out_of_sync = self.lka_steering_cmd_counter % 4 != (CS.camera_lka_steering_cmd_counter + 1) % 4 - sync_steer = (init_lka_counter or out_of_sync) and self.CP.networkLocation == NetworkLocation.fwdCamera - - steer_step = self.params.INACTIVE_STEER_STEP - if CC.latActive or sync_steer: - steer_step = self.params.STEER_STEP - - # Avoid GM EPS faults when transmitting messages too close together: skip this transmit if we just received the - # next Panda loopback confirmation in the current CS frame. + steer_step = self.params.STEER_STEP if CC.latActive else self.params.INACTIVE_STEER_STEP + + if self.CP.networkLocation == NetworkLocation.fwdCamera: + # Also send at 50Hz: + # - on startup, first few msgs are blocked + # - until we're in sync with camera so counters align when relay closes, preventing a fault. + # openpilot can subtly drift, so this is activated throughout a drive to stay synced + out_of_sync = self.lka_steering_cmd_counter % 4 != (CS.camera_lka_steering_cmd_counter + 1) % 4 + if not self.sent_lka_steering_cmd or out_of_sync: + steer_step = self.params.STEER_STEP + if CS.loopback_lka_steering_cmd_updated: self.lka_steering_cmd_counter += 1 self.sent_lka_steering_cmd = True - elif (self.frame - self.last_steer_frame) >= steer_step: + + # Avoid GM EPS faults when transmitting messages too close together: skip this transmit if we just + # received the ASCMLKASteeringCmd loopback confirmation in the current CS frame + if (self.frame - self.last_steer_frame) >= steer_step and not CS.loopback_lka_steering_cmd_updated: # Initialize ASCMLKASteeringCmd counter using the camera until we get a msg on the bus - if init_lka_counter: + if not self.sent_lka_steering_cmd: self.lka_steering_cmd_counter = CS.pt_lka_steering_cmd_counter + 1 if CC.latActive: From 303e21cbccc5bbbc8450d6a09221b77e127631b7 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 9 Feb 2023 04:40:22 +0800 Subject: [PATCH 324/484] cabana: improve the welcome page (#27261) --- tools/cabana/detailwidget.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index 46f7a148f4..a7ad572dc1 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -232,9 +232,14 @@ WelcomeWidget::WelcomeWidget(QWidget *parent) : QWidget(parent) { return hlayout; }; + auto lb = new QLabel(tr("<-Select a message to to view details")); + lb->setAlignment(Qt::AlignHCenter); + main_layout->addWidget(lb); main_layout->addLayout(newShortcutRow("Pause", "Space")); main_layout->addLayout(newShortcutRow("Help", "Alt + H")); main_layout->addStretch(0); setStyleSheet("QLabel{color:darkGray;}"); + setBackgroundRole(QPalette::Base); + setAutoFillBackground(true); } From 80bf755b075b2b56373b4ca49c2c95d1837e7648 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 9 Feb 2023 04:40:43 +0800 Subject: [PATCH 325/484] cabana: set correct geometry for legend (#27260) --- tools/cabana/chartswidget.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index f3982bb7a2..4f1f293615 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -310,7 +310,7 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { axis_y->setLabelFormat("%.1f"); chart->addAxis(axis_x, Qt::AlignBottom); chart->addAxis(axis_y, Qt::AlignLeft); - chart->legend()->layout()->setContentsMargins(0, 0, 40, 0); + chart->legend()->layout()->setContentsMargins(16, 0, 40, 0); chart->legend()->setShowToolTips(true); chart->setMargins({0, 0, 0, 0}); From 56bc5b39e3eae122d6b8f85c72c023665b32d89f Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Wed, 8 Feb 2023 16:00:30 -0500 Subject: [PATCH 326/484] VW MQB: Add FW for 2018 Volkswagen Tiguan (#27264) --- selfdrive/car/volkswagen/values.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py index 9f6dee2689..72fcc5d92d 100755 --- a/selfdrive/car/volkswagen/values.py +++ b/selfdrive/car/volkswagen/values.py @@ -729,6 +729,7 @@ FW_VERSIONS = { }, CAR.TIGUAN_MK2: { (Ecu.engine, 0x7e0, None): [ + b'\xf1\x8703N906026D \xf1\x893680', b'\xf1\x8704E906027NB\xf1\x899504', b'\xf1\x8704L906026EJ\xf1\x893661', b'\xf1\x8704L906027G \xf1\x899893', @@ -746,12 +747,14 @@ FW_VERSIONS = { b'\xf1\x870D9300043 \xf1\x895202', b'\xf1\x870DL300011N \xf1\x892001', b'\xf1\x870DL300011N \xf1\x892012', + b'\xf1\x870DL300012P \xf1\x892103', b'\xf1\x870DL300013A \xf1\x893005', b'\xf1\x870DL300013G \xf1\x892119', b'\xf1\x870DL300013G \xf1\x892120', ], (Ecu.srs, 0x715, None): [ b'\xf1\x875Q0959655AR\xf1\x890317\xf1\x82\02331310031333334313132573732379333313100', + b'\xf1\x875Q0959655BJ\xf1\x890336\xf1\x82\x1312110031333300314232583732379333423100', b'\xf1\x875Q0959655BM\xf1\x890403\xf1\x82\02316143231313500314641011750179333423100', b'\xf1\x875Q0959655BT\xf1\x890403\xf1\x82\02312110031333300314240583752379333423100', b'\xf1\x875Q0959655BT\xf1\x890403\xf1\x82\02331310031333336313140013950399333423100', @@ -763,6 +766,7 @@ FW_VERSIONS = { b'\xf1\x875Q0909143M \xf1\x892041\xf1\x820529A6060603', b'\xf1\x875Q0909144AB\xf1\x891082\xf1\x82\x0521A60604A1', b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567A6000600', + b'\xf1\x875QF909144A \xf1\x895581\xf1\x82\x0571A60834A1', b'\xf1\x875QF909144B \xf1\x895582\xf1\x82\00571A60634A1', b'\xf1\x875QM909144B \xf1\x891081\xf1\x82\x0521A60604A1', b'\xf1\x875QM909144C \xf1\x891082\xf1\x82\x0521A60604A1', @@ -771,6 +775,7 @@ FW_VERSIONS = { (Ecu.fwdRadar, 0x757, None): [ b'\xf1\x872Q0907572AA\xf1\x890396', b'\xf1\x872Q0907572J \xf1\x890156', + b'\xf1\x872Q0907572M \xf1\x890233', b'\xf1\x872Q0907572Q \xf1\x890342', b'\xf1\x872Q0907572R \xf1\x890372', b'\xf1\x872Q0907572T \xf1\x890383', From fcaa6fb06fd68a71b129487d80ee00918695fc5b Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Wed, 8 Feb 2023 22:03:23 +0100 Subject: [PATCH 327/484] cabana: extend color palette, make chart colors match signal view (#27258) * cabana: extend color palette, make chart colors match signal view * vary saturation too * cleanup --- tools/cabana/binaryview.cc | 4 +--- tools/cabana/chartswidget.cc | 1 + tools/cabana/historylog.cc | 2 +- tools/cabana/signaledit.cc | 2 +- tools/cabana/util.cc | 15 +++++++++++++++ tools/cabana/util.h | 8 +++----- 6 files changed, 22 insertions(+), 10 deletions(-) diff --git a/tools/cabana/binaryview.cc b/tools/cabana/binaryview.cc index 4d7b3148c6..ddad4cd446 100644 --- a/tools/cabana/binaryview.cc +++ b/tools/cabana/binaryview.cc @@ -173,7 +173,6 @@ void BinaryViewModel::refresh() { if ((dbc_msg = dbc()->msg(msg_id))) { row_count = dbc_msg->size; items.resize(row_count * column_count); - int i = 0; for (auto sig : dbc_msg->getSignals()) { auto [start, end] = getSignalRange(sig); for (int j = start; j <= end; ++j) { @@ -185,10 +184,9 @@ void BinaryViewModel::refresh() { } if (j == start) sig->is_little_endian ? items[idx].is_lsb = true : items[idx].is_msb = true; if (j == end) sig->is_little_endian ? items[idx].is_msb = true : items[idx].is_lsb = true; - items[idx].bg_color = getColor(i); + items[idx].bg_color = getColor(sig); items[idx].sigs.push_back(sig); } - ++i; } } else { row_count = can->lastMessage(msg_id).dat.size(); diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 4f1f293615..4faca8314b 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -525,6 +525,7 @@ void ChartView::updateSeries(const Signal *sig, const std::vector *even s.vals.clear(); s.vals.reserve(settings.max_cached_minutes * 60 * 100); // [n]seconds * 100hz s.last_value_mono_time = 0; + s.series->setColor(getColor(sig)); } struct Chunk { diff --git a/tools/cabana/historylog.cc b/tools/cabana/historylog.cc index 95bd94e76b..3a3397af42 100644 --- a/tools/cabana/historylog.cc +++ b/tools/cabana/historylog.cc @@ -49,7 +49,7 @@ QVariant HistoryLogModel::headerData(int section, Qt::Orientation orientation, i } return show_signals ? QString::fromStdString(sigs[section - 1]->name).replace('_', ' ') : "Data"; } else if (role == Qt::BackgroundRole && section > 0 && show_signals) { - return QBrush(QColor(getColor(section - 1))); + return QBrush(getColor(sigs[section - 1])); } } return {}; diff --git a/tools/cabana/signaledit.cc b/tools/cabana/signaledit.cc index b871ffa21a..2f1a968645 100644 --- a/tools/cabana/signaledit.cc +++ b/tools/cabana/signaledit.cc @@ -279,7 +279,7 @@ void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op } // color label - auto bg_color = QColor(getColor(item->row())); + auto bg_color = getColor(item->sig); QRect rc{option.rect.left() + 3, option.rect.top(), 22, option.rect.height()}; painter->setPen(Qt::NoPen); painter->setBrush(item->highlight ? bg_color.darker(125) : bg_color); diff --git a/tools/cabana/util.cc b/tools/cabana/util.cc index f9d19dfda3..d978e4aa12 100644 --- a/tools/cabana/util.cc +++ b/tools/cabana/util.cc @@ -3,6 +3,10 @@ #include #include #include +#include + +#include +#include #include "selfdrive/ui/qt/util.h" @@ -96,6 +100,17 @@ void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem & } } +QColor getColor(const Signal *sig) { + float h = 19 * (float)sig->lsb / 64.0; + h = fmod(h, 1.0); + + size_t hash = qHash(QString::fromStdString(sig->name)); + float s = 0.25 + 0.25 * (float)(hash & 0xff) / 255.0; + float v = 0.75 + 0.25 * (float)((hash >> 8) & 0xff) / 255.0; + + return QColor::fromHsvF(h, s, v); +} + NameValidator::NameValidator(QObject *parent) : QRegExpValidator(QRegExp("^(\\w+)"), parent) { } QValidator::State NameValidator::validate(QString &input, int &pos) const { diff --git a/tools/cabana/util.h b/tools/cabana/util.h index b76652f4b0..af5032fd42 100644 --- a/tools/cabana/util.h +++ b/tools/cabana/util.h @@ -7,6 +7,8 @@ #include #include +#include "opendbc/can/common_dbc.h" + class ChangeTracker { public: void compute(const QByteArray &dat, double ts, uint32_t freq); @@ -33,11 +35,7 @@ public: inline QString toHex(const QByteArray &dat) { return dat.toHex(' ').toUpper(); } inline char toHex(uint value) { return "0123456789ABCDEF"[value & 0xF]; } -inline const QString &getColor(int i) { - // TODO: add more colors - static const QString SIGNAL_COLORS[] = {"#9FE2BF", "#40E0D0", "#6495ED", "#CCCCFF", "#FF7F50", "#FFBF00"}; - return SIGNAL_COLORS[i % std::size(SIGNAL_COLORS)]; -} +QColor getColor(const Signal *sig); class NameValidator : public QRegExpValidator { Q_OBJECT From 99f12ca7f31a1d6ac8a54254f30d4a31ce628268 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 9 Feb 2023 05:29:16 +0800 Subject: [PATCH 328/484] cabana: fix coredump caused by getColor in updateSeries (#27265) --- tools/cabana/chartswidget.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 4faca8314b..8debb75153 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -525,7 +525,7 @@ void ChartView::updateSeries(const Signal *sig, const std::vector *even s.vals.clear(); s.vals.reserve(settings.max_cached_minutes * 60 * 100); // [n]seconds * 100hz s.last_value_mono_time = 0; - s.series->setColor(getColor(sig)); + s.series->setColor(getColor(s.sig)); } struct Chunk { From f4f081c56209a2fc0669e76e881821b43830462c Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 9 Feb 2023 07:18:07 +0800 Subject: [PATCH 329/484] cabana: imporve series selector dialog (#27266) * imporve series selector dialog * typo --- tools/cabana/chartswidget.cc | 170 +++++++++++++++++------------------ tools/cabana/chartswidget.h | 26 +++--- 2 files changed, 96 insertions(+), 100 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 8debb75153..134a162679 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -251,12 +252,14 @@ void ChartsWidget::resizeEvent(QResizeEvent *event) { } void ChartsWidget::newChart() { - SeriesSelector dlg(this); + SeriesSelector dlg(tr("New Chart"), this); if (dlg.exec() == QDialog::Accepted) { - QList series_list = dlg.series(); - if (!series_list.isEmpty()) { + auto items = dlg.seletedItems(); + if (!items.isEmpty()) { auto c = createChart(); - c->addSeries(series_list); + for (auto it : items) { + c->addSeries(it->msg_id, it->sig); + } } } } @@ -425,33 +428,24 @@ void ChartView::msgRemoved(uint32_t address) { } } -void ChartView::addSeries(const QList &series_list) { - for (auto &s : series_list) { - if (auto m = dbc()->msg(s[0])) { - auto it = m->sigs.find(s[2]); - if (it != m->sigs.end() && !hasSeries(s[0], &(it->second))) { - addSeries(s[0], &(it->second)); - } - } - } -} - void ChartView::manageSeries() { - SeriesSelector dlg(this); + SeriesSelector dlg(tr("Mange Chart"), this); for (auto &s : sigs) { - dlg.addSeries(s.msg_id, msgName(s.msg_id), QString::fromStdString(s.sig->name)); + dlg.addSelected(s.msg_id, s.sig); } - - int ret = dlg.exec(); - if (ret == QDialog::Accepted) { - QList series_list = dlg.series(); - if (series_list.isEmpty()) { + if (dlg.exec() == QDialog::Accepted) { + auto items = dlg.seletedItems(); + if (items.isEmpty()) { emit remove(); } else { - addSeries(series_list); + for (auto s : items) { + if (!hasSeries(s->msg_id, s->sig)) { + addSeries(s->msg_id, s->sig); + } + } for (auto it = sigs.begin(); it != sigs.end(); /**/) { - bool exists = std::any_of(series_list.cbegin(), series_list.cend(), [&](auto &s) { - return s[0] == it->msg_id && s[2] == it->sig->name.c_str(); + bool exists = std::any_of(items.cbegin(), items.cend(), [&](auto &s) { + return s->msg_id == it->msg_id && s->sig == it->sig; }); it = exists ? ++it : removeItem(it); } @@ -737,11 +731,9 @@ void ChartView::dropEvent(QDropEvent *event) { event->accept(); } else { ChartView *source_chart = (ChartView *)event->source(); - QList series; for (auto &s : source_chart->sigs) { - series.push_back({s.msg_id, msgName(s.msg_id), QString::fromStdString(s.sig->name)}); + addSeries(s.msg_id, s.sig); } - addSeries(series); emit source_chart->remove(); event->acceptProposedAction(); } @@ -836,40 +828,37 @@ void ChartView::handleMarkerClicked() { // SeriesSelector -SeriesSelector::SeriesSelector(QWidget *parent) { - setWindowTitle(tr("Manage Chart Series")); - QHBoxLayout *contents_layout = new QHBoxLayout(); - - QVBoxLayout *left_layout = new QVBoxLayout(); - left_layout->addWidget(new QLabel(tr("Select Signals:"))); +SeriesSelector::SeriesSelector(QString title, QWidget *parent) : QDialog(parent) { + setWindowTitle(title); + QGridLayout *main_layout = new QGridLayout(this); - msgs_combo = new QComboBox(this); + // left column + main_layout->addWidget(new QLabel(tr("Available Signals")), 0, 0); + main_layout->addWidget(msgs_combo = new QComboBox(this), 1, 0); msgs_combo->setEditable(true); - msgs_combo->lineEdit()->setPlaceholderText(tr("Select Msg")); + msgs_combo->lineEdit()->setPlaceholderText(tr("Select a msg...")); msgs_combo->setInsertPolicy(QComboBox::NoInsert); msgs_combo->completer()->setCompletionMode(QCompleter::PopupCompletion); msgs_combo->completer()->setFilterMode(Qt::MatchContains); - left_layout->addWidget(msgs_combo); - sig_list = new QListWidget(this); - sig_list->setSortingEnabled(true); - sig_list->setToolTip(tr("Double click on an item to add signal to chart")); - left_layout->addWidget(sig_list); - - QVBoxLayout *right_layout = new QVBoxLayout(); - right_layout->addWidget(new QLabel(tr("Chart Signals:"))); - chart_series = new QListWidget(this); - chart_series->setSortingEnabled(true); - chart_series->setToolTip(tr("Double click on an item to remove signal from chart")); - right_layout->addWidget(chart_series); - contents_layout->addLayout(left_layout); - contents_layout->addLayout(right_layout); + main_layout->addWidget(available_list = new QListWidget(this), 2, 0); - auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + // buttons + QVBoxLayout *btn_layout = new QVBoxLayout(); + QPushButton *add_btn = new QPushButton(utils::icon("chevron-right"), "", this); + QPushButton *remove_btn = new QPushButton(utils::icon("chevron-left"), "", this); + btn_layout->addStretch(0); + btn_layout->addWidget(add_btn); + btn_layout->addWidget(remove_btn); + btn_layout->addStretch(0); + main_layout->addLayout(btn_layout, 0, 1, 3, 1); - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->addLayout(contents_layout); - main_layout->addWidget(buttonBox); + // right column + main_layout->addWidget(new QLabel(tr("Selected Signals")), 0, 2); + main_layout->addWidget(selected_list = new QListWidget(this), 1, 2, 2, 1); + + auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + main_layout->addWidget(buttonBox, 3, 2); for (auto it = can->can_msgs.cbegin(); it != can->can_msgs.cend(); ++it) { if (auto m = dbc()->msg(it.key())) { @@ -877,56 +866,59 @@ SeriesSelector::SeriesSelector(QWidget *parent) { } } msgs_combo->model()->sort(0); - + msgs_combo->setCurrentIndex(-1); + + QObject::connect(msgs_combo, qOverload(&QComboBox::currentIndexChanged), this, &SeriesSelector::updateAvailableList); + QObject::connect(available_list, &QListWidget::currentRowChanged, [=](int row) { add_btn->setEnabled(row != -1); }); + QObject::connect(selected_list, &QListWidget::currentRowChanged, [=](int row) { remove_btn->setEnabled(row != -1); }); + QObject::connect(available_list, &QListWidget::itemDoubleClicked, this, &SeriesSelector::add); + QObject::connect(selected_list, &QListWidget::itemDoubleClicked, this, &SeriesSelector::remove); + QObject::connect(add_btn, &QPushButton::clicked, [this]() { if (auto item = available_list->currentItem()) add(item); }); + QObject::connect(remove_btn, &QPushButton::clicked, [this]() { if (auto item = selected_list->currentItem()) remove(item);}); QObject::connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); QObject::connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); - QObject::connect(msgs_combo, SIGNAL(currentIndexChanged(int)), SLOT(msgSelected(int))); - QObject::connect(sig_list, &QListWidget::itemDoubleClicked, this, &SeriesSelector::addSignal); - QObject::connect(chart_series, &QListWidget::itemDoubleClicked, [](QListWidgetItem *item) { delete item; }); +} + +void SeriesSelector::add(QListWidgetItem *item) { + auto it = (ListItem *)item; + addItemToList(selected_list, it->msg_id, it->sig, true); + delete item; +} - if (int index = msgs_combo->currentIndex(); index >= 0) { - msgSelected(index); +void SeriesSelector::remove(QListWidgetItem *item) { + auto it = (ListItem *)item; + if (it->msg_id == msgs_combo->currentData().toString()) { + addItemToList(available_list, it->msg_id, it->sig); } + delete item; } -void SeriesSelector::msgSelected(int index) { +void SeriesSelector::updateAvailableList(int index) { + if (index == -1) return; + available_list->clear(); QString msg_id = msgs_combo->itemData(index).toString(); - sig_list->clear(); - if (auto m = dbc()->msg(msg_id)) { - for (auto &[name, s] : m->sigs) { - QStringList data({msg_id, m->name, name}); - QListWidgetItem *item = new QListWidgetItem(name, sig_list); - item->setData(Qt::UserRole, data); - sig_list->addItem(item); + auto selected_items = seletedItems(); + for (auto &[name, s] : dbc()->msg(msg_id)->sigs) { + bool is_selected = std::any_of(selected_items.begin(), selected_items.end(), [=, sig=&s](auto it) { return it->msg_id == msg_id && it->sig == sig; }); + if (!is_selected) { + addItemToList(available_list, msg_id, &s); } } } -void SeriesSelector::addSignal(QListWidgetItem *item) { - QStringList data = item->data(Qt::UserRole).toStringList(); - addSeries(data[0], data[1], data[2]); -} +void SeriesSelector::addItemToList(QListWidget *parent, const QString id, const Signal *sig, bool show_msg_name) { + QString text = QString(" %1").arg(getColor(sig).name(), sig->name.c_str()); + if (show_msg_name) text += QString(" %0 %1").arg(msgName(id), id); -void SeriesSelector::addSeries(const QString &id, const QString &msg_name, const QString &sig_name) { - QStringList data({id, msg_name, sig_name}); - for (int i = 0; i < chart_series->count(); ++i) { - if (chart_series->item(i)->data(Qt::UserRole).toStringList() == data) { - return; - } - } - QListWidgetItem *new_item = new QListWidgetItem(chart_series); - new_item->setData(Qt::UserRole, data); - chart_series->addItem(new_item); - QLabel *label = new QLabel(QString("%0 %1 %2").arg(data[2]).arg(data[1]).arg(data[0]), chart_series); + QLabel *label = new QLabel(text); label->setContentsMargins(5, 0, 5, 0); + auto new_item = new ListItem(id, sig, parent); new_item->setSizeHint(label->sizeHint()); - chart_series->setItemWidget(new_item, label); + parent->setItemWidget(new_item, label); } -QList SeriesSelector::series() { - QList ret; - for (int i = 0; i < chart_series->count(); ++i) { - ret.push_back(chart_series->item(i)->data(Qt::UserRole).toStringList()); - } +QList SeriesSelector::seletedItems() { + QList ret; + for (int i = 0; i < selected_list->count(); ++i) ret.push_back((ListItem *)selected_list->item(i)); return ret; } diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index 3e32dc069f..953026d935 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -28,7 +28,6 @@ class ChartView : public QChartView { public: ChartView(QWidget *parent = nullptr); void addSeries(const QString &msg_id, const Signal *sig); - void addSeries(const QList &series_list); void removeSeries(const QString &msg_id, const Signal *sig); bool hasSeries(const QString &msg_id, const Signal *sig) const; void updateSeries(const Signal *sig = nullptr, const std::vector *events = nullptr, bool clear = true); @@ -157,19 +156,24 @@ private: }; class SeriesSelector : public QDialog { - Q_OBJECT - public: - SeriesSelector(QWidget *parent); - void addSeries(const QString &id, const QString& msg_name, const QString &sig_name); - QList series(); + struct ListItem : public QListWidgetItem { + ListItem(const QString &msg_id, const Signal *sig, QListWidget *parent) : msg_id(msg_id), sig(sig), QListWidgetItem(parent) {} + QString msg_id; + const Signal *sig; + }; -private slots: - void msgSelected(int index); - void addSignal(QListWidgetItem *item); + SeriesSelector(QString title, QWidget *parent); + QList seletedItems(); + inline void addSelected(const QString &id, const Signal *sig) { addItemToList(selected_list, id, sig, true); } private: + void updateAvailableList(int index); + void addItemToList(QListWidget *parent, const QString id, const Signal *sig, bool show_msg_name = false); + void add(QListWidgetItem *item); + void remove(QListWidgetItem *item); + QComboBox *msgs_combo; - QListWidget *sig_list; - QListWidget *chart_series; + QListWidget *available_list; + QListWidget *selected_list; }; From d096ec1329dcf55bdda3125c76b6a89849892dc4 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 8 Feb 2023 15:18:27 -0800 Subject: [PATCH 330/484] bump opendbc (#27253) --- opendbc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendbc b/opendbc index 0b7439f551..d103b156b3 160000 --- a/opendbc +++ b/opendbc @@ -1 +1 @@ -Subproject commit 0b7439f551168f202fb033b7e3e177048687e726 +Subproject commit d103b156b36f2bc9d0d6ad9ba1af02a42ecc2b46 From 276586fdf051caa86feef1820e6732567d22c19c Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Thu, 9 Feb 2023 00:18:56 +0100 Subject: [PATCH 331/484] cabana: bit level change frequency highlighting (#27259) * cabana: bit level change highlighting * change default color --- tools/cabana/binaryview.cc | 25 ++++++++++++++++++++++++- tools/cabana/binaryview.h | 2 +- tools/cabana/streams/abstractstream.cc | 1 + tools/cabana/streams/abstractstream.h | 1 + tools/cabana/util.cc | 8 ++++++++ tools/cabana/util.h | 1 + 6 files changed, 36 insertions(+), 2 deletions(-) diff --git a/tools/cabana/binaryview.cc b/tools/cabana/binaryview.cc index ddad4cd446..a46c25bdfa 100644 --- a/tools/cabana/binaryview.cc +++ b/tools/cabana/binaryview.cc @@ -7,6 +7,8 @@ #include #include +#include + #include "tools/cabana/commands.h" #include "tools/cabana/streams/abstractstream.h" @@ -212,6 +214,19 @@ void BinaryViewModel::updateState() { for (int i = 0; i < binary.size(); ++i) { for (int j = 0; j < 8; ++j) { items[i * column_count + j].val = ((binary[i] >> (7 - j)) & 1) != 0 ? '1' : '0'; + + // Bit update frequency based highlighting + bool has_signal = items[i * column_count + j].sigs.size() > 0; + double offset = has_signal ? 50 : 0; + + double min_f = last_msg.bit_change_counts[i][7 - j] == 0 ? offset : offset + 25; + double max_f = 255.0; + + double factor = 0.25; + double scaler = max_f / log2(1.0 + factor); + + double alpha = std::clamp(offset + log2(1.0 + factor * (double)last_msg.bit_change_counts[i][7 - j] / (double)last_msg.count) * scaler, min_f, max_f); + items[i * column_count + j].bg_color.setAlpha(alpha); } hex[0] = toHex(binary[i] >> 4); hex[1] = toHex(binary[i] & 0xf); @@ -264,10 +279,18 @@ void BinaryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op painter->setPen(option.palette.color(QPalette::BrightText)); } else if (!item->sigs.isEmpty() && (!bin_view->selectionModel()->hasSelection() || !item->sigs.contains(bin_view->resize_sig))) { bool sig_hovered = item->sigs.contains(bin_view->hovered_sig); - painter->fillRect(option.rect, sig_hovered ? item->bg_color.darker(125) : item->bg_color); // 4/5x brightness + int min_alpha = item->sigs.contains(bin_view->hovered_sig) ? 255 : 50; + QColor bg = item->bg_color; + if (bg.alpha() < min_alpha) { + bg.setAlpha(min_alpha); + } + painter->fillRect(option.rect, sig_hovered ? bg.darker(125) : bg); // 4/5x brightness painter->setPen(sig_hovered ? option.palette.color(QPalette::BrightText) : Qt::black); + } else { + painter->fillRect(option.rect, item->bg_color); } + painter->drawText(option.rect, Qt::AlignCenter, item->val); if (item->is_msb || item->is_lsb) { painter->setFont(small_font); diff --git a/tools/cabana/binaryview.h b/tools/cabana/binaryview.h index f697ea28cb..d006672793 100644 --- a/tools/cabana/binaryview.h +++ b/tools/cabana/binaryview.h @@ -36,7 +36,7 @@ public: } struct Item { - QColor bg_color = QApplication::style()->standardPalette().color(QPalette::Base); + QColor bg_color = QColor(102, 86, 169, 0); bool is_msb = false; bool is_lsb = false; QString val = "-"; diff --git a/tools/cabana/streams/abstractstream.cc b/tools/cabana/streams/abstractstream.cc index 8a77e0eb39..94d653bcb0 100644 --- a/tools/cabana/streams/abstractstream.cc +++ b/tools/cabana/streams/abstractstream.cc @@ -34,6 +34,7 @@ bool AbstractStream::updateEvent(const Event *event) { change_trackers[id].compute(data.dat, data.ts, data.freq); data.colors = change_trackers[id].colors; data.last_change_t = change_trackers[id].last_change_t; + data.bit_change_counts = change_trackers[id].bit_change_counts; } double ts = millis_since_boot(); diff --git a/tools/cabana/streams/abstractstream.h b/tools/cabana/streams/abstractstream.h index 0dbb3d96a6..8c10d959cb 100644 --- a/tools/cabana/streams/abstractstream.h +++ b/tools/cabana/streams/abstractstream.h @@ -16,6 +16,7 @@ struct CanData { QByteArray dat; QVector colors; QVector last_change_t; + QVector> bit_change_counts; }; class AbstractStream : public QObject { diff --git a/tools/cabana/util.cc b/tools/cabana/util.cc index d978e4aa12..2806e175d7 100644 --- a/tools/cabana/util.cc +++ b/tools/cabana/util.cc @@ -18,6 +18,7 @@ void ChangeTracker::compute(const QByteArray &dat, double ts, uint32_t freq) { if (prev_dat.size() != dat.size()) { colors.resize(dat.size()); last_change_t.resize(dat.size()); + bit_change_counts.resize(dat.size()); std::fill(colors.begin(), colors.end(), QColor(0, 0, 0, 0)); std::fill(last_change_t.begin(), last_change_t.end(), ts); } else { @@ -39,6 +40,13 @@ void ChangeTracker::compute(const QByteArray &dat, double ts, uint32_t freq) { colors[i] = blend(colors[i], QColor(102, 86, 169, start_alpha / 2)); // Greyish/Blue } + // Track bit level changes + for (int bit = 0; bit < 8; bit++){ + if ((cur ^ last) & (1 << bit)) { + bit_change_counts[i][bit] += 1; + } + } + last_change_t[i] = ts; } else { // Fade out diff --git a/tools/cabana/util.h b/tools/cabana/util.h index af5032fd42..5d5b44d63c 100644 --- a/tools/cabana/util.h +++ b/tools/cabana/util.h @@ -17,6 +17,7 @@ public: QVector last_change_t; QVector colors; + QVector> bit_change_counts; private: const int periodic_threshold = 10; From ec6a55057ffe381fb65ba946e29cb59f0ee694e7 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 8 Feb 2023 17:51:53 -0800 Subject: [PATCH 332/484] Rename GM CS variable to be consistent to the CAN parsers --- selfdrive/car/gm/carcontroller.py | 2 +- selfdrive/car/gm/carstate.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/selfdrive/car/gm/carcontroller.py b/selfdrive/car/gm/carcontroller.py index affbcdc0fa..97f8b60e59 100644 --- a/selfdrive/car/gm/carcontroller.py +++ b/selfdrive/car/gm/carcontroller.py @@ -56,7 +56,7 @@ class CarController: # - on startup, first few msgs are blocked # - until we're in sync with camera so counters align when relay closes, preventing a fault. # openpilot can subtly drift, so this is activated throughout a drive to stay synced - out_of_sync = self.lka_steering_cmd_counter % 4 != (CS.camera_lka_steering_cmd_counter + 1) % 4 + out_of_sync = self.lka_steering_cmd_counter % 4 != (CS.cam_lka_steering_cmd_counter + 1) % 4 if not self.sent_lka_steering_cmd or out_of_sync: steer_step = self.params.STEER_STEP diff --git a/selfdrive/car/gm/carstate.py b/selfdrive/car/gm/carstate.py index 8ea8ea8874..b73b7e7059 100644 --- a/selfdrive/car/gm/carstate.py +++ b/selfdrive/car/gm/carstate.py @@ -19,7 +19,7 @@ class CarState(CarStateBase): self.shifter_values = can_define.dv["ECMPRDNL2"]["PRNDL2"] self.loopback_lka_steering_cmd_updated = False self.pt_lka_steering_cmd_counter = 0 - self.camera_lka_steering_cmd_counter = 0 + self.cam_lka_steering_cmd_counter = 0 self.buttons_counter = 0 def update(self, pt_cp, cam_cp, loopback_cp): @@ -35,7 +35,7 @@ class CarState(CarStateBase): self.loopback_lka_steering_cmd_updated = len(loopback_cp.vl_all["ASCMLKASteeringCmd"]["RollingCounter"]) > 0 if self.CP.networkLocation == NetworkLocation.fwdCamera: self.pt_lka_steering_cmd_counter = pt_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"] - self.camera_lka_steering_cmd_counter = cam_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"] + self.cam_lka_steering_cmd_counter = cam_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"] ret.wheelSpeeds = self.get_wheel_speeds( pt_cp.vl["EBCMWheelSpdFront"]["FLWheelSpd"], From 134766fe26443b5f2dca8e9df274229894028035 Mon Sep 17 00:00:00 2001 From: eFini Date: Thu, 9 Feb 2023 10:05:23 +0800 Subject: [PATCH 333/484] Toyota: Car Port for Toyota C-HR 2021 (TSS2) (#27212) * Toyota C-HR 2021 TSS2 Port * remove --------- Co-authored-by: Shane Smiskol --- docs/CARS.md | 5 +++-- selfdrive/car/tests/routes.py | 1 + selfdrive/car/toyota/interface.py | 2 +- selfdrive/car/toyota/values.py | 26 +++++++++++++++++++++++--- 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index c48698b84f..b73f09334d 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,7 +4,7 @@ A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system. -# 233 Supported Cars +# 234 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness|Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| @@ -174,7 +174,8 @@ A supported vehicle is one that just works when you install a comma three. All s |Toyota|Avalon 2022|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| |Toyota|Avalon Hybrid 2019-21|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| |Toyota|Avalon Hybrid 2022|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| -|Toyota|C-HR 2017-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|C-HR 2017-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|C-HR 2021|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| |Toyota|C-HR Hybrid 2017-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| |Toyota|Camry 2018-20|All|Stock|0 mph[6](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| |Toyota|Camry 2021-22|All|openpilot|0 mph[6](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| diff --git a/selfdrive/car/tests/routes.py b/selfdrive/car/tests/routes.py index ce38eeb0de..535d3f5415 100644 --- a/selfdrive/car/tests/routes.py +++ b/selfdrive/car/tests/routes.py @@ -179,6 +179,7 @@ routes = [ CarTestRoute("0a0de17a1e6a2d15|2020-09-21--21-24-41", TOYOTA.PRIUS_TSS2), CarTestRoute("9b36accae406390e|2021-03-30--10-41-38", TOYOTA.MIRAI), CarTestRoute("cd9cff4b0b26c435|2021-05-13--15-12-39", TOYOTA.CHR), + CarTestRoute("ea8fbe72b96a185c|2023-02-08--15-11-46", TOYOTA.CHR_TSS2), CarTestRoute("57858ede0369a261|2021-05-18--20-34-20", TOYOTA.CHRH), CarTestRoute("14623aae37e549f3|2021-10-24--01-20-49", TOYOTA.PRIUS_V), diff --git a/selfdrive/car/toyota/interface.py b/selfdrive/car/toyota/interface.py index 8e180e2301..438a9c11fc 100644 --- a/selfdrive/car/toyota/interface.py +++ b/selfdrive/car/toyota/interface.py @@ -72,7 +72,7 @@ class CarInterface(CarInterfaceBase): tire_stiffness_factor = 0.5533 ret.mass = 4481. * CV.LB_TO_KG + STD_CARGO_KG # mean between min and max - elif candidate in (CAR.CHR, CAR.CHRH): + elif candidate in (CAR.CHR, CAR.CHRH, CAR.CHR_TSS2): stop_and_go = True ret.wheelbase = 2.63906 ret.steerRatio = 13.6 diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index 3046814fac..7e801990a7 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -49,6 +49,7 @@ class CAR: CAMRY_TSS2 = "TOYOTA CAMRY 2021" # TSS 2.5 CAMRYH_TSS2 = "TOYOTA CAMRY HYBRID 2021" CHR = "TOYOTA C-HR 2018" + CHR_TSS2 = "TOYOTA C-HR 2021" CHRH = "TOYOTA C-HR HYBRID 2018" COROLLA = "TOYOTA COROLLA 2017" COROLLA_TSS2 = "TOYOTA COROLLA TSS2 2019" @@ -115,7 +116,8 @@ CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = { CAR.CAMRYH: ToyotaCarInfo("Toyota Camry Hybrid 2018-20", video_link="https://www.youtube.com/watch?v=Q2DYY0AWKgk"), CAR.CAMRY_TSS2: ToyotaCarInfo("Toyota Camry 2021-22", footnotes=[Footnote.CAMRY]), CAR.CAMRYH_TSS2: ToyotaCarInfo("Toyota Camry Hybrid 2021-23"), - CAR.CHR: ToyotaCarInfo("Toyota C-HR 2017-21"), + CAR.CHR: ToyotaCarInfo("Toyota C-HR 2017-20"), + CAR.CHR_TSS2: ToyotaCarInfo("Toyota C-HR 2021"), CAR.CHRH: ToyotaCarInfo("Toyota C-HR Hybrid 2017-19"), CAR.COROLLA: ToyotaCarInfo("Toyota Corolla 2017-19"), CAR.COROLLA_TSS2: [ @@ -633,6 +635,23 @@ FW_VERSIONS = { b'8646FF407000 ', ], }, + CAR.CHR_TSS2: { + (Ecu.abs, 0x7b0, None): [ + b'F1526F4270\x00\x00\x00\x00\x00\x00', + ], + (Ecu.eps, 0x7a1, None): [ + b'8965B10091\x00\x00\x00\x00\x00\x00', + ], + (Ecu.engine, 0x700, None): [ + b'\x0189663F459000\x00\x00\x00\x00', + ], + (Ecu.fwdRadar, 0x750, 0xf): [ + b'\x018821FF410300\x00\x00\x00\x00', + ], + (Ecu.fwdCamera, 0x750, 0x6d): [ + b'\x028646FF411100\x00\x00\x00\x008646GF409000\x00\x00\x00\x00', + ], + }, CAR.CHRH: { (Ecu.engine, 0x700, None): [ b'\x0289663F405100\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', @@ -2030,6 +2049,7 @@ DBC = { CAR.LEXUS_RX_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.LEXUS_RXH_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.CHR: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'), + CAR.CHR_TSS2: dbc_dict('toyota_nodsu_pt_generated', None), CAR.CHRH: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'), CAR.CAMRY: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'), CAR.CAMRYH: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'), @@ -2072,7 +2092,7 @@ EPS_SCALE = defaultdict(lambda: 73, {CAR.PRIUS: 66, CAR.COROLLA: 88, CAR.LEXUS_I # Toyota/Lexus Safety Sense 2.0 and 2.5 TSS2_CAR = {CAR.RAV4_TSS2, CAR.RAV4_TSS2_2022, CAR.COROLLA_TSS2, CAR.COROLLAH_TSS2, CAR.LEXUS_ES_TSS2, CAR.LEXUS_ESH_TSS2, CAR.RAV4H_TSS2, CAR.RAV4H_TSS2_2022, CAR.LEXUS_RX_TSS2, CAR.LEXUS_RXH_TSS2, CAR.HIGHLANDER_TSS2, CAR.HIGHLANDERH_TSS2, CAR.PRIUS_TSS2, CAR.CAMRY_TSS2, CAR.CAMRYH_TSS2, - CAR.MIRAI, CAR.LEXUS_NX_TSS2, CAR.LEXUS_NXH_TSS2, CAR.ALPHARD_TSS2, CAR.AVALON_TSS2, CAR.AVALONH_TSS2, CAR.ALPHARDH_TSS2} + CAR.MIRAI, CAR.LEXUS_NX_TSS2, CAR.LEXUS_NXH_TSS2, CAR.ALPHARD_TSS2, CAR.AVALON_TSS2, CAR.AVALONH_TSS2, CAR.ALPHARDH_TSS2, CAR.CHR_TSS2} NO_DSU_CAR = TSS2_CAR | {CAR.CHR, CAR.CHRH, CAR.CAMRY, CAR.CAMRYH} @@ -2080,7 +2100,7 @@ NO_DSU_CAR = TSS2_CAR | {CAR.CHR, CAR.CHRH, CAR.CAMRY, CAR.CAMRYH} UNSUPPORTED_DSU_CAR = {CAR.LEXUS_IS, CAR.LEXUS_RC} # these cars have a radar which sends ACC messages instead of the camera -RADAR_ACC_CAR = {CAR.RAV4H_TSS2_2022, CAR.RAV4_TSS2_2022} +RADAR_ACC_CAR = {CAR.RAV4H_TSS2_2022, CAR.RAV4_TSS2_2022, CAR.CHR_TSS2} EV_HYBRID_CAR = {CAR.AVALONH_2019, CAR.AVALONH_TSS2, CAR.CAMRYH, CAR.CAMRYH_TSS2, CAR.CHRH, CAR.COROLLAH_TSS2, CAR.HIGHLANDERH, CAR.HIGHLANDERH_TSS2, CAR.PRIUS, CAR.PRIUS_V, CAR.RAV4H, CAR.RAV4H_TSS2, CAR.RAV4H_TSS2_2022, CAR.LEXUS_CTH, CAR.MIRAI, CAR.LEXUS_ESH, CAR.LEXUS_ESH_TSS2, CAR.LEXUS_NXH, CAR.LEXUS_RXH, From ccfa73ff12da9ef5d2d939fb9018d9103128d299 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 8 Feb 2023 18:33:19 -0800 Subject: [PATCH 334/484] Remove Bolt EV platform from torque params Was merged into EUV --- selfdrive/car/torque_data/override.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/selfdrive/car/torque_data/override.yaml b/selfdrive/car/torque_data/override.yaml index 0a49d39914..462ce91b36 100644 --- a/selfdrive/car/torque_data/override.yaml +++ b/selfdrive/car/torque_data/override.yaml @@ -26,7 +26,6 @@ COMMA BODY: [.nan, 1000, .nan] RAM 1500 5TH GEN: [2.0, 2.0, 0.0] RAM HD 5TH GEN: [1.4, 1.4, 0.0] SUBARU OUTBACK 6TH GEN: [2.3, 2.3, 0.11] -CHEVROLET BOLT EV 2022: [2.0, 2.0, 0.05] CHEVROLET BOLT EUV 2022: [2.0, 2.0, 0.05] CHEVROLET SILVERADO 1500 2020: [1.9, 1.9, 0.112] CHEVROLET EQUINOX 2019: [2.0, 2.0, 0.05] From 955c1dff454babe200862f0011e639ec500938b3 Mon Sep 17 00:00:00 2001 From: ZwX1616 Date: Wed, 8 Feb 2023 19:06:59 -0800 Subject: [PATCH 335/484] ui: dmoji (#27070) * will draw * gradient * bg * add sense of depth * refactor * cleanup * fix endpoint * clean up * use array * 1 matmul * lives in modeldata * standstill only * remove bad pts * do less math * smooth * kind * fix d * lineswidth * fade in and out * hide * quick * gray out * fix joints * offset * rest icon * clean up * circle as state * longlat * new design * gap and g * scaledown, fix jts and unlock not standstill * width * different * intermediate * sand * motion effect * vis * fix bg * clean up * t * btn_size * small speedup * sligh * clean up * facelift * match .2 * .4 is good * size * no ss * rhd compat * less filt * more line * engaged * not white * drop prop * this is fine * independent falg * sg * dont look like hitman * cleanup * little more --------- Co-authored-by: Adeeb Shihadeh --- selfdrive/assets/img_driver_face.png | Bin 24112 -> 19875 bytes selfdrive/ui/qt/onroad.cc | 62 +++++++++++++++++++++++---- selfdrive/ui/qt/onroad.h | 2 + selfdrive/ui/ui.cc | 35 ++++++++++++++- selfdrive/ui/ui.h | 18 ++++++++ 5 files changed, 108 insertions(+), 9 deletions(-) diff --git a/selfdrive/assets/img_driver_face.png b/selfdrive/assets/img_driver_face.png index ddde478cd789ec47ae87593d8d413336de653218..03765a0376dc33e72f88fb68d0f3879bffff90ff 100644 GIT binary patch literal 19875 zcmV)BK*PU@P) zaB^>EX>4U6ba`-PAZ2)IW&i+q+O559(&RdhrTJg0=p~4x__Z90q|9aqv;27;dPG)c zRaRHG*<|Fs@m>#ixkLg7=YRm>+W+tW^IiYzfBmoU+`U{+uC4S^ZvOZ5r^mq$?f(6* zpMQt@IsZQYo9oY?g@1kh`o|wcUP}C)e*am=pZ5=5&;Rj(zuytwe|`OGfBq!4KNtG* z2Y)Y^{N~6H>z{9u*W>T?@cg+@-@hM9Z}p#V^^JcX<t^D~X)pO~8T*Uvr{`rNc zvPvuSt`y@=A>HTS?RN*se^MIfcmG{|-}#w8yZ9}nzjwld{LAD0{r$fGd4c|Ak^la5 z{pZ#H$4~!h@#FaOdiqamS^lgMKm6N22>IVH{#oLG`r`DDJBt7M2dk(2UmoYrKi{|7 z_wC=)ji<1p<&Q<(*WcCgGraL-$ilZ=xF?&l?s>obnxTitu6$Q` z!U`jNVV~b;nB3wMcVtguj5DV5T4RrToa_}_xiw%8J{Kvh7^S%9i z-wcg!UV*p9z{SGZ?EmK1^}qYW|C?Xe_f3~5-0dH0#eGFBm)mgX{5S7nL&EoG-||`D zzrX%$|NT$)gQ>EE`7Cqe0muFRF7f8@AGVdwJ}2In`Tge=;i~J;1y~}!J99H3u>*Gr zDTfm78e!+%oa5pq~L+6Vm~|Yvg>ZU@9|-4U;3Uef90!R`}#Njv1@;J^}qc3zu2|#XV>D{Dc@KB zv1`0~)%y1mK{(0o89NrAz>ZgU0E3S1+18+A_|~vtvImbWQJs_wnbujVm_Z zvH2Ot1kcKz-^_KV`+Zj2-~LAA=1QG!>b1ms_6A1gucnXqb$=;uKr^lDV?jsaE_Qg^ z<{Q>x%~s<%OKJBLS=vcR0-GKd_};m-{e(0(Pkld&Z+N-!aK$z9kiyhPH+0c;Qj-9^v%V)#`iodHo#V5s2dmz^BwmFoo2ri+cpazfHB9u z!2G;+uayUB>wE6+&I{arvcYBwDKDN!`>sW)$aGLO`OD>Hv5*# zlUaicOoVG?q7k30YsdI;VSF{j_YL)X6L(&?Mg|r!|HR90XlwB(8cyD0=Dymsb{Q#s zJ>Vg4T63-z*FN`IG2?;F)D_t^o|WuXZI9t9k zpT(zgdibJu&lEG}GxJ;f1TZkrG6~tEE?{M0ChU5u`@LD-c&~!BtPka=OQIwS{o#Xb3RRz2p5T`ka&D?$&(Ojnp6 z_=XdX);`Z32ztaIdbj0%bs zHrCyFuvW^uu+rVqcP{wV4XPLZeZA;#n6wpXbMQGQky- zeoM!V14s$p%z;MvD(i%+-de8K#}yLZ41j2SA7nBBBbD1WG_Kw;JE9Pn!uo!&AY(oF z11Ro-k>S{26ON4yV$(P_IPv@(d*usAe&^mFpb7r67sRJo_-Nd0UGKybya~V>UnWlB zfcLlOHtKL=X|fw@ueq=OrqnZ56ieD=reM6NY;WQY@4oXp+znJ1fBdkAFL7O;hPC9c zJzRt}+Up|F?^SPbDCHp$p3fjIY1=IW2-wBKRURD`I4?Nb_*!6{f$)J5mM?-$&sBIM zW>L7{l&^oi9yrfHQQwfUy}lg*-BRD0&rm*?3HVk%7CRcEp0x_d z)}95@c)zuWjF|04TWK)138soNmy!kJCI)Q)0~=m=2zJ)q`Vh`5Yt8evcVBCeORQnP^@51tNWeM! z&+Qxapnq>1Z6B!z!GutCmU}|U+R&gzUu!DoB+-d4{KVlq-jET zvgEQTR58Z4v1NH0QwZLnfJY4|%o8z)9j*gy&3k_#iNFN$^JN>6HT7nnLM-qEuJnfr zVNJUcTP5sl7(mm0?({%K6L0%Ky6igC1pvKRkhGyXhK^zas0#ofV2l(LZN!|fM zB<}uB^ZK|52o5gjH98@vzB(&JgorTM)^d$}>kA4ekQO84vB>T zSfH`$2Ju0p$+*SFm5J70#2Ma-Q-S>tD-OFtsK9veZ0<3Gwa|w5+E43`-0H=Mp~~&v zs}XfpZK1Xf96_wX0wH^&UEi;dazl>bJ8(8A1Bi@_fM{u;o8@x1->)_-8T%Imv2LOc z23PWl38}JQ2@=2az`2Q@gjrM#&Yv#!i|v+Cf9w)o4T(vm^`Qt6wuzYoe#34AYF%n;hM<~s{FC30t-tvwDVZe7foJYOx)d&B)ISCAK$S3^W zkh|dQy|^J8{z62$2F6T`S_traH$*xZ(dX^voM>-&VWH862nx0&FD!@~#^N^axC^Rv z&Zl0n!nb$|JV~a-<|7IxP=i%1&>k7`p_ATTpcRz>w}=xERTMrHp+k&$Ok5RTVG3Xj>}?okHwYl_6H5Ou)qz~ck-iG|hrv!aHQgP{Hfultf@TG* z^5&+H!$u|86%h9wlfWC})?oU3W#spZawzM0N5dH(^H*>AO(<36eGc=m~xB{8jbb3 zfsOl8(X)p2+%!IDVu1KapqU5j3R(jw=29yBhj%}iCUCS-oaR1J4pDlH`=BsCF9r4d zMPz`WiGt8*?$xliLD*L)W@d5Ba{EZ21PTJ6MkubdF8BvGA^CtoK%M2hfC<60ePy8kV~#5#0I`8) z5(Afp510i7l|e<^6PLmu2M*7TjPZP$aV=n;d4j<6H=rMFhGY!W$o{w$3%2vZL2q%Q zJ~u>yaCspV6VKLaeWZ^G&QFBFDE33R!2_To@cxbLf)ebw%qA4T-~mUc6{jv zYV0+F=zFFL9rv!HM%_aK4u<<4q7gdZ6A4qSTtl`G6bd~jNAhD2Y!6gktA%G)^Iq6Y z$2=hl1AYU=#R5V`y3GAtnlxNw{)90BG5v-3*E?i~CsZm)BHBpvNr4B%2J`VNa06_@ z@b<-4-``LogI(Yu7|M!2Y8ME}oZu^wkd5fgFbvms0SNcsyV*wJ0z9wnVkrpyr9ivP z`pFXETgd2082XSqSPn|KB5R3WbA#ZRR7^PRV0XZHu0;St)ZkGsdh?EWIwW&6w1;+P zByh>Nxp#XBf~7HGoXjFDB_m^?7-&US^^#R0rn(HDzyN~a#0gt~941tZ1e6G}Dtdwo zgaU}&(4z=r<+q&FEC94b3ptT6*1mpG6xzpkJ|Ou9p^WWr&|tCN!d}8D4u%m0@8;8* z_3dmwK^8zoiQw~zr{YnF6Zi!|MjZ*iGq|xO&d-Kus39goefjma(&xH^c|S zB5J%c(H9@!o{2;()!t_Jqe45t)-E&;(W$a_v;KAq&Q5d;<8|dSi-Qmxh>DY0ReuB5 zL|)eISQ0pBdh#O3gHb#&Ot5T}d&Hll8w3TI9R64FFSty>kLv?h1>w2d=@uag0X?~3 zAoiF2@N)%6Baw1J$7Rqj%oI5UQ3HJ+Q|apbjDGOc3tGl1o0~L;841PLi!6BMGXs=v z01LQ-`aJAupA^jw2va<+)CV-sOIB_;FS=)Rh>xc+DO})*nWLwnxeSyzwn->WrbGGx zXN>iU3kZh_o4#b5J5Io#e{ou)6k$>0$J4K4K{yk^V>z-a0W%AL*DOlUx8k_fi|0)``Ca0UwHrl~eU z?yAp)ikp9M@J14qB%fmU(5Ef;4G-ow6T)VPHO-B3U)uMAv=PszDt|p!5x@)frpV*A z5=;2JMC#gWG=@HAH9V23E~Rl4M#K6geD5hlcIDxymvn95{fkHsyY$PdNxJ}aJRp6x zDj;RBmc449DKE5ZOv9V!bNwZH-eyL*fTUlG#Uv3+DA0$<2X1W-hbnKtX9;3#G*84m z;3uz%laB1%0|4Od7qJF*5bDXYa5*+LAASP(a|>n!u;$q0`Hzf%I7ejqB8K7l2X_SM z?05|n8BnL7-^z3J{5_(f5qg0&-W~uz`)cJIE7296zytomI->W(YI)S}V$XTp4kUVl z6pp=mko5)_Hcmo}t}OXtDF`Gy4$twJ^6ZEI&Vk^nM2(9JRD~xV`w=ET$V;pb?0s;j zAo7;vMU|}#0~Bn~h?@`u7LC{RY|V*XxO|`jeh~|Z*vqV5E4O456GuD>gYEcR?0o$7 zEBR*0GjSA&45>wUV@H4**bQ(Pe+U}cTb`dO2@~3K)B;Mu4%0knKUtiB`8+pw+~m0^ z)#u{#xqX@{JBKDB9d+*wjF1509dg4p!SIF?N|Y&_9m zjNC*J!)6g5A0b-Z{{0eUY<50k4ATKW@Q_0i30zrq#J3V=H{p_CA5F=yOX0&=z$-IU z>zJYcVFJ)YD@$L9SV&(5COVfW(#)6Fj5vBQDNn#G!`K5gkK&J_0+#mPo}5j^zzj%_ z{yQ|oT)b~l>e?ONzEP^s7R2Vgvtf7=$BxJ|DUWZBb5h5bXLIGB`W6!3jyS=8OI=sr zC7rD0(NuY$KyjZ0q@!KD26y5)v96=a(1t|4} zs2@kn>REhst{=;;I0+ACbz8hd2saIpO@y_3%L4M1*)xx24B!u|{VgOU$m$_q>XisL zJQVNzDb$%e2g3s^D3C@M!hSKno0eRl+<$%KxWIW3wtT&uk!JR!&6C0o! zEcVwXMuve5$cP6qFuDX+!myx`t~+rU{VX??m%J`0q;=+ac;E|7gg{l)QJ>=^y>Cy+ zO&c^5!pB>14|GV9YT;3y6$KE5n2($FY)*tPAXww?#o_;f=SdqZr-Dc-*Uvl=kN0Bc5hkh!V97= zS(Cgy2Eys^bGE>wFl33vW$|L?Q#a%rtZk=UBY%^J3D-8}4CLi08eof204!lo0w6jU z=3OVT3aJ8Pipf+5RQV2oCt^wzXn_5?wlxDR*xLsrx@@GYnhqNPWGDCe5e#+1bN1tb znxYQLd{+Ntv(uMwkynp|5_-%E*{cx3VMa_z`RCO87X*pco?9YtYa>*$)6ZvKp_n9@ z$ z!{Pu{Gb{Hw$M*!EHG9v)sNd>>>`2E!k*;&?<+1wh_VW|*qj=o>D?%_U4X(h6zN73| zIac|DlV)PULU$t*K*487BC+~6rYqj(XqZ6o5gAt6?Zr@Eg3sWCPxm0_Bl30ZUU+;# z+MLZcTh2+;9|RX7C*rtE7cjeLb?e|v%@Kc=n}*hVq~tAd$#c}>*cD^PJjwZjEK5(K$Ht{TO<~dCb zdmt=kJk($z#R@lZR^aJKPp_22TJ@aP@ZKiQR00 zc@;bwLJOhl%6)iRBINKyEEJ$IJeZ7Tlo5p|p&W0bd^0hz(IOR1fo(%=*VuR(TGwcK zj^y8#%kv=iLI)gII!A63f63;gkU0dEWmW~oMv%P+YiU-wkBZlsHOv4sg3^dK_kwqI zOHvN8wUnZ88G9(d0R*AzFT;JNU%q!dNP%+4N#MWX|$MuZ02ZFnbkpK-Sp(L(ZI(}$b8zBf32+0%% zWX>egQ{w}5YDqecDImyRkBU)U-+!V>*AHL_Y&UVRwAVwFYOY4o;PIJG0@0gRSfS>D zk6Eo(v@oT9SnA2E;(qec5GVgZC=MT#Z}o#DE&fkPa{Z?l03wiNz8@hXW~)CuC+0QcJhpp| z)bRF@EJOLuiO_NO?D-ld_h}yh;`6+}yzmV-`8W{6U2I7=`VlZCrM!gk<#_d5~}Z!yDT!Rd)0;7sOj)X?EFU?<34lyCCMS7KiQ@!%=n zsgRHdo4~k>>3$;nXb>F23{(kf^u<$uR3$$i9^onfbTs_0=FaN=_aXe267-AhEe$qb zd?c;T`o=OQw}eHrnp#|>tF-AX(Qq-J7E65-s}31H%NzR%`AMZc{X!uie~jV zA$<@fbH)fCZPk*8pxVkEc=U*<+}K#VFJxePzmq$KTmEa5{GWagMDV{}di`5B64e>7JBZBVtF*W6JbbqgxIOM54fYD9zJSKCk8Erfkau`2m9mdH* zycTz_YH_EBZ}Mf2@l5Q1*%<1cn@Er5wwWTflHf|^)Z9Rl65Q#DFp0B&9XS3^TiY#} zj2HI%1^p{LfNnXG^|ejFbo==%2;dfZ6p0rEq&|@rQU_0lZg-#nW0FM7VJnlRfE!h3 z+8djH>a0**bB6dE;6(@+B}EqYw&Xe7fHt8te!K!*nO>-vxW{dmxyV!q5w9T`8^)rL zgAnIZiIPD`I7bwd#XuQI5ItGeyRU|wj)(hgvd4%U1y2=bHlNhb4=P*$bA$)hQsFtvPH zO1W&WdHy`@9RD5T_{OPnD?<695k(sD{R;W6h3?41)0P`^^jsYVY0(gI*TSU;qMhKtUM<6ohvSI?3XDz7J8@`HtJJ8C9cyA15exbiS_lXP#pDl zM^J8*+OgJnZe8~BlQ{YJ6{Z*kc1H|)%yu$WMCF%_-^AEsJ^B4>9M^xg5=-&#M>x;rCxXGobH@;_Nen@vd=*w| z4$Q*XQ|=V=Ws2hzF&QKMSbHtw^qADt|KVhJ`Jc7u^{*TJ5{M(J`ZHgCa@P|U8~C;m za723>mesmb>p}|?{6r2Y`PGDu@W=Qv@#I+bXC2*V*9RWD?NNZX=@;c>&`m#?r}C}| zBS?~XJyYWUL{?;57&AQ}2)encwa@TzRL1+?Bt;=o2FY^$ip~3p66;r&?y&|@A}OKe z4Q;Xb!gVl!g6RD_VjOc~(X)~A804?Ee}J@=xuO)iEG)0&4KmK<*xNAC5) zNN?;GOAWbXo1u83bAoRJK>5`W0r2=A&uI^x>~%i`)_bEmS^V#XrC!x;p!|Hq`_~id zVYc->7zt^BD)9DE*~*T^LmdXj{&G(eG!|;z?kXhjl2Yz;Ds?2s5k-ps!Xc%H9{zl< zA>^bso0TMluBpfz#aPKMyXrywmq~rk|LFnrrY{cEDUZpOsB9t+uZIs1iCW(iO6+o zCsCRqqKG}asCPo^Kh;{nei~bna)12o67XQ$;=TfJc#f}~4wkzUz%1ko8`KCN&{&5B ze+a9Uw1odA)&H-W)*rH{9;e5qf)2rDXBMX8J{UdgzGH?84c6_pNvV7IfRp~p#LTdp zKC8r(9`?Ofu$&LO$hI|@oWYi(g_m>i+%`@q&<}Zfg7g~&zb#yV`Bxi_5)e;Z>95@5 zHOWmtwP16cdWE4?Ta9m36@(x1w(|q>5R3@tvn`<3HZMHG!%d4u&8oJ~ZU8GMA9w&p z{PG3+j@t1!N6@xtoHFw7542wY5|P=Odv-f)>is+j(TpUCSnJ*vNuC51>J~8|?D?-y z*Wf8rxV-R(CcuLft?1HDtT@YoL>yKNU}*8^4VZAT|72irw;>0OYJVdBw=Sr3A%L5d zVFgX0cl(2u{SJU6YRyz%!)5K2c(YZjv6<9|Mz{XhKT&FcM7#mZwTcQnYPb?wbhK)z zK6ltz`$Ie?2pV{_#{-NyCj=wSK3Qc$lWUabSL|`fCtro(LmmpY%u7h5usx=K`_rOA z3`jyVSwS!}7;#GF?*+CD|Z3w^93z}YW+G2lMePi-ii%2;nupf-9cc+1|tVRU#kTwjsBeKrG#^L+dN8AH{9CaYS28 zO2l6i!QmoYZfN8UEpwZLV9zXlvZF(v1?Ru`KqBnA^^W2Hga3a6UTTi>-vVKl%-wcv zlvBE}J^z9=d>4`*g=HMP&CN;KAa63}vpse(d?J2ejb3bbty+mHx-!9u0HPkVgQcH* z65%$bc_aq$aWqIZ-bz?x4~6ZeZb^#}86 zWtxe7tTThLcN>k;{g+_tekYs%&hoA8OZN9S z+kV(MnAgW`{mw~`?03>d=Qab&Q7`q4W# z;3-wyr+dQMPj*hkTBkDN3d*K0?uLui{94dx*(`xDbem($Cn4CTZE~epTnGsogNR$g zsdLTG$%S5^N6WAumkH9J<{Ry}B^nkBF$sq7l%DLP7#5Y<@6tVdt1L?X9ib58En+SU zq&9;Mj=vBWds%VRWqQd<&GFQ6;->}Li#UC3)g&9%_u;*-M;BMN0slUS#g8y==`)yO z4QB8}nQ-HYZ2Nk$72P=*C^vQjfz)5S!L9CuXq`+w%)A3r#DNZzXH9rU>z;~-P4Lbt zrCbEHSZrP98H~kTkq0R-e{;3J#LKsC*zC!V#eY6U!jHGccv|eBu+!?_`mkY*XifOD zOu=GLMNKxjVUeHZll)0xrxas7jefioA$RIxZ5BUuvCv5yi^?PNfM#|OUIHZs*c$@5 zHn?mK){V1MaF{YoDx)5%f$IK>SdPzN(7z5gi`UR)Y}nQI2W5eaJTFFP^P08}0$Fba zv0hHJNxEjVu)BP>wM^V~#ZS6rZ}uilFxyiq5Nh}#@B|BLZdiFt{MwFW^WA3{dQQO< zAPZ~5ax2eEgpMeiSi*Jo+lovOdKSLN446_#|EvZM7IIss;`^}xP8K)VawNKn8&I7UUIoKz~qxEQ!28g{a7~U2&oetbn zik>ZNe!`A7KpM;f!Ds5x+UO1F#b<6GNnz0-Bm=K1GAH)Ql({FzES7b~Mgu$oupp8) zs}&H%T(R3=cth`Fx0&F+RLUd#8vH`B8|ha7JIaQaL2KcWl;xa6xSgNYaz{^W2rKn>mG5ZUzgKa5&RGBG z1F!5kc&$RMr@{=THRK6RemhN^ z_k(rvO+9BsjhR8HHzEdP4tC3@f-5CEqa@#VyiW{42zXQieCTzsgwMX6+f&EHdR<3? zCbL>*4b15jh}^>bdk7vRi}~E`cS3d&|FDr4MD1)#v?Z+*-qy7(o2`=i1ebx~Rx84K zv`tF|iX-LcRf7P=fsZarvMhSR{vnVB0)Ujv7sg2JB9L^MIa?fmg`oicgKeJEq|eh8 z%yB@-?q*RG!QlCo78wRX%6MUah@6U>1AqzJM4*D6_F`eeD9T*B!qNPa^?rnu7v{2& zd`^3SKs*yn0*}zMrwS_Ys0J+T1|Odg40DOB&mhLC0J7Z_!M0j^U_w%GzzZ#y1AZ^N zzzY9qBa^T#_rN~F_>Xj1)a7i7Er2SJT&zzgoTIREN;gW<=^h7Sn&UW3reIX#oEAWQ zABSs*%NC~+qPS^Tl8IBM|sj;+xWOe)rP-lTZIxq5i#4A2=Wm4 zV18yNT2hwUx59NB4xnV-cQS-&45&o3@SYcBb)BsOpkDMpC>0RHaQrVLec5B;v`MC5 zC0uf~_qKV?X*iNdHckP3J)-kmAP_u-csf}79pHKn)p!hkj-6luL06M5tytm!OOSF{ z#m5q&;@V}u6f~k#dlZr-k3B_T`*aze{%o_7k?VvrL8mE>u=adPD(AM7(+0!KKIdB>59tXVX_@I#2SDy654Eqxh;(*00I)WIG6p4EG2j@79K3UO*X2Q zY%wJ?u3_-0{;F*)C74dF0t#NcRqfQ{h8{@(B*EG@WQpSevQ-hc=`?G(cSIdIX#=(S z$mmZ*#L~?3(~g$R_HV z2G?3FUW02r{!~+Z%kW7UMO67~TXH^4(J5?7`~; zH1aOnf=%EN$y|3l(&w?XJap@zhV^z6wLi**i~YtNK?$=}IQ@(VV$1ol5nDnH2up(N zPoo6f!yYtO={x<}xWwpS_UQ$$%Fh`4Ik0LDtGKJJh5)do8Slxz13{J?dm8)Mr27%& z#&0lAD}hcS8;$Cc-eD--?m?_MXk3UWY zu;R!{sTq~3M-)1-Z;V5Ab4kK&2I zbBqvox8u~2XMYYZCn0LRUpnPQ(OPYu32bJ~Ev~TL0Bf{6xY(Hl19v>xT$WQg5-XQsp-y+;&22Se zI0R<-9q;K*YrfRjZylcKGi`XfNl!iIYK??3yZ0Co`nMHC=c zW5gnfG?qW!@HuCSbWFJY#v_=k9|@;D=YL0f;5?31$iuNNtgu}YENJbHIx!7VZEUdp zHT*#3v~e`snSB!Y?lPxW0M2$qj9?W0; zQLtWPTN1nPwuMegS@;eO9nt+a?@iV*$G9q+i|(UK@_c&+JIGqYyOm zR0F~W3xM$kFmhc3hf9zMk4GH0*qzG4wJ@P%;~xaY%2oub!v`+HoBV^je%iIZ+J4&^cCD&`aM zLUy`$?GdvfIBkz5^RKEkIS_#RfsLedy)9pU)tT82XT*Ob0ut2iJF?vZ7q(2*U97ft zd0r2QJD)|L*sPJqseo|zIG{S#r2h((5dc#-m4ZhL>!MvFXs>)23`g)F4ct(k_B1sjg;sJ%_3UO=}iU8we}gG#TjkIq&J@f;>C==V)!e(GP)%kr~U$>r|4NbUwP08d6_2b#RsE1f4$C_~_- z1&GB_Af@d)qkoa?<{^JoYPtjB$P|jr0T51L7cuv|5O(J-C?I14pTms9TYuziS$5J+ zS%l{yR{0W(i#236mE4Dro0)hKF&AXE5jSoJxAY8YiO|c_UuGvfss?doQG<sTY2f}9v$n-|o^FAm*KHMobWSNag0Fys1<>1%dI92pq;@As2|^(@_#);aL~ z-;@So5Y{a%#f!@Cx6<)(Rw|c(^`n*G_cpd(0CPCiB>C4j6DncUsP0iE7H&YvF#oq^ z`<_rZ@g}}?`+xE6=72X(!dUElDuDp*d3d%|aozgf=YUh^Ld_A8Me#k;>?+Vb9ON?J zzjMYMGj;*Fz4kL_Y>eP2m1^e$T89VIP3V+J@@Yt0{d0%t2p9+DG zX6N+4UtSo8Q@2z(iIj;A~w z{@+Ohk6)*ZlZvcE>($943q{Yn?MaQMfuJ=`jkNOob!f?w3ph^jY4Oak>wv_@%AV~| z8qdj3Kag+f&T>v)ZrC`!rg}q5C$lt;*)+Jhw^^4m`#CG5YyTOq=s+bKh=HgP&cF~G zatT=%JanP~h_jDJdQgp1{_2zo8$2z!16!QxI$N>y>M@{$7Y??Bh53G9YFu~~H1H%= zoqKcIqm?%fs=}&xflmXU&R|u&wO(L%_rQ_3F9#o-)1Y*niVc{tQ8+so6kQCTmL5pZ zG4N%2<8%JVY4WN*)wHAfakx%Ka>Mn25rF7{3~WA)%@R%_zA(Pu8Q4691cfIw1G`T@ zIo#Kdwfo)D;fN!s7Op4>2F{CWNJBaGg8_RyYV_BpZzzF{Us2r zem#aS6s`T+)=oIn1%M#fydJ~YIBAa0*?Q?TyS&h{X#@U~F(fmf!7>x@Xhu^Er*d1Y z|9CWl<*$49w8JPP5$>vw;@Q*ZEW2fi4Y=^JIrgkw7CrJL6@$lyNB*YvuO{))z;)nZ!L z%)dj22|Z_u8>^VOSIplwd=$eI9uvVlI~UC6LLs9@*uNuvTXLi??)LsPK5-^+JH=yb z%q1Qnb0UEGEoUDccryKFV=k;7uD`5>pLc@&*oI(~d-Cbalv0Cm+2VHNuL)Rh`wZ&k z+QDWiz`ucw3cd2{{Q^jWV?460aj%i*%rUZ23ZY>>a&{9YgvyAmZ=;!$kFH!!6U=Ru zb0#i00)lYq#2lEn&Hac5=gkJETZJHUy!>^Dk;Tl?wtCrTVowM}uRF^!%zs~nWtDq(8Yjr}aMIbwKC^$EQB(EL(6l21H6#Nxif6)OK=V}q zguu)#AbHabyS9+-!7WP$n{6h3{$_@=;~Fkg_sfF5=2+w03ZJ&yTowp02WKz`v*g2m zvSwuy(KdPY`$R?x}6)+RR8L3Q#f2#LM);K)a(Mn!8Vlharn zEnTs2u(w%DVJDt#w2;+y#-LegS|GisSZHI#X#1Apk(4L<*-6-lV|o43u$(?m<0zvBl65ZdQzavuNiZqj~qQ zjMTViPfcZ&A<#XkH%(~c@z$WA8>aP}r{TDs$)REc7keF@1@kqfZ-Fe7_j;V3I-WUs z`xhm}mh_|noq^2=-pLtkmhqkg#kCIn1A{M&pm9_7dyRGq2LQcw^#!*g_O3uiu3F>!l- z=(i_eka)PsgW-;ApMo{$Z?|GNf5yHChWj`6IcK;a8;%IgPd%}Z$U^|P3B3I6(?PGv zm}l(fDp-Ah`lR(?)J_+5tgu5YkXXNBuw=Xno>&`rcK9f`PGOBePb2`9aI8X!P3hm?~(a{6>HKn>7o(lM3@K%O7 zGWAT)n(1=H&1!zsw!dzDDtheiJelJFP6Dka7%))B0Vrw9p56YU@s(&#-Ddlp>V}A! z5y^N)zuW9|JA+lNxxmW{BLv_r7oO)F^5!X?0n*-G2N1RQ zmt!!wz?KqA7I1!#*Fm4HL*cW-Lv5X^d}=$n;=NCz;}qIBAhq5}hI$dX8pdY+2T9;tn008KD*{0Be0l|tjaW1{XG^S&`2EgF#4lA8J z)ELzcpnMIC%jtCQJcXT3xH9|tJ9=b)ECKvju7W~Xzp>r#gfmcI_ELp z!@psKn|8FOb1^odXCdqijOJV{yFh?hSdYU2FR z?R&Zt9I|aC5eme^pYH<(PqA0?XacqV)-$!x9PMd&4c~84bsolK8vr(vZoZF%)-#gn zSIGy|Jgr{A%9YT@FXwnWruVjAB|N{@s@weCnTlw7<1nIseCvJx{a#zD!k-Z-ztW9j z=GM{8koV8WJRgk?);dZ6&awTgeTL;hzse5BskD4Oq4wWslW;qx&5_{fQM^~MLk{Nm z9l<*J?j)7Z!xSizm6+YhTgg(ll-AYk1+$Lzh)oBki$28}ptt8>w-u2mHHrCd+eI8A zVQjY0fY|$(*f=#2S;CjY8K@GR;N%dx?>-OD#@f}lZR+vwXM!IhOFN7jDK#Q5KnbGb z0xw5N!EUip+h3-$fgN!O(E3+W6ohYo%xJ&PM?*R}Ul#F%6g$BwryAh-U%5QRFcBKt znU+wF?6PLRpXNyC+q3$R0R9{&TFwx6=EUZQ@Znz5lEtwvl_U#`bbV2?xWqZmgLRs5 zdaOTl5Ch!Wm?Fr$COICDrnqW&Z5ao_iEB=J;_Z3;CUCYcS9GxRv_fi$j=_eNGOP*B z{-qYtaj7PCyF*GIw=`dP(BG_4_OE$}kJ^4Y)OJ3>iJQ)>N2Op64v({h(-Bz?we(2C z@+XgoFHiT)7t~WMkR8rwiemZ~L4?my;xq5*K+b-|XLj6=$9T5*nG5tJqD2>%Qhp*mR*ztByqP=pGhR%q41dwn9(V5mp;2L~85jjr%`#GnxR}YV ziosV12qAzOOv=pE=Oif!&+&B+A7AfcJj?sspQ9(2HyPj)iD#K^Si~E|Q=68~d7n7K zN^*|)oOr~b3lcwaU2*x1bJ1aeXGV;4YMwYkEEYRh>0nkeRN@KZXin8AU&y$e z+3|z_!S8O(!uXh*6pjO3FSh+L1O#`1X5F^Gk8Qho0{EYSE3NIXHh}3*((7$4dIa=t z0~gnAP1yr3cYyvUT{dJ#^3xOw1>pURz9|dz-2&ZfZf~u9oIU_)>MD5y92^3}Mao|H zcz39?w|~#H`uhQ=0&=`#dS>JR000JJOGiWi000000Qp0^e*gdg32;bRa{vGf6951U z69E94oEQKA00(qQO+^Ri0tf~a55j#+V*mgR2;>2$M`Y&Wt&j^LBCevBPoiefG!tc-LCb1BV&@=bXLQ$NO9Fv)0~gAAw=1YUg_* z0xDnS+_x@7WAru17!i#6sPduvHbgvn zg{G_DLx7W0+^Fwit{|{N_G`#^l#m6i&1R(lq{a-DdcGtv3R)^SmLaJXq!@81xfPg@ z5TL2?mnAegf>Samr9l*H(3V_jCn$s`mCKP76|e{nLCF`O*wX?jr6Re&5-RXw*25A! zDF$dMX#}tmWY$LHQtO(^(MZaQrD$*80hH;4SxbN?)u1dXD;Kk!eFaLP*ETdacv89; zNi{0Oiaj3zQQ6h>YzCK9gR!vl2}-VQNN5anG(pL;U6)0R)qT=dfLvo|YR3{54oYJf zkgjxB!|nHkh3!6N?k%bPVTz6;EbN1!bEHmaa=bg4u;dR^Lfv^ysP{6zh8mr)x49wIO+>$r*rMf6Ow0$MSK?#dL zI7))aLVx;*Z3%hsT7j{&pDpK=Pv8COE2d@6NkVl~K5L?`3~ zOwK!`EdUhTP56psnRBuL&)qc-Oo@*Y$mLQ(C~(FeR;He?HehOa?nVyV&*%2Pk9}Px zOzw~dBP`{^lZ!PM0uvXOfQu(Zz>@ju04sL1z5IBEiWSnv#IY z@iwWrr3!YH$q_Qh9####0oer z#&rk+laEAXaGkAP!wUTn_~kD}ON=MbSW2x!JDdC1Yz{0b2H+RnMG*RNu>__LcuEDQ z?Nlyg$|W%6STB0A#p@|tZ)^=MG7=kmj&MrjI zNBQ3>F< zSimsLFx!+OXJHgzr!(8+lx>`+2I7#;Y?H@-gxl>PTMvE5Prn%JA zQdDjOw#%{56ti|po90rSl=B%X=WOG0DlA1=$TlwK$tbFXY~yk$B$f4OUR<_uF-}5J zC1o3Dxp09RkWoamL8ZH7*6c++0%IjhPp`mD#=VY2%76G`{LCu+ZFw;;J=QL$o z1B%d4w7KO%!{uhG@&Q7t&oo>NpgpENqbNed(T*|>4R6$>Xm6QmD2mWfw8eym7K0)* z6h&w#iXt==MQA9B&``Ej`vMA~p(sK_QG|w~2n|IQ0Sz~qQA1UGs4&w|6rrIgLPJq` zLc>jJRORzHVWy#isr2!c&`=bip$u4=dZ6L88meMTsgt0gD2mWf6rrKNHeqFIo1@8O zXn3uL;vm%mO>K(-90w@cGg>ri{J^P%hGJ$8G@qFSO|J8rzHCE_<_DNsGCDI2!J&LW zQ|e;CrfLzK`m)Rv=fq4yaA?b7K$G+JnjsxbaM%`oj?0bxLJc;J;0$kr8kh5{wPyr} zz)W>aF7^p&Pwdy18K&Q9xv5{QCEL^l&d_@Dxo4G&;M57s9H-<`o!A;UHP3OFVa7Qu zH}?Qs93U{XXH+%d)Cx?WLmHAKT*v|t9G1V@Ov9(tlofcDUw_gl&@kJS6M)Px^OJ+~ zeZU$4U?A3$Ww;Pf*$7U!{UilvjpCdG%5oFTDH~y%0Sy-fm<4OMX5Q^tuGz+Cv8^?M zukw@(-q>WYHRS{)t(HyMmAL6Us>&l<3d0sX}SUWCjf2)aMlbTY!;lko5%*#?Erw^uC!-0 zS;kuscF7VbEjp^IKUUStmb(>)?pjS1tRZm#$@1gM-eNHo!=2UZ= z5sh*Rz)=8~1Nhezznz>BzV1!C?mf5QUI?7uz%_V zx8zl_was~-1n~Kxz^wO+v3kwwG_g)~3?O}!FNcgXr<$sI0>H~GwRx&|%nF)NBbLYV z6$wme*WeSN7_sPLRC-Q>Tb^j)sgl+OjQMYRhMV`YNreI;PM88t~5mP6GH3 zfXAD^{RzNv-vMfgZ;v2f17Ht;Zv*&1Xlpekz+(-p5~9K>GE#?*0r)w9`vLq4z&~PT zh}7Wtu2V~WZUS%xfGeBDl%&}q=Db>#4dUyVQwkpdmgfLG0^p+}a%58whqP5G;E6p5 zte#I*KMUY9F##G9KXaj3Wn^o4K40Jrv1uVjNudxzMjkT+N^j~f~uL-VO~ zLRz;e!S4f@)qkBOOEIPqmTKzoKt~qcr>a{<$~^d?gei|XGq`qPaqX*1KLBvvoFE>1 zb>9~-t8$@&Ixr}!e^&qxcYN(~0J{uA@8xAls=5=vwEzxveB?0z_W*dZuRpEFVK8WX z7I!u-Y+Z){yarT^`OI4Y+zQ~5#xAexha&Q$W{?{+AX=3R>5Hc}!h}XV4B(3` zf4>mG{i?d#nC~x9XE!gPeO30f$3ZgYpxq9Q#dJTiV#fh|qhnsP6Tqdax@ozaVLwlM z0KB|qfhMHGh+@mu`Ea3OnNWeL4ZRKEj~(y5ty$D#stKoaRx-`%p!IJ6H;KsYkp|F^ zb0mmJoK0vWD>Ajx&j5I^x zc&~^&R0=T3QeR+jjsW;u$H;gyfNwJ1=UcO;3tx$qTa{XLs(DSO(i$)({h|kO9szI} znmzw5I!KG=O}hb{+w=Z$5qYj8U`#+UL=%tw&yK^LR@Ez2b!X})$K;S{I$m3<`mLTW zJFTi83mF&HECfs)DJC}3cK|%v^7m~3cLR72v6yDf0GtQl{GRtuHm^yrSkExY?O8x@ zz6;>{9q(NL;A&d5@!i$@yQi^cU3#*4dAH({*)ws2S{R>JZF%KiRrjgtUt11yruq97 zsw%W-qpaC=s`^+@7u}<(7hAFEA>!B#JyGbuVkAuEr)p%7X$|N`A${6RaKwq>5{Jw zy}wUK$XYDB86%E~dCrE%;wlywHGk7{n0olPmB?PD}*vdu-RdV9~;A5hf;%r>3DY7 z+4!n@wyOT2=WD-f?iQ#Hnig1~su!y2Pd7C6mshQ@@4+rLJiYeJiY4=D*IGK_SpdK9 z`P!QS+y>wVtX%P~aBj1#@Ie3t3n|ew&Bq%IBLtt#IY+e6XW!R`tRLvnlq<$}KTtA6oDFz_5Os&%{PQ3gGzx zpF0TPb67mwd#eJQwrtDu(SI}Ms-6IF=alSX?H;np3`7)&`xl^-PGj-B=)2Htm(F?9;9B!+1Nbn2TLA1cVxi5(R&Z#BtjYyGe~4k{J^&8^ z__c`KUBJZuX8^7=Vh33%vTQ|T#h=s;3;`-` z-WEI#w+KvkaMTWv@TcR_WSUaiy%vB<044NA&!EthI#87cP>C5OWIP622=Lhtp^jTC zL{=bKZ?+J{TAst~>RE0hMhzh4w%V0L-)0F-&epH?yAn$1S7F0aB~I2uia*>+rLv9L ami>RG56lgMCO}*O0000 z_rH+*wiKChpTa;7@>Yt(#OfZ<4~tm}M|;4;vhl(^LPY(^dJw{AW#bHtEk=`>iqUzS%50N%{8j- z$}YEbt}VxX))E^I{qB-cj%50y#B^ssZ~#kZ+am187o{8V$4mZA-@bR<;TqMTD5;wl zMQRVR(z%ps6UMbyKKdP1edq!xBn^$!Zt0wz)h33|ebyV9U zXQ@r8YTtjZ*86*kv+;BfP`O7s$9q9g_n=5aShT@)=_vPzrD8X z6F4xs-JHHIcQ(qw-d}iMmn_G?(pTy16akofI-qi4$#ix1?y6iLj0}~Wqq$n|?=?>6 z$h@{*(Qo%LPVexpK9mFKA9N_MXp#83ics5Gvq8OcN2OENzr=L9%JxytG5Y=`gwtur zZVL>4qIw@d!Eg8Vei_V}DLdL8pTpDRG>Fa%b-r`IY&ofPAM|kp<#+ zzKZI)zD{K&AJcQ<6hn;d>Pa=~WglezU7ba{*l=#c!xw8T;8~4&&zlcB(+Vt1^}Sc9kt% zi0RiWvH=pO^YSAouY>r%4A=ZHM}}epDP27~cShu^yRtK;9=-o?80E4f_4YOLPy1~} zWwAH|zwC5Y?&JKjg_SPnEV`P2ko70yMXXfmUEaGTlun7}-V;TN{dv+}L)$+P_wG#; z0BJk%so;41veS(vpP1vOy;HzSzWtnL?r%hYUtd4Ua?ZY4?4Pdnj@Ig;WgOExEbR|< zWiG&av(~E1&L!7ARXM@wxT$ozAK08~?yu8Rb0_=$y}N4*^UA(|bwa&&W|Q8>Qhm5| zq+5Lb#L9EA%>5l_5P0mZ_mSsu`tAFg>vI2Z|L|OC-rWZV1=?@#Ta_1{643Rk(75~P zelxmbefoaGs0ujiJ;$${E*JVi|I(_>%CwZH_aq~<^Xp(fXc;@5k9B3&R^_K;X^9(7=+%RgYM?11Zi&xlDvkUkj_tGyQ}+{2IU_)9zTcY z*8$hNTs!)2yEdn5lsl3t|A<8rV1@=sHX){}*G@4xf8al6a11SO0y8l6L49S%;c|3q z1YG@@ZHGzbp7A?;prkM?r>oiMS2%DY-zw2I?xxQbIy}L{y9GOUqT9~R#(V8{k)ivY8$KH` ztOb93sm(CUum;YvOL@V6BHISwwGQlZKMbc+?(6Hyp51%uu(|CXXLc;uY~<{GKThZ8 zdRWw3gy;i1E^p8=Un$*=mVx!t2oIFK6?uViZ{0zlE2;jr1Uv%lrz}qP2;H{~zc=FV zDph&lg2r#Rh%KFOL;2norbGvazoc@9Tw6a5XM?Wy5{%X6Fwq>NCwhiMx9l-PY>xEo zRIGna5|9P<*~bde__hIUx{9Gzx(y`X!f){Pb!F^&k6C?t$^$=7i^H0Bh3+_g8JHJ|*qtVQEWy`b=0tfN3x~e< zv$NVv9R|I31^q^J{_m}~#syt6`SvO&!Alyy@7pa=CrfpAt-VK`K5uO~bY~d^82{?7 z4|kZpqD8@Rkq^zUpc{Is8M*>Wanv_eyRUcnlH9UKcZ082+2RZ^U$QVoItZ=fhClZ) zFbxO(y+IIRb+_CALPMiIFcIvx`#z-i>z$Po4xeB-0srs5P0RAoH4c0O*DloG;`^{- z8mVql&hdMm^F7iI=8_Z40aidxr`&N-+`rNv;B>LP!u`{=Q$MxR_4(9>?rWCb*_Gr2 zzqt~_6js5xcK`0s)8C1{cWU!={Z**w)Yf}-Ct}~nIzL_HBN>cXV6Nji9kj49-UXwl z(|Ypt+6B|0x{lD<{&j)FqI2si`adbPp??n2OAb3Pb9&J0v>$xcuYt}{+1}~vuxhuk zzFnJg|8}+wiDBDYdqv>>0@be=xNH?%D><-VcDkv|-Ii1C2k7*82`o z0s29qdvZj7Bl_!ntW^f{F4_BSs=H+AeceRLE;!|W08a1g+LSFmblY0`Hhvo0637=P z6^!$%ym1Gb8an5`z0;8&;_G8&aQ8yq=-IjApD1h-vkUdkC|4ii8rN~`FFELM1@!AE zs2DL#k5qe$WL^csr-|f&x1fC=v3M-K>A(UyogBHca{rRq*Q*?2!}bRCZ{hD>V|IbR zq1Odtc8&FN=#oX^+9LTUEE`{k-CD@45qqi48E+r&^L0{ovjO%2P%@&XrU6CT;L;<+#1vy%;h?>0y$w4;fktpG@Tt zgKD`tjBf+>mJlDQzuUL-%i#O#IGnBDuCmj+JZx`NcVxx;@b_-tr^Zs3?e{k}mUFAZ%}4=`c%WE6TVsfRdkMgES&Qf!b1@O}(5 zEa%s&c>XuknRSf)M}Gr*fHMHoTZ!*Qu*kLfx!3NBXZY=@vf-o0Vc9rxmq#!IZzNcl zA)O<1c?fX&DmSkL15EUQ7IItlfGf6feAvKei2R1C{1VHLsl#ArN8kgTuF8Mvx4ANH z{$BNSr*D({eq%g7hVm*7=jqRKx>h-ai#$?u%g8|vrb!?X{xCQcb z7rAok(%NCjxAn^7Dt^>CqCfr&nN#<2+Ri|RA4{SfNCQ?>X!PIUIWlZ_b#U)-L{8UL z4sZX;HO1-tD-Usgo7FzBJ6GWTC+$S$6NKb`9o*Rt>+8EH!`pxGoZkWs$X={RLx?8=GUTrQlzmo&Yi1Pt~IA)ZbT+%r|} za~}hsQIn~sA-&`qJDqZ0A1eb}&Dq2-P4$jfc`k=%v#PW4bVoZ~ZSEO0asIhl2L*tZ zvHULO>R(cgYLoK{qwk*euS8RJo=tLcH^pJKHQ~**(ZMoIJ6l1Z`(Z>s33crBv9q>r-XV#_dfhcy^oP%{Bn&Y?M1YiUV`8YSXU%H3v|hxS8r7Y<&E< z%EY>HSH9@uz%s=wUX50jN@kawO@Aw7IZJ>b)LX&5lz?9V42!mJj#Is zY_`n+L8Rh!W)-$-*8PHKlZ<8HEP&DbksTs&Op`h~&yWc6Rj!Rb{x`CZtM_sgdm{tM z2lx%XK2^r1{~BfrSJDROxh|GmJMe2b8}i=XPu{=ab8S67Dt9=A%Y8@a%vH8N#wt55 zuUDb}GkkF+C;^2smzxVJ*F9d!f~y)M2b?e7%U?Paa{D&iLlp_86@& zV7PekANm1h6tM088QuGbwjKswGzfY;zP_$pIUdOBY}7trFjrYN$IfAVdrMcf z55M8;8>;M#UGBF#U0v?m1m*KmwgI+ZP1*Qu81yHQDX;I^DDT=Qs|v2IpL>A0Ns4~U zT!Hq1=bb@wzZ&8^Ud?&<={hRX`R|7=@&Q%(FhlpazC)wn+)n2P}##R0h zXF&bULEAvp!(BSiP*d^jZb(3nb~xn$uKc?+KA9_5qtx!i9=w}aCgdH>aGug!c*m-g6VvED0`flv25 zqCY^`ALIELDhBog?S5OdJYX?bd2a{~F=4yvKE`S<(ZSV!x^CI|zN&KXbt#8M7apJb z1L`nlUXgvWeyXzmDU}Pa?93lr~FF`BOPtJl2qCwhN;!h*)M*e53F?k z%x_b<->`V`>%z8@y2rDNZ-;(OuT91NtvTi18gU=ry6ig($^5pe{2+(%+nla6k8z;a zD-V9U+UNEge0{7uFdt!oMCj>n#N&vfO6c`pulmaX_#_IP$8jy1gc zK+%h5K%ZTK;1eRL($T&ly6c1b5f13(*L!7uKz-9_*|()Ua6#wVd}DOaMNV)!%E^PQ z!)4`_!>xR9rR!zBUS+?{Du?)3-LegImxi`}yK6(Iw}y-ETAgh&_nlGxjRi(Ov>G?C zQ&mfMUV%IU(IRyHqx$zE{mB%{X0m)r7l8W*&KSP29!H0I&hGGu(7)8LtBlUvojs0{ z;?QXkHKXrc^f{bte5~qSq}nUXmQKFo+5gll z_lk6G`%o9u`y0ydJa+!NuE1o9ss#ERWZ;5_dOx{PQEcy&PN&@Ilm~h}l03=R&AT!k zI8YrU^*cg&dnuIs4pHsTANcwOUG9xSFc1s6!{hE8WBZM2kk3i^b{oRWkFUY+Q(^x> z-pRVpl)qV9MzANS*K>rDZ1IMsSmEH|TkXxsK-nf9<961p=a2f}Sxb(v47m@!=#Yel9 zzLBz7Fm3c)%UJZB(0_=tLC-QQGvn!Rp4Sv6zSS$>-17AL&{%#8=9c`B*VamQpj`2=RANC}uFV{9O z`1If3uMBu6U42B`e#xFr#4cLZzt4B!!{X>aRj?Yn($RsH;EFxO-+C$KJOhdZMy^C5y-hR*_O~lJvIRR`hdVEu8n$KAFaANN5%lNpl_+pW$w;PCV!`xjVt z=$AuuI@`o*AFGZabOC849T^%Y2({!WH<^=4jh%O3NZ?P)4IbR9n!3c+db=U}Xw=ki z(vF~3uIYUC-2*(iE08{x463FPKiXg?AU7a&NzXb?M)pP;&;DZDe~pB4DrH?`L2zM$ zvP>-?o;arPz9J)io_+oW}d38-d zL;vKoOLzssChzio9G-Sx?d_{Exafi_py$}DSsFBfNaP(9Lv{r@T6D+=3 z!wCbdn9(&l%|*DR9(avD)@5-0S8xKZZDTfleP9r)Zr(RA3Na%Cnx%s8TNY=Q@dQD{ z{Dm&#hU-R)58ZvNZlcebpc`84HFWr?9=2?QlcBYTp5viqXs%5`yRx6Re28P+O2!_r z`EDm&?sTmtW2hBoR~ntt{{*tGA)-qF;rOn$3F%P=U%m>*$z^Y}o3m~C9&9AMRw}w^ zvzbS$CFo78z}tGYIHim6()5s6o24H*Qdl6TW3NgrY<)g0E#*Ye6;7Nd7OT{>yW zb^n(3rn!FJRs$Q~oSy-%B?8&0zf(ZypMNwt^kw8}*-ZDi$C(CJhp4LzeW}WNecsvu zOL<3)UwwoQo?&o5n{C$LrbfG_2=oy14ak{@pAHcRr>~8UC+X zakb$bhb=LGeUx!q+~~}Cn+P^k?=dhgVXIM(PBs}>sg4?O(4qpD9(NC=7L;Q#9SV3( z@w={^L)ZTvi0{kd_uC`oA6s^fbU3)T<)jf=!pQQ0?x_iri8n=JZu^<9fqq4N>^T4$ zcZHz#vjXcn4t!V-!Jw5hASFsR3oQ|&9yO&tGlHe9UG^WQA~Tk#fbP4LszaMV7;55 z_Z(vBT`|^(!3HsWQ+>qGsZIGtWk;M_Sh0@6pXCSLI-xIg)-Am=Kbyk62W(tRT|Qvs zRU&qs&mrJPbM{txnHSDPiv*|tS!1QYbR8H@4znDEe!#41;SAl9qdtwB8UUyID~0Vs zM}D35`#W%uCm*%@9Jyuq&kcO|)n#*eMu8&`bLoHr9Faf2@2>pDm@fbyL&9bM&aV3y zW6oj$w$bD3IsKm4g|SV4?(G{x`B)WqfbzIvC{PjC8l%$Jt8A&jY8fYA$f0G4|Pj-MWo#2^&6Fb3rBY zM=!BxUrP8<(@5`z;E!Fi3{66UJzW7A>42h9FbFKwf5u1w`~NS;x9#-lp!`E_z0Tcv`Bt~y9F>Hw4)b*u-z23vfn&boLKE68S#`!PJbA6Zc z*5Yye*r8rj3TI4JZ=s(P!{z85mz86mnvlz(w=mIvDr|uZbo$Q#18^18vm?gJ6q67|C}{8)SkTgn!?q3?{tb-xH-qc_s( z`l&m`X?zOfEW7UMEO$M7R9(|+tad*;m4^bF6*Oc(QiV4Uk=f#`RtVHPYy}S zzNignr$!upri2KNx}(NDbNC{$yeP+Zl$c0dL(d^%F<_s%hT1-67wX|z53#-x-u21g!5~rqWH8JqCLiMo+P@6V(x9tA{MuKg{T8 zsQPl)$q-Q0TTfDjeo}ROzBdxb&@DYU^Zk{;{tGq!%on37j?brT$I?b1Zwxx*S5Geu z0%Ltk(>xdY#FTZE=q*?Y^!;k7?m}&IvsB z6&3Y+g}UIu=m=6W*y8omo9le!v_qKuh58N?<*Y@}3H!009&^T}2t&v8q&1eVC?fN~ z5bFN^${6vK=@)sS7ZvG8>Kf#@BC=@#)2Xek4HSzH?5})$3}@-FTLFnZZ}DkIRn~NL z_PSKC;j@|O5fiNSnWP)6aad3~I(|D6rnh$0Nr=}%UWEK6bdpim2G?f@ACCM>7coVA zuGCD@E67THx9smIeR=;~y#B5bLuwfaq?t@D|p>Ne5Oh z-X4Lhoxps`;2e6}8%*rt`nd^ipL*XIZl(`LfKkg{y$sfTKO~^k&Y)lYPzrIX1KMW@ zh$4awFCu%U7Ivm(Ng>|x%<>70 zK)5kfx57ZZz1qTLVWk6Stq?mnhM(!DYy{YVTD$Q|WUW|ew3&ARF}%}bw-fRtSD)nz zYy{Z2&qTD^>nF%E3#@yy?zjkFT>!zhHl=g=FLvQ zMhU;{wn}95FEsRBe4^mR25(eFy#DJ5)A0{vqtVXO2IJ5jyUkp&0Bb?_w}uGdX$IK( zki1Sa*3;qSD-f2?N$KXrk%B=sf9dFl7Oe0QGV{xHVEeBWHXLkgl~nAs^kc)vlMLNd zs^-N`!G^voGJ4SmX9j*>LuaWArQ z{JM%$E2zy^9IN&)i(XFv3|E|Gzh;;l`@F-2qene@PN(f)10TEI82eZdn?4XSlwyGZ zeC!aI7mj22rABWIid5Ui2Ef2?9m7sSPkiA&`!#QI6`Ro)sSce)+*9R~LHLF>IH@4P zAA;y*8S{f|Dcfj---PgyFy#^ZQTZIlBi1;abQRstQ zxy$~-sk?{ukmn#DK`uv9Np}TWQnxp7Y5+@`*b$wh^zkH z>O76_c7Q6s-NNPS;Mfouc;hVKZ&UpP#@ir`b`88!Id+}w(RGgK;BUZeVd3!ong$X^$6pdEPn6Tv?SIT+EeIGMm)h4^Xcb3GveU|s2#Buz8F zGUORW9Y4=Tterg-uWogd>sV;dUOlFMf!9*S14q%vVom}%2=UX0>PP_VS6@3l4Q3;s zIHUZwI^UygC%&DLJ2T>RkmNHCK%aL&^1Bi9G>d-V2s+9>W^sE#`^6t*87DxH)lDop ze}AKpbx2OQ>$Hh@bVqcG3os)$H+Ef6l^=_KvY$P&BC2#3)^*Bi$9DMyr{8oL6>~j< z#XBh-F@DPrI#GUFNsREsK6fA&r07={tPF{!>QiacnC;l~L>I0c@&o=-P9PZk&^L;d z_srq)G4SYu?W+;KrfOaIZsPmNDPyyKhQr#a{Thz<EUhz<)QxdGtCXVLN?G4G7jfxA5am{^vb_siTZ|Uhi-4R2i(k zf*aoQiep^qiK+PfHUfHRMo{LC5wV;6DZ}reUnXGxHcIi|$8Yz`wB02@B4s}Xgv0+D zU4nAtO}F7;MZce^GT4FJNh7+U9RtiK1Ka~rKAN-*IgODJ$bPixEva`g&HBd#PMn*A zIU|9&mNxVK3iNkjTRW*GZem(Qc9F^r(qFJS8TmEhUliYqx;|O7LzR;ir?=6E+BqAs z(uZ@&9+mL03|s>Xj<8uC{Kn|dN!az8z`mWrM#x*hcef4$;&4(%lPM3a3&?86?vF?; z{mrtmbP&8TMq=powDGM-^sBa(4_}Av^N^C$)A_!AstoT}kZ4NT#@Zk|&LDhx3}2*& zIQ9~beCnE@;1%=**~Jk?RZF|1s>HD*GWI z23l1XOVQUSa^vjdVjevG@A}|K0j!p^#ZvYi1_V!yk$C!zBVlx^#-ASH*Bw1h%XJD@ zYG?m!iU-@e`3oS6YqwL%$3PooFA-x+()EU!u+yrjSi0FXGL#V9D@Ic3-(i@IT!n9a zvpbbuoErorsW}_-gDT=qIj`EZG@$!b6L7TSsentOgeP^io_!iC@mYdR#aGCan!7P? z4pL)G+mmacW)AgrxxHd!wCS81OXfB563eiJOlFJU8E zp;7djXqw6t>3i)1udp-lXgQ&kq7#TAN9#J?ACG7X`XVGX)0XswZ7kKUx(bOQ()S>S zQiz+TI>G*6C}0##VzhWFUG_DZ^NXF{+Pek$nbRe6fQt|-6}`&=r!gm1^ES0ftZ?a7M=NC=c`)1*xyB->jR&K;2TQktsa^48IhU?BfCwPM_v#3I zYSfXqS7{O;TpD{T-8;qy_yv}Mud{5!RmFd`K%P#|4*ucQhl)6iEuhr{wJy^yx+EJT zSJTJb4~oQSPD-&Iw5_rhIYoHq)DY=5vo@tjV#X<(y0!@C{p#!{-5!bi1x@3E&wX<% zqz-@Fn&JG95M=hWB4$q$j;fw4@1JGc6-JMj6JcZis5tg5NJi!P|C5LG-fF1ImMA};)23l zt!8n84e#2HvT%)cI9NMnOndCq^NEyQT^qdSOF?yZ)6h=rRw|yj^ZNqfmKG_xr}#Km zM!&qo)yJCS*t`vG8w9-OBlotv3mfL@7GiDcDKP$GLcYe$;b*!$R9By6DgAt>I%kY-j09b-kSevS%TFI-kk` zNbVX1^EQjC+Y?Fy3h{6x=S4#NJgB!mQZOd5Bk(D8{b!p#5&0~lxAR>Z9%I*%$UmKO z7PCv)l(a?oqceFW+@r)s;(lEhKH>hH#UJAad$)!6bW0{KQLgVbq>9mxengBu6brCa zAK`y|DbKj^k+>h%MI3OiAK@Q5m4o|51UsM}h-;uFUU|`gQB?sRyA_ST2xRf3bdiwE z^JQ`p?P>nWYmv0bW&|;={fK9omlN1=pNev$H*uc zar4csFjz9-hsL;={}G6B`DNF>B7EzkzcSLM1vuN%7M>-p zBEIu`HUN%6QfYV!wDN|L`NB#pm^ONRHCuSv#%V)aV*R)^pbhDgW|kjxuds~J?;OGR z)w0vx@?wj?)9Ed**n_Qcp3}QsAmVj!1jX$~z*$21zzFy|5q)3Hw*Gc_28bm ztB&)6rn}eZUG$^NH!`R%H*}*?9z!YC@^7Bhe#84o|C{BH>^Z3t(d3 z-Asfru_kN6&pgkMC_>ABwyRO?6i>>IARjoj-=Z+OFqUgAj>sz+zV-IpZI(V)XYLd@ z)7R{tk9-F)A4b(U*s-R)N%M_Oj3#7bT@;VvPVL#b{bM`o(+J-~K8idh%MRdAkZ6jL zU~q|L6vH>wS=7iBoVZ21bg!R%z=DxDVe4lgE782C-k2%V`8 z;F&(il`U)=9lWu%ps#AfI|^Gj20{Lou6+gkt~&IriiX&i_MDCKPxj-ZqA#lDq}Wbj zJ`{tn^C|FHkp0d~L4g>}Z&ZyUw8(hu75a+chSYsKTFke)LyNwFQ3$bB{f!2+l?q<- zaEboZR>U0{6UPo!--oh}T=Z#hu;P%DIYnqS<69q9uym8<8fAp#H!T`X!~92?$+iUbqA~R2qI-4&-&n zCy;Go#4$ZK??Q%B4BtkNlcRZ~)?7%w!63ds6ROkCz#k|-}$taJ#oh1c+07;3j=UJ37>p*rY2e2Z9l$k0J9Jv`1Wnf@WoCC(G8muD46suGk^~zi2jeZb?Z6@ra1UIPQM-g|4ziH zrfq84ho*sjeya>$>bfO$(RnGhoh%N5=TDJ-J;;jL>9Bci#IC1yxMU78yqbS?(!gkh zBlZvIli$uVsCp{+wnF}l)TJlO08riS;=y3nM6_-sCaci4Cz47(uKcAiuI(KZ8m+#< z?!k^E%zFcr@drEI!hbig|8R!s90bGkA*F^1`{>bvw%UOKvjOsTq$CYIGA=P}i0!Yg z!d{OEN|NpXo>nuA{pJL5qjjI~b$V*+2U=Ia@tY%^xyiBxyB2ar#HLO?^!6AAvHSTt zuB`U34;Z~M%WxQ^?`KDsFVdzj3t4I4M-av{wRNi}m9Br-qOONBZPLlH0f038K4=?1 zzU?ZOPXIdQv`NSKyb!IEAe*`=ZJi$e=mq>{zca2(`^p|BEb1KFMM}HY?=$`F3fAiWUXxrV;6D&qUL2;mMEz7rh|>n{BEi8+b{Tc^GozlI z1K*VjI1)>z6Jsg=skTULE5>ZV?~H_>GK|pl)Vn3Pev&e*+IETA7wI93J^ges+$>BT zDVW`zfsyN|ut^<<+QDf;C`_m1*p3oIK&yp8sh;B#m{vj6t<9)in*zsskz~q=Ckqqt zqI3LuSp*dbe5&JL&*6f@e9)6)J4y_3oq!CbT%Q@%LJU;JqaPZA4j z_@M03NrOZqT&WAb+Rp`JADbAvcf#$+oBWaY%17on2LDpe#wre6SLJXD1JSoVGIM?? zmuuuWFHoyk`*N&hgzmE=4?N-k-^)f~s(sFa-KQhAPQ6jY?fg*FigFDfazHlmF-S;GaA~5qX23{AJ zx>COYG2dB5Uos_rDEL@fH5q)%#+Xy|4?uQFw1-WPutEW(>#b9etC686p6FuhH-2OB zjZH^j{-au|@mopStVJ(xhy@Eic4(HLtQ6yJtH*ilzxVw;E3i4xO=Qv2g~2m)Gy(G@ z1GjnG{G#Vc%pVNx zz^&t9|2=G<+PW8XSiCtWXpFWG4GZdbfL$iXPaOuHy4lCNu#T{~NOc1>=hocQ9 zdiZKX>f6#e-9yLxtib2}Zujx2GF-2u{ZlDSMw>oW@B`#`h>mW5L8Q~O0j;XiQoA*g zM(%I2x7yHc0oQLMZPTr$W7cDC)V;Jp)9A6@t>T#W z>{tSf{UB<)k8O0;v(|W80f{Z9U!R$<*#{Yzs4{^l!D$8f?Z~txbBa3b2SM+P>=5ex zdZJ@b_Hpy39O=;Otqy<YX`qnk2;#3L7~t)wqPwomk?bA8^#>ar4Z{?>e$WvyoG5$0<65ve zoqkX_LaKMwVW>1|wiR+XqNRVO>1!D1Re_behzXwkjR3MQxVwm9>Nyx7w+6!>tC4=^ zVUmMFCu66CfoCR@jB%pA8K6!5J;5uRhh^HI<6(X|(clf|#P(<}zH1CfPX*>o59f;0 z^$0u`{HXPAG=ltRmR~Td1UOZ{X<;X&Gvd1<7K?$EYLgg#snJ^l6YD%!^KG*PUJs`< z5!G3OFZzuR+7=;0DdrsD_lLl|bQQxdHF{%Ej*oTTz)E$Bg%7b7rbGu{__={sSoWdc zs(yrpmCnE)5W_AtdIm)kIQx3HAOHu$wfH~-d!5pF!>y=492?c{IP3yIXK;>4kXtW<^!N5 zm7!Zynx43%d7!jT$m!QXj&%Kp?`y%72Znb^Wq+%KOPU8tLq~#v)+Lpl3pmZmxFu8r zr86wvnXcgoFtp(mcyF|0bOW1WNoDyEzocoP6x-3Phf}@pNEp1~x25D@-h6qclXV8+ zpmbg5G4UM+XKaI*uQbBZcB5EpA0K1HrT_M49c}isu+ylU7dwRpj)B%>)@jus9!52` z?Cd2Krt9qJLPA{tYrV#G$achgLZSV{;N}9`iA7W6n1Ov_4MfdaEE6UzsA-k(eJZDb zY!2|Z0@gyES(=6s7`Q^1tC-)<$ z$f`c9#zv3jUy00{Q*Ww2v$kLa*s$T#%3lYNS#>r7^J4_qnYDOnJ*3rho$5z%40$a5 znuhyzF)Hw^i74hy__~N`#xWa#1vdixz@CNNip0`kC7p&uXHM&q%HmI6X2twMbeKg} z>C~5J#cXH#*$B)=pl}5ELG73L!TC);%B99=d*8689KA`8`GfDrHJq+_zZl0%KRqKb zuckAnXV~f^;a8yb`g(C?Q{;ijS;%*o=4`U8Y7w?M=_&Z#93Q{U0gno!7~sx*Q)y9? zIPkFtcF#vPMD$=x&p(c&?-!HL(rg5#dj!~lAA`icrcYBZeY&I4=!ycHj~8%nlM}DQzYS1T$05 zMqqLyzz)3$Qj)TsC_uPI0qJ+@MgXqW3;3+Xrlt!xzf-`whDj1A;d^KSr=X7vC}!1` zBQU$72nLXyY*i%rLQ-HdP~}W^E{fLF*<_|#-xbeSt3D-Z8Jl|oI3)=hY*f5|7*yrgwC$1E3t9O?w)7y`G4@67 zMyzChnyYSfAo`+37SUy|`p5n=5e}z5sxG#$9YeiFyC%_P$ zJzV!ic0^u|?2HV(3~AYoQ>U1!*c78;y>E$s@arPE>Hh%gHL;?EP2E!z`G0#6=?uK^ zMu455?tgS^M^QSlwD;KPQ-7(|l;TzTMJe~vf@f?_?~PFUkH&c2(lNp2#58TcHT#OM zG=>>|o{RvyLdoQ#`BAo1SK-$`vUxwE=lfy04#m8A13lHxQMPG}5No-)##FaUn#U+g z*!&F1O|$oai$-^Wg4tViu&{o}){gXBl*O003v}q_UAMID5N*Spq+5$f-P4fp-)_pMR@QDeuI)SXQ;h(Fz77(5q)Agh>Awr^m^*M_^~1$>^j)`C zt27_|_LlhH82LL=nsiI3lff)aVgwlc<&YngZZ+_l@T`5$qHUk>p~=9vAlAQvKs`$> zM&MLz^}`*@)iHiP+%Q&s#Ax28u?+6GMu!ckw-NQFqmsv3TW}fJ=@+4h)43473^{{-Y8*OC z^mkXtN>^#>-{kHc za~EGg9#a>?($vm)tcB=t=7mVt>CX`A$w#Av?Ng8+BI3Cc`4Xb1DA@_T=B>h$B&wnG zjBS+qv;2SlhrEr6v6#Rkaw$eM_5ZQQMxwNLz$cJ<*}Vec%#r+ej(GzrGA z(JD&K{*NW$>t2Y~X6Q;qXRHg5oXc8ZohD5ppNxC}*&S(od0#qk_|l5ngODyJN_9)I zYH1819uB=rb2id6y*cDuYU_;P4oH&)EO%h%&V-G}PaMdZNZa%k2u6B)1i#i~ybw77 z>EZ_g%~;c)@b+G02&^lu+I0XHVm-eKqGtHrNn&4a(`FP90Jrw(_0|vOKWcbp_aZ#roUE0VPz?UFbd4sL9&&LqG z%u|%GSsiI}xdHwi0pk7v?C)9Rw17N&_5c7K+et)0R0a&6cHH+PZ4O+RFX*KCf-P%? zvbY&e-SW0sB&Yqb_JAP_@>ayZdcBRh-hf{E)=ALmJ&>CaD`_>}$Sy5YdkKCup>&BA zREACmmR(Z(P1@jmF5)i)%Lt_yawisNA)i8ChHQc?+azAg1}+_VcR~(AzJutctx=~F zanR{%RQOgxzTG15vA;G>tdCB_tl!`s);62Cj=)Yua+7vo{eOza^9wm0hF?!HPC}Zb zzhd;L96u!(IN#AEn1qd<8l8;14(Z}iZb^5NSmM$6>#VIaug0^>bTMRl$E33apH|y` zzG0Bs4r_N7Oh5$pdWe2A`t9VVp9VCOtrE2!&9zB4W2~9*&c49_*V7vYq#dx)XV6kx z0nP&(#^amm_C#mC9a6rgy^Z+qha7-(F&A?MJe~eP;nS_cN0BD!LX28pwZX9hqThsm z8*S3^Q?)Rg2D(U{-!gYL#`NwH$k$u;@=Z+w!sq@C z{N91hQ=9m!vw`=+4dRiB9;t0#$G;HU>9?*<{~KwNG@sPzB0P;ToLalmWYLr+VT-OV zz`qG;ll0r$c;4+B1g(42(+Ya7+2Eo}N$|S2ya*{xA05?z5{O((VHLQ)pAmx((Y(l5UP>w*-)X=zHgE6;E3>@gsA9+eA(=7wFfHk? zeU$2A2K!b-zlkvEx1--sHkadEYI6qb@Ng6}>aD+Xks5IPm+nOGO!3Xy=0<>V)uUKV zT;?V{9ni~eBS~=U%wPIASdYV}YjSZUqcbI>47RRJeub2zYXFbzDndg8f9ciDY(0O$ zKE?Phk6eP}rf(I{H-Zw#p}E0$xD6XQn=eLmbU9?A_t5yiCsKMO|I#?dn7B#~UV80( zmI_Be`yZkS=-jk>p^IWTfoy^3MXr+c++s&94G2yr*w!ymnqfe^7m^x;U(^0-(uBP0~de9fWkYa0#wek&>i))JJqO5E&%++PfgQ+0{hG zn8qNCv#xAYCog|fTovOPg&xP>@SSIr-_Rxw__YS*wa6J6zAVT|ZT$CaVLj7gZlHUA zn_%##w@FKq9$~H4#%HN5{=QHU;DTO2RXTP}@Qn2-1>@cMQv6iR#Pz{~*aST=#b>o) z^V~uoF}SYK+fA@SpvN#>r9-R^*XeMs%c#`lQyAX`D8~LVUVQ#E3?xzS{PpR&aa?Ha zjO~$$@eP}H9(Vr<;NUQbntJxrIC#4JT^^bE9MPGpeuNm5s6`h(OTrm~y&z6O*Q#>^M!${o1kOx<0;f2NRy^0mQQS@d zdStl^asuL3lj(Nh7f9|=a=auIL<7!RR`C76+(5yd*3#X~oDpCgA73!$ zUzp^e5$ER%V$Gg2fOA7cD?a~G@hjI0W&0o#UB)n$@urgxM@mxXUq348D1mNJ5bTdi zxMx^<1Q_o}7mWG;+AQPK604RGEQdI!WWC?G$HgT>T*Pjl#9d4gMo&b$m+57B|-PsrKG zIdpcU1CDP^im{AV(47CFJph@m9#KNNM^j246;=9B0pKf#dW7=daCU9S@4Q zZ^?}PZ{vw*VS8b$U!FPMf*m|9qd_cMRjQ|exvBU`1;m>mA4e`kN?&=vWU($3h7%3< zL)^@%fNj#dhCS44aQK)4r{~o(ju%1fdcu>Nir18kw>Q)8je-6|?qGx4b((raJTESY z{kYuN;e18`Zyj`Cj>OGT=w3*dBVD9-6Twp&5S2XZetiS}QJeg>z`9u-ZV`U;YLA+yljR>@T@L$~Iw@+P ziZT0E639mQSL8=X?n*mi6{OE*LGpTG2$ z=I@acI0}82yePq`81BwA(mNb0<42F0rY_N!>j-Mq%)lN+G8-U5^Q|ZC}t29UPb5#6ay_Fjz02=8>Ww zTNd+i5J*my$=NMRx#Q_E)zOlTN!81aN8rcI;3t>9mym z!c50{9K3NRb_QEA0xw!ptSuc|iy5HHBYP7+qK zbM8CIK|adh(XN1|gGk6tI#tK@S{706Cn!1BHID$lH+XkSiDFGh0L`+SuX%gtr<6G4|8C>~lGp?;f)exs#H z#{wM_=6;_Po>|He!C!*DWc|IqDX?sW%Tk|EI!Kek6Z{XX4n%Z2O)Ad$=9lq1IWmTp z`YH3Fe+0g%6?{bZAGv+PUf-G3Z?cQ@OkGiYD%W`pPM;gg?nuS8Yz?R1e63%Z^78iz z;(K@jc4>#^j@3W7n)vZN8LZn2;>^985~Y5N`b~C`KBWUimzVUVTqPZ!PX~(g3${-2 zi=nbcxo*hhO&ND1LOz zQ$&APL~p6@XDO#tcTquH>la{`X6T}V&i`KlcGA$214ZrXOxKy}x0t&q&6!!bjtx&; z7D5Hun~dbB{V>XpL)JvK*Q$$?^x2*)(4S1AZ;K1{e3bZ2DHl5raO zf354VmnHLhCBGMa|KbSZJc8e~Rts-~=yAL*4(ifX0Dg)6pB!*tj-YDjH#*P=^OBcX z=F@>9=RoU*^$u5<<;f8!8K}+ zv6F_C94IcCbg=Wmf3>gtH8a*#*jGv>$}?$40xLOC=wLaKG|>Y^)#GCedcI=;c9PJN zIf|Pm8R$IoePcnq6Idyrc=-VE%R5JqXSGq#TeTnWuepI*CP)L7YyQUVCTza z^2COmF?vVAn0|a>ffhKXKl-1bxvBPQ3ZkFnk|l8ZmjdzsnL^|F3FFeH-3!J`?|iIN z;NTY}+>^r6pA!oD)abF-r1~~)zl@{)b5=>(eB3dX`lBnsb}bwzRSx{ihI*!;G@$9&ikR;NFA6!htDVCU0gy>Jtbq|TVUt6&TT{m4iH z>6nx_Oa2$>hB@xxJQ?XK?QQymU6&kf)2AUQoulZ*gWcl_od3}5#FQ!N&yEFsa!_4~ zMCXpCtcxQHhcVFn*bQz_GV|YqoBFXMMtuY~Hx<2J0qHA|CLh@K6u%<>$|8$agUz5S9Y(6kGov8_gcuQ`j^rM~^2fvzb zci+swe6HH~IoaXeM=&}@I#lxiOTzx^Xur?sq8mRm{kcE#)3Bojbsc;+8h_-?JQC}M z1$`4#FLTY3H3AII3WbCDT#IK~b@c6?$Tti77{cE=t^Lc1i_WZpH2FN68wbtG@0jCj znlQwyIh5SA?=;0el2{lCJ*v@%Mj9n-pPf6J;52cQH7ndGm=PHfvo5#akF-huHlpxG zLQ6+iMb2x{;VZD+VkE(hn3TA`RM2nz2eIs14zSB3i;(G@$R)6cBHGz6M`B46C&i1T zu=!Yw(9mlKiqF!HcqeE}PwYn-eacm7d`sRJXd7;^J@M%(>f5;;GH^dbcfVrCQ({2u zZ#VjsQr*8Ubc{DcQuAIn(XPqMC)9Cu+K$7m5$(_lph+s4O~rxExPLBi^y^MLJSH;w zS8_m14afh{ICb~5#PrHk(3x#JjcIdq?uPz7E3=OdruRRR3E-0*xG=v9ybjpUL9(w{ zlEJt}2=xz(yGSQG&fFZQdf%iZ&8ve29s6!c=Q6Q}5h?0N8Ti3nUjWXU* zaMPh(>N*v)XU$7sb8ukSw5qRkx4%+@Z((Ju%H9F6fc?B zs_S#uu5}0#dm4c6!@__%s?VFJT=Z9G?UHnC?!gMuRgkMo$P?I0bNxnfke{A#kkfQ4 zp#70n>m9H!K3HJW?WI+8l=2#gM-zD;L^NYxlCA=rngdF4eUHYm&E4Q@8>F;EVQ@N;=<8*< z`A~V|Y_`duE{4FnETW~c+A%cOpp~Lsrf-EoLB0i|g;xt9X=m=g&P@4! zNXZVKGX}Z^`#SO(WKlStUDxrUW%niND}XyWNW}UA|Julbh)z2CJt%3p`(81d1)-;d ztEs1j=}k>LkDrGlhaeXe`fLL~C>AfIx>`A@4fzz02idfAie6@&X| z+MkB#OWao?-{A9Q+5hAPd&T}$q;v`QF8I-{f>w^|N<)v`)|llUb;7`OK&L|1<3ZUBb+Dgk16SQh^3F3$okzB^;MIT znyBne#)k3c)Uo}~De8|Oiat71dey1FV z)GsaRIAr%lCOOG>6aICT@*l{K$cl=eT=(3$abnLu9x_ESjl zr|<$FPvjD-by@utWM4tPN_>CKAttVmfqQ)hR+qF7fcN?gY%zp5 z^l{dQBl_6T>d5_((zCt(_vLlSwUzvdc<-Rxc_Q9@Av`v~o z(;5gZXvv;nv;x9@JtQ~1$KqHTbqv^fnS*vl+wsAV_L+kbynCt~c@Mf6)} zn|{}JY&x~4H+lx%WOiDs_&$Pk_DMZ;3_e}9ehO)lPQj?{716->!uRFOK<)&mGyPJX z7=xE&j+bDae-aT+?LRtjG`Vfj@1W7dw6)RUS_9E*B7*2EY1d zXVZP=zq*bmFVO&kk2Cji@Y7#c#;9)|4m5K^k)oP zAK2hHvLJw6COKY{^vxI@RWMFZ>MB-zzO%q@2)!7^L>{r@R}J8wc3+ke4GbM&{+0x^nwEHXH+~d$Ti;SkekQ&E6L!Z1jWG zi#TnPt`4%EiTlA}qZh?;(|Nw_LN7BYIy2@b9Y|~c#a?X8u-zQ_zg%YqPA845s~K>b z@6fzgEa{sawI8dA^<}BmYtRr4UQW`zY}1)#y)*Hd9485kCYW@ommLGxPbT=nrln%O zf==sQiNA-PPt>1U`U8P$1LWrk&YY&d0eW1CZ}30OAqJxbYFcGGOQ{iHkoK$wi>xRu zO7Z5j;q9Ozk2Y94(F-&E{n(~3wa4Xf&Q}aQUIn?v?76LcZEjyNUky3lrYX{D4nk{C ziW7EM7kQcke}F9e@%eIM=o;YEpoj{7<~tYHeJn>T>927 zChBq=#>K@Ltc~P8;Q+e$!*#KNSsnQ_ayQa8y$ym^KUnPdXc5Hsuw9|9&kMSp0eB#y zd%Y&0ND7x-1j{VW%{C9OpQ%$ zIw2M}1bQ@oUM^7$P7{<5iTP@xhwnbfcWeBnnxEFh?}D&lRQ@M5GLbgD0^Z5mhTK8K z->S&(Bk?p*-+<9Uh%UMBg_K-b=q&k^+}NiTM;}Ta6$4*h7Qvk;A(aC z;Kvv%|RoX`25mAz(zCZXCOIAa|y*~5!oPHAtf{Qz&lT`DjlCm;OX_Uh^_{w zAstH)jh5(R_n?9f90Rs?-vaX(dlB zE;ZkBYN_Ln@VCRWLz6KZhYhiHHP_R0xO5`Y)$1~C`wNFoY^7vs; z?Pj>l5nxdFN1CMn$aIknBM2?%+6%e0NkA=abkNWN>j>l}i0{+oxr!;ZlAGazm3rVEbt<>tIB;1Ah7ym7r(zXh~CFP z8_^~Gr;tZBi3Ph4BRT0UP58UAAwW%9Bkf`7Lr1qWy8A-(*R}o6|Ot4VDwwBrt4DEo^ytrti(T>^vtE zJ5d<@(stHFs@@yWcP?~)ugiITY(<~mlU|~$B&~eaf}uN+TalZq{I;sB2|0;A0{nMG z7=6rDw^SpaNW@R$#5in+@lP`eK23i#K{`u}!0bR#?FQ^bU!s`%n(rR$M8}uxLGZ5v zUOTF0%&$YvLr$9Z9~UuBMXC;Vddl?(L|@N7138uPyOBIQ!K054eKphIIQX;P2+ri2 z!RGS_u=9Ki$xS&kV@VqQ1+t3^}np6VXJT#?B?xpPm~dSWVd0 zJ*9Bm$~VpB77n~ViITCqM83A^#w|hHVUGe-}uMj=4j?8QSXbvy_=imhJM=uUjIjIPz|UK%;lZoW+O265nxwc0jay}c6;<)6ts>ZD}nv2 zGt9^zQ%4HNwi!GQ^42;&*N(wv&L|^4%GuTBaIKAK%yN=$7iN$C>jt%;eLlbIQP9ot zKzZh_MM>vodfOfRl{r{;dM%cEQ4SW&Iq;s9fi2ng*;st*T^dPF+NXZ7Gl^C*h=be%XfgN9V95x~t|*p_#Yjq=-R?r}Y)O7N_BkB; z;j3s#bIZ6)^D2h@%*H{b`lP-!PWbvqW8<0OYy=kK2ypg%8FE>0n%3**MC#I&PHH10uvemeylm)8ZCPHZ9-j{xt)!`Yy?I< z0vs^3+*%)vy%f>v#*+|zNM?3xI_klnC;a~($l!l-5Gjji00000NkvXXu0mjfm8BqG diff --git a/selfdrive/ui/qt/onroad.cc b/selfdrive/ui/qt/onroad.cc index 7a4b4240c3..33b1ea8e27 100644 --- a/selfdrive/ui/qt/onroad.cc +++ b/selfdrive/ui/qt/onroad.cc @@ -231,7 +231,7 @@ AnnotatedCameraWidget::AnnotatedCameraWidget(VisionStreamType type, QWidget* par experimental_btn = new ExperimentalButton(this); main_layout->addWidget(experimental_btn, 0, Qt::AlignTop | Qt::AlignRight); - dm_img = loadPixmap("../assets/img_driver_face.png", {img_size, img_size}); + dm_img = loadPixmap("../assets/img_driver_face.png", {img_size + 5, img_size + 5}); } void AnnotatedCameraWidget::updateState(const UIState &s) { @@ -275,7 +275,7 @@ void AnnotatedCameraWidget::updateState(const UIState &s) { setProperty("speed", cur_speed); setProperty("setSpeed", set_speed); setProperty("speedUnit", s.scene.is_metric ? tr("km/h") : tr("mph")); - setProperty("hideDM", cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE); + setProperty("hideDM", (cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE)); setProperty("status", s.status); // update engageability/experimental mode button @@ -286,6 +286,9 @@ void AnnotatedCameraWidget::updateState(const UIState &s) { setProperty("dmActive", sm["driverMonitoringState"].getDriverMonitoringState().getIsActiveMode()); setProperty("rightHandDM", sm["driverMonitoringState"].getDriverMonitoringState().getIsRHD()); } + + // DM icon transition + dm_fade_state = fmax(0.0, fmin(1.0, dm_fade_state+0.2*(0.5-(float)(dmActive)))); } void AnnotatedCameraWidget::drawHud(QPainter &p) { @@ -435,12 +438,6 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) { configFont(p, "Inter", 66, "Regular"); drawText(p, rect().center().x(), 290, speedUnit, 200); - // dm icon - if (!hideDM) { - int dm_icon_x = rightHandDM ? rect().right() - btn_size / 2 - (bdr_s * 2) : btn_size / 2 + (bdr_s * 2); - drawIcon(p, dm_icon_x, rect().bottom() - footer_h / 2, - dm_img, blackColor(70), dmActive ? 1.0 : 0.2); - } p.restore(); } @@ -544,6 +541,49 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) { painter.restore(); } +void AnnotatedCameraWidget::drawDriverState(QPainter &painter, const UIState *s) { + const UIScene &scene = s->scene; + + painter.save(); + + // base icon + int x = rightHandDM ? rect().right() - (btn_size - 24) / 2 - (bdr_s * 2) : (btn_size - 24) / 2 + (bdr_s * 2); + int y = rect().bottom() - footer_h / 2; + float opacity = dmActive ? 0.65 : 0.2; + drawIcon(painter, x, y, dm_img, blackColor(0), opacity); + + // circle background + painter.setOpacity(1.0); + painter.setPen(Qt::NoPen); + painter.setBrush(blackColor(70)); + painter.drawEllipse(x - btn_size / 2, y - btn_size / 2, btn_size, btn_size); + + // face + QPointF face_kpts_draw[std::size(default_face_kpts_3d)]; + float kp; + for (int i = 0; i < std::size(default_face_kpts_3d); ++i) { + kp = (scene.face_kpts_draw[i].v[2] - 8) / 120 + 1.0; + face_kpts_draw[i] = QPointF(scene.face_kpts_draw[i].v[0] * kp + x, scene.face_kpts_draw[i].v[1] * kp + y); + } + + painter.setPen(QPen(QColor::fromRgbF(1.0, 1.0, 1.0, opacity), 5.2, Qt::SolidLine, Qt::RoundCap)); + painter.drawPolyline(face_kpts_draw, std::size(default_face_kpts_3d)); + + // tracking arcs + const int arc_l = 133; + const float arc_t_default = 6.7; + const float arc_t_extend = 12.0; + QColor arc_color = QColor::fromRgbF(0.09, 0.945, 0.26, 0.4*(1.0-dm_fade_state)*(s->engaged())); + float delta_x = -scene.driver_pose_sins[1] * arc_l / 2; + float delta_y = -scene.driver_pose_sins[0] * arc_l / 2; + painter.setPen(QPen(arc_color, arc_t_default+arc_t_extend*fmin(1.0, scene.driver_pose_diff[1] * 5.0), Qt::SolidLine, Qt::RoundCap)); + painter.drawArc(QRectF(std::fmin(x + delta_x, x), y - arc_l / 2, fabs(delta_x), arc_l), (scene.driver_pose_sins[1]>0 ? 90 : -90) * 16, 180 * 16); + painter.setPen(QPen(arc_color, arc_t_default+arc_t_extend*fmin(1.0, scene.driver_pose_diff[0] * 5.0), Qt::SolidLine, Qt::RoundCap)); + painter.drawArc(QRectF(x - arc_l / 2, std::fmin(y + delta_y, y), arc_l, fabs(delta_y)), (scene.driver_pose_sins[0]>0 ? 0 : 180) * 16, 180 * 16); + + painter.restore(); +} + void AnnotatedCameraWidget::drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd) { painter.save(); @@ -654,6 +694,12 @@ void AnnotatedCameraWidget::paintGL() { } } + // DMoji + if (!hideDM && (sm.rcv_frame("driverStateV2") > s->scene.started_frame)) { + update_dmonitoring(s, sm["driverStateV2"].getDriverStateV2(), dm_fade_state, rightHandDM); + drawDriverState(painter, s); + } + drawHud(painter); double cur_draw_t = millis_since_boot(); diff --git a/selfdrive/ui/qt/onroad.h b/selfdrive/ui/qt/onroad.h index 73c2e03795..73c2a37892 100644 --- a/selfdrive/ui/qt/onroad.h +++ b/selfdrive/ui/qt/onroad.h @@ -80,6 +80,7 @@ private: bool dmActive = false; bool hideDM = false; bool rightHandDM = false; + float dm_fade_state = 1.0; bool has_us_speed_limit = false; bool has_eu_speed_limit = false; bool v_ego_cluster_seen = false; @@ -97,6 +98,7 @@ protected: void drawLaneLines(QPainter &painter, const UIState *s); void drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd); void drawHud(QPainter &p); + void drawDriverState(QPainter &painter, const UIState *s); inline QColor redColor(int alpha = 255) { return QColor(201, 34, 49, alpha); } inline QColor whiteColor(int alpha = 255) { return QColor(255, 255, 255, alpha); } inline QColor blackColor(int alpha = 255) { return QColor(0, 0, 0, alpha); } diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index 4df963167a..6c850b8ca4 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -112,6 +112,39 @@ void update_model(UIState *s, const cereal::ModelDataV2::Reader &model) { update_line_data(s, model_position, 0.9, 1.22, &scene.track_vertices, max_idx, false); } +void update_dmonitoring(UIState *s, const cereal::DriverStateV2::Reader &driverstate, float dm_fade_state, bool is_rhd) { + UIScene &scene = s->scene; + const auto driver_orient = is_rhd ? driverstate.getRightDriverData().getFaceOrientation() : driverstate.getLeftDriverData().getFaceOrientation(); + for (int i = 0; i < std::size(scene.driver_pose_vals); i++) { + float v_this = (i == 0 ? (driver_orient[i] < 0 ? 0.7 : 0.9) : 0.4) * driver_orient[i]; + scene.driver_pose_diff[i] = fabs(scene.driver_pose_vals[i] - v_this); + scene.driver_pose_vals[i] = 0.8 * v_this + (1 - 0.8) * scene.driver_pose_vals[i]; + scene.driver_pose_sins[i] = sinf(scene.driver_pose_vals[i]*(1.0-dm_fade_state)); + scene.driver_pose_coss[i] = cosf(scene.driver_pose_vals[i]*(1.0-dm_fade_state)); + } + + const mat3 r_xyz = (mat3){{ + scene.driver_pose_coss[1]*scene.driver_pose_coss[2], + scene.driver_pose_coss[1]*scene.driver_pose_sins[2], + -scene.driver_pose_sins[1], + + -scene.driver_pose_sins[0]*scene.driver_pose_sins[1]*scene.driver_pose_coss[2] - scene.driver_pose_coss[0]*scene.driver_pose_sins[2], + -scene.driver_pose_sins[0]*scene.driver_pose_sins[1]*scene.driver_pose_sins[2] + scene.driver_pose_coss[0]*scene.driver_pose_coss[2], + -scene.driver_pose_sins[0]*scene.driver_pose_coss[1], + + scene.driver_pose_coss[0]*scene.driver_pose_sins[1]*scene.driver_pose_coss[2] - scene.driver_pose_sins[0]*scene.driver_pose_sins[2], + scene.driver_pose_coss[0]*scene.driver_pose_sins[1]*scene.driver_pose_sins[2] + scene.driver_pose_sins[0]*scene.driver_pose_coss[2], + scene.driver_pose_coss[0]*scene.driver_pose_coss[1], + }}; + + // transform vertices + for (int kpi = 0; kpi < std::size(default_face_kpts_3d); kpi++) { + vec3 kpt_this = default_face_kpts_3d[kpi]; + kpt_this = matvecmul3(r_xyz, kpt_this); + scene.face_kpts_draw[kpi] = (vec3){{(float)kpt_this.v[0], (float)kpt_this.v[1], (float)(kpt_this.v[2] * (1.0-dm_fade_state) + 8 * dm_fade_state)}}; + } +} + static void update_sockets(UIState *s) { s->sm->update(0); } @@ -213,7 +246,7 @@ void UIState::updateStatus() { UIState::UIState(QObject *parent) : QObject(parent) { sm = std::make_unique>({ "modelV2", "controlsState", "liveCalibration", "radarState", "deviceState", "roadCameraState", - "pandaStates", "carParams", "driverMonitoringState", "carState", "liveLocationKalman", + "pandaStates", "carParams", "driverMonitoringState", "carState", "liveLocationKalman", "driverStateV2", "wideRoadCameraState", "managerState", "navInstruction", "navRoute", "gnssMeasurements", }); diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index 9e1c54948b..e3eb97a762 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -25,6 +25,16 @@ typedef cereal::CarControl::HUDControl::AudibleAlert AudibleAlert; const mat3 DEFAULT_CALIBRATION = {{ 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0 }}; +const vec3 default_face_kpts_3d[] = { + {-5.98, -51.20, 8.00}, {-17.64, -49.14, 8.00}, {-23.81, -46.40, 8.00}, {-29.98, -40.91, 8.00}, {-32.04, -37.49, 8.00}, + {-34.10, -32.00, 8.00}, {-36.16, -21.03, 8.00}, {-36.16, 6.40, 8.00}, {-35.47, 10.51, 8.00}, {-32.73, 19.43, 8.00}, + {-29.30, 26.29, 8.00}, {-24.50, 33.83, 8.00}, {-19.01, 41.37, 8.00}, {-14.21, 46.17, 8.00}, {-12.16, 47.54, 8.00}, + {-4.61, 49.60, 8.00}, {4.99, 49.60, 8.00}, {12.53, 47.54, 8.00}, {14.59, 46.17, 8.00}, {19.39, 41.37, 8.00}, + {24.87, 33.83, 8.00}, {29.67, 26.29, 8.00}, {33.10, 19.43, 8.00}, {35.84, 10.51, 8.00}, {36.53, 6.40, 8.00}, + {36.53, -21.03, 8.00}, {34.47, -32.00, 8.00}, {32.42, -37.49, 8.00}, {30.36, -40.91, 8.00}, {24.19, -46.40, 8.00}, + {18.02, -49.14, 8.00}, {6.36, -51.20, 8.00}, {-5.98, -51.20, 8.00}, +}; + struct Alert { QString text1; QString text2; @@ -103,6 +113,13 @@ typedef struct UIScene { // lead QPointF lead_vertices[2]; + // DMoji state + float driver_pose_vals[3]; + float driver_pose_diff[3]; + float driver_pose_sins[3]; + float driver_pose_coss[3]; + vec3 face_kpts_draw[std::size(default_face_kpts_3d)]; + float light_sensor; bool started, ignition, is_metric, map_on_left, longitudinal_control; uint64_t started_frame; @@ -183,6 +200,7 @@ public slots: void ui_update_params(UIState *s); int get_path_length_idx(const cereal::ModelDataV2::XYZTData::Reader &line, const float path_height); void update_model(UIState *s, const cereal::ModelDataV2::Reader &model); +void update_dmonitoring(UIState *s, const cereal::DriverStateV2::Reader &driverstate, float dm_fade_state, bool is_rhd); void update_leads(UIState *s, const cereal::RadarState::Reader &radar_state, const cereal::ModelDataV2::XYZTData::Reader &line); void update_line_data(const UIState *s, const cereal::ModelDataV2::XYZTData::Reader &line, float y_off, float z_off, QPolygonF *pvd, int max_idx, bool allow_invert); From a522dce0173c1f73d36b2d287f71cccc705305a1 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 8 Feb 2023 19:14:24 -0800 Subject: [PATCH 336/484] add dmoji to release notes --- RELEASES.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 9103fbc4b5..2e8dbb44bd 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,8 +1,10 @@ -Version 0.9.1 (2022-12-XX) +Version 0.9.1 (2023-2-20) ======================== * Adjust alert volume using ambient noise level * Removed driver monitoring timer resetting on interaction if face detected and distracted -* New German translation thanks to Vrabetz and CzokNorris! +* UI updates + * Driver monitoring icon shows driver's head pose + * German translation thanks to Vrabetz and CzokNorris! * Chevrolet Bolt EV 2022-23 support thanks to JasonJShuler! * Genesis GV60 2023 support thanks to sunnyhaibin! * Hyundai Tucson 2022-23 support From d74470252dc9c30dee3fc001d8d6ab4871cb8780 Mon Sep 17 00:00:00 2001 From: Vivek Aithal Date: Wed, 8 Feb 2023 19:18:36 -0800 Subject: [PATCH 337/484] Chrysler: torque tuning + increased steer rate (#26934) * increase chrysler steer rate * use torque control * update offline values for chrysler, jeep * bump panda * add blacklist * add lag buffer to the torque vals --------- Co-authored-by: Adeeb Shihadeh --- selfdrive/car/chrysler/interface.py | 22 ++++++++++++++-------- selfdrive/car/chrysler/values.py | 13 +++++++++++-- selfdrive/car/torque_data/params.yaml | 14 +++++++------- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/selfdrive/car/chrysler/interface.py b/selfdrive/car/chrysler/interface.py index da086105e3..2b2fde8dc3 100755 --- a/selfdrive/car/chrysler/interface.py +++ b/selfdrive/car/chrysler/interface.py @@ -2,7 +2,7 @@ from cereal import car from panda import Panda from selfdrive.car import STD_CARGO_KG, get_safety_config -from selfdrive.car.chrysler.values import CAR, RAM_HD, RAM_DT, RAM_CARS, ChryslerFlags +from selfdrive.car.chrysler.values import CAR, RAM_HD, RAM_DT, RAM_CARS, CHRYSLER_OLD_TUNING_BLACKLIST, ChryslerFlags from selfdrive.car.interfaces import CarInterfaceBase @@ -24,6 +24,7 @@ class CarInterface(CarInterfaceBase): ret.safetyConfigs[0].safetyParam |= Panda.FLAG_CHRYSLER_RAM_DT ret.minSteerSpeed = 3.8 # m/s + CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) if candidate not in RAM_CARS: # Newer FW versions standard on the following platforms, or flashed by a dealer onto older platforms have a higher minimum steering speed. new_eps_platform = candidate in (CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020, CAR.JEEP_CHEROKEE_2019) @@ -36,9 +37,12 @@ class CarInterface(CarInterfaceBase): ret.mass = 2242. + STD_CARGO_KG ret.wheelbase = 3.089 ret.steerRatio = 16.2 # Pacifica Hybrid 2017 - ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kiBP = [[9., 20.], [9., 20.]] - ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.15, 0.30], [0.03, 0.05]] - ret.lateralTuning.pid.kf = 0.00006 + + if candidate in CHRYSLER_OLD_TUNING_BLACKLIST: + ret.lateralTuning.init('pid') + ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kiBP = [[9., 20.], [9., 20.]] + ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.15, 0.30], [0.03, 0.05]] + ret.lateralTuning.pid.kf = 0.00006 # Jeep elif candidate in (CAR.JEEP_CHEROKEE, CAR.JEEP_CHEROKEE_2019): @@ -46,9 +50,12 @@ class CarInterface(CarInterfaceBase): ret.wheelbase = 2.71 ret.steerRatio = 16.7 ret.steerActuatorDelay = 0.2 - ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kiBP = [[9., 20.], [9., 20.]] - ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.15, 0.30], [0.03, 0.05]] - ret.lateralTuning.pid.kf = 0.00006 + + if candidate in CHRYSLER_OLD_TUNING_BLACKLIST: + ret.lateralTuning.init('pid') + ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kiBP = [[9., 20.], [9., 20.]] + ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.15, 0.30], [0.03, 0.05]] + ret.lateralTuning.pid.kf = 0.00006 # Ram elif candidate == CAR.RAM_1500: @@ -56,7 +63,6 @@ class CarInterface(CarInterfaceBase): ret.wheelbase = 3.88 ret.steerRatio = 16.3 ret.mass = 2493. + STD_CARGO_KG - CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) ret.minSteerSpeed = 14.5 # Older EPS FW allow steer to zero if any(fw.ecu == 'eps' and fw.fwVersion[:4] <= b"6831" for fw in car_fw): diff --git a/selfdrive/car/chrysler/values.py b/selfdrive/car/chrysler/values.py index 7629a2f086..e932238e6a 100644 --- a/selfdrive/car/chrysler/values.py +++ b/selfdrive/car/chrysler/values.py @@ -46,8 +46,12 @@ class CarControllerParams: self.STEER_DELTA_DOWN = 6 self.STEER_MAX = 261 # EPS allows more, up to 350? else: - self.STEER_DELTA_UP = 3 - self.STEER_DELTA_DOWN = 3 + if CP.carFingerprint in CHRYSLER_OLD_TUNING_BLACKLIST: + self.STEER_DELTA_UP = 3 + self.STEER_DELTA_DOWN = 3 + else: + self.STEER_DELTA_UP = 6 + self.STEER_DELTA_DOWN = 6 self.STEER_MAX = 261 # higher than this faults the EPS STEER_THRESHOLD = 120 @@ -56,6 +60,11 @@ RAM_DT = {CAR.RAM_1500, } RAM_HD = {CAR.RAM_HD, } RAM_CARS = RAM_DT | RAM_HD +# the increased steer rate hasn't been verified on these cars. +# remove from this list once it's been tested and confirmed to not fault +CHRYSLER_OLD_TUNING_BLACKLIST = {CAR.PACIFICA_2017_HYBRID, CAR.PACIFICA_2018, CAR.PACIFICA_2018_HYBRID, + CAR.PACIFICA_2020, CAR.JEEP_CHEROKEE} + @dataclass class ChryslerCarInfo(CarInfo): package: str = "Adaptive Cruise Control (ACC)" diff --git a/selfdrive/car/torque_data/params.yaml b/selfdrive/car/torque_data/params.yaml index 7da508a156..6f8cfe0ce6 100644 --- a/selfdrive/car/torque_data/params.yaml +++ b/selfdrive/car/torque_data/params.yaml @@ -4,11 +4,11 @@ ACURA RDX 2020: [1.4314459806646749, 0.33874701282109954, 0.18048847083897598] AUDI A3 3RD GEN: [1.5122414863077502, 1.7443517531719404, 0.15194151892450905] AUDI Q3 2ND GEN: [1.4439223359448605, 1.2254955789112076, 0.1413798895978097] CHEVROLET VOLT PREMIER 2017: [1.5961527626411784, 1.8422651988094612, 0.1572393918005158] -CHRYSLER PACIFICA 2018: [1.593387270257916, 1.3366521181047952, 0.13776367250652022] -CHRYSLER PACIFICA 2020: [1.4323553627965695, 1.509076559398423, 0.14328246159386085] -CHRYSLER PACIFICA HYBRID 2017: [1.3032470208409048, 1.06831764583744, 0.13287170990024627] -CHRYSLER PACIFICA HYBRID 2018: [1.6068280248761635, 1.2943025830995154, 0.1358557824293823] -CHRYSLER PACIFICA HYBRID 2019: [1.4624643614072217, 1.1958788168371808, 0.15748488008472716] +CHRYSLER PACIFICA 2018: [2.07140, 1.3366521181047952, 0.13776367250652022] +CHRYSLER PACIFICA 2020: [1.86206, 1.509076559398423, 0.14328246159386085] +CHRYSLER PACIFICA HYBRID 2017: [1.79422, 1.06831764583744, 0.116237] +CHRYSLER PACIFICA HYBRID 2018: [2.08887, 1.2943025830995154, 0.114818] +CHRYSLER PACIFICA HYBRID 2019: [1.90120, 1.1958788168371808, 0.131520] GENESIS G70 2018: [3.8520195946707947, 2.354697063349854, 0.06830285485626221] GMC ACADIA DENALI 2018: [1.3181430320331884, 1.1853735340610179, 0.3450592280031644] HONDA ACCORD 2018: [1.7135052593468778, 0.3461280068322071, 0.21579936052863807] @@ -39,8 +39,8 @@ HYUNDAI SONATA 2019: [2.2200457811703953, 1.2967330275895228, 0.1403992098658639 HYUNDAI SONATA 2020: [2.9638737459977467, 2.1259108157250735, 0.07813665616927593] HYUNDAI SONATA HYBRID 2021: [2.8990264092395734, 2.061410192222139, 0.0899805488717382] HYUNDAI TUCSON HYBRID 4TH GEN: [2.035545, 2.035545, 0.110272] -JEEP GRAND CHEROKEE 2019: [1.7321233388827006, 1.289689569171081, 0.15046331002097185] -JEEP GRAND CHEROKEE V6 2018: [1.8776598027756923, 1.4057367824262523, 0.11725947414922003] +JEEP GRAND CHEROKEE 2019: [2.30972, 1.289689569171081, 0.117048] +JEEP GRAND CHEROKEE V6 2018: [2.27116, 1.4057367824262523, 0.11725947414922003] KIA EV6 2022: [3.2, 2.093457, 0.05] KIA K5 2021: [2.405339728085138, 1.460032270828705, 0.11650989850813716] KIA NIRO EV 2020: [2.9215954981365337, 2.1500583840260044, 0.09236802474810267] From dae133181701f5f1cc84daa081a8c71d8c7e084a Mon Sep 17 00:00:00 2001 From: Jason Wen <47793918+sunnyhaibin@users.noreply.github.com> Date: Wed, 8 Feb 2023 22:21:50 -0500 Subject: [PATCH 338/484] HKG: Car Port for Kia Niro Hybrid 2023 (HDA1) (#26827) * HKG: Car Port for Kia Niro Hybrid 2023 (HDA1) * bump opendbc * bump * Found gear signal * Only set one flag * bump opendbc * Update CARS.md * Update routes.py * bump opendbc * Update CARS.md * some values cleanup * new route with rlogs * set from data * merge (3854+3391+3227+3336)/4 * fine move back down here for now fine move back down here for now * Update selfdrive/car/hyundai/interface.py --------- Co-authored-by: Shane Smiskol --- RELEASES.md | 1 + docs/CARS.md | 3 ++- selfdrive/car/hyundai/carstate.py | 8 ++++++-- selfdrive/car/hyundai/interface.py | 11 +++++++---- selfdrive/car/hyundai/values.py | 17 +++++++++++++++-- selfdrive/car/tests/routes.py | 1 + selfdrive/car/torque_data/override.yaml | 1 + 7 files changed, 33 insertions(+), 9 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 2e8dbb44bd..d99fe8183f 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -9,6 +9,7 @@ Version 0.9.1 (2023-2-20) * Genesis GV60 2023 support thanks to sunnyhaibin! * Hyundai Tucson 2022-23 support * Kia K5 Hybrid 2020 support thanks to sunnyhaibin! +* Kia Niro Hybrid 2023 support thanks to sunnyhaibin! * Kia Sorento 2022-23 support thanks to sunnyhaibin! * Kia Sorento Plug-in Hybrid 2022 support thanks to sunnyhaibin! * Volkswagen Crafter and MAN TGE 2017-23 support thanks to jyoung8607! diff --git a/docs/CARS.md b/docs/CARS.md index b73f09334d..a72bf74278 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,7 +4,7 @@ A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system. -# 234 Supported Cars +# 235 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness|Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| @@ -107,6 +107,7 @@ A supported vehicle is one that just works when you install a comma three. All s |Kia|Niro EV 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| |Kia|Niro Hybrid 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F|| |Kia|Niro Hybrid 2022|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| +|Kia|Niro Hybrid 2023[5](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|| |Kia|Niro Plug-in Hybrid 2018-19|All|openpilot available[1](#footnotes)|10 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| |Kia|Niro Plug-in Hybrid 2020|All|openpilot available[1](#footnotes)|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai D|| |Kia|Optima 2017|Advanced Smart Cruise Control|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai B|| diff --git a/selfdrive/car/hyundai/carstate.py b/selfdrive/car/hyundai/carstate.py index da1a7bfa78..06efa54605 100644 --- a/selfdrive/car/hyundai/carstate.py +++ b/selfdrive/car/hyundai/carstate.py @@ -21,7 +21,9 @@ class CarState(CarStateBase): self.cruise_buttons = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES) self.main_buttons = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES) - self.gear_msg_canfd = "GEAR_ALT" if CP.flags & HyundaiFlags.CANFD_ALT_GEARS else "GEAR_SHIFTER" + self.gear_msg_canfd = "GEAR_ALT_2" if CP.flags & HyundaiFlags.CANFD_ALT_GEARS_2 else \ + "GEAR_ALT" if CP.flags & HyundaiFlags.CANFD_ALT_GEARS else \ + "GEAR_SHIFTER" if CP.carFingerprint in CANFD_CAR: self.shifter_values = can_define.dv[self.gear_msg_canfd]["GEAR"] elif self.CP.carFingerprint in FEATURES["use_cluster_gears"]: @@ -425,7 +427,9 @@ class CarState(CarStateBase): def get_can_parser_canfd(CP): cruise_btn_msg = "CRUISE_BUTTONS_ALT" if CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS else "CRUISE_BUTTONS" - gear_msg = "GEAR_ALT" if CP.flags & HyundaiFlags.CANFD_ALT_GEARS else "GEAR_SHIFTER" + gear_msg = "GEAR_ALT_2" if CP.flags & HyundaiFlags.CANFD_ALT_GEARS_2 else \ + "GEAR_ALT" if CP.flags & HyundaiFlags.CANFD_ALT_GEARS else \ + "GEAR_SHIFTER" signals = [ ("WHEEL_SPEED_1", "WHEEL_SPEEDS"), ("WHEEL_SPEED_2", "WHEEL_SPEEDS"), diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py index c708e2b78e..d3a29dc0cb 100644 --- a/selfdrive/car/hyundai/interface.py +++ b/selfdrive/car/hyundai/interface.py @@ -35,9 +35,12 @@ class CarInterface(CarInterfaceBase): # non-HDA2 if 0x1cf not in fingerprint[4]: ret.flags |= HyundaiFlags.CANFD_ALT_BUTTONS.value - # ICE cars do not have 0x130; GEARS message on 0x40 instead + # ICE cars do not have 0x130; GEARS message on 0x40 or 0x70 instead if 0x130 not in fingerprint[4]: - ret.flags |= HyundaiFlags.CANFD_ALT_GEARS.value + if 0x40 not in fingerprint[4]: + ret.flags |= HyundaiFlags.CANFD_ALT_GEARS_2.value + else: + ret.flags |= HyundaiFlags.CANFD_ALT_GEARS.value if candidate not in CANFD_RADAR_SCC_CAR: ret.flags |= HyundaiFlags.CANFD_CAMERA_SCC.value @@ -129,8 +132,8 @@ class CarInterface(CarInterfaceBase): ret.mass = 1985. + STD_CARGO_KG ret.wheelbase = 2.78 ret.steerRatio = 14.4 * 1.1 # 10% higher at the center seems reasonable - elif candidate in (CAR.KIA_NIRO_EV, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021): - ret.mass = 1737. + STD_CARGO_KG + elif candidate in (CAR.KIA_NIRO_EV, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_NIRO_HEV_2ND_GEN): + ret.mass = 3452. * CV.LB_TO_KG + STD_CARGO_KG # average of all the cars ret.wheelbase = 2.7 ret.steerRatio = 13.9 if CAR.KIA_NIRO_HEV_2021 else 13.73 # Spec tire_stiffness_factor = 0.385 diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 4c8e924c49..af0bc18af9 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -61,6 +61,8 @@ class HyundaiFlags(IntFlag): ENABLE_BLINKERS = 32 + CANFD_ALT_GEARS_2 = 64 + class CAR: # Hyundai @@ -100,6 +102,7 @@ class CAR: KIA_NIRO_EV = "KIA NIRO EV 2020" KIA_NIRO_PHEV = "KIA NIRO HYBRID 2019" KIA_NIRO_HEV_2021 = "KIA NIRO HYBRID 2021" + KIA_NIRO_HEV_2ND_GEN = "KIA NIRO HYBRID 2ND GEN" KIA_OPTIMA_G4 = "KIA OPTIMA 4TH GEN" KIA_OPTIMA_G4_FL = "KIA OPTIMA 4TH GEN FACELIFT" KIA_OPTIMA_H = "KIA OPTIMA HYBRID 2017 & SPORTS 2019" @@ -208,6 +211,7 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { HyundaiCarInfo("Kia Niro Hybrid 2021", harness=Harness.hyundai_f), # TODO: could be hyundai_d, verify HyundaiCarInfo("Kia Niro Hybrid 2022", harness=Harness.hyundai_h), ], + CAR.KIA_NIRO_HEV_2ND_GEN: HyundaiCarInfo("Kia Niro Hybrid 2023", harness=Harness.hyundai_a), CAR.KIA_OPTIMA_G4: HyundaiCarInfo("Kia Optima 2017", "Advanced Smart Cruise Control", harness=Harness.hyundai_b), # TODO: may support 2016, 2018 CAR.KIA_OPTIMA_G4_FL: HyundaiCarInfo("Kia Optima 2019-20", harness=Harness.hyundai_g), CAR.KIA_OPTIMA_H: [ @@ -1627,6 +1631,14 @@ FW_VERSIONS = { b'\xf1\x00MQ4_ SCC FHCUP 1.00 1.06 99110-P2000 ', ], }, + CAR.KIA_NIRO_HEV_2ND_GEN: { + (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00SG2HMFC AT USA LHD 1.01 1.08 99211-AT000 220531', + ], + (Ecu.fwdRadar, 0x7d0, None): [ + b'\xf1\x00SG2_ RDR ----- 1.00 1.01 99110-AT000 ', + ], + }, } CHECKSUM = { @@ -1644,7 +1656,7 @@ FEATURES = { "use_fca": {CAR.SONATA, CAR.SONATA_HYBRID, CAR.ELANTRA, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.KIA_STINGER, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_FORTE, CAR.KIA_NIRO_EV, CAR.PALISADE, CAR.GENESIS_G70, CAR.GENESIS_G70_2020, CAR.KONA, CAR.SANTA_FE, CAR.KIA_SELTOS, CAR.KONA_HEV, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.TUCSON, CAR.KONA_EV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020}, } -CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.TUCSON_4TH_GEN, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, CAR.SANTA_CRUZ_1ST_GEN, CAR.KIA_SPORTAGE_5TH_GEN, CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.GENESIS_GV60_EV_1ST_GEN, CAR.KIA_SORENTO_4TH_GEN} +CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.TUCSON_4TH_GEN, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, CAR.SANTA_CRUZ_1ST_GEN, CAR.KIA_SPORTAGE_5TH_GEN, CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.GENESIS_GV60_EV_1ST_GEN, CAR.KIA_SORENTO_4TH_GEN, CAR.KIA_NIRO_HEV_2ND_GEN} # The radar does SCC on these cars when HDA I, rather than the camera CANFD_RADAR_SCC_CAR = {CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.KIA_SORENTO_4TH_GEN} @@ -1652,7 +1664,7 @@ CANFD_RADAR_SCC_CAR = {CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, C # The camera does SCC on these cars, rather than the radar CAMERA_SCC_CAR = {CAR.KONA_EV_2022, } -HYBRID_CAR = {CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.KIA_K5_HEV_2020} # these cars use a different gas signal +HYBRID_CAR = {CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.KIA_K5_HEV_2020, CAR.KIA_NIRO_HEV_2ND_GEN} # these cars use a different gas signal EV_CAR = {CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.KIA_NIRO_EV, CAR.KONA_EV_2022, CAR.KIA_EV6, CAR.IONIQ_5, CAR.GENESIS_GV60_EV_1ST_GEN} # these cars require a special panda safety mode due to missing counters and checksums in the messages @@ -1714,4 +1726,5 @@ DBC = { CAR.KIA_SORENTO_PHEV_4TH_GEN: dbc_dict('hyundai_canfd', None), CAR.GENESIS_GV60_EV_1ST_GEN: dbc_dict('hyundai_canfd', None), CAR.KIA_SORENTO_4TH_GEN: dbc_dict('hyundai_canfd', None), + CAR.KIA_NIRO_HEV_2ND_GEN: dbc_dict('hyundai_canfd', None), } diff --git a/selfdrive/car/tests/routes.py b/selfdrive/car/tests/routes.py index 535d3f5415..98db2520c3 100644 --- a/selfdrive/car/tests/routes.py +++ b/selfdrive/car/tests/routes.py @@ -127,6 +127,7 @@ routes = [ CarTestRoute("50c6c9b85fd1ff03|2020-10-26--17-56-06", HYUNDAI.KIA_NIRO_EV), CarTestRoute("173219cf50acdd7b|2021-07-05--10-27-41", HYUNDAI.KIA_NIRO_PHEV), CarTestRoute("34a875f29f69841a|2021-07-29--13-02-09", HYUNDAI.KIA_NIRO_HEV_2021), + CarTestRoute("db04d2c63990e3ba|2023-02-08--16-52-39", HYUNDAI.KIA_NIRO_HEV_2ND_GEN), CarTestRoute("50a2212c41f65c7b|2021-05-24--16-22-06", HYUNDAI.KIA_FORTE), CarTestRoute("192283cdbb7a58c2|2022-10-15--01-43-18", HYUNDAI.KIA_SPORTAGE_5TH_GEN), CarTestRoute("c5ac319aa9583f83|2021-06-01--18-18-31", HYUNDAI.ELANTRA), diff --git a/selfdrive/car/torque_data/override.yaml b/selfdrive/car/torque_data/override.yaml index 462ce91b36..52c9e8d547 100644 --- a/selfdrive/car/torque_data/override.yaml +++ b/selfdrive/car/torque_data/override.yaml @@ -38,6 +38,7 @@ GENESIS GV70 1ST GEN: [2.42, 2.42, 0.1] KIA SORENTO PLUG-IN HYBRID 4TH GEN: [2.5, 2.5, 0.1] GENESIS GV60 ELECTRIC 1ST GEN: [2.5, 2.5, 0.1] KIA SORENTO 4TH GEN: [2.5, 2.5, 0.1] +KIA NIRO HYBRID 2ND GEN: [2.42, 2.5, 0.12] # Dashcam or fallback configured as ideal car mock: [10.0, 10, 0.0] From 542345a7c2606e0c2ea9b88c7d91a22c3a97f88a Mon Sep 17 00:00:00 2001 From: Jason Wen <47793918+sunnyhaibin@users.noreply.github.com> Date: Thu, 9 Feb 2023 00:25:31 -0500 Subject: [PATCH 339/484] Hyundai CAN-FD: use common PT bus function (#27268) * Hyundai CAN-FD: PT bus detection cleanup * Do this when ICE CAN-FD is supported! * Only needs to be checked for non-HDA2, HDA2 uses 0x1cf * Unused Co-authored-by: Shane Smiskol * Keep it the same for now --------- Co-authored-by: Shane Smiskol --- selfdrive/car/hyundai/carstate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/car/hyundai/carstate.py b/selfdrive/car/hyundai/carstate.py index 06efa54605..0bab188790 100644 --- a/selfdrive/car/hyundai/carstate.py +++ b/selfdrive/car/hyundai/carstate.py @@ -6,6 +6,7 @@ from cereal import car from common.conversions import Conversions as CV from opendbc.can.parser import CANParser from opendbc.can.can_define import CANDefine +from selfdrive.car.hyundai.hyundaicanfd import get_e_can_bus from selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, FEATURES, CAMERA_SCC_CAR, CANFD_CAR, EV_CAR, HYBRID_CAR, Buttons, CarControllerParams from selfdrive.car.interfaces import CarStateBase @@ -515,8 +516,7 @@ class CarState(CarStateBase): ("ACCELERATOR_BRAKE_ALT", 100), ] - bus = 5 if CP.flags & HyundaiFlags.CANFD_HDA2 else 4 - return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, bus) + return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, get_e_can_bus(CP)) @staticmethod def get_cam_can_parser_canfd(CP): From 1e49c54ffb274a7987626ebfaa8eb3e75ac6fe7c Mon Sep 17 00:00:00 2001 From: YassineYousfi Date: Wed, 8 Feb 2023 22:21:31 -0800 Subject: [PATCH 340/484] new model: improved height estimation (#27234) * 07a3a2b2-6dbe-43c3-9bb9-a8023932e054/449 c3d7d18a-6207-42e7-84d9-601ecb3c6f0e/700 * 07a3a2b2-6dbe-43c3-9bb9-a8023932e054/449 db92e7f3-3f4b-4f0f-95ff-76cb6ed6894a/700 * update ref commit --- selfdrive/modeld/models/supercombo.onnx | 2 +- selfdrive/test/process_replay/model_replay_ref_commit | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/modeld/models/supercombo.onnx b/selfdrive/modeld/models/supercombo.onnx index 2daf01ea14..a483fa4db4 100644 --- a/selfdrive/modeld/models/supercombo.onnx +++ b/selfdrive/modeld/models/supercombo.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d8f2f9d53908e7a5496412956548dab1aa08ed0b5fb9d1c685f275369f07a57 +oid sha256:736ddc08497d7596bae4d9515a8efb996676be80e67a6d34d632bb8af2ed3fa9 size 45962515 diff --git a/selfdrive/test/process_replay/model_replay_ref_commit b/selfdrive/test/process_replay/model_replay_ref_commit index fa4fa9540d..c64f522352 100644 --- a/selfdrive/test/process_replay/model_replay_ref_commit +++ b/selfdrive/test/process_replay/model_replay_ref_commit @@ -1 +1 @@ -2a255ecea3665a3d295210195fd8ef0791ab49a0 +ba947edbb131a2a36ced7c490dfcf3280ad5b167 From 37abe3711788d87255b6b1719ea2dc30f0b57522 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 10 Feb 2023 02:37:37 +0800 Subject: [PATCH 341/484] cabana: set series color in createSeries (#27270) * set color in createSeries * fix --- tools/cabana/chartswidget.cc | 9 +++++---- tools/cabana/chartswidget.h | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 134a162679..e31e437280 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -365,7 +365,7 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { } void ChartView::addSeries(const QString &msg_id, const Signal *sig) { - QXYSeries *series = createSeries(series_type); + QXYSeries *series = createSeries(series_type, getColor(sig)); chart()->addSeries(series); series->attachAxis(axis_x); series->attachAxis(axis_y); @@ -519,8 +519,8 @@ void ChartView::updateSeries(const Signal *sig, const std::vector *even s.vals.clear(); s.vals.reserve(settings.max_cached_minutes * 60 * 100); // [n]seconds * 100hz s.last_value_mono_time = 0; - s.series->setColor(getColor(s.sig)); } + s.series->setColor(getColor(s.sig)); struct Chunk { std::vector::const_iterator first, second; @@ -777,13 +777,14 @@ void ChartView::drawForeground(QPainter *painter, const QRectF &rect) { } } -QXYSeries *ChartView::createSeries(QAbstractSeries::SeriesType type) { +QXYSeries *ChartView::createSeries(QAbstractSeries::SeriesType type, QColor color) { QXYSeries *series = nullptr; if (type == QAbstractSeries::SeriesTypeLine) { series = new QLineSeries(this); } else { series = new QScatterSeries(this); } + series->setColor(color); // TODO: Due to a bug in CameraWidget the camera frames // are drawn instead of the graphs on MacOS. Re-enable OpenGL when fixed #ifndef __APPLE__ @@ -802,7 +803,7 @@ void ChartView::setSeriesType(QAbstractSeries::SeriesType type) { s.series->deleteLater(); } for (auto &s : sigs) { - auto series = createSeries(series_type); + auto series = createSeries(series_type, getColor(s.sig)); chart()->addSeries(series); series->attachAxis(axis_x); series->attachAxis(axis_y); diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index 953026d935..25949dd654 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -76,7 +76,7 @@ private: void drawForeground(QPainter *painter, const QRectF &rect) override; std::tuple getNiceAxisNumbers(qreal min, qreal max, int tick_count); qreal niceNumber(qreal x, bool ceiling); - QXYSeries *createSeries(QAbstractSeries::SeriesType type); + QXYSeries *createSeries(QAbstractSeries::SeriesType type, QColor color); void updateSeriesPoints(); int y_label_width = 0; From b2d9a24d99407b3eb3a63b68404f60496c007c02 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 9 Feb 2023 11:43:34 -0800 Subject: [PATCH 342/484] Chrysler: remove Pacifica 2018 ICE from tuning blacklist (#27272) remove Pacifica 2018 ICE from blacklist --- selfdrive/car/chrysler/values.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/selfdrive/car/chrysler/values.py b/selfdrive/car/chrysler/values.py index e932238e6a..49f8a080cb 100644 --- a/selfdrive/car/chrysler/values.py +++ b/selfdrive/car/chrysler/values.py @@ -62,8 +62,7 @@ RAM_CARS = RAM_DT | RAM_HD # the increased steer rate hasn't been verified on these cars. # remove from this list once it's been tested and confirmed to not fault -CHRYSLER_OLD_TUNING_BLACKLIST = {CAR.PACIFICA_2017_HYBRID, CAR.PACIFICA_2018, CAR.PACIFICA_2018_HYBRID, - CAR.PACIFICA_2020, CAR.JEEP_CHEROKEE} +CHRYSLER_OLD_TUNING_BLACKLIST = {CAR.PACIFICA_2017_HYBRID, CAR.PACIFICA_2018_HYBRID, CAR.PACIFICA_2020, CAR.JEEP_CHEROKEE} @dataclass class ChryslerCarInfo(CarInfo): From be6f5c6a3392a170c266254898c1bc7c7b182502 Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Thu, 9 Feb 2023 15:34:08 -0500 Subject: [PATCH 343/484] VW MQB: Add FW for 2023 Volkswagen Tiguan (#27273) * VW MQB: Add FW for 2023 Volkswagen Tiguan * regen CARS.md --- docs/CARS.md | 2 +- selfdrive/car/volkswagen/values.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index a72bf74278..c00bd4f324 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -241,7 +241,7 @@ A supported vehicle is one that just works when you install a comma three. All s |Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| |Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| |Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Volkswagen|Tiguan 2019-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Volkswagen|Tiguan 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| |Volkswagen|Touran 2017|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py index 72fcc5d92d..cd30bef73b 100755 --- a/selfdrive/car/volkswagen/values.py +++ b/selfdrive/car/volkswagen/values.py @@ -222,7 +222,7 @@ CAR_INFO: Dict[str, Union[VWCarInfo, List[VWCarInfo]]] = { ], CAR.TAOS_MK1: VWCarInfo("Volkswagen Taos 2022"), CAR.TCROSS_MK1: VWCarInfo("Volkswagen T-Cross 2021", footnotes=[Footnote.VW_MQB_A0]), - CAR.TIGUAN_MK2: VWCarInfo("Volkswagen Tiguan 2019-22"), + CAR.TIGUAN_MK2: VWCarInfo("Volkswagen Tiguan 2018-23"), CAR.TOURAN_MK2: VWCarInfo("Volkswagen Touran 2017"), CAR.TRANSPORTER_T61: [ VWCarInfo("Volkswagen Caravelle 2020"), @@ -734,6 +734,7 @@ FW_VERSIONS = { b'\xf1\x8704L906026EJ\xf1\x893661', b'\xf1\x8704L906027G \xf1\x899893', b'\xf1\x875N0906259 \xf1\x890002', + b'\xf1\x875NA906259H \xf1\x890002', b'\xf1\x875NA907115E \xf1\x890005', b'\xf1\x8783A907115B \xf1\x890005', b'\xf1\x8783A907115F \xf1\x890002', @@ -751,6 +752,7 @@ FW_VERSIONS = { b'\xf1\x870DL300013A \xf1\x893005', b'\xf1\x870DL300013G \xf1\x892119', b'\xf1\x870DL300013G \xf1\x892120', + b'\xf1\x870DL300014C \xf1\x893703', ], (Ecu.srs, 0x715, None): [ b'\xf1\x875Q0959655AR\xf1\x890317\xf1\x82\02331310031333334313132573732379333313100', @@ -761,6 +763,7 @@ FW_VERSIONS = { b'\xf1\x875Q0959655BT\xf1\x890403\xf1\x82\x1331310031333334313140013750379333423100', b'\xf1\x875Q0959655BT\xf1\x890403\xf1\x82\x1331310031333334313140573752379333423100', b'\xf1\x875Q0959655CB\xf1\x890421\xf1\x82\x1316143231313500314647021750179333613100', + b'\xf1\x875Q0959655CG\xf1\x890421\xf1\x82\x1331310031333300314240024050409333613100', ], (Ecu.eps, 0x712, None): [ b'\xf1\x875Q0909143M \xf1\x892041\xf1\x820529A6060603', @@ -768,6 +771,7 @@ FW_VERSIONS = { b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567A6000600', b'\xf1\x875QF909144A \xf1\x895581\xf1\x82\x0571A60834A1', b'\xf1\x875QF909144B \xf1\x895582\xf1\x82\00571A60634A1', + b'\xf1\x875QF909144B \xf1\x895582\xf1\x82\x0571A62A32A1', b'\xf1\x875QM909144B \xf1\x891081\xf1\x82\x0521A60604A1', b'\xf1\x875QM909144C \xf1\x891082\xf1\x82\x0521A60604A1', b'\xf1\x875QM909144C \xf1\x891082\xf1\x82\00521A60804A1', From cd37231666f7758697cbdeece7db6067ee37dd75 Mon Sep 17 00:00:00 2001 From: ClockeNessMnstr Date: Thu, 9 Feb 2023 15:51:50 -0500 Subject: [PATCH 344/484] add g++-12 dependancy for Ubuntu 22.04 (#27263) add g++-12 dependancy for 22.04 scons -u -j8 gave clang++ not finding iostream and others. https://askubuntu.com/questions/1449769/clang-cannot-find-iostream solution at the bottom of the page worked. installed g++-12 and built fine after that. --- tools/ubuntu_setup.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/ubuntu_setup.sh b/tools/ubuntu_setup.sh index 09296ef94d..71bad2e8a2 100755 --- a/tools/ubuntu_setup.sh +++ b/tools/ubuntu_setup.sh @@ -86,6 +86,7 @@ function install_ubuntu_lts_latest_requirements() { install_ubuntu_common_requirements $SUDO apt-get install -y --no-install-recommends \ + g++-12 \ qtbase5-dev \ qtchooser \ qt5-qmake \ From 9aac0806e3c25c374b67770086cdfd67ffafaf07 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 9 Feb 2023 13:24:52 -0800 Subject: [PATCH 345/484] GM: increase min steer speed to avoid temporary faults (#27274) * increase min steer speed to avoid temp volt faults * actually 10.15 is safe * Update ref_commit --- selfdrive/car/gm/interface.py | 2 +- selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index 3c2a12ef86..f7473d88fc 100755 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -101,7 +101,7 @@ class CarInterface(CarInterfaceBase): # Start with a baseline tuning for all GM vehicles. Override tuning as needed in each model section below. # Some GMs need some tolerance above 10 kph to avoid a fault - ret.minSteerSpeed = 10.1 * CV.KPH_TO_MS + ret.minSteerSpeed = 10.2 * CV.KPH_TO_MS ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.00]] ret.lateralTuning.pid.kf = 0.00004 # full torque for 20 deg at 80mph means 0.00007818594 diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 170beafe88..8a75a72483 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -a6b17ab903022b42677abe2968f3c6e50c331980 +35a3dbcbcd8504388ea2a70965be0b4e0869b06a From ab6bb8a2fadcbd2e0f34c2ec4df5801419500cc9 Mon Sep 17 00:00:00 2001 From: chaichuansheng88 <124353026+chaichuansheng88@users.noreply.github.com> Date: Fri, 10 Feb 2023 05:50:53 +0800 Subject: [PATCH 346/484] Hyundai: add IONIQ 5 fwdCamera FW version (#27209) Update values.py Co-authored-by: Shane Smiskol --- selfdrive/car/hyundai/values.py | 1 + 1 file changed, 1 insertion(+) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index af0bc18af9..2241b871a1 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -1557,6 +1557,7 @@ FW_VERSIONS = { b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.05 99211-GI010 220614', b'\xf1\x00NE1 MFC AT EUR RHD 1.00 1.01 99211-GI010 211007', b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.01 99211-GI010 211007', + b'\xf1\x00NE1 MFC AT EUR RHD 1.00 1.02 99211-GI010 211206', ], }, CAR.TUCSON_4TH_GEN: { From cb0522379d85cdf41cdb8ebd11b9119a0b7a364e Mon Sep 17 00:00:00 2001 From: N0VA-DKW <40765867+N0VA-DKW@users.noreply.github.com> Date: Thu, 9 Feb 2023 16:16:07 -0600 Subject: [PATCH 347/484] Subaru: add Crosstrek 2022 FW versions (#27222) * Update values.py * Revert "Update values.py" This reverts commit cd9f841c7a97ca800564605e68ea15d611abb3bc. * do clean up later --------- Co-authored-by: Shane Smiskol --- selfdrive/car/subaru/values.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/selfdrive/car/subaru/values.py b/selfdrive/car/subaru/values.py index 6ac2637fa2..5c7d3f5e4d 100644 --- a/selfdrive/car/subaru/values.py +++ b/selfdrive/car/subaru/values.py @@ -253,6 +253,7 @@ FW_VERSIONS = { b'\xca!f@\x07', b'\xca!fp\x07', b'\xf3"f@\x07', + b'\xe6!fp\x07', ], (Ecu.transmission, 0x7e1, None): [ b'\xe6\xf5\004\000\000', @@ -262,6 +263,7 @@ FW_VERSIONS = { b'\xf1\x00\xd7\x10@', b'\xe6\xf5D0\x00', b'\xe9\xf6F0\x00', + b'\xe9\xf5B0\x00', ], }, CAR.FORESTER: { From a1e5ab1f0756b2955d8970fbbb47c90ec7bede8a Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 9 Feb 2023 14:21:41 -0800 Subject: [PATCH 348/484] Add C-HR 2021 to release notes --- RELEASES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASES.md b/RELEASES.md index d99fe8183f..cce7207ab0 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -12,6 +12,7 @@ Version 0.9.1 (2023-2-20) * Kia Niro Hybrid 2023 support thanks to sunnyhaibin! * Kia Sorento 2022-23 support thanks to sunnyhaibin! * Kia Sorento Plug-in Hybrid 2022 support thanks to sunnyhaibin! +* Toyota C-HR 2021 support thanks to eFiniLan! * Volkswagen Crafter and MAN TGE 2017-23 support thanks to jyoung8607! Version 0.9.0 (2022-11-21) From 95fc84e0b78843a7c8b1a3c42a751d072378960d Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 10 Feb 2023 06:52:44 +0800 Subject: [PATCH 349/484] cabana: catch exceptions thrown from opendbc (#27242) * catch exceptions thrown from opendbc * Update tools/cabana/mainwin.cc Co-authored-by: Cameron Clough * Update tools/cabana/mainwin.cc Co-authored-by: Cameron Clough * show error line in detailed text --------- Co-authored-by: Cameron Clough --- tools/cabana/dbcmanager.cc | 14 ++++++++++---- tools/cabana/dbcmanager.h | 2 +- tools/cabana/mainwin.cc | 24 ++++++++++++++++++------ 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/tools/cabana/dbcmanager.cc b/tools/cabana/dbcmanager.cc index 83f5fdb74a..3d565e7067 100644 --- a/tools/cabana/dbcmanager.cc +++ b/tools/cabana/dbcmanager.cc @@ -13,10 +13,16 @@ void DBCManager::open(const QString &dbc_file_name) { initMsgMap(); } -void DBCManager::open(const QString &name, const QString &content) { - std::istringstream stream(content.toStdString()); - dbc = const_cast(dbc_parse_from_stream(name.toStdString(), stream)); - initMsgMap(); +bool DBCManager::open(const QString &name, const QString &content, QString *error) { + try { + std::istringstream stream(content.toStdString()); + dbc = const_cast(dbc_parse_from_stream(name.toStdString(), stream)); + initMsgMap(); + return true; + } catch (std::exception &e) { + if (error) *error = e.what(); + } + return false; } void DBCManager::initMsgMap() { diff --git a/tools/cabana/dbcmanager.h b/tools/cabana/dbcmanager.h index 03bd16f2a5..41471a6169 100644 --- a/tools/cabana/dbcmanager.h +++ b/tools/cabana/dbcmanager.h @@ -22,7 +22,7 @@ public: ~DBCManager(); void open(const QString &dbc_file_name); - void open(const QString &name, const QString &content); + bool open(const QString &name, const QString &content, QString *error = nullptr); QString generateDBC(); void addSignal(const QString &id, const Signal &sig); void updateSignal(const QString &id, const QString &sig_name, const Signal &sig); diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index b24c4e6dc4..bce6d313c8 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -220,9 +220,16 @@ void MainWindow::loadFile(const QString &fn) { QFile file(fn); if (file.open(QIODevice::ReadOnly)) { auto dbc_name = QFileInfo(fn).baseName(); - dbc()->open(dbc_name, file.readAll()); - setCurrentFile(fn); - statusBar()->showMessage(tr("DBC File %1 loaded").arg(fn), 2000); + QString error; + bool ret = dbc()->open(dbc_name, file.readAll(), &error); + if (ret) { + setCurrentFile(fn); + statusBar()->showMessage(tr("DBC File %1 loaded").arg(fn), 2000); + } else { + QMessageBox msg_box(QMessageBox::Warning, tr("Failed to load DBC file"), tr("Failed to parse DBC file %1").arg(fn)); + msg_box.setDetailedText(error); + msg_box.exec(); + } } } } @@ -251,11 +258,16 @@ void MainWindow::loadDBCFromOpendbc(const QString &name) { void MainWindow::loadDBCFromClipboard() { remindSaveChanges(); QString dbc_str = QGuiApplication::clipboard()->text(); - dbc()->open("from_clipboard.dbc", dbc_str); - if (dbc()->messages().size() > 0) { + QString error; + bool ret = dbc()->open("clipboard", dbc_str, &error); + if (ret && dbc()->messages().size() > 0) { QMessageBox::information(this, tr("Load From Clipboard"), tr("DBC Successfully Loaded!")); } else { - QMessageBox::warning(this, tr("Load From Clipboard"), tr("Failed to parse dbc from clipboard!\nMake sure that you paste the text with correct format.")); + QMessageBox msg_box(QMessageBox::Warning, tr("Failed to load DBC from clipboard"), tr("Make sure that you paste the text with correct format.")); + if (!error.isEmpty()) { + msg_box.setDetailedText(error); + } + msg_box.exec(); } } From 8f5057ff2dbea4ebc85bc8f55bc7a37e4d5969b6 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 9 Feb 2023 15:37:39 -0800 Subject: [PATCH 350/484] GM: enforce steering command message timing (#27250) * draft * bump opendbc * still draft * that's not right * superset of the changes, 33hz * cleanup * this should work * remove line * pass it in again * actually no need to check updated now * now_nanos * consistent name * fix replay * one line isn't that bad switch switch * fix CarController tests * Update ref_commit --- selfdrive/car/body/carcontroller.py | 2 +- selfdrive/car/body/interface.py | 4 ++-- selfdrive/car/chrysler/carcontroller.py | 2 +- selfdrive/car/chrysler/interface.py | 4 ++-- selfdrive/car/ford/carcontroller.py | 2 +- selfdrive/car/ford/interface.py | 4 ++-- selfdrive/car/gm/carcontroller.py | 11 +++++++---- selfdrive/car/gm/carstate.py | 2 ++ selfdrive/car/gm/interface.py | 4 ++-- selfdrive/car/honda/carcontroller.py | 2 +- selfdrive/car/honda/interface.py | 4 ++-- selfdrive/car/hyundai/carcontroller.py | 2 +- selfdrive/car/hyundai/interface.py | 4 ++-- selfdrive/car/interfaces.py | 2 +- selfdrive/car/mazda/carcontroller.py | 2 +- selfdrive/car/mazda/interface.py | 4 ++-- selfdrive/car/mock/interface.py | 2 +- selfdrive/car/nissan/carcontroller.py | 2 +- selfdrive/car/nissan/interface.py | 4 ++-- selfdrive/car/subaru/carcontroller.py | 2 +- selfdrive/car/subaru/interface.py | 4 ++-- selfdrive/car/tesla/carcontroller.py | 2 +- selfdrive/car/tesla/interface.py | 4 ++-- selfdrive/car/tests/test_car_interfaces.py | 8 ++++---- selfdrive/car/tests/test_models.py | 2 +- selfdrive/car/toyota/carcontroller.py | 2 +- selfdrive/car/toyota/interface.py | 4 ++-- selfdrive/car/volkswagen/carcontroller.py | 2 +- selfdrive/car/volkswagen/interface.py | 4 ++-- selfdrive/controls/controlsd.py | 6 +++++- selfdrive/test/process_replay/ref_commit | 2 +- 31 files changed, 57 insertions(+), 48 deletions(-) diff --git a/selfdrive/car/body/carcontroller.py b/selfdrive/car/body/carcontroller.py index 00673a7e28..bcaf6f6f93 100644 --- a/selfdrive/car/body/carcontroller.py +++ b/selfdrive/car/body/carcontroller.py @@ -35,7 +35,7 @@ class CarController: torque -= deadband return torque - def update(self, CC, CS): + def update(self, CC, CS, now_nanos): torque_l = 0 torque_r = 0 diff --git a/selfdrive/car/body/interface.py b/selfdrive/car/body/interface.py index a90c4cfb87..850a3538ad 100644 --- a/selfdrive/car/body/interface.py +++ b/selfdrive/car/body/interface.py @@ -43,5 +43,5 @@ class CarInterface(CarInterfaceBase): return ret - def apply(self, c): - return self.CC.update(c, self.CS) + def apply(self, c, now_nanos): + return self.CC.update(c, self.CS, now_nanos) diff --git a/selfdrive/car/chrysler/carcontroller.py b/selfdrive/car/chrysler/carcontroller.py index ba6aaf8250..20a44bce21 100644 --- a/selfdrive/car/chrysler/carcontroller.py +++ b/selfdrive/car/chrysler/carcontroller.py @@ -19,7 +19,7 @@ class CarController: self.packer = CANPacker(dbc_name) self.params = CarControllerParams(CP) - def update(self, CC, CS): + def update(self, CC, CS, now_nanos): can_sends = [] lkas_active = CC.latActive and self.lkas_control_bit_prev diff --git a/selfdrive/car/chrysler/interface.py b/selfdrive/car/chrysler/interface.py index 2b2fde8dc3..657b018c00 100755 --- a/selfdrive/car/chrysler/interface.py +++ b/selfdrive/car/chrysler/interface.py @@ -106,5 +106,5 @@ class CarInterface(CarInterfaceBase): return ret - def apply(self, c): - return self.CC.update(c, self.CS) + def apply(self, c, now_nanos): + return self.CC.update(c, self.CS, now_nanos) diff --git a/selfdrive/car/ford/carcontroller.py b/selfdrive/car/ford/carcontroller.py index 00fb461223..99072ae975 100644 --- a/selfdrive/car/ford/carcontroller.py +++ b/selfdrive/car/ford/carcontroller.py @@ -21,7 +21,7 @@ class CarController: self.lkas_enabled_last = False self.steer_alert_last = False - def update(self, CC, CS): + def update(self, CC, CS, now_nanos): can_sends = [] actuators = CC.actuators diff --git a/selfdrive/car/ford/interface.py b/selfdrive/car/ford/interface.py index 8ce30e5d68..9e1366618c 100644 --- a/selfdrive/car/ford/interface.py +++ b/selfdrive/car/ford/interface.py @@ -78,5 +78,5 @@ class CarInterface(CarInterfaceBase): return ret - def apply(self, c): - return self.CC.update(c, self.CS) + def apply(self, c, now_nanos): + return self.CC.update(c, self.CS, now_nanos) diff --git a/selfdrive/car/gm/carcontroller.py b/selfdrive/car/gm/carcontroller.py index 97f8b60e59..73085d30b0 100644 --- a/selfdrive/car/gm/carcontroller.py +++ b/selfdrive/car/gm/carcontroller.py @@ -13,6 +13,8 @@ LongCtrlState = car.CarControl.Actuators.LongControlState # Camera cancels up to 0.1s after brake is pressed, ECM allows 0.5s CAMERA_CANCEL_DELAY_FRAMES = 10 +# Enforce a minimum interval between steering messages to avoid a fault +MIN_STEER_MSG_INTERVAL_MS = 15 class CarController: @@ -37,7 +39,7 @@ class CarController: self.packer_obj = CANPacker(DBC[self.CP.carFingerprint]['radar']) self.packer_ch = CANPacker(DBC[self.CP.carFingerprint]['chassis']) - def update(self, CC, CS): + def update(self, CC, CS, now_nanos): actuators = CC.actuators hud_control = CC.hudControl hud_alert = hud_control.visualAlert @@ -64,9 +66,10 @@ class CarController: self.lka_steering_cmd_counter += 1 self.sent_lka_steering_cmd = True - # Avoid GM EPS faults when transmitting messages too close together: skip this transmit if we just - # received the ASCMLKASteeringCmd loopback confirmation in the current CS frame - if (self.frame - self.last_steer_frame) >= steer_step and not CS.loopback_lka_steering_cmd_updated: + # Avoid GM EPS faults when transmitting messages too close together: skip this transmit if we + # received the ASCMLKASteeringCmd loopback confirmation too recently + last_lka_steer_msg_ms = (now_nanos - CS.loopback_lka_steering_cmd_ts_nanos) * 1e-6 + if (self.frame - self.last_steer_frame) >= steer_step and last_lka_steer_msg_ms > MIN_STEER_MSG_INTERVAL_MS: # Initialize ASCMLKASteeringCmd counter using the camera until we get a msg on the bus if not self.sent_lka_steering_cmd: self.lka_steering_cmd_counter = CS.pt_lka_steering_cmd_counter + 1 diff --git a/selfdrive/car/gm/carstate.py b/selfdrive/car/gm/carstate.py index b73b7e7059..3ddec13ced 100644 --- a/selfdrive/car/gm/carstate.py +++ b/selfdrive/car/gm/carstate.py @@ -18,6 +18,7 @@ class CarState(CarStateBase): can_define = CANDefine(DBC[CP.carFingerprint]["pt"]) self.shifter_values = can_define.dv["ECMPRDNL2"]["PRNDL2"] self.loopback_lka_steering_cmd_updated = False + self.loopback_lka_steering_cmd_ts_nanos = 0 self.pt_lka_steering_cmd_counter = 0 self.cam_lka_steering_cmd_counter = 0 self.buttons_counter = 0 @@ -33,6 +34,7 @@ class CarState(CarStateBase): # Variables used for avoiding LKAS faults self.loopback_lka_steering_cmd_updated = len(loopback_cp.vl_all["ASCMLKASteeringCmd"]["RollingCounter"]) > 0 + self.loopback_lka_steering_cmd_ts_nanos = loopback_cp.ts_nanos["ASCMLKASteeringCmd"]["RollingCounter"] if self.CP.networkLocation == NetworkLocation.fwdCamera: self.pt_lka_steering_cmd_counter = pt_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"] self.cam_lka_steering_cmd_counter = cam_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"] diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index f7473d88fc..5ad1776f97 100755 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -236,5 +236,5 @@ class CarInterface(CarInterfaceBase): return ret - def apply(self, c): - return self.CC.update(c, self.CS) + def apply(self, c, now_nanos): + return self.CC.update(c, self.CS, now_nanos) diff --git a/selfdrive/car/honda/carcontroller.py b/selfdrive/car/honda/carcontroller.py index ab944a30aa..4dc1dc8131 100644 --- a/selfdrive/car/honda/carcontroller.py +++ b/selfdrive/car/honda/carcontroller.py @@ -124,7 +124,7 @@ class CarController: self.brake = 0.0 self.last_steer = 0.0 - def update(self, CC, CS): + def update(self, CC, CS, now_nanos): actuators = CC.actuators hud_control = CC.hudControl hud_v_cruise = hud_control.setSpeed * CV.MS_TO_KPH if hud_control.speedVisible else 255 diff --git a/selfdrive/car/honda/interface.py b/selfdrive/car/honda/interface.py index 98243d81dd..66c0ce4275 100755 --- a/selfdrive/car/honda/interface.py +++ b/selfdrive/car/honda/interface.py @@ -348,5 +348,5 @@ class CarInterface(CarInterfaceBase): # pass in a car.CarControl # to be called @ 100hz - def apply(self, c): - return self.CC.update(c, self.CS) + def apply(self, c, now_nanos): + return self.CC.update(c, self.CS, now_nanos) diff --git a/selfdrive/car/hyundai/carcontroller.py b/selfdrive/car/hyundai/carcontroller.py index 6f043fddbb..7c31a48ba5 100644 --- a/selfdrive/car/hyundai/carcontroller.py +++ b/selfdrive/car/hyundai/carcontroller.py @@ -54,7 +54,7 @@ class CarController: self.car_fingerprint = CP.carFingerprint self.last_button_frame = 0 - def update(self, CC, CS): + def update(self, CC, CS, now_nanos): actuators = CC.actuators hud_control = CC.hudControl diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py index d3a29dc0cb..b746680930 100644 --- a/selfdrive/car/hyundai/interface.py +++ b/selfdrive/car/hyundai/interface.py @@ -330,5 +330,5 @@ class CarInterface(CarInterfaceBase): return ret - def apply(self, c): - return self.CC.update(c, self.CS) + def apply(self, c, now_nanos): + return self.CC.update(c, self.CS, now_nanos) diff --git a/selfdrive/car/interfaces.py b/selfdrive/car/interfaces.py index e75067da7e..249818369c 100644 --- a/selfdrive/car/interfaces.py +++ b/selfdrive/car/interfaces.py @@ -233,7 +233,7 @@ class CarInterfaceBase(ABC): return reader @abstractmethod - def apply(self, c: car.CarControl) -> Tuple[car.CarControl.Actuators, List[bytes]]: + def apply(self, c: car.CarControl, now_nanos: int) -> Tuple[car.CarControl.Actuators, List[bytes]]: pass def create_common_events(self, cs_out, extra_gears=None, pcm_enable=True, allow_enable=True, diff --git a/selfdrive/car/mazda/carcontroller.py b/selfdrive/car/mazda/carcontroller.py index 027822cc3f..524a02a370 100644 --- a/selfdrive/car/mazda/carcontroller.py +++ b/selfdrive/car/mazda/carcontroller.py @@ -15,7 +15,7 @@ class CarController: self.brake_counter = 0 self.frame = 0 - def update(self, CC, CS): + def update(self, CC, CS, now_nanos): can_sends = [] apply_steer = 0 diff --git a/selfdrive/car/mazda/interface.py b/selfdrive/car/mazda/interface.py index 65444ff6e0..2930b002d4 100755 --- a/selfdrive/car/mazda/interface.py +++ b/selfdrive/car/mazda/interface.py @@ -69,5 +69,5 @@ class CarInterface(CarInterfaceBase): return ret - def apply(self, c): - return self.CC.update(c, self.CS) + def apply(self, c, now_nanos): + return self.CC.update(c, self.CS, now_nanos) diff --git a/selfdrive/car/mock/interface.py b/selfdrive/car/mock/interface.py index 3ac487dbb7..13210c86d5 100755 --- a/selfdrive/car/mock/interface.py +++ b/selfdrive/car/mock/interface.py @@ -57,7 +57,7 @@ class CarInterface(CarInterfaceBase): return ret - def apply(self, c): + def apply(self, c, now_nanos): # in mock no carcontrols actuators = car.CarControl.Actuators.new_message() return actuators, [] diff --git a/selfdrive/car/nissan/carcontroller.py b/selfdrive/car/nissan/carcontroller.py index ff13812398..45c3dd720c 100644 --- a/selfdrive/car/nissan/carcontroller.py +++ b/selfdrive/car/nissan/carcontroller.py @@ -18,7 +18,7 @@ class CarController: self.packer = CANPacker(dbc_name) - def update(self, CC, CS): + def update(self, CC, CS, now_nanos): actuators = CC.actuators hud_control = CC.hudControl pcm_cancel_cmd = CC.cruiseControl.cancel diff --git a/selfdrive/car/nissan/interface.py b/selfdrive/car/nissan/interface.py index 2e769cf662..074cd1cc57 100644 --- a/selfdrive/car/nissan/interface.py +++ b/selfdrive/car/nissan/interface.py @@ -56,5 +56,5 @@ class CarInterface(CarInterfaceBase): return ret - def apply(self, c): - return self.CC.update(c, self.CS) + def apply(self, c, now_nanos): + return self.CC.update(c, self.CS, now_nanos) diff --git a/selfdrive/car/subaru/carcontroller.py b/selfdrive/car/subaru/carcontroller.py index a56e63408e..24d85877d7 100644 --- a/selfdrive/car/subaru/carcontroller.py +++ b/selfdrive/car/subaru/carcontroller.py @@ -19,7 +19,7 @@ class CarController: self.p = CarControllerParams(CP) self.packer = CANPacker(DBC[CP.carFingerprint]['pt']) - def update(self, CC, CS): + def update(self, CC, CS, now_nanos): actuators = CC.actuators hud_control = CC.hudControl pcm_cancel_cmd = CC.cruiseControl.cancel diff --git a/selfdrive/car/subaru/interface.py b/selfdrive/car/subaru/interface.py index f94333baab..733482ef82 100644 --- a/selfdrive/car/subaru/interface.py +++ b/selfdrive/car/subaru/interface.py @@ -112,5 +112,5 @@ class CarInterface(CarInterfaceBase): return ret - def apply(self, c): - return self.CC.update(c, self.CS) + def apply(self, c, now_nanos): + return self.CC.update(c, self.CS, now_nanos) diff --git a/selfdrive/car/tesla/carcontroller.py b/selfdrive/car/tesla/carcontroller.py index 6e2869d1c2..1f18c3d0ea 100644 --- a/selfdrive/car/tesla/carcontroller.py +++ b/selfdrive/car/tesla/carcontroller.py @@ -14,7 +14,7 @@ class CarController: self.pt_packer = CANPacker(DBC[CP.carFingerprint]['pt']) self.tesla_can = TeslaCAN(self.packer, self.pt_packer) - def update(self, CC, CS): + def update(self, CC, CS, now_nanos): actuators = CC.actuators pcm_cancel_cmd = CC.cruiseControl.cancel diff --git a/selfdrive/car/tesla/interface.py b/selfdrive/car/tesla/interface.py index 6a73472108..70d49896cb 100755 --- a/selfdrive/car/tesla/interface.py +++ b/selfdrive/car/tesla/interface.py @@ -58,5 +58,5 @@ class CarInterface(CarInterfaceBase): return ret - def apply(self, c): - return self.CC.update(c, self.CS) + def apply(self, c, now_nanos): + return self.CC.update(c, self.CS, now_nanos) diff --git a/selfdrive/car/tests/test_car_interfaces.py b/selfdrive/car/tests/test_car_interfaces.py index 24c995a2d0..78ecbe425e 100755 --- a/selfdrive/car/tests/test_car_interfaces.py +++ b/selfdrive/car/tests/test_car_interfaces.py @@ -59,15 +59,15 @@ class TestCarInterfaces(unittest.TestCase): CC = car.CarControl.new_message() for _ in range(10): car_interface.update(CC, []) - car_interface.apply(CC) - car_interface.apply(CC) + car_interface.apply(CC, 0) + car_interface.apply(CC, 0) CC = car.CarControl.new_message() CC.enabled = True for _ in range(10): car_interface.update(CC, []) - car_interface.apply(CC) - car_interface.apply(CC) + car_interface.apply(CC, 0) + car_interface.apply(CC, 0) # Test radar interface RadarInterface = importlib.import_module(f'selfdrive.car.{car_params.carName}.radar_interface').RadarInterface diff --git a/selfdrive/car/tests/test_models.py b/selfdrive/car/tests/test_models.py index 4627b9a570..6fbe1436f1 100755 --- a/selfdrive/car/tests/test_models.py +++ b/selfdrive/car/tests/test_models.py @@ -149,7 +149,7 @@ class TestCarModelBase(unittest.TestCase): for i, msg in enumerate(self.can_msgs): CS = self.CI.update(CC, (msg.as_builder().to_bytes(),)) - self.CI.apply(CC) + self.CI.apply(CC, msg.logMonoTime) if CS.canValid: can_valid = True diff --git a/selfdrive/car/toyota/carcontroller.py b/selfdrive/car/toyota/carcontroller.py index 222cf70613..2e0d7009c8 100644 --- a/selfdrive/car/toyota/carcontroller.py +++ b/selfdrive/car/toyota/carcontroller.py @@ -34,7 +34,7 @@ class CarController: self.gas = 0 self.accel = 0 - def update(self, CC, CS): + def update(self, CC, CS, now_nanos): actuators = CC.actuators hud_control = CC.hudControl pcm_cancel_cmd = CC.cruiseControl.cancel diff --git a/selfdrive/car/toyota/interface.py b/selfdrive/car/toyota/interface.py index 438a9c11fc..a09b0e5265 100644 --- a/selfdrive/car/toyota/interface.py +++ b/selfdrive/car/toyota/interface.py @@ -263,5 +263,5 @@ class CarInterface(CarInterfaceBase): # pass in a car.CarControl # to be called @ 100hz - def apply(self, c): - return self.CC.update(c, self.CS) + def apply(self, c, now_nanos): + return self.CC.update(c, self.CS, now_nanos) diff --git a/selfdrive/car/volkswagen/carcontroller.py b/selfdrive/car/volkswagen/carcontroller.py index 4b19f4d13c..5d00b5a52f 100644 --- a/selfdrive/car/volkswagen/carcontroller.py +++ b/selfdrive/car/volkswagen/carcontroller.py @@ -24,7 +24,7 @@ class CarController: self.hcaSameTorqueCount = 0 self.hcaEnabledFrameCount = 0 - def update(self, CC, CS, ext_bus): + def update(self, CC, CS, ext_bus, now_nanos): actuators = CC.actuators hud_control = CC.hudControl can_sends = [] diff --git a/selfdrive/car/volkswagen/interface.py b/selfdrive/car/volkswagen/interface.py index 1e620b4f73..521c68184d 100644 --- a/selfdrive/car/volkswagen/interface.py +++ b/selfdrive/car/volkswagen/interface.py @@ -244,5 +244,5 @@ class CarInterface(CarInterfaceBase): return ret - def apply(self, c): - return self.CC.update(c, self.CS, self.ext_bus) + def apply(self, c, now_nanos): + return self.CC.update(c, self.CS, self.ext_bus, now_nanos) diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index fff6bcf576..1bb5be9a56 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -187,6 +187,7 @@ class Controls: # TODO: no longer necessary, aside from process replay self.sm['liveParameters'].valid = True + self.can_log_mono_time = 0 self.startup_event = get_startup_event(car_recognized, controller_available, len(self.CP.carFw) > 0) @@ -425,6 +426,8 @@ class Controls: # Update carState from CAN can_strs = messaging.drain_sock_raw(self.can_sock, wait_for_one=True) CS = self.CI.update(self.CC, can_strs) + if len(can_strs) and REPLAY: + self.can_log_mono_time = messaging.log_from_bytes(can_strs[0]).logMonoTime self.sm.update(0) @@ -731,7 +734,8 @@ class Controls: if not self.read_only and self.initialized: # send car controls over can - self.last_actuators, can_sends = self.CI.apply(CC) + now_nanos = self.can_log_mono_time if REPLAY else int(sec_since_boot() * 1e9) + self.last_actuators, can_sends = self.CI.apply(CC, now_nanos) self.pm.send('sendcan', can_list_to_can_capnp(can_sends, msgtype='sendcan', valid=CS.canValid)) CC.actuatorsOutput = self.last_actuators if self.CP.steerControlType == car.CarParams.SteerControlType.angle: diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 8a75a72483..d334792c48 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -35a3dbcbcd8504388ea2a70965be0b4e0869b06a +9bfd30202a9e70d9d7459cc02f86c3e55d7c864c From 37f3fe5eadf41a070bb59a62ce2913c77250afe7 Mon Sep 17 00:00:00 2001 From: Erich Moraga <33645296+ErichMoraga@users.noreply.github.com> Date: Thu, 9 Feb 2023 18:37:34 -0600 Subject: [PATCH 351/484] Add several missing CHR_TSS2 firmwares (#27278) `@Dk#3633` 2021 C-HR DongleID/route 4930c054f086c108|2023-02-09--18-39-48 Verified working great, per user. Hot off the heels of @eFiniLan's https://github.com/commaai/openpilot/pull/27212 :wink: --- selfdrive/car/toyota/values.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index 7e801990a7..b28059ae23 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -233,7 +233,7 @@ FW_QUERY_CONFIG = FwQueryConfig( # FIXME: On some models, abs can sometimes be missing Ecu.abs: [CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.SIENNA, CAR.LEXUS_IS], # On some models, the engine can show on two different addresses - Ecu.engine: [CAR.CAMRY, CAR.COROLLA_TSS2, CAR.CHR, CAR.LEXUS_IS, CAR.LEXUS_RC], + Ecu.engine: [CAR.CAMRY, CAR.COROLLA_TSS2, CAR.CHR, CAR.CHR_TSS2, CAR.LEXUS_IS, CAR.LEXUS_RC], } ) @@ -637,18 +637,25 @@ FW_VERSIONS = { }, CAR.CHR_TSS2: { (Ecu.abs, 0x7b0, None): [ + b'F152610260\x00\x00\x00\x00\x00\x00', b'F1526F4270\x00\x00\x00\x00\x00\x00', ], (Ecu.eps, 0x7a1, None): [ b'8965B10091\x00\x00\x00\x00\x00\x00', + b'8965B10110\x00\x00\x00\x00\x00\x00', ], (Ecu.engine, 0x700, None): [ b'\x0189663F459000\x00\x00\x00\x00', ], + (Ecu.engine, 0x7e0, None): [ + b'\x0331014000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203402\x00\x00\x00\x00', + ], (Ecu.fwdRadar, 0x750, 0xf): [ + b'\x018821FF410200\x00\x00\x00\x00', b'\x018821FF410300\x00\x00\x00\x00', ], (Ecu.fwdCamera, 0x750, 0x6d): [ + b'\x028646FF410200\x00\x00\x00\x008646GF408200\x00\x00\x00\x00', b'\x028646FF411100\x00\x00\x00\x008646GF409000\x00\x00\x00\x00', ], }, From 29ca52bc48dbcfb5e0f74a889229cf44f7a686a6 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 9 Feb 2023 16:53:16 -0800 Subject: [PATCH 352/484] GM: split steering speeds (#27277) * split steering speeds * update docs * Update ref_commit --- docs/CARS.md | 6 +++--- selfdrive/car/gm/interface.py | 4 ++-- selfdrive/test/process_replay/ref_commit | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index c00bd4f324..be4163d457 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -17,11 +17,11 @@ A supported vehicle is one that just works when you install a comma three. All s |Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| |Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| |Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| -|Cadillac|Escalade ESV 2016[3](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|| +|Cadillac|Escalade ESV 2016[3](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|| |Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|| |Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|| |Chevrolet|Silverado 1500 2020-21|Safety Package II|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|| -|Chevrolet|Volt 2017-18[3](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|| +|Chevrolet|Volt 2017-18[3](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|| |Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|| |Chrysler|Pacifica 2019-20|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|| |Chrysler|Pacifica 2021|All|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|| @@ -36,7 +36,7 @@ A supported vehicle is one that just works when you install a comma three. All s |Genesis|GV60 (Advanced Trim) 2023[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|| |Genesis|GV60 (Performance Trim) 2023[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| |Genesis|GV70 2022-23[5](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| -|GMC|Acadia 2018[3](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|| +|GMC|Acadia 2018[3](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|| |GMC|Sierra 1500 2020-21|Driver Alert Package II|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|| |Honda|Accord 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| |Honda|Accord Hybrid 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|| diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index 5ad1776f97..8bfc067b48 100755 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -67,6 +67,7 @@ class CarInterface(CarInterfaceBase): ret.pcmCruise = True ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM ret.minEnableSpeed = 5 * CV.KPH_TO_MS + ret.minSteerSpeed = 10 * CV.KPH_TO_MS # Tuning for experimental long ret.longitudinalTuning.kpV = [2.0, 1.5] @@ -89,6 +90,7 @@ class CarInterface(CarInterfaceBase): ret.pcmCruise = False # stock non-adaptive cruise control is kept off # supports stop and go, but initial engage must (conservatively) be above 18mph ret.minEnableSpeed = 18 * CV.MPH_TO_MS + ret.minSteerSpeed = 7 * CV.MPH_TO_MS # Tuning ret.longitudinalTuning.kpV = [2.4, 1.5] @@ -100,8 +102,6 @@ class CarInterface(CarInterfaceBase): ret.dashcamOnly = candidate in {CAR.CADILLAC_ATS, CAR.HOLDEN_ASTRA, CAR.MALIBU, CAR.BUICK_REGAL, CAR.EQUINOX} # Start with a baseline tuning for all GM vehicles. Override tuning as needed in each model section below. - # Some GMs need some tolerance above 10 kph to avoid a fault - ret.minSteerSpeed = 10.2 * CV.KPH_TO_MS ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.00]] ret.lateralTuning.pid.kf = 0.00004 # full torque for 20 deg at 80mph means 0.00007818594 diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index d334792c48..dc7f985eb3 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -9bfd30202a9e70d9d7459cc02f86c3e55d7c864c +9c5d75e9255a5a19d28f4317c382f00f4bc0ac61 From 5ee47408218d0e36681a0ec300e455972db5ef64 Mon Sep 17 00:00:00 2001 From: Manu Date: Fri, 10 Feb 2023 01:54:25 +0100 Subject: [PATCH 353/484] Toyota: C-HR Hybrid 2022 support (#27269) * Added C-HR 2022 * add harness link * Revert "add harness link" This reverts commit 54eaf4a5d83b2e844175cdcf4637329490e1a5b0. * Revert "Added C-HR 2022" This reverts commit b75a54886fbd0236a309b5c89bf74fd748a5b9e2. * Added Toyota C-HR Hybrid 2022 Added Toyota C-HR Hybrid 2022 * another year * it's a hybrid * fix platform name * new route * add to releases * right right, it's a hybrid --------- Co-authored-by: Shane Smiskol --- RELEASES.md | 1 + docs/CARS.md | 3 ++- selfdrive/car/tests/routes.py | 1 + selfdrive/car/torque_data/substitute.yaml | 1 + selfdrive/car/toyota/interface.py | 2 +- selfdrive/car/toyota/values.py | 26 ++++++++++++++++++++--- 6 files changed, 29 insertions(+), 5 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index cce7207ab0..29fa14aad4 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -13,6 +13,7 @@ Version 0.9.1 (2023-2-20) * Kia Sorento 2022-23 support thanks to sunnyhaibin! * Kia Sorento Plug-in Hybrid 2022 support thanks to sunnyhaibin! * Toyota C-HR 2021 support thanks to eFiniLan! +* Toyota C-HR Hybrid 2022 support thanks to Korben00! * Volkswagen Crafter and MAN TGE 2017-23 support thanks to jyoung8607! Version 0.9.0 (2022-11-21) diff --git a/docs/CARS.md b/docs/CARS.md index be4163d457..74e64b969f 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,7 +4,7 @@ A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system. -# 235 Supported Cars +# 236 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness|Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| @@ -178,6 +178,7 @@ A supported vehicle is one that just works when you install a comma three. All s |Toyota|C-HR 2017-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| |Toyota|C-HR 2021|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| |Toyota|C-HR Hybrid 2017-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|C-HR Hybrid 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| |Toyota|Camry 2018-20|All|Stock|0 mph[6](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| |Toyota|Camry 2021-22|All|openpilot|0 mph[6](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| |Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| diff --git a/selfdrive/car/tests/routes.py b/selfdrive/car/tests/routes.py index 98db2520c3..a4d2f55581 100644 --- a/selfdrive/car/tests/routes.py +++ b/selfdrive/car/tests/routes.py @@ -182,6 +182,7 @@ routes = [ CarTestRoute("cd9cff4b0b26c435|2021-05-13--15-12-39", TOYOTA.CHR), CarTestRoute("ea8fbe72b96a185c|2023-02-08--15-11-46", TOYOTA.CHR_TSS2), CarTestRoute("57858ede0369a261|2021-05-18--20-34-20", TOYOTA.CHRH), + CarTestRoute("6719965b0e1d1737|2023-02-09--22-44-05", TOYOTA.CHRH_TSS2), CarTestRoute("14623aae37e549f3|2021-10-24--01-20-49", TOYOTA.PRIUS_V), CarTestRoute("202c40641158a6e5|2021-09-21--09-43-24", VOLKSWAGEN.ARTEON_MK1), diff --git a/selfdrive/car/torque_data/substitute.yaml b/selfdrive/car/torque_data/substitute.yaml index 1ac43efc39..61243424f0 100644 --- a/selfdrive/car/torque_data/substitute.yaml +++ b/selfdrive/car/torque_data/substitute.yaml @@ -9,6 +9,7 @@ TOYOTA ALPHARD 2020: TOYOTA SIENNA 2018 TOYOTA PRIUS v 2017 : TOYOTA PRIUS 2017 TOYOTA RAV4 2022: TOYOTA RAV4 HYBRID 2022 TOYOTA C-HR HYBRID 2018: TOYOTA C-HR 2018 +TOYOTA C-HR HYBRID 2022: TOYOTA C-HR 2021 LEXUS IS 2018: LEXUS NX 2018 LEXUS CT HYBRID 2018 : LEXUS NX 2018 LEXUS ES HYBRID 2018: TOYOTA CAMRY HYBRID 2018 diff --git a/selfdrive/car/toyota/interface.py b/selfdrive/car/toyota/interface.py index a09b0e5265..8b3fd048d9 100644 --- a/selfdrive/car/toyota/interface.py +++ b/selfdrive/car/toyota/interface.py @@ -72,7 +72,7 @@ class CarInterface(CarInterfaceBase): tire_stiffness_factor = 0.5533 ret.mass = 4481. * CV.LB_TO_KG + STD_CARGO_KG # mean between min and max - elif candidate in (CAR.CHR, CAR.CHRH, CAR.CHR_TSS2): + elif candidate in (CAR.CHR, CAR.CHRH, CAR.CHR_TSS2, CAR.CHRH_TSS2): stop_and_go = True ret.wheelbase = 2.63906 ret.steerRatio = 13.6 diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index b28059ae23..b6a556438d 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -51,6 +51,7 @@ class CAR: CHR = "TOYOTA C-HR 2018" CHR_TSS2 = "TOYOTA C-HR 2021" CHRH = "TOYOTA C-HR HYBRID 2018" + CHRH_TSS2 = "TOYOTA C-HR HYBRID 2022" COROLLA = "TOYOTA COROLLA 2017" COROLLA_TSS2 = "TOYOTA COROLLA TSS2 2019" # LSS2 Lexus UX Hybrid is same as a TSS2 Corolla Hybrid @@ -119,6 +120,7 @@ CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = { CAR.CHR: ToyotaCarInfo("Toyota C-HR 2017-20"), CAR.CHR_TSS2: ToyotaCarInfo("Toyota C-HR 2021"), CAR.CHRH: ToyotaCarInfo("Toyota C-HR Hybrid 2017-19"), + CAR.CHRH_TSS2: ToyotaCarInfo("Toyota C-HR Hybrid 2022"), CAR.COROLLA: ToyotaCarInfo("Toyota Corolla 2017-19"), CAR.COROLLA_TSS2: [ ToyotaCarInfo("Toyota Corolla 2020-22", video_link="https://www.youtube.com/watch?v=_66pXk0CBYA"), @@ -712,6 +714,23 @@ FW_VERSIONS = { b'8646FF407000 ', ], }, + CAR.CHRH_TSS2: { + (Ecu.eps, 0x7a1, None): [ + b'8965B10092\x00\x00\x00\x00\x00\x00', + ], + (Ecu.abs, 0x7b0, None): [ + b'F152610041\x00\x00\x00\x00\x00\x00', + ], + (Ecu.engine, 0x700, None): [ + b'\x0189663F438000\x00\x00\x00\x00', + ], + (Ecu.fwdRadar, 0x750, 15): [ + b'\x018821FF410500\x00\x00\x00\x00', + ], + (Ecu.fwdCamera, 0x750, 109): [ + b'\x028646FF413100\x00\x00\x00\x008646GF411100\x00\x00\x00\x00', + ], + }, CAR.COROLLA: { (Ecu.engine, 0x7e0, None): [ b'\x0230ZC2000\x00\x00\x00\x00\x00\x00\x00\x0050212000\x00\x00\x00\x00\x00\x00\x00\x00', @@ -2058,6 +2077,7 @@ DBC = { CAR.CHR: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'), CAR.CHR_TSS2: dbc_dict('toyota_nodsu_pt_generated', None), CAR.CHRH: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'), + CAR.CHRH_TSS2: dbc_dict('toyota_nodsu_pt_generated', None), CAR.CAMRY: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'), CAR.CAMRYH: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'), CAR.CAMRY_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), @@ -2099,7 +2119,7 @@ EPS_SCALE = defaultdict(lambda: 73, {CAR.PRIUS: 66, CAR.COROLLA: 88, CAR.LEXUS_I # Toyota/Lexus Safety Sense 2.0 and 2.5 TSS2_CAR = {CAR.RAV4_TSS2, CAR.RAV4_TSS2_2022, CAR.COROLLA_TSS2, CAR.COROLLAH_TSS2, CAR.LEXUS_ES_TSS2, CAR.LEXUS_ESH_TSS2, CAR.RAV4H_TSS2, CAR.RAV4H_TSS2_2022, CAR.LEXUS_RX_TSS2, CAR.LEXUS_RXH_TSS2, CAR.HIGHLANDER_TSS2, CAR.HIGHLANDERH_TSS2, CAR.PRIUS_TSS2, CAR.CAMRY_TSS2, CAR.CAMRYH_TSS2, - CAR.MIRAI, CAR.LEXUS_NX_TSS2, CAR.LEXUS_NXH_TSS2, CAR.ALPHARD_TSS2, CAR.AVALON_TSS2, CAR.AVALONH_TSS2, CAR.ALPHARDH_TSS2, CAR.CHR_TSS2} + CAR.MIRAI, CAR.LEXUS_NX_TSS2, CAR.LEXUS_NXH_TSS2, CAR.ALPHARD_TSS2, CAR.AVALON_TSS2, CAR.AVALONH_TSS2, CAR.ALPHARDH_TSS2, CAR.CHR_TSS2, CAR.CHRH_TSS2} NO_DSU_CAR = TSS2_CAR | {CAR.CHR, CAR.CHRH, CAR.CAMRY, CAR.CAMRYH} @@ -2107,9 +2127,9 @@ NO_DSU_CAR = TSS2_CAR | {CAR.CHR, CAR.CHRH, CAR.CAMRY, CAR.CAMRYH} UNSUPPORTED_DSU_CAR = {CAR.LEXUS_IS, CAR.LEXUS_RC} # these cars have a radar which sends ACC messages instead of the camera -RADAR_ACC_CAR = {CAR.RAV4H_TSS2_2022, CAR.RAV4_TSS2_2022, CAR.CHR_TSS2} +RADAR_ACC_CAR = {CAR.RAV4H_TSS2_2022, CAR.RAV4_TSS2_2022, CAR.CHR_TSS2, CAR.CHRH_TSS2} -EV_HYBRID_CAR = {CAR.AVALONH_2019, CAR.AVALONH_TSS2, CAR.CAMRYH, CAR.CAMRYH_TSS2, CAR.CHRH, CAR.COROLLAH_TSS2, CAR.HIGHLANDERH, CAR.HIGHLANDERH_TSS2, CAR.PRIUS, +EV_HYBRID_CAR = {CAR.AVALONH_2019, CAR.AVALONH_TSS2, CAR.CAMRYH, CAR.CAMRYH_TSS2, CAR.CHRH, CAR.CHRH_TSS2, CAR.COROLLAH_TSS2, CAR.HIGHLANDERH, CAR.HIGHLANDERH_TSS2, CAR.PRIUS, CAR.PRIUS_V, CAR.RAV4H, CAR.RAV4H_TSS2, CAR.RAV4H_TSS2_2022, CAR.LEXUS_CTH, CAR.MIRAI, CAR.LEXUS_ESH, CAR.LEXUS_ESH_TSS2, CAR.LEXUS_NXH, CAR.LEXUS_RXH, CAR.LEXUS_RXH_TSS2, CAR.LEXUS_NXH_TSS2, CAR.PRIUS_TSS2, CAR.ALPHARDH_TSS2} From 79789011019e923c66a9db05c2da362237ce96b3 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 9 Feb 2023 18:54:17 -0800 Subject: [PATCH 354/484] controlsd: revert CAN timeout threshold (#27275) * controlsd: revert CAN timeout threshold * dt_ctrl doesn't make sense there --- selfdrive/controls/controlsd.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 1bb5be9a56..63395adf64 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -167,11 +167,11 @@ class Controls: self.state = State.disabled self.enabled = False self.active = False - self.can_rcv_timeout = False self.soft_disable_timer = 0 self.mismatch_counter = 0 self.cruise_mismatch_counter = 0 - self.can_rcv_timeout_counter = 0 + self.can_rcv_timeout_counter = 0 # conseuctive timeout count + self.can_rcv_cum_timeout_counter = 0 # cumulative timeout count self.last_blinker_frame = 0 self.last_steering_pressed_frame = 0 self.distance_traveled = 0 @@ -349,9 +349,10 @@ class Controls: self.events.add(EventName.canError) # generic catch-all. ideally, a more specific event should be added above instead + can_rcv_timeout = self.can_rcv_timeout_counter >= 5 has_disable_events = self.events.any(ET.NO_ENTRY) and (self.events.any(ET.SOFT_DISABLE) or self.events.any(ET.IMMEDIATE_DISABLE)) no_system_errors = (not has_disable_events) or (len(self.events) == num_events) - if (not self.sm.all_checks() or self.can_rcv_timeout) and no_system_errors: + if (not self.sm.all_checks() or can_rcv_timeout) and no_system_errors: if not self.sm.all_alive(): self.events.add(EventName.commIssue) elif not self.sm.all_freq_ok(): @@ -363,7 +364,7 @@ class Controls: 'invalid': [s for s, valid in self.sm.valid.items() if not valid], 'not_alive': [s for s, alive in self.sm.alive.items() if not alive], 'not_freq_ok': [s for s, freq_ok in self.sm.freq_ok.items() if not freq_ok], - 'can_rcv_timeout': self.can_rcv_timeout, + 'can_rcv_timeout': can_rcv_timeout, } if logs != self.logged_comm_issue: cloudlog.event("commIssue", error=True, **logs) @@ -445,9 +446,9 @@ class Controls: # Check for CAN timeout if not can_strs: self.can_rcv_timeout_counter += 1 - self.can_rcv_timeout = True + self.can_rcv_cum_timeout_counter += 1 else: - self.can_rcv_timeout = False + self.can_rcv_timeout_counter = 0 # When the panda and controlsd do not agree on controls_allowed # we want to disengage openpilot. However the status from the panda goes through @@ -785,7 +786,7 @@ class Controls: controlsState.cumLagMs = -self.rk.remaining * 1000. controlsState.startMonoTime = int(start_time * 1e9) controlsState.forceDecel = bool(force_decel) - controlsState.canErrorCounter = self.can_rcv_timeout_counter + controlsState.canErrorCounter = self.can_rcv_cum_timeout_counter controlsState.experimentalMode = self.params.get_bool("ExperimentalMode") and self.CP.openpilotLongitudinalControl lat_tuning = self.CP.lateralTuning.which() From 5ab2d35f7b7d1f224b531e245db7e35925da5e33 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 9 Feb 2023 19:27:51 -0800 Subject: [PATCH 355/484] controlsd: higher default set speed in experimental mode (#27279) * controlsd: higher default set speed in experimental mode * update tests * update tests --- selfdrive/controls/controlsd.py | 6 +++-- selfdrive/controls/lib/drive_helpers.py | 26 ++++++++++-------- selfdrive/controls/tests/test_cruise_speed.py | 27 ++++++++++--------- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 63395adf64..2c359ec32a 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -183,6 +183,7 @@ class Controls: self.steer_limited = False self.desired_curvature = 0.0 self.desired_curvature_rate = 0.0 + self.experimental_mode = False self.v_cruise_helper = VCruiseHelper(self.CP) # TODO: no longer necessary, aside from process replay @@ -544,7 +545,7 @@ class Controls: else: self.state = State.enabled self.current_alert_types.append(ET.ENABLE) - self.v_cruise_helper.initialize_v_cruise(CS) + self.v_cruise_helper.initialize_v_cruise(CS, self.experimental_mode) # Check if openpilot is engaged and actuators are enabled self.enabled = self.state in ENABLED_STATES @@ -787,7 +788,7 @@ class Controls: controlsState.startMonoTime = int(start_time * 1e9) controlsState.forceDecel = bool(force_decel) controlsState.canErrorCounter = self.can_rcv_cum_timeout_counter - controlsState.experimentalMode = self.params.get_bool("ExperimentalMode") and self.CP.openpilotLongitudinalControl + controlsState.experimentalMode = self.experimental_mode lat_tuning = self.CP.lateralTuning.which() if self.joystick_mode: @@ -838,6 +839,7 @@ class Controls: self.prof.checkpoint("Ratekeeper", ignore=True) self.is_metric = self.params.get_bool("IsMetric") + self.experimental_mode = self.params.get_bool("ExperimentalMode") and self.CP.openpilotLongitudinalControl # Sample data from sockets and get a carState CS = self.data_sample() diff --git a/selfdrive/controls/lib/drive_helpers.py b/selfdrive/controls/lib/drive_helpers.py index 3d5ec8ac1d..a332d06765 100644 --- a/selfdrive/controls/lib/drive_helpers.py +++ b/selfdrive/controls/lib/drive_helpers.py @@ -8,10 +8,12 @@ from selfdrive.modeld.constants import T_IDXS # WARNING: this value was determined based on the model's training distribution, # model predictions above this speed can be unpredictable -V_CRUISE_MAX = 145 # kph -V_CRUISE_MIN = 8 # kph -V_CRUISE_ENABLE_MIN = 40 # kph -V_CRUISE_INITIAL = 255 # kph +# V_CRUISE's are in kph +V_CRUISE_MIN = 8 +V_CRUISE_MAX = 145 +V_CRUISE_UNSET = 255 +V_CRUISE_INITIAL = 40 +V_CRUISE_INITIAL_EXPERIMENTAL_MODE = 105 IMPERIAL_INCREMENT = 1.6 # should be CV.MPH_TO_KPH, but this causes rounding errors MIN_SPEED = 1.0 @@ -39,15 +41,15 @@ CRUISE_INTERVAL_SIGN = { class VCruiseHelper: def __init__(self, CP): self.CP = CP - self.v_cruise_kph = V_CRUISE_INITIAL - self.v_cruise_cluster_kph = V_CRUISE_INITIAL + self.v_cruise_kph = V_CRUISE_UNSET + self.v_cruise_cluster_kph = V_CRUISE_UNSET self.v_cruise_kph_last = 0 self.button_timers = {ButtonType.decelCruise: 0, ButtonType.accelCruise: 0} self.button_change_states = {btn: {"standstill": False, "enabled": False} for btn in self.button_timers} @property def v_cruise_initialized(self): - return self.v_cruise_kph != V_CRUISE_INITIAL + return self.v_cruise_kph != V_CRUISE_UNSET def update_v_cruise(self, CS, enabled, is_metric): self.v_cruise_kph_last = self.v_cruise_kph @@ -62,8 +64,8 @@ class VCruiseHelper: self.v_cruise_kph = CS.cruiseState.speed * CV.MS_TO_KPH self.v_cruise_cluster_kph = CS.cruiseState.speedCluster * CV.MS_TO_KPH else: - self.v_cruise_kph = V_CRUISE_INITIAL - self.v_cruise_cluster_kph = V_CRUISE_INITIAL + self.v_cruise_kph = V_CRUISE_UNSET + self.v_cruise_cluster_kph = V_CRUISE_UNSET def _update_v_cruise_non_pcm(self, CS, enabled, is_metric): # handle button presses. TODO: this should be in state_control, but a decelCruise press @@ -125,16 +127,18 @@ class VCruiseHelper: self.button_timers[b.type.raw] = 1 if b.pressed else 0 self.button_change_states[b.type.raw] = {"standstill": CS.cruiseState.standstill, "enabled": enabled} - def initialize_v_cruise(self, CS): + def initialize_v_cruise(self, CS, experimental_mode: bool) -> None: # initializing is handled by the PCM if self.CP.pcmCruise: return + initial = V_CRUISE_INITIAL_EXPERIMENTAL_MODE if experimental_mode else V_CRUISE_INITIAL + # 250kph or above probably means we never had a set speed if any(b.type in (ButtonType.accelCruise, ButtonType.resumeCruise) for b in CS.buttonEvents) and self.v_cruise_kph_last < 250: self.v_cruise_kph = self.v_cruise_kph_last else: - self.v_cruise_kph = int(round(clip(CS.vEgo * CV.MS_TO_KPH, V_CRUISE_ENABLE_MIN, V_CRUISE_MAX))) + self.v_cruise_kph = int(round(clip(CS.vEgo * CV.MS_TO_KPH, initial, V_CRUISE_MAX))) self.v_cruise_cluster_kph = self.v_cruise_kph diff --git a/selfdrive/controls/tests/test_cruise_speed.py b/selfdrive/controls/tests/test_cruise_speed.py index cd1d31cf07..b83116af45 100755 --- a/selfdrive/controls/tests/test_cruise_speed.py +++ b/selfdrive/controls/tests/test_cruise_speed.py @@ -3,7 +3,7 @@ import numpy as np from parameterized import parameterized_class import unittest -from selfdrive.controls.lib.drive_helpers import VCruiseHelper, V_CRUISE_MIN, V_CRUISE_MAX, V_CRUISE_ENABLE_MIN, IMPERIAL_INCREMENT +from selfdrive.controls.lib.drive_helpers import VCruiseHelper, V_CRUISE_MIN, V_CRUISE_MAX, V_CRUISE_INITIAL, IMPERIAL_INCREMENT from cereal import car from common.conversions import Conversions as CV from selfdrive.test.longitudinal_maneuvers.maneuver import Maneuver @@ -53,16 +53,16 @@ class TestVCruiseHelper(unittest.TestCase): for _ in range(2): self.v_cruise_helper.update_v_cruise(car.CarState(cruiseState={"available": False}), enabled=False, is_metric=False) - def enable(self, v_ego): + def enable(self, v_ego, experimental_mode): # Simulates user pressing set with a current speed - self.v_cruise_helper.initialize_v_cruise(car.CarState(vEgo=v_ego)) + self.v_cruise_helper.initialize_v_cruise(car.CarState(vEgo=v_ego), experimental_mode) def test_adjust_speed(self): """ Asserts speed changes on falling edges of buttons. """ - self.enable(V_CRUISE_ENABLE_MIN * CV.KPH_TO_MS) + self.enable(V_CRUISE_INITIAL * CV.KPH_TO_MS, False) for btn in (ButtonType.accelCruise, ButtonType.decelCruise): for pressed in (True, False): @@ -86,7 +86,7 @@ class TestVCruiseHelper(unittest.TestCase): CS.buttonEvents = [ButtonEvent(type=ButtonType.decelCruise, pressed=pressed)] self.v_cruise_helper.update_v_cruise(CS, enabled=enabled, is_metric=False) if pressed: - self.enable(V_CRUISE_ENABLE_MIN * CV.KPH_TO_MS) + self.enable(V_CRUISE_INITIAL * CV.KPH_TO_MS, False) # Expected diff on enabling. Speed should not change on falling edge of pressed self.assertEqual(not pressed, self.v_cruise_helper.v_cruise_kph == self.v_cruise_helper.v_cruise_kph_last) @@ -96,7 +96,7 @@ class TestVCruiseHelper(unittest.TestCase): Asserts we don't increment set speed if user presses resume/accel to exit cruise standstill. """ - self.enable(0) + self.enable(0, False) for standstill in (True, False): for pressed in (True, False): @@ -116,7 +116,7 @@ class TestVCruiseHelper(unittest.TestCase): for v_ego in np.linspace(0, 100, 101): self.reset_cruise_speed_state() - self.enable(V_CRUISE_ENABLE_MIN * CV.KPH_TO_MS) + self.enable(V_CRUISE_INITIAL * CV.KPH_TO_MS, False) # first decrement speed, then perform gas pressed logic expected_v_cruise_kph = self.v_cruise_helper.v_cruise_kph - IMPERIAL_INCREMENT @@ -137,13 +137,14 @@ class TestVCruiseHelper(unittest.TestCase): Asserts allowed cruise speeds on enabling with SET. """ - for v_ego in np.linspace(0, 100, 101): - self.reset_cruise_speed_state() - self.assertFalse(self.v_cruise_helper.v_cruise_initialized) + for experimental_mode in (True, False): + for v_ego in np.linspace(0, 100, 101): + self.reset_cruise_speed_state() + self.assertFalse(self.v_cruise_helper.v_cruise_initialized) - self.enable(float(v_ego)) - self.assertTrue(V_CRUISE_ENABLE_MIN <= self.v_cruise_helper.v_cruise_kph <= V_CRUISE_MAX) - self.assertTrue(self.v_cruise_helper.v_cruise_initialized) + self.enable(float(v_ego), experimental_mode) + self.assertTrue(V_CRUISE_INITIAL <= self.v_cruise_helper.v_cruise_kph <= V_CRUISE_MAX) + self.assertTrue(self.v_cruise_helper.v_cruise_initialized) if __name__ == "__main__": From 1f85f84027015ee20fa4affff23e121f4388d93c Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 9 Feb 2023 20:44:29 -0800 Subject: [PATCH 356/484] UI: fix old frame shown on startup (#27283) Co-authored-by: Comma Device --- selfdrive/ui/qt/widgets/cameraview.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/selfdrive/ui/qt/widgets/cameraview.cc b/selfdrive/ui/qt/widgets/cameraview.cc index 4a7874b160..8c7a7072e2 100644 --- a/selfdrive/ui/qt/widgets/cameraview.cc +++ b/selfdrive/ui/qt/widgets/cameraview.cc @@ -382,6 +382,10 @@ void CameraWidget::vipcThread() { } } emit vipcThreadFrameReceived(); + } else { + if (!isVisible()) { + vipc_client->connected = false; + } } } From 429311a2cc17ac4d50e28574c1ea35d42a725dc9 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 11 Feb 2023 02:12:33 +0800 Subject: [PATCH 357/484] cabana: fix the text of signal value being clipped (#27292) fix value be clipped --- tools/cabana/util.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/cabana/util.cc b/tools/cabana/util.cc index 2806e175d7..20f4c58423 100644 --- a/tools/cabana/util.cc +++ b/tools/cabana/util.cc @@ -82,6 +82,12 @@ void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem & QStyleOptionViewItemV4 opt = option; initStyleOption(&opt, index); + auto byte_list = opt.text.split(" "); + if (byte_list.size() <= 1) { + QStyledItemDelegate::paint(painter, option, index); + return; + } + if ((option.state & QStyle::State_Selected) && (option.state & QStyle::State_Active)) { painter->setPen(option.palette.color(QPalette::HighlightedText)); } else { @@ -98,7 +104,7 @@ void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem & QList colors = index.data(Qt::UserRole).toList(); int i = 0; - for (auto &byte : opt.text.split(" ")) { + for (auto &byte : byte_list) { if (i < colors.size()) { painter->fillRect(pos.marginsAdded(margins), colors[i].value()); } From 237428d0fd42bd0d00156029b38afced3222280e Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 11 Feb 2023 02:13:15 +0800 Subject: [PATCH 358/484] cabana: dont reset filter after seek (#27290) --- tools/cabana/messageswidget.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index 359fe10f50..8279d08a5c 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -182,11 +182,10 @@ void MessageListModel::sortMessages() { void MessageListModel::msgsReceived(const QHash *new_msgs) { int prev_row_count = msgs.size(); - bool update_all = new_msgs->size() == can->can_msgs.size(); - if (update_all || (filter_str.isEmpty() && msgs.size() != can->can_msgs.size())) { + if (filter_str.isEmpty() && msgs.size() != can->can_msgs.size()) { msgs = can->can_msgs.keys(); } - if (update_all || msgs.size() != prev_row_count) { + if (msgs.size() != prev_row_count) { sortMessages(); return; } From 409c1c9c6558850bbced8ba05bf125102e6543bb Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 11 Feb 2023 02:13:28 +0800 Subject: [PATCH 359/484] cabana: set pen width in createSeries (#27288) --- tools/cabana/chartswidget.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index e31e437280..b228a56973 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -789,6 +789,10 @@ QXYSeries *ChartView::createSeries(QAbstractSeries::SeriesType type, QColor colo // are drawn instead of the graphs on MacOS. Re-enable OpenGL when fixed #ifndef __APPLE__ series->setUseOpenGL(true); + // Qt doesn't properly apply device pixel ratio in OpenGL mode + QPen pen = series->pen(); + pen.setWidth(2.0 * qApp->devicePixelRatio()); + series->setPen(pen); #endif return series; } From d19b6683655813d18c4e485b613dd32666197232 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 11 Feb 2023 02:13:39 +0800 Subject: [PATCH 360/484] cabana: fix incorrect bit color after seek (#27287) --- tools/cabana/streams/abstractstream.cc | 1 + tools/cabana/util.cc | 1 + 2 files changed, 2 insertions(+) diff --git a/tools/cabana/streams/abstractstream.cc b/tools/cabana/streams/abstractstream.cc index 94d653bcb0..13b154a7ea 100644 --- a/tools/cabana/streams/abstractstream.cc +++ b/tools/cabana/streams/abstractstream.cc @@ -72,6 +72,7 @@ void AbstractStream::updateLastMsgsTo(double sec) { m.dat = QByteArray((char *)c.getDat().begin(), c.getDat().size()); m.colors = QVector(m.dat.size(), QColor(0, 0, 0, 0)); m.last_change_t = QVector(m.dat.size(), m.ts); + m.bit_change_counts.resize(m.dat.size()); } else { m.freq = m.count / std::max(1.0, m.ts); } diff --git a/tools/cabana/util.cc b/tools/cabana/util.cc index 20f4c58423..454dd29b87 100644 --- a/tools/cabana/util.cc +++ b/tools/cabana/util.cc @@ -62,6 +62,7 @@ void ChangeTracker::compute(const QByteArray &dat, double ts, uint32_t freq) { void ChangeTracker::clear() { prev_dat.clear(); last_change_t.clear(); + bit_change_counts.clear(); colors.clear(); } From fae5553838f87b61619c7ba5e673f91dd836401c Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Fri, 10 Feb 2023 19:14:45 +0100 Subject: [PATCH 361/484] cabana: add include to fix macos build (#27291) * cabana: add include to fix macos build * Update util.h --------- Co-authored-by: Adeeb Shihadeh --- tools/cabana/util.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/cabana/util.h b/tools/cabana/util.h index 5d5b44d63c..8ec4cda90c 100644 --- a/tools/cabana/util.h +++ b/tools/cabana/util.h @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include From 7cfa236ff2a40e80eef8b779131184eea5778242 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 11 Feb 2023 03:31:54 +0800 Subject: [PATCH 362/484] cabana: improve SignalItemDelegate::paint (#27295) --- tools/cabana/signaledit.cc | 12 ++++++------ tools/cabana/signaledit.h | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/tools/cabana/signaledit.cc b/tools/cabana/signaledit.cc index 2f1a968645..7988037c34 100644 --- a/tools/cabana/signaledit.cc +++ b/tools/cabana/signaledit.cc @@ -266,6 +266,7 @@ void SignalModel::handleSignalRemoved(const Signal *sig) { SignalItemDelegate::SignalItemDelegate(QObject *parent) { name_validator = new NameValidator(this); double_validator = new QDoubleValidator(this); + small_font.setPointSize(8); double_validator->setLocale(QLocale::C); // Match locale of QString::toDouble() instead of system } @@ -280,20 +281,19 @@ void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op // color label auto bg_color = getColor(item->sig); - QRect rc{option.rect.left() + 3, option.rect.top(), 22, option.rect.height()}; + QRect rc{option.rect.left(), option.rect.top(), 18, option.rect.height()}; painter->setPen(Qt::NoPen); painter->setBrush(item->highlight ? bg_color.darker(125) : bg_color); - painter->drawRoundedRect(rc.adjusted(0, 2, 0, -2), 5, 5); + painter->drawRoundedRect(rc.adjusted(0, 2, 0, -2), 3, 3); painter->setPen(item->highlight ? Qt::white : Qt::black); + painter->setFont(small_font); painter->drawText(rc, Qt::AlignCenter, QString::number(item->row() + 1)); // signal name - QFont font; - font.setBold(true); - painter->setFont(font); + painter->setFont(option.font); painter->setPen((option.state & QStyle::State_Selected ? option.palette.highlightedText() : option.palette.text()).color()); QString text = index.data(Qt::DisplayRole).toString(); - QRect text_rect = option.rect.adjusted(rc.width() + 9, 0, 0, 0); + QRect text_rect = option.rect.adjusted(rc.width() + 6, 0, 0, 0); text = painter->fontMetrics().elidedText(text, Qt::ElideRight, text_rect.width()); painter->drawText(text_rect, option.displayAlignment, text); painter->restore(); diff --git a/tools/cabana/signaledit.h b/tools/cabana/signaledit.h index ae88a5b13a..6a904e7328 100644 --- a/tools/cabana/signaledit.h +++ b/tools/cabana/signaledit.h @@ -68,6 +68,7 @@ public: void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QValidator *name_validator, *double_validator; + QFont small_font; }; class SignalView : public QWidget { From 152f1bf4858bf8e21d4a059baa5ec5fb4239c8fa Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 11 Feb 2023 04:57:20 +0800 Subject: [PATCH 363/484] cabana: fix inconsistent size for play button (#27298) --- tools/cabana/videowidget.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/cabana/videowidget.cc b/tools/cabana/videowidget.cc index d8d89a708c..cd3dc0b516 100644 --- a/tools/cabana/videowidget.cc +++ b/tools/cabana/videowidget.cc @@ -32,6 +32,7 @@ VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent) { // btn controls QHBoxLayout *control_layout = new QHBoxLayout(); play_btn = new QPushButton(); + play_btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); control_layout->addWidget(play_btn); QButtonGroup *group = new QButtonGroup(this); From 3d2c156bbdfe455b97e6852882c5576600c6a600 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 11 Feb 2023 05:40:39 +0800 Subject: [PATCH 364/484] cabana: fix y axis precision (#27299) --- tools/cabana/chartswidget.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index b228a56973..5063ec9dea 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -310,7 +310,6 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { chart->setBackgroundVisible(false); axis_x = new QValueAxis(this); axis_y = new QValueAxis(this); - axis_y->setLabelFormat("%.1f"); chart->addAxis(axis_x, Qt::AlignBottom); chart->addAxis(axis_y, Qt::AlignLeft); chart->legend()->layout()->setContentsMargins(16, 0, 40, 0); From 0fdcf4efc0740b3caf2cf4683be301edc5bf3fe4 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 11 Feb 2023 06:50:52 +0800 Subject: [PATCH 365/484] cabana: fix text-overflow for long msg name (#27297) --- tools/cabana/detailwidget.cc | 3 ++- tools/cabana/detailwidget.h | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index a7ad572dc1..25d9b3b9ca 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -32,7 +32,8 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart time_label = new QLabel(this); time_label->setStyleSheet("font-weight:bold"); toolbar->addWidget(time_label); - name_label = new QLabel(this); + name_label = new ElidedLabel(this); + name_label->setContentsMargins(5, 0, 5, 0); name_label->setStyleSheet("font-weight:bold;"); name_label->setAlignment(Qt::AlignCenter); name_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); diff --git a/tools/cabana/detailwidget.h b/tools/cabana/detailwidget.h index a62e23f226..0983f49924 100644 --- a/tools/cabana/detailwidget.h +++ b/tools/cabana/detailwidget.h @@ -5,6 +5,7 @@ #include #include +#include "selfdrive/ui/qt/widgets/controls.h" #include "tools/cabana/binaryview.h" #include "tools/cabana/chartswidget.h" #include "tools/cabana/historylog.h" @@ -40,7 +41,8 @@ private: void updateState(const QHash * msgs = nullptr); QString msg_id; - QLabel *name_label, *time_label, *warning_icon, *warning_label; + QLabel *time_label, *warning_icon, *warning_label; + ElidedLabel *name_label; QWidget *warning_widget; QTabBar *tabbar; QTabWidget *tab_widget; From 5831a83c907f9f90aac2c53839cd37554b5b235a Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 10 Feb 2023 16:14:53 -0800 Subject: [PATCH 366/484] GM: add hysteresis to cluster speed (#27301) * add cluster hysteresis * Update ref_commit --- selfdrive/car/gm/carstate.py | 3 +++ selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/selfdrive/car/gm/carstate.py b/selfdrive/car/gm/carstate.py index 3ddec13ced..90705d871e 100644 --- a/selfdrive/car/gm/carstate.py +++ b/selfdrive/car/gm/carstate.py @@ -17,6 +17,9 @@ class CarState(CarStateBase): super().__init__(CP) can_define = CANDefine(DBC[CP.carFingerprint]["pt"]) self.shifter_values = can_define.dv["ECMPRDNL2"]["PRNDL2"] + self.cluster_speed_hyst_gap = CV.KPH_TO_MS / 2. + self.cluster_min_speed = CV.KPH_TO_MS / 2. + self.loopback_lka_steering_cmd_updated = False self.loopback_lka_steering_cmd_ts_nanos = 0 self.pt_lka_steering_cmd_counter = 0 diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index dc7f985eb3..0081a20e21 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -9c5d75e9255a5a19d28f4317c382f00f4bc0ac61 +fbf92051507b7a7585907b58baadec6b8cc475af From c05aa0e87159e07e66dba946db08735ae83bc442 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 10 Feb 2023 16:32:21 -0800 Subject: [PATCH 367/484] Chrysler: log temporary LKAS faults (#27303) * Log temp lkas faults for non-ram * bump to master * add to signals * whoops * ... come onnnnn --- opendbc | 2 +- selfdrive/car/chrysler/carstate.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/opendbc b/opendbc index d103b156b3..6eab6cf6ad 160000 --- a/opendbc +++ b/opendbc @@ -1 +1 @@ -Subproject commit d103b156b36f2bc9d0d6ad9ba1af02a42ecc2b46 +Subproject commit 6eab6cf6ad5ac69539037f4508582fbfaf239eab diff --git a/selfdrive/car/chrysler/carstate.py b/selfdrive/car/chrysler/carstate.py index 0f0d30782a..fdc5aa338a 100644 --- a/selfdrive/car/chrysler/carstate.py +++ b/selfdrive/car/chrysler/carstate.py @@ -81,8 +81,9 @@ class CarState(CarStateBase): if self.CP.carFingerprint in RAM_CARS: self.auto_high_beam = cp_cam.vl["DAS_6"]['AUTO_HIGH_BEAM_ON'] # Auto High Beam isn't Located in this message on chrysler or jeep currently located in 729 message - ret.steerFaultTemporary = cp.vl["EPS_3"]["DASM_FAULT"] == 1 + ret.steerFaultTemporary = cp.vl["EPS_3"]["DASM_FAULT"] == 1 else: + ret.steerFaultTemporary = cp.vl["EPS_2"]["LKAS_TEMPORARY_FAULT"] == 1 ret.steerFaultPermanent = cp.vl["EPS_2"]["LKAS_STATE"] == 4 # blindspot sensors @@ -135,6 +136,7 @@ class CarState(CarStateBase): ("COUNTER", "EPS_2",), ("COLUMN_TORQUE", "EPS_2"), ("EPS_TORQUE_MOTOR", "EPS_2"), + ("LKAS_TEMPORARY_FAULT", "EPS_2"), ("LKAS_STATE", "EPS_2"), ("COUNTER", "CRUISE_BUTTONS"), ] From 93330302c8abae41175db272f451fd0c5cd2366b Mon Sep 17 00:00:00 2001 From: Yassine Yousfi Date: Fri, 10 Feb 2023 16:52:52 -0800 Subject: [PATCH 368/484] add new model to release notes --- RELEASES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 29fa14aad4..ded06346c6 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -5,6 +5,8 @@ Version 0.9.1 (2023-2-20) * UI updates * Driver monitoring icon shows driver's head pose * German translation thanks to Vrabetz and CzokNorris! +* New driving model + * 30% improved height estimation resulting in better driving performance for tall cars * Chevrolet Bolt EV 2022-23 support thanks to JasonJShuler! * Genesis GV60 2023 support thanks to sunnyhaibin! * Hyundai Tucson 2022-23 support From b1f84f94001e6c8de8bcedb72652831de01dcee4 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 10 Feb 2023 22:28:47 -0800 Subject: [PATCH 369/484] Chrysler: setup panda safety flag for old tuning's steer limits --- panda | 2 +- selfdrive/car/chrysler/interface.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/panda b/panda index 7d27eb10fb..b29ae782f7 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 7d27eb10fb32095e73f60c59a123cae08cc53719 +Subproject commit b29ae782f78be87e7ac1414773c6015b14eea728 diff --git a/selfdrive/car/chrysler/interface.py b/selfdrive/car/chrysler/interface.py index 657b018c00..4b22c478a7 100755 --- a/selfdrive/car/chrysler/interface.py +++ b/selfdrive/car/chrysler/interface.py @@ -23,6 +23,9 @@ class CarInterface(CarInterfaceBase): elif candidate in RAM_DT: ret.safetyConfigs[0].safetyParam |= Panda.FLAG_CHRYSLER_RAM_DT + if candidate in CHRYSLER_OLD_TUNING_BLACKLIST: + ret.safetyConfigs[0].safetyParam |= Panda.FLAG_CHRYSLER_LOWER_RATE + ret.minSteerSpeed = 3.8 # m/s CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) if candidate not in RAM_CARS: From 69e52f02fd21844ff068c495b7fcb01ebc53bea5 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 10 Feb 2023 22:43:19 -0800 Subject: [PATCH 370/484] bump panda --- panda | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda b/panda index b29ae782f7..d15250cb14 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit b29ae782f78be87e7ac1414773c6015b14eea728 +Subproject commit d15250cb1454292c6f1217c79642b9ffd93e7595 From 4f2134468f2b2b94daa24d9ba80cc70da0ad8224 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 10 Feb 2023 23:46:48 -0800 Subject: [PATCH 371/484] events: reword ESP off alert (#27304) * Rename event * rename to Control * rename to disabled --- selfdrive/controls/lib/events.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/controls/lib/events.py b/selfdrive/controls/lib/events.py index 3036828662..6d5ed45076 100644 --- a/selfdrive/controls/lib/events.py +++ b/selfdrive/controls/lib/events.py @@ -729,8 +729,8 @@ EVENTS: Dict[int, Dict[str, Union[Alert, AlertCallbackType]]] = { }, EventName.espDisabled: { - ET.SOFT_DISABLE: soft_disable_alert("ESP Off"), - ET.NO_ENTRY: NoEntryAlert("ESP Off"), + ET.SOFT_DISABLE: soft_disable_alert("Electronic Stability Control Disabled"), + ET.NO_ENTRY: NoEntryAlert("Electronic Stability Control Disabled"), }, EventName.lowBattery: { From ed52916d55cd802be60b9db03f3ce7fe131fcef2 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 10 Feb 2023 23:47:01 -0800 Subject: [PATCH 372/484] update chrysler test segment with new flag --- selfdrive/test/process_replay/ref_commit | 2 +- selfdrive/test/process_replay/test_processes.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 0081a20e21..10665ae18c 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -fbf92051507b7a7585907b58baadec6b8cc475af +69e52f02fd21844ff068c495b7fcb01ebc53bea5 \ No newline at end of file diff --git a/selfdrive/test/process_replay/test_processes.py b/selfdrive/test/process_replay/test_processes.py index 569090f606..3ff3d36ebc 100755 --- a/selfdrive/test/process_replay/test_processes.py +++ b/selfdrive/test/process_replay/test_processes.py @@ -48,7 +48,7 @@ segments = [ ("TOYOTA3", "regen89026F6BD8D|2022-09-27--15-45-37--0"), ("HONDA", "regenC7D5645EB17|2022-09-27--15-47-29--0"), ("HONDA2", "regenCC2ECCE5742|2022-09-27--16-18-01--0"), - ("CHRYSLER", "regenC253C4DAC90|2022-09-27--15-51-03--0"), + ("CHRYSLER", "regenC253C4DAC90|2023-02-10--15-51-03--0"), ("RAM", "regen20490083AE7|2022-09-27--15-53-15--0"), ("SUBARU", "regen1E72BBDCED5|2022-09-27--15-55-31--0"), ("GM", "regen45B05A80EF6|2022-09-27--15-57-22--0"), From 7b654337d6b47e14e0df76ffa43aeac8869e8245 Mon Sep 17 00:00:00 2001 From: Kyumin Han Date: Sat, 11 Feb 2023 01:04:17 -0700 Subject: [PATCH 373/484] HKG: remove Kia Stinger 2018 from legacy safety mode (#27302) * Remove Kia stinger from legacy safety system array Enable OP long * update docs --------- Co-authored-by: Shane Smiskol --- docs/CARS.md | 2 +- selfdrive/car/hyundai/values.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index 74e64b969f..e6b6bbee7d 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -119,7 +119,7 @@ A supported vehicle is one that just works when you install a comma three. All s |Kia|Sorento Plug-in Hybrid 2022-23[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|| |Kia|Sportage 2023[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|| |Kia|Sportage Hybrid 2023[5](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|| -|Kia|Stinger 2018-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| +|Kia|Stinger 2018-20|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|| |Kia|Stinger 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| |Kia|Telluride 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| |Lexus|CT Hybrid 2017-18|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 2241b871a1..bde38b47cc 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -1669,7 +1669,8 @@ HYBRID_CAR = {CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.KIA_NIRO_PHEV, CAR.KIA_N EV_CAR = {CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.KIA_NIRO_EV, CAR.KONA_EV_2022, CAR.KIA_EV6, CAR.IONIQ_5, CAR.GENESIS_GV60_EV_1ST_GEN} # these cars require a special panda safety mode due to missing counters and checksums in the messages -LEGACY_SAFETY_MODE_CAR = {CAR.HYUNDAI_GENESIS, CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.IONIQ_PHEV, CAR.IONIQ, CAR.KONA_EV, CAR.KIA_SORENTO, CAR.SONATA_LF, CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.VELOSTER, CAR.KIA_STINGER, CAR.GENESIS_G70, CAR.GENESIS_G80, CAR.KIA_CEED, CAR.ELANTRA, CAR.IONIQ_HEV_2022} +LEGACY_SAFETY_MODE_CAR = {CAR.HYUNDAI_GENESIS, CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.IONIQ_PHEV, CAR.IONIQ, CAR.KONA_EV, CAR.KIA_SORENTO, CAR.SONATA_LF, CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.VELOSTER, + CAR.GENESIS_G70, CAR.GENESIS_G80, CAR.KIA_CEED, CAR.ELANTRA, CAR.IONIQ_HEV_2022} # If 0x500 is present on bus 1 it probably has a Mando radar outputting radar points. # If no points are outputted by default it might be possible to turn it on using selfdrive/debug/hyundai_enable_radar_points.py From 6474e16a720e88181f03652dcfe91306ddbdc06e Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 11 Feb 2023 00:12:33 -0800 Subject: [PATCH 374/484] GM: add permanent ACC fault signal (#27306) * bump opendbc * check signal * bump opendbc --- opendbc | 2 +- selfdrive/car/gm/carstate.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/opendbc b/opendbc index 6eab6cf6ad..510bfc0695 160000 --- a/opendbc +++ b/opendbc @@ -1 +1 @@ -Subproject commit 6eab6cf6ad5ac69539037f4508582fbfaf239eab +Subproject commit 510bfc06954e31257f8d8de17adf92f9a68a1b71 diff --git a/selfdrive/car/gm/carstate.py b/selfdrive/car/gm/carstate.py index 90705d871e..3c7d35f2dc 100644 --- a/selfdrive/car/gm/carstate.py +++ b/selfdrive/car/gm/carstate.py @@ -100,7 +100,8 @@ class CarState(CarStateBase): ret.parkingBrake = pt_cp.vl["VehicleIgnitionAlt"]["ParkBrake"] == 1 ret.cruiseState.available = pt_cp.vl["ECMEngineStatus"]["CruiseMainOn"] != 0 ret.espDisabled = pt_cp.vl["ESPStatus"]["TractionControlOn"] != 1 - ret.accFaulted = pt_cp.vl["AcceleratorPedal2"]["CruiseState"] == AccState.FAULTED + ret.accFaulted = (pt_cp.vl["AcceleratorPedal2"]["CruiseState"] == AccState.FAULTED or + pt_cp.vl["EBCMFrictionBrakeStatus"]["FrictionBrakeUnavailable"] == 1) ret.cruiseState.enabled = pt_cp.vl["AcceleratorPedal2"]["CruiseState"] != AccState.OFF ret.cruiseState.standstill = pt_cp.vl["AcceleratorPedal2"]["CruiseState"] == AccState.STANDSTILL @@ -155,6 +156,7 @@ class CarState(CarStateBase): ("RLWheelSpd", "EBCMWheelSpdRear"), ("RRWheelSpd", "EBCMWheelSpdRear"), ("MovingBackward", "EBCMWheelSpdRear"), + ("FrictionBrakeUnavailable", "EBCMFrictionBrakeStatus"), ("PRNDL2", "ECMPRDNL2"), ("ManualMode", "ECMPRDNL2"), ("LKADriverAppldTrq", "PSCMStatus"), @@ -180,6 +182,7 @@ class CarState(CarStateBase): ("VehicleIgnitionAlt", 10), ("EBCMWheelSpdFront", 20), ("EBCMWheelSpdRear", 20), + ("EBCMFrictionBrakeStatus", 20), ("AcceleratorPedal2", 33), ("ASCMSteeringButton", 33), ("ECMEngineStatus", 100), From 4998b600ecebf712b127b1e132d249a1b51a90bc Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sat, 11 Feb 2023 17:57:10 -0800 Subject: [PATCH 375/484] more release notes --- RELEASES.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index ded06346c6..f3d4cfb64f 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,12 +1,12 @@ -Version 0.9.1 (2023-2-20) +Version 0.9.1 (2023-2-23) ======================== -* Adjust alert volume using ambient noise level -* Removed driver monitoring timer resetting on interaction if face detected and distracted +* New driving model + * 30% improved height estimation resulting in better driving performance for tall cars +* Driver monitoring: removed timer resetting on user interaction if distracted * UI updates + * Adjust alert volume using ambient noise level * Driver monitoring icon shows driver's head pose * German translation thanks to Vrabetz and CzokNorris! -* New driving model - * 30% improved height estimation resulting in better driving performance for tall cars * Chevrolet Bolt EV 2022-23 support thanks to JasonJShuler! * Genesis GV60 2023 support thanks to sunnyhaibin! * Hyundai Tucson 2022-23 support From 341eb2e0fd46b79eeb4096b28443f304c3e71528 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sat, 11 Feb 2023 21:12:07 -0800 Subject: [PATCH 376/484] boardd: retry on bad SPI RX data len (#27314) Co-authored-by: Comma Device --- selfdrive/boardd/spi.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/selfdrive/boardd/spi.cc b/selfdrive/boardd/spi.cc index c9d4b2ea0e..9a10e30f95 100644 --- a/selfdrive/boardd/spi.cc +++ b/selfdrive/boardd/spi.cc @@ -316,7 +316,10 @@ int PandaSpiHandle::spi_transfer(uint8_t endpoint, uint8_t *tx_data, uint16_t tx goto transfer_fail; } rx_data_len = *(uint16_t *)(rx_buf+1); - assert(rx_data_len < SPI_BUF_SIZE); + if (rx_data_len >= SPI_BUF_SIZE) { + LOGE("SPI: RX data len larger than buf size %d", rx_data_len); + goto transfer_fail; + } // Read data transfer.len = rx_data_len + 1; From f71aad955ca906a69da6d24dee63d1482077de8e Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 13 Feb 2023 10:49:33 +0800 Subject: [PATCH 377/484] cabana: cancel signal highlighting on leaveEvent (#27315) cacnel signal hilighting on leaveEvent --- tools/cabana/signaledit.cc | 5 +++++ tools/cabana/signaledit.h | 1 + 2 files changed, 6 insertions(+) diff --git a/tools/cabana/signaledit.cc b/tools/cabana/signaledit.cc index 7988037c34..0499b1be8a 100644 --- a/tools/cabana/signaledit.cc +++ b/tools/cabana/signaledit.cc @@ -449,3 +449,8 @@ void SignalView::signalHovered(const Signal *sig) { } } } + +void SignalView::leaveEvent(QEvent *event) { + emit highlight(nullptr); + QWidget::leaveEvent(event); +} diff --git a/tools/cabana/signaledit.h b/tools/cabana/signaledit.h index 6a904e7328..c0b649209a 100644 --- a/tools/cabana/signaledit.h +++ b/tools/cabana/signaledit.h @@ -89,6 +89,7 @@ signals: private: void rowsChanged(); + void leaveEvent(QEvent *event); QString msg_id; QTreeView *tree; From e219da29b28807a324af1e9c54a3791d8e295230 Mon Sep 17 00:00:00 2001 From: Miniz199922 <124948595+Miniz199922@users.noreply.github.com> Date: Sun, 12 Feb 2023 21:55:31 -0500 Subject: [PATCH 378/484] Chrysler: add missing 2020 Jeep SRT address (#27312) * 2020 Jeep srt fingerprint Please update to include my 2020 Jeep srt * Update selfdrive/car/chrysler/values.py * Revert "Update selfdrive/car/chrysler/values.py" This reverts commit 83fc38acbf7e7b257713a0a85096d7d34a6e0c90. * Update selfdrive/car/chrysler/values.py * rm new fp --------- Co-authored-by: Shane Smiskol --- selfdrive/car/chrysler/values.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/car/chrysler/values.py b/selfdrive/car/chrysler/values.py index 49f8a080cb..c6b8bcadb0 100644 --- a/selfdrive/car/chrysler/values.py +++ b/selfdrive/car/chrysler/values.py @@ -142,7 +142,7 @@ FINGERPRINTS = { }], CAR.JEEP_CHEROKEE_2019: [{ # Jeep Grand Cherokee 2019, including most 2020 models - 55: 8, 168: 8, 179: 8, 181: 8, 256: 4, 257: 5, 258: 8, 264: 8, 268: 8, 272: 6, 273: 6, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 292: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 341: 8, 344: 8, 352: 8, 362: 8, 368: 8, 376: 3, 384: 8, 388: 4, 416: 7, 448: 6, 456: 4, 464: 8, 500: 8, 501: 8, 512: 8, 514: 8, 520: 8, 530: 8, 532: 8, 544: 8, 557: 8, 559: 8, 560: 8, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 618: 8, 624: 8, 625: 8, 632: 8, 639: 8, 640: 1, 656: 4, 658: 6, 660: 8, 671: 8, 672: 8, 676: 8, 678: 8, 680: 8, 683: 8, 684: 8, 703: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 737: 8, 738: 8, 746: 5, 752: 2, 754: 8, 760: 8, 761: 8, 764: 8, 766: 8, 773: 8, 776: 8, 779: 8, 782: 8, 783: 8, 784: 8, 785: 8, 792: 8, 799: 8, 800: 8, 804: 8, 806: 2, 808: 8, 810: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 831: 6, 832: 8, 838: 2, 840: 8, 844: 5, 847: 1, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 882: 8, 897: 8, 906: 8, 924: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 960: 4, 968: 8, 969: 4, 970: 8, 973: 8, 974: 5, 976: 8, 977: 4, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1062: 8, 1098: 8, 1100: 8, 1216: 8, 1218: 8, 1220: 8, 1223: 8, 1225: 8, 1227: 8, 1235: 8, 1242: 8, 1250: 8, 1251: 8, 1252: 8, 1254: 8, 1264: 8, 1284: 8, 1536: 8, 1537: 8, 1543: 8, 1545: 8, 1562: 8, 1568: 8, 1570: 8, 1572: 8, 1593: 8, 1856: 8, 1858: 8, 1860: 8, 1863: 8, 1865: 8, 1867: 8, 1875: 8, 1882: 8, 1890: 8, 1891: 8, 1892: 8, 1894: 8, 1896: 8, 1904: 8, 2015: 8, 2016: 8, 2017: 8, 2024: 8, 2025: 8 + 55: 8, 168: 8, 179: 8, 181: 8, 256: 4, 257: 5, 258: 8, 264: 8, 268: 8, 272: 6, 273: 6, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 292: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 341: 8, 344: 8, 352: 8, 362: 8, 368: 8, 376: 3, 384: 8, 388: 4, 416: 7, 448: 6, 456: 4, 464: 8, 500: 8, 501: 8, 512: 8, 514: 8, 520: 8, 530: 8, 532: 8, 544: 8, 557: 8, 559: 8, 560: 8, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 618: 8, 624: 8, 625: 8, 632: 8, 639: 8, 640: 1, 656: 4, 658: 6, 660: 8, 671: 8, 672: 8, 676: 8, 678: 8, 680: 8, 683: 8, 684: 8, 703: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 737: 8, 738: 8, 746: 5, 752: 2, 754: 8, 760: 8, 761: 8, 764: 8, 766: 8, 773: 8, 776: 8, 779: 8, 782: 8, 783: 8, 784: 8, 785: 8, 792: 8, 799: 8, 800: 8, 804: 8, 806: 2, 808: 8, 810: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 831: 6, 832: 8, 838: 2, 840: 8, 844: 5, 847: 1, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 874: 2, 882: 8, 897: 8, 906: 8, 924: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 960: 4, 968: 8, 969: 4, 970: 8, 973: 8, 974: 5, 976: 8, 977: 4, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1062: 8, 1098: 8, 1100: 8, 1216: 8, 1218: 8, 1220: 8, 1223: 8, 1225: 8, 1227: 8, 1235: 8, 1242: 8, 1250: 8, 1251: 8, 1252: 8, 1254: 8, 1264: 8, 1284: 8, 1536: 8, 1537: 8, 1543: 8, 1545: 8, 1562: 8, 1568: 8, 1570: 8, 1572: 8, 1593: 8, 1856: 8, 1858: 8, 1860: 8, 1863: 8, 1865: 8, 1867: 8, 1875: 8, 1882: 8, 1890: 8, 1891: 8, 1892: 8, 1894: 8, 1896: 8, 1904: 8, 2015: 8, 2016: 8, 2017: 8, 2024: 8, 2025: 8 }], } From 38cf0b8382d6f41e4e9ccd2610bdabe34ff33a87 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 13 Feb 2023 12:11:32 +0800 Subject: [PATCH 379/484] cabana: fixed the buggy has_more_data, reduce unncessary calls to fetchMore (#27319) --- tools/cabana/historylog.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/cabana/historylog.cc b/tools/cabana/historylog.cc index 3a3397af42..e4ad99758b 100644 --- a/tools/cabana/historylog.cc +++ b/tools/cabana/historylog.cc @@ -81,11 +81,12 @@ void HistoryLogModel::updateState() { if (!msg_id.isEmpty()) { uint64_t current_time = (can->lastMessage(msg_id).ts + can->routeStartTime()) * 1e9 + 1; auto new_msgs = dynamic_mode ? fetchData(current_time, last_fetch_time) : fetchData(0); - if ((has_more_data = !new_msgs.empty())) { + if (!new_msgs.empty()) { beginInsertRows({}, 0, new_msgs.size() - 1); messages.insert(messages.begin(), std::move_iterator(new_msgs.begin()), std::move_iterator(new_msgs.end())); endInsertRows(); } + has_more_data = new_msgs.size() >= batch_size; last_fetch_time = current_time; } } @@ -93,11 +94,12 @@ void HistoryLogModel::updateState() { void HistoryLogModel::fetchMore(const QModelIndex &parent) { if (!messages.empty()) { auto new_msgs = fetchData(messages.back().mono_time); - if ((has_more_data = !new_msgs.empty())) { + if (!new_msgs.empty()) { beginInsertRows({}, messages.size(), messages.size() + new_msgs.size() - 1); messages.insert(messages.end(), std::move_iterator(new_msgs.begin()), std::move_iterator(new_msgs.end())); endInsertRows(); } + has_more_data = new_msgs.size() >= batch_size; } } From fff6e8baf6791382fc350950ee02e2ab0db78e44 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 13 Feb 2023 12:11:55 +0800 Subject: [PATCH 380/484] cabana: fix crash in BinaryView::refresh (#27318) --- tools/cabana/binaryview.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cabana/binaryview.cc b/tools/cabana/binaryview.cc index a46c25bdfa..52b31b3559 100644 --- a/tools/cabana/binaryview.cc +++ b/tools/cabana/binaryview.cc @@ -143,8 +143,8 @@ void BinaryView::refresh() { anchor_index = QModelIndex(); resize_sig = nullptr; hovered_sig = nullptr; - highlightPosition(QCursor::pos()); model->refresh(); + highlightPosition(QCursor::pos()); } QSet BinaryView::getOverlappingSignals() const { From e952ad2c66ccaf68027509f5da6b310ddb9c35db Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 14 Feb 2023 02:44:44 +0800 Subject: [PATCH 381/484] cabana: draw borders around signals (#27307) * cabana: draw borders around signals * add padding * merge #27294 * add comment * typo * fix alpha * cleanup * spacing =2 --------- Co-authored-by: Willem Melching --- tools/cabana/binaryview.cc | 86 +++++++++++++++++++++++++++++++------- tools/cabana/binaryview.h | 2 + 2 files changed, 74 insertions(+), 14 deletions(-) diff --git a/tools/cabana/binaryview.cc b/tools/cabana/binaryview.cc index 52b31b3559..6f62a04871 100644 --- a/tools/cabana/binaryview.cc +++ b/tools/cabana/binaryview.cc @@ -1,5 +1,7 @@ #include "tools/cabana/binaryview.h" +#include + #include #include #include @@ -7,8 +9,6 @@ #include #include -#include - #include "tools/cabana/commands.h" #include "tools/cabana/streams/abstractstream.h" @@ -83,7 +83,7 @@ void BinaryView::mousePressEvent(QMouseEvent *event) { if (bit_idx == s->lsb || bit_idx == s->msb) { anchor_index = model->bitIndex(bit_idx == s->lsb ? s->msb : s->lsb, true); resize_sig = s; - delegate->selection_color = item->bg_color; + delegate->selection_color = getColor(s); break; } } @@ -266,6 +266,17 @@ BinaryItemDelegate::BinaryItemDelegate(QObject *parent) : QStyledItemDelegate(pa hex_font.setBold(true); } +bool BinaryItemDelegate::isSameColor(const QModelIndex &index, int dx, int dy) const { + QModelIndex index2 = index.sibling(index.row() + dy, index.column() + dx); + if (!index2.isValid()) { + return false; + } + auto color1 = ((const BinaryViewModel::Item *)index.internalPointer())->bg_color; + auto color2 = ((const BinaryViewModel::Item *)index2.internalPointer())->bg_color; + // Ignore alpha + return (color1.red() == color2.red()) && (color2.green() == color2.green()) && (color1.blue() == color2.blue()); +} + void BinaryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { auto item = (const BinaryViewModel::Item *)index.internalPointer(); BinaryView *bin_view = (BinaryView *)parent(); @@ -277,24 +288,71 @@ void BinaryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op } else if (option.state & QStyle::State_Selected) { painter->fillRect(option.rect, selection_color); painter->setPen(option.palette.color(QPalette::BrightText)); - } else if (!item->sigs.isEmpty() && (!bin_view->selectionModel()->hasSelection() || !item->sigs.contains(bin_view->resize_sig))) { - bool sig_hovered = item->sigs.contains(bin_view->hovered_sig); - int min_alpha = item->sigs.contains(bin_view->hovered_sig) ? 255 : 50; + } else if (!bin_view->selectionModel()->hasSelection() || !item->sigs.contains(bin_view->resize_sig)) { // not resizing QColor bg = item->bg_color; - if (bg.alpha() < min_alpha) { - bg.setAlpha(min_alpha); + if (bin_view->hovered_sig && item->sigs.contains(bin_view->hovered_sig)) { + bg.setAlpha(255); + painter->fillRect(option.rect, bg.darker(125)); // 4/5x brightness + painter->setPen(option.palette.color(QPalette::BrightText)); + } else { + if (item->sigs.size() > 0) { + drawBorder(painter, option, index); + bg.setAlpha(std::max(50, bg.alpha())); + } + painter->fillRect(option.rect, bg); + painter->setPen(Qt::black); } - painter->fillRect(option.rect, sig_hovered ? bg.darker(125) : bg); // 4/5x brightness - painter->setPen(sig_hovered ? option.palette.color(QPalette::BrightText) : Qt::black); - } else { - painter->fillRect(option.rect, item->bg_color); } - painter->drawText(option.rect, Qt::AlignCenter, item->val); if (item->is_msb || item->is_lsb) { painter->setFont(small_font); - painter->drawText(option.rect, Qt::AlignHCenter | Qt::AlignBottom, item->is_msb ? "MSB" : "LSB"); + painter->drawText(option.rect.adjusted(8, 0, -8, -3), Qt::AlignRight | Qt::AlignBottom, item->is_msb ? "M" : "L"); } painter->restore(); } + +// Draw border on edge of signal +void BinaryItemDelegate::drawBorder(QPainter* painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { + auto item = (const BinaryViewModel::Item *)index.internalPointer(); + QColor border_color = item->bg_color; + border_color.setAlphaF(1.0); + + bool draw_left = !isSameColor(index, -1, 0); + bool draw_top = !isSameColor(index, 0, -1); + bool draw_right = !isSameColor(index, 1, 0); + bool draw_bottom = !isSameColor(index, 0, 1); + + const int spacing = 2; + QRect rc = option.rect.adjusted(draw_left * 3, draw_top * spacing, draw_right * -3, draw_bottom * -spacing); + QRegion subtract; + if (!draw_top) { + if (!draw_left && !isSameColor(index, -1, -1)) { + subtract += QRect{rc.left(), rc.top(), 3, spacing}; + } else if (!draw_right && !isSameColor(index, 1, -1)) { + subtract += QRect{rc.right() - 2, rc.top(), 3, spacing}; + } + } + if (!draw_bottom) { + if (!draw_left && !isSameColor(index, -1, 1)) { + subtract += QRect{rc.left(), rc.bottom() - (spacing - 1), 3, spacing}; + } else if (!draw_right && !isSameColor(index, 1, 1)) { + subtract += QRect{rc.right() - 2, rc.bottom() - (spacing - 1), 3, spacing}; + } + } + + painter->setPen(QPen(border_color, 1)); + if (draw_left) painter->drawLine(rc.topLeft(), rc.bottomLeft()); + if (draw_right) painter->drawLine(rc.topRight(), rc.bottomRight()); + if (draw_bottom) painter->drawLine(rc.bottomLeft(), rc.bottomRight()); + if (draw_top) painter->drawLine(rc.topLeft(), rc.topRight()); + + painter->setClipRegion(QRegion(rc).subtracted(subtract)); + if (!subtract.isEmpty()) { + // fill gaps inside corners. + painter->setPen(QPen(border_color, 2)); + for (auto &r : subtract) { + painter->drawRect(r); + } + } +} diff --git a/tools/cabana/binaryview.h b/tools/cabana/binaryview.h index d006672793..18d87bdd57 100644 --- a/tools/cabana/binaryview.h +++ b/tools/cabana/binaryview.h @@ -13,6 +13,8 @@ public: BinaryItemDelegate(QObject *parent); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void setSelectionColor(const QColor &color) { selection_color = color; } + bool isSameColor(const QModelIndex &index, int dx, int dy) const; + void drawBorder(QPainter* painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; QFont small_font, hex_font; QColor selection_color; From 507badb11717e23681ddaf7a3f54ebe348978aef Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 14 Feb 2023 06:40:47 +0800 Subject: [PATCH 382/484] cabana: set legend marker shape by the series type (#27296) set legend marker shape by the series type --- tools/cabana/chartswidget.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 5063ec9dea..aceef40556 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -780,8 +780,10 @@ QXYSeries *ChartView::createSeries(QAbstractSeries::SeriesType type, QColor colo QXYSeries *series = nullptr; if (type == QAbstractSeries::SeriesTypeLine) { series = new QLineSeries(this); + chart()->legend()->setMarkerShape(QLegend::MarkerShapeRectangle); } else { series = new QScatterSeries(this); + chart()->legend()->setMarkerShape(QLegend::MarkerShapeCircle); } series->setColor(color); // TODO: Due to a bug in CameraWidget the camera frames From e3de6b88342f050edc0eb89d65b55de0fe2f1a8c Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Mon, 13 Feb 2023 16:15:59 -0800 Subject: [PATCH 383/484] Chrysler: revert torque tuning (#27327) * Chrysler: revert torque tuning * update refs * update refs + route --- panda | 2 +- selfdrive/car/chrysler/interface.py | 23 ++++++++----------- selfdrive/car/chrysler/values.py | 11 ++------- selfdrive/test/process_replay/ref_commit | 2 +- .../test/process_replay/test_processes.py | 2 +- 5 files changed, 14 insertions(+), 26 deletions(-) diff --git a/panda b/panda index d15250cb14..14051f9ce3 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit d15250cb1454292c6f1217c79642b9ffd93e7595 +Subproject commit 14051f9ce3c9cbaf7044c81c9902dd0f1cea3980 diff --git a/selfdrive/car/chrysler/interface.py b/selfdrive/car/chrysler/interface.py index 4b22c478a7..961684f398 100755 --- a/selfdrive/car/chrysler/interface.py +++ b/selfdrive/car/chrysler/interface.py @@ -2,7 +2,7 @@ from cereal import car from panda import Panda from selfdrive.car import STD_CARGO_KG, get_safety_config -from selfdrive.car.chrysler.values import CAR, RAM_HD, RAM_DT, RAM_CARS, CHRYSLER_OLD_TUNING_BLACKLIST, ChryslerFlags +from selfdrive.car.chrysler.values import CAR, RAM_HD, RAM_DT, RAM_CARS, ChryslerFlags from selfdrive.car.interfaces import CarInterfaceBase @@ -23,9 +23,6 @@ class CarInterface(CarInterfaceBase): elif candidate in RAM_DT: ret.safetyConfigs[0].safetyParam |= Panda.FLAG_CHRYSLER_RAM_DT - if candidate in CHRYSLER_OLD_TUNING_BLACKLIST: - ret.safetyConfigs[0].safetyParam |= Panda.FLAG_CHRYSLER_LOWER_RATE - ret.minSteerSpeed = 3.8 # m/s CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) if candidate not in RAM_CARS: @@ -41,11 +38,10 @@ class CarInterface(CarInterfaceBase): ret.wheelbase = 3.089 ret.steerRatio = 16.2 # Pacifica Hybrid 2017 - if candidate in CHRYSLER_OLD_TUNING_BLACKLIST: - ret.lateralTuning.init('pid') - ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kiBP = [[9., 20.], [9., 20.]] - ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.15, 0.30], [0.03, 0.05]] - ret.lateralTuning.pid.kf = 0.00006 + ret.lateralTuning.init('pid') + ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kiBP = [[9., 20.], [9., 20.]] + ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.15, 0.30], [0.03, 0.05]] + ret.lateralTuning.pid.kf = 0.00006 # Jeep elif candidate in (CAR.JEEP_CHEROKEE, CAR.JEEP_CHEROKEE_2019): @@ -54,11 +50,10 @@ class CarInterface(CarInterfaceBase): ret.steerRatio = 16.7 ret.steerActuatorDelay = 0.2 - if candidate in CHRYSLER_OLD_TUNING_BLACKLIST: - ret.lateralTuning.init('pid') - ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kiBP = [[9., 20.], [9., 20.]] - ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.15, 0.30], [0.03, 0.05]] - ret.lateralTuning.pid.kf = 0.00006 + ret.lateralTuning.init('pid') + ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kiBP = [[9., 20.], [9., 20.]] + ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.15, 0.30], [0.03, 0.05]] + ret.lateralTuning.pid.kf = 0.00006 # Ram elif candidate == CAR.RAM_1500: diff --git a/selfdrive/car/chrysler/values.py b/selfdrive/car/chrysler/values.py index c6b8bcadb0..16ebb4fa11 100644 --- a/selfdrive/car/chrysler/values.py +++ b/selfdrive/car/chrysler/values.py @@ -46,12 +46,8 @@ class CarControllerParams: self.STEER_DELTA_DOWN = 6 self.STEER_MAX = 261 # EPS allows more, up to 350? else: - if CP.carFingerprint in CHRYSLER_OLD_TUNING_BLACKLIST: - self.STEER_DELTA_UP = 3 - self.STEER_DELTA_DOWN = 3 - else: - self.STEER_DELTA_UP = 6 - self.STEER_DELTA_DOWN = 6 + self.STEER_DELTA_UP = 3 + self.STEER_DELTA_DOWN = 3 self.STEER_MAX = 261 # higher than this faults the EPS STEER_THRESHOLD = 120 @@ -60,9 +56,6 @@ RAM_DT = {CAR.RAM_1500, } RAM_HD = {CAR.RAM_HD, } RAM_CARS = RAM_DT | RAM_HD -# the increased steer rate hasn't been verified on these cars. -# remove from this list once it's been tested and confirmed to not fault -CHRYSLER_OLD_TUNING_BLACKLIST = {CAR.PACIFICA_2017_HYBRID, CAR.PACIFICA_2018_HYBRID, CAR.PACIFICA_2020, CAR.JEEP_CHEROKEE} @dataclass class ChryslerCarInfo(CarInfo): diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 10665ae18c..98c78d3be3 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -69e52f02fd21844ff068c495b7fcb01ebc53bea5 \ No newline at end of file +5244c5fea84822bdbcd899a280090e47fb0581b7 \ No newline at end of file diff --git a/selfdrive/test/process_replay/test_processes.py b/selfdrive/test/process_replay/test_processes.py index 3ff3d36ebc..569090f606 100755 --- a/selfdrive/test/process_replay/test_processes.py +++ b/selfdrive/test/process_replay/test_processes.py @@ -48,7 +48,7 @@ segments = [ ("TOYOTA3", "regen89026F6BD8D|2022-09-27--15-45-37--0"), ("HONDA", "regenC7D5645EB17|2022-09-27--15-47-29--0"), ("HONDA2", "regenCC2ECCE5742|2022-09-27--16-18-01--0"), - ("CHRYSLER", "regenC253C4DAC90|2023-02-10--15-51-03--0"), + ("CHRYSLER", "regenC253C4DAC90|2022-09-27--15-51-03--0"), ("RAM", "regen20490083AE7|2022-09-27--15-53-15--0"), ("SUBARU", "regen1E72BBDCED5|2022-09-27--15-55-31--0"), ("GM", "regen45B05A80EF6|2022-09-27--15-57-22--0"), From fe6dc7842ec6ffe7a79c98d3dfe701bd101032c6 Mon Sep 17 00:00:00 2001 From: Nelson Chen Date: Mon, 13 Feb 2023 17:06:43 -0800 Subject: [PATCH 384/484] Make the latest month in releases.md consistent with 0-padding (#27330) --- RELEASES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index f3d4cfb64f..ceae8d10cb 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,4 +1,4 @@ -Version 0.9.1 (2023-2-23) +Version 0.9.1 (2023-02-23) ======================== * New driving model * 30% improved height estimation resulting in better driving performance for tall cars From d5d19978ad9de0066be943019bd5b7fd89af3487 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 14 Feb 2023 10:25:59 +0800 Subject: [PATCH 385/484] cabana: add name validation when editing message (#27316) * check duplicate name * cant use ' untitled' as msg name * const QString UNTITLED --- tools/cabana/dbcmanager.h | 6 ++++-- tools/cabana/detailwidget.cc | 33 +++++++++++++++++++++++++-------- tools/cabana/detailwidget.h | 5 +++++ 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/tools/cabana/dbcmanager.h b/tools/cabana/dbcmanager.h index 41471a6169..7571d1c44e 100644 --- a/tools/cabana/dbcmanager.h +++ b/tools/cabana/dbcmanager.h @@ -54,6 +54,8 @@ private: std::map msgs; }; +const QString UNTITLED = "untitled"; + // TODO: Add helper function in dbc.h double get_raw_value(uint8_t *data, size_t data_size, const Signal &sig); bool operator==(const Signal &l, const Signal &r); @@ -63,7 +65,7 @@ int bigEndianBitIndex(int index); void updateSigSizeParamsFromRange(Signal &s, int start_bit, int size); std::pair getSignalRange(const Signal *s); DBCManager *dbc(); -inline QString msgName(const QString &id, const char *def = "untitled") { +inline QString msgName(const QString &id) { auto msg = dbc()->msg(id); - return msg ? msg->name : def; + return msg ? msg->name : UNTITLED; } diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index 25d9b3b9ca..27c8cbccc9 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -1,6 +1,5 @@ #include "tools/cabana/detailwidget.h" -#include #include #include #include @@ -190,11 +189,13 @@ void DetailWidget::removeMsg() { // EditMessageDialog -EditMessageDialog::EditMessageDialog(const QString &msg_id, const QString &title, int size, QWidget *parent) : QDialog(parent) { - setWindowTitle(tr("Edit message")); +EditMessageDialog::EditMessageDialog(const QString &msg_id, const QString &title, int size, QWidget *parent) + : original_name(title), QDialog(parent) { + setWindowTitle(tr("Edit message: %1").arg(msg_id)); QFormLayout *form_layout = new QFormLayout(this); - form_layout->addRow("ID", new QLabel(msg_id)); + form_layout->addRow("", error_label = new QLabel); + error_label->setVisible(false); name_edit = new QLineEdit(title, this); name_edit->setValidator(new NameValidator(name_edit)); form_layout->addRow(tr("Name"), name_edit); @@ -205,12 +206,28 @@ EditMessageDialog::EditMessageDialog(const QString &msg_id, const QString &title size_spin->setValue(size); form_layout->addRow(tr("Size"), size_spin); - auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - form_layout->addRow(buttonBox); + btn_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + btn_box->button(QDialogButtonBox::Ok)->setEnabled(false); + form_layout->addRow(btn_box); + setFixedWidth(parent->width() * 0.9); + connect(name_edit, &QLineEdit::textEdited, this, &EditMessageDialog::validateName); + connect(btn_box, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(btn_box, &QDialogButtonBox::rejected, this, &QDialog::reject); +} - connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); - connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); +void EditMessageDialog::validateName(const QString &text) { + bool valid = false; + error_label->setVisible(false); + if (!text.isEmpty() && text != original_name && text.compare(UNTITLED, Qt::CaseInsensitive) != 0) { + valid = std::none_of(dbc()->messages().begin(), dbc()->messages().end(), + [&text](auto &m) { return m.second.name == text; }); + if (!valid) { + error_label->setText(tr("Name already exists")); + error_label->setVisible(true); + } + } + btn_box->button(QDialogButtonBox::Ok)->setEnabled(valid); } // WelcomeWidget diff --git a/tools/cabana/detailwidget.h b/tools/cabana/detailwidget.h index 0983f49924..c5d5fe9a4d 100644 --- a/tools/cabana/detailwidget.h +++ b/tools/cabana/detailwidget.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -14,8 +15,12 @@ class EditMessageDialog : public QDialog { public: EditMessageDialog(const QString &msg_id, const QString &title, int size, QWidget *parent); + void validateName(const QString &text); + QString original_name; + QDialogButtonBox *btn_box; QLineEdit *name_edit; + QLabel *error_label; QSpinBox *size_spin; }; From 65a9843ff9bbdf224d539655ea2658101df4da19 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 13 Feb 2023 21:12:13 -0800 Subject: [PATCH 386/484] Hyundai: detect LFA HDA message (#27323) * detect lfa message * comment * ? * fix * Update ref_commit --- selfdrive/car/hyundai/carcontroller.py | 6 +----- selfdrive/car/hyundai/interface.py | 4 ++++ selfdrive/car/hyundai/values.py | 3 +-- selfdrive/test/process_replay/ref_commit | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/selfdrive/car/hyundai/carcontroller.py b/selfdrive/car/hyundai/carcontroller.py index 7c31a48ba5..516666eff1 100644 --- a/selfdrive/car/hyundai/carcontroller.py +++ b/selfdrive/car/hyundai/carcontroller.py @@ -178,11 +178,7 @@ class CarController: hud_control.leadVisible, set_speed_in_units, stopping, CC.cruiseControl.override)) # 20 Hz LFA MFA message - if self.frame % 5 == 0 and self.car_fingerprint in (CAR.SONATA, CAR.PALISADE, CAR.IONIQ, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV_2021, - CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KIA_CEED, CAR.KIA_SELTOS, CAR.KONA_EV, CAR.KONA_EV_2022, - CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.SANTA_FE_2022, - CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.GENESIS_G70_2020, CAR.SANTA_FE_PHEV_2022, - CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020): + if self.frame % 5 == 0 and self.CP.flags & HyundaiFlags.SEND_LFA.value: can_sends.append(hyundaican.create_lfahda_mfc(self.packer, CC.enabled)) # 5 Hz ACC options diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py index b746680930..ae0f26274c 100644 --- a/selfdrive/car/hyundai/interface.py +++ b/selfdrive/car/hyundai/interface.py @@ -43,6 +43,10 @@ class CarInterface(CarInterfaceBase): ret.flags |= HyundaiFlags.CANFD_ALT_GEARS.value if candidate not in CANFD_RADAR_SCC_CAR: ret.flags |= HyundaiFlags.CANFD_CAMERA_SCC.value + else: + # Send LFA message on cars with HDA + if 0x485 in fingerprint[2]: + ret.flags |= HyundaiFlags.SEND_LFA.value ret.steerActuatorDelay = 0.1 # Default delay ret.steerLimitTimer = 0.4 diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index bde38b47cc..261f98f38e 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -58,10 +58,9 @@ class HyundaiFlags(IntFlag): CANFD_CAMERA_SCC = 8 ALT_LIMITS = 16 - ENABLE_BLINKERS = 32 - CANFD_ALT_GEARS_2 = 64 + SEND_LFA = 128 class CAR: diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 98c78d3be3..a4811375e4 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -5244c5fea84822bdbcd899a280090e47fb0581b7 \ No newline at end of file +3629f9d22d0bd70cc9ac19cf2045a6cd47b21fdb From b536b9c4c07742a035723c3a0e1aeb646db6555a Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 13 Feb 2023 21:39:06 -0800 Subject: [PATCH 387/484] process replay: don't upload if diff doesn't exist (#27333) * fail the replay, successfully print * should still run * debug * outside? * don't fail * don't fail * don't fail * test * test * test * try this now * clean up, works! --- .github/workflows/selfdrive_tests.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index 834b2141d0..efddde720d 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -250,6 +250,7 @@ jobs: ${{ env.RUN }} "CI=1 coverage run selfdrive/test/process_replay/test_processes.py -j$(nproc) && \ coverage xml" - name: Print diff + id: print-diff if: always() run: cat selfdrive/test/process_replay/diff.txt - uses: actions/upload-artifact@v2 @@ -259,7 +260,7 @@ jobs: name: process_replay_diff.txt path: selfdrive/test/process_replay/diff.txt - name: Upload reference logs - if: ${{ failure() && github.event_name == 'pull_request' && github.repository == 'commaai/openpilot' && env.AZURE_TOKEN != '' }} + if: ${{ failure() && steps.print-diff.outcome == 'success' && github.event_name == 'pull_request' && github.repository == 'commaai/openpilot' && env.AZURE_TOKEN != '' }} run: | ${{ env.RUN }} "CI=1 AZURE_TOKEN='$AZURE_TOKEN' python selfdrive/test/process_replay/test_processes.py -j$(nproc) --upload-only" - name: "Upload coverage to Codecov" From a6bf26f5eb5a3cb4059783ce538901b7562a96db Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 14 Feb 2023 16:46:13 +0800 Subject: [PATCH 388/484] cabana: fix two bugs in chart (#27336) fix two bugs --- tools/cabana/chartswidget.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index aceef40556..ec54d7f06a 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -364,6 +364,8 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { } void ChartView::addSeries(const QString &msg_id, const Signal *sig) { + if (hasSeries(msg_id, sig)) return; + QXYSeries *series = createSeries(series_type, getColor(sig)); chart()->addSeries(series); series->attachAxis(axis_x); @@ -438,9 +440,7 @@ void ChartView::manageSeries() { emit remove(); } else { for (auto s : items) { - if (!hasSeries(s->msg_id, s->sig)) { - addSeries(s->msg_id, s->sig); - } + addSeries(s->msg_id, s->sig); } for (auto it = sigs.begin(); it != sigs.end(); /**/) { bool exists = std::any_of(items.cbegin(), items.cend(), [&](auto &s) { @@ -852,7 +852,9 @@ SeriesSelector::SeriesSelector(QString title, QWidget *parent) : QDialog(parent) // buttons QVBoxLayout *btn_layout = new QVBoxLayout(); QPushButton *add_btn = new QPushButton(utils::icon("chevron-right"), "", this); + add_btn->setEnabled(false); QPushButton *remove_btn = new QPushButton(utils::icon("chevron-left"), "", this); + remove_btn->setEnabled(false); btn_layout->addStretch(0); btn_layout->addWidget(add_btn); btn_layout->addWidget(remove_btn); From 286e42948e7cd6da66bc6a6c48aedc7ff88fd9eb Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Tue, 14 Feb 2023 19:46:29 +0100 Subject: [PATCH 389/484] cabana: add keyboard shortcuts to binary view (#27338) * cabana: add keyboard shortcuts to binary view * typo * whitespace * keep signal highlighted --- tools/cabana/binaryview.cc | 59 ++++++++++++++++++++++++++++++++++++ tools/cabana/binaryview.h | 4 +++ tools/cabana/detailwidget.cc | 3 ++ 3 files changed, 66 insertions(+) diff --git a/tools/cabana/binaryview.cc b/tools/cabana/binaryview.cc index 6f62a04871..16df867d56 100644 --- a/tools/cabana/binaryview.cc +++ b/tools/cabana/binaryview.cc @@ -7,9 +7,11 @@ #include #include #include +#include #include #include "tools/cabana/commands.h" +#include "tools/cabana/signaledit.h" #include "tools/cabana/streams/abstractstream.h" // BinaryView @@ -38,6 +40,63 @@ BinaryView::BinaryView(QWidget *parent) : QTableView(parent) { QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &BinaryView::refresh); QObject::connect(UndoStack::instance(), &QUndoStack::indexChanged, this, &BinaryView::refresh); + + addShortcuts(); +} + +void BinaryView::addShortcuts() { + // Delete (x, backspace, delete) + QShortcut *shortcut_delete_x = new QShortcut(QKeySequence(Qt::Key_X), this); + QShortcut *shortcut_delete_backspace = new QShortcut(QKeySequence(Qt::Key_Backspace), this); + QShortcut *shortcut_delete_delete = new QShortcut(QKeySequence(Qt::Key_Delete), this); + QObject::connect(shortcut_delete_delete, &QShortcut::activated, shortcut_delete_x, &QShortcut::activated); + QObject::connect(shortcut_delete_backspace, &QShortcut::activated, shortcut_delete_x, &QShortcut::activated); + QObject::connect(shortcut_delete_x, &QShortcut::activated, [=]{ + if (hovered_sig != nullptr) { + emit removeSignal(hovered_sig); + hovered_sig = nullptr; + } + }); + + // Change endianness (e) + QShortcut *shortcut_endian = new QShortcut(QKeySequence(Qt::Key_E), this); + QObject::connect(shortcut_endian, &QShortcut::activated, [=]{ + if (hovered_sig != nullptr) { + const Signal *hovered_sig_prev = hovered_sig; + Signal s = *hovered_sig; + s.is_little_endian = !s.is_little_endian; + emit editSignal(hovered_sig, s); + + hovered_sig = nullptr; + highlight(hovered_sig_prev); + } + }); + + // Change signedness (s) + QShortcut *shortcut_sign = new QShortcut(QKeySequence(Qt::Key_S), this); + QObject::connect(shortcut_sign, &QShortcut::activated, [=]{ + if (hovered_sig != nullptr) { + const Signal *hovered_sig_prev = hovered_sig; + Signal s = *hovered_sig; + s.is_signed = !s.is_signed; + emit editSignal(hovered_sig, s); + + hovered_sig = nullptr; + highlight(hovered_sig_prev); + } + }); + + // Open chart (c, p, g) + QShortcut *shortcut_plot = new QShortcut(QKeySequence(Qt::Key_P), this); + QShortcut *shortcut_plot_g = new QShortcut(QKeySequence(Qt::Key_G), this); + QShortcut *shortcut_plot_c = new QShortcut(QKeySequence(Qt::Key_C), this); + QObject::connect(shortcut_plot_g, &QShortcut::activated, shortcut_plot, &QShortcut::activated); + QObject::connect(shortcut_plot_c, &QShortcut::activated, shortcut_plot, &QShortcut::activated); + QObject::connect(shortcut_plot, &QShortcut::activated, [=]{ + if (hovered_sig != nullptr) { + emit showChart(model->msg_id, hovered_sig, true, false); + } + }); } QSize BinaryView::minimumSizeHint() const { diff --git a/tools/cabana/binaryview.h b/tools/cabana/binaryview.h index 18d87bdd57..7cf9a8081c 100644 --- a/tools/cabana/binaryview.h +++ b/tools/cabana/binaryview.h @@ -68,8 +68,12 @@ signals: void signalHovered(const Signal *sig); void addSignal(int start_bit, int size, bool little_endian); void resizeSignal(const Signal *sig, int from, int size); + void removeSignal(const Signal *sig); + void editSignal(const Signal *origin_s, Signal &s); + void showChart(const QString &name, const Signal *sig, bool show, bool merge); private: + void addShortcuts(); void refresh(); std::tuple getSelection(QModelIndex index); void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags flags) override; diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index 27c8cbccc9..2036912344 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -74,6 +74,9 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart QObject::connect(binary_view, &BinaryView::addSignal, signal_view->model, &SignalModel::addSignal); QObject::connect(binary_view, &BinaryView::signalHovered, signal_view, &SignalView::signalHovered); QObject::connect(binary_view, &BinaryView::signalClicked, signal_view, &SignalView::expandSignal); + QObject::connect(binary_view, &BinaryView::editSignal, signal_view->model, &SignalModel::saveSignal); + QObject::connect(binary_view, &BinaryView::removeSignal, signal_view->model, &SignalModel::removeSignal); + QObject::connect(binary_view, &BinaryView::showChart, charts, &ChartsWidget::showChart); QObject::connect(signal_view, &SignalView::showChart, charts, &ChartsWidget::showChart); QObject::connect(signal_view, &SignalView::highlight, binary_view, &BinaryView::highlight); QObject::connect(tab_widget, &QTabWidget::currentChanged, [this]() { updateState(); }); From b3f327d273c42b4852d00ded35984b833a966a2b Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 14 Feb 2023 12:51:24 -0800 Subject: [PATCH 390/484] HKG: log AEB/FCW for Optima G4 FL (#27337) FL uses FCA --- selfdrive/car/hyundai/values.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 261f98f38e..4ff944cebd 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -1653,7 +1653,7 @@ FEATURES = { "use_elect_gears": {CAR.KIA_NIRO_EV, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_OPTIMA_H, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.IONIQ, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.KONA_EV_2022, CAR.KIA_K5_HEV_2020}, # these cars use the FCA11 message for the AEB and FCW signals, all others use SCC12 - "use_fca": {CAR.SONATA, CAR.SONATA_HYBRID, CAR.ELANTRA, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.KIA_STINGER, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_FORTE, CAR.KIA_NIRO_EV, CAR.PALISADE, CAR.GENESIS_G70, CAR.GENESIS_G70_2020, CAR.KONA, CAR.SANTA_FE, CAR.KIA_SELTOS, CAR.KONA_HEV, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.TUCSON, CAR.KONA_EV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020}, + "use_fca": {CAR.SONATA, CAR.SONATA_HYBRID, CAR.ELANTRA, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.KIA_STINGER, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_FORTE, CAR.KIA_NIRO_EV, CAR.PALISADE, CAR.GENESIS_G70, CAR.GENESIS_G70_2020, CAR.KONA, CAR.SANTA_FE, CAR.KIA_SELTOS, CAR.KONA_HEV, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.TUCSON, CAR.KONA_EV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020, CAR.KIA_OPTIMA_G4_FL}, } CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.TUCSON_4TH_GEN, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, CAR.SANTA_CRUZ_1ST_GEN, CAR.KIA_SPORTAGE_5TH_GEN, CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.GENESIS_GV60_EV_1ST_GEN, CAR.KIA_SORENTO_4TH_GEN, CAR.KIA_NIRO_HEV_2ND_GEN} From e7786738c45aa325d2c4a3649e9b485171e18306 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 14 Feb 2023 13:14:32 -0800 Subject: [PATCH 391/484] Hyundai: temporarily remove Tucson FW that requires different tuning (#27340) --- selfdrive/car/hyundai/values.py | 1 - 1 file changed, 1 deletion(-) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 4ff944cebd..a6842ddb1d 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -1562,7 +1562,6 @@ FW_VERSIONS = { CAR.TUCSON_4TH_GEN: { (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.01 99211-N9240 14T', - b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.00 99211-CW010 14X', ], (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00NX4__ 1.01 1.00 99110-N9100 ', From 1cdf80003dcaea79375cf398ec65849424f2f44b Mon Sep 17 00:00:00 2001 From: Vivek Aithal Date: Tue, 14 Feb 2023 13:22:49 -0800 Subject: [PATCH 392/484] GM: update steering limits (#27331) * reduce steer down limit, driver allowance * update refs * update refs --- panda | 2 +- selfdrive/car/gm/values.py | 4 ++-- selfdrive/test/process_replay/ref_commit | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/panda b/panda index 14051f9ce3..0d2ee00921 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 14051f9ce3c9cbaf7044c81c9902dd0f1cea3980 +Subproject commit 0d2ee009218009173f26c73a16aefa12dd169de8 diff --git a/selfdrive/car/gm/values.py b/selfdrive/car/gm/values.py index 7628d8420b..1c956192eb 100644 --- a/selfdrive/car/gm/values.py +++ b/selfdrive/car/gm/values.py @@ -14,8 +14,8 @@ class CarControllerParams: STEER_STEP = 3 # Active control frames per command (~33hz) INACTIVE_STEER_STEP = 10 # Inactive control frames per command (10hz) STEER_DELTA_UP = 10 # Delta rates require review due to observed EPS weakness - STEER_DELTA_DOWN = 25 - STEER_DRIVER_ALLOWANCE = 50 + STEER_DELTA_DOWN = 15 + STEER_DRIVER_ALLOWANCE = 65 STEER_DRIVER_MULTIPLIER = 4 STEER_DRIVER_FACTOR = 100 NEAR_STOP_BRAKE_PHASE = 0.5 # m/s diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index a4811375e4..85c7d8e80b 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -3629f9d22d0bd70cc9ac19cf2045a6cd47b21fdb +f4efbb65a7eb9a8f4e23492372e707674f80114e \ No newline at end of file From 62b233369bed7a156a019adc5f856f913a50ce61 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 14 Feb 2023 13:27:24 -0800 Subject: [PATCH 393/484] HKG: log AEB/FCW for Kia Niro Hybrid 2021 (#27341) Kia Niro Hybrid uses FCA too --- selfdrive/car/hyundai/values.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index a6842ddb1d..9067299473 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -1652,7 +1652,7 @@ FEATURES = { "use_elect_gears": {CAR.KIA_NIRO_EV, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_OPTIMA_H, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.IONIQ, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.KONA_EV_2022, CAR.KIA_K5_HEV_2020}, # these cars use the FCA11 message for the AEB and FCW signals, all others use SCC12 - "use_fca": {CAR.SONATA, CAR.SONATA_HYBRID, CAR.ELANTRA, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.KIA_STINGER, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_FORTE, CAR.KIA_NIRO_EV, CAR.PALISADE, CAR.GENESIS_G70, CAR.GENESIS_G70_2020, CAR.KONA, CAR.SANTA_FE, CAR.KIA_SELTOS, CAR.KONA_HEV, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.TUCSON, CAR.KONA_EV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020, CAR.KIA_OPTIMA_G4_FL}, + "use_fca": {CAR.SONATA, CAR.SONATA_HYBRID, CAR.ELANTRA, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.KIA_STINGER, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_FORTE, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV_2021, CAR.PALISADE, CAR.GENESIS_G70, CAR.GENESIS_G70_2020, CAR.KONA, CAR.SANTA_FE, CAR.KIA_SELTOS, CAR.KONA_HEV, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.TUCSON, CAR.KONA_EV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020, CAR.KIA_OPTIMA_G4_FL}, } CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.TUCSON_4TH_GEN, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, CAR.SANTA_CRUZ_1ST_GEN, CAR.KIA_SPORTAGE_5TH_GEN, CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.GENESIS_GV60_EV_1ST_GEN, CAR.KIA_SORENTO_4TH_GEN, CAR.KIA_NIRO_HEV_2ND_GEN} From 5af5f02ba82a0094c7f721ad8dd6138f503455d8 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 14 Feb 2023 13:29:23 -0800 Subject: [PATCH 394/484] better min enable speed alert (#27317) --- selfdrive/controls/lib/events.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/controls/lib/events.py b/selfdrive/controls/lib/events.py index 6d5ed45076..a85e48649f 100644 --- a/selfdrive/controls/lib/events.py +++ b/selfdrive/controls/lib/events.py @@ -230,7 +230,7 @@ def startup_master_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubM return StartupAlert("WARNING: This branch is not tested", branch, alert_status=AlertStatus.userPrompt) def below_engage_speed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: - return NoEntryAlert(f"Speed Below {get_display_speed(CP.minEnableSpeed, metric)}") + return NoEntryAlert(f"Drive above {get_display_speed(CP.minEnableSpeed, metric)} to engage") def below_steer_speed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: From 6f1d5edab832487c1b11d5e36ec077fdc3def560 Mon Sep 17 00:00:00 2001 From: Mitchell Goff Date: Tue, 14 Feb 2023 14:32:08 -0800 Subject: [PATCH 395/484] Add osmnx to xx packages (#27342) --- .pre-commit-config.yaml | 2 +- poetry.lock | 269 +++++++++++++++++++++++++++++++++++++--- pyproject.toml | 3 +- 3 files changed, 254 insertions(+), 20 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index edbca52fed..1779947ff9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,7 +19,7 @@ repos: rev: v2.2.1 hooks: - id: codespell - exclude: '^(third_party/)|(body/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(opendbc/)|(laika_repo/)|(rednose_repo/)|(selfdrive/ui/translations/.*.ts)' + exclude: '^(third_party/)|(body/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(opendbc/)|(laika_repo/)|(rednose_repo/)|(selfdrive/ui/translations/.*.ts)|(poetry.lock)' args: # if you've got a short variable name that's getting flagged, add it here - -L bu,ro,te,ue,alo,hda,ois,nam,nams,ned,som,parm,setts,inout,warmup diff --git a/poetry.lock b/poetry.lock index 8edfbb868c..91e1b5c826 100644 --- a/poetry.lock +++ b/poetry.lock @@ -564,6 +564,34 @@ python-versions = ">=3.7" [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} +[[package]] +name = "click-plugins" +version = "1.1.1" +description = "An extension module for click to enable registering CLI commands via setuptools entry-points." +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +click = ">=4.0" + +[package.extras] +dev = ["coveralls", "pytest (>=3.6)", "pytest-cov", "wheel"] + +[[package]] +name = "cligj" +version = "0.7.2" +description = "Click params for commmand line interfaces to GeoJSON" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, <4" + +[package.dependencies] +click = ">=4.0" + +[package.extras] +test = ["pytest-cov"] + [[package]] name = "cloudpickle" version = "2.2.0" @@ -959,6 +987,29 @@ python-versions = ">=3.7" docs = ["furo (>=2022.6.21)", "sphinx (>=5.1.1)", "sphinx-autodoc-typehints (>=1.19.1)"] testing = ["covdefaults (>=2.2)", "coverage (>=6.4.2)", "pytest (>=7.1.2)", "pytest-cov (>=3)", "pytest-timeout (>=2.1)"] +[[package]] +name = "fiona" +version = "1.9.1" +description = "Fiona reads and writes spatial data files" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +attrs = ">=19.2.0" +certifi = "*" +click = ">=8.0,<9.0" +click-plugins = ">=1.0" +cligj = ">=0.5" +munch = ">=2.3.2" +setuptools = "*" + +[package.extras] +all = ["Fiona[calc,s3,test]"] +calc = ["shapely"] +s3 = ["boto3 (>=1.3.1)"] +test = ["Fiona[s3]", "pytest (>=7)", "pytest-cov", "pytz"] + [[package]] name = "flake8" version = "4.0.1" @@ -1092,6 +1143,21 @@ python-versions = ">=3.7" packaging = "*" SQLAlchemy = ">=1.4" +[[package]] +name = "geopandas" +version = "0.12.2" +description = "Geographic pandas extensions" +category = "dev" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +fiona = ">=1.8" +packaging = "*" +pandas = ">=1.0.0" +pyproj = ">=2.6.1.post1" +shapely = ">=1.7" + [[package]] name = "gevent" version = "22.10.1" @@ -2325,27 +2391,18 @@ python-versions = ">=3.5" [[package]] name = "networkx" -version = "2.3" +version = "2.8.8" description = "Python package for creating and manipulating graphs and networks" category = "dev" optional = false -python-versions = ">=3.5" - -[package.dependencies] -decorator = ">=4.3.0" +python-versions = ">=3.8" [package.extras] -all = ["gdal", "lxml", "matplotlib", "nose", "numpy", "pandas", "pydot", "pygraphviz", "pyyaml", "scipy"] -gdal = ["gdal"] -lxml = ["lxml"] -matplotlib = ["matplotlib"] -nose = ["nose"] -numpy = ["numpy"] -pandas = ["pandas"] -pydot = ["pydot"] -pygraphviz = ["pygraphviz"] -pyyaml = ["pyyaml"] -scipy = ["scipy"] +default = ["matplotlib (>=3.4)", "numpy (>=1.19)", "pandas (>=1.3)", "scipy (>=1.8)"] +developer = ["mypy (>=0.982)", "pre-commit (>=2.20)"] +doc = ["nb2plots (>=0.6)", "numpydoc (>=1.5)", "pillow (>=9.2)", "pydata-sphinx-theme (>=0.11)", "sphinx (>=5.2)", "sphinx-gallery (>=0.11)", "texext (>=0.6.6)"] +extra = ["lxml (>=4.6)", "pydot (>=1.4.2)", "pygraphviz (>=1.9)", "sympy (>=1.10)"] +test = ["codecov (>=2.1)", "pytest (>=7.2)", "pytest-cov (>=4.0)"] [[package]] name = "nodeenv" @@ -2542,6 +2599,31 @@ python-versions = ">=3.6" [package.dependencies] requests = "*" +[[package]] +name = "osmnx" +version = "1.2.2" +description = "Retrieve, model, analyze, and visualize OpenStreetMap street networks and other spatial data" +category = "dev" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +geopandas = ">=0.11" +matplotlib = ">=3.5" +networkx = ">=2.8" +numpy = ">=1.22" +pandas = ">=1.4" +pyproj = ">=3.3" +requests = ">=2.28" +Rtree = ">=1.0" +Shapely = ">=1.8,<2.0" + +[package.extras] +entropy = ["scipy"] +nearest-neighbor = ["scikit-learn", "scipy"] +raster = ["gdal", "rasterio"] +web-map = ["folium"] + [[package]] name = "packaging" version = "21.3" @@ -3486,6 +3568,14 @@ python-versions = "*" numpy = ">=1.11.0" scipy = ">=0.17.1" +[[package]] +name = "rtree" +version = "1.0.1" +description = "R-Tree spatial index for Python GIS" +category = "dev" +optional = false +python-versions = ">=3.7" + [[package]] name = "s2sphere" version = "0.2.5" @@ -3688,6 +3778,19 @@ typing-extensions = "*" test = ["pytest (>=6.2)", "virtualenv (>20)"] toml = ["setuptools (>=42)"] +[[package]] +name = "shapely" +version = "1.8.5.post1" +description = "Geometric objects, predicates, and operations" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +all = ["numpy", "pytest", "pytest-cov"] +test = ["pytest", "pytest-cov"] +vectorized = ["numpy"] + [[package]] name = "shellingham" version = "1.5.0" @@ -4464,7 +4567,7 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] lock-version = "1.1" python-versions = "~3.8" -content-hash = "3c2597d2199a29ef79dd4d5fbc5e2350d86c82a3fe6716d13b93ebc268053eb9" +content-hash = "9e9495c896e6fd0855803aeaf46513c6c22424b86be820759a8baf27d44e73ee" [metadata.files] adal = [ @@ -4917,6 +5020,14 @@ click = [ {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, ] +click-plugins = [ + {file = "click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b"}, + {file = "click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8"}, +] +cligj = [ + {file = "cligj-0.7.2-py3-none-any.whl", hash = "sha256:c1ca117dbce1fe20a5809dc96f01e1c2840f6dcc939b3ddbb1111bf330ba82df"}, + {file = "cligj-0.7.2.tar.gz", hash = "sha256:a4bc13d623356b373c2c27c53dbd9c68cae5d526270bfa71f6c6fa69669c6b27"}, +] cloudpickle = [ {file = "cloudpickle-2.2.0-py3-none-any.whl", hash = "sha256:7428798d5926d8fcbfd092d18d01a2a03daf8237d8fcdc8095d256b8490796f0"}, {file = "cloudpickle-2.2.0.tar.gz", hash = "sha256:3f4219469c55453cfe4737e564b67c2a149109dabf7f242478948b895f61106f"}, @@ -5339,6 +5450,28 @@ filelock = [ {file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"}, {file = "filelock-3.8.0.tar.gz", hash = "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc"}, ] +fiona = [ + {file = "Fiona-1.9.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:902b67b2d012c5797b5d7d3cb3b46dcf9a342cf90a7f7e53fb12c83738d19926"}, + {file = "Fiona-1.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e2f4535ae2c8446e6b328745a44567478d5a077ed63c888b8c212dddb1e11925"}, + {file = "Fiona-1.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3d7f3286fb59b93cefefb89014b6fa8413126e180e15c576db859ba936cf334"}, + {file = "Fiona-1.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:692fe64c0f3a39742d6f7a8e420a8387f6aec3b6818b727d2dfc98a0c40e992d"}, + {file = "Fiona-1.9.1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:a017a39650df0cc541c57cf7de450bb4cee6fd9760eb716323b594c1074634a2"}, + {file = "Fiona-1.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c3b52d49bc379fdcfd1250b38e7e00ab24ee14eb765376c793bbe251ffd09d6a"}, + {file = "Fiona-1.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dbb0ae851cd4417104c469335a01f938251a8639317f93d422c5c808150bd27"}, + {file = "Fiona-1.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:4a6d8fcbbdafa8af8ac1904628b0267382ed9f9921933d061d7bfc5d3f3daf99"}, + {file = "Fiona-1.9.1-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:74f056a84dc52a0b21a2cf024601a69105596f06f28d40b45049948be17b4df2"}, + {file = "Fiona-1.9.1-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:5b9f3f6c782bb4ae2c924eefd373cabdeaaa99f86477b9c7c71eb20c052ee7c5"}, + {file = "Fiona-1.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:5d6183189a7e05e2498d38a1df3ab07e1353fa48e977cbc3a31203927bd06bca"}, + {file = "Fiona-1.9.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:5f2c14beec98a330aee1fd81fa0447a6aa1d5e0a75d000c0052dbe1f23dd6cfd"}, + {file = "Fiona-1.9.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:585a47ec21f2d198abc112158eaf12a6587a272beb7f001162d8c3b262676666"}, + {file = "Fiona-1.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4368b4054f21518a19dea54ce9ac445c40418c6331c0c99d1531c3ddff05da"}, + {file = "Fiona-1.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:839ed0db23198bb754f0d655d4eeaf5f9c141bef734557e77e95e4dc83e42e7f"}, + {file = "Fiona-1.9.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:decca3956032ae2d2c19d2f7fa8a4553c43a6e66eb5abe9a05f6ddadcb1bfe5c"}, + {file = "Fiona-1.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cb827fa0030d03d8080723137c74b865ec18dbade87c02ed60215491a315c6be"}, + {file = "Fiona-1.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7e0f36484757baa8cd1c0602941e029ff992282776f9afae4c5b90f501ff005"}, + {file = "Fiona-1.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:f98933b552adf0506799097934a6950de412288e7a50466144d04874d6f63fbc"}, + {file = "Fiona-1.9.1.tar.gz", hash = "sha256:3a3725e94840a387fef48726d60db6a6791563f366939d22378a4661f8941be7"}, +] flake8 = [ {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, @@ -5463,6 +5596,10 @@ geoalchemy2 = [ {file = "GeoAlchemy2-0.12.5-py2.py3-none-any.whl", hash = "sha256:3a59eb651df95b3dfee8e1d82f4d18c80b75f712860a0a3080defc6b0435070d"}, {file = "GeoAlchemy2-0.12.5.tar.gz", hash = "sha256:31c2502dce317b57b335e4eb87562d501fa39e46c728be514d9b86091e08dd67"}, ] +geopandas = [ + {file = "geopandas-0.12.2-py3-none-any.whl", hash = "sha256:0a470e4bf6f5367e6fd83ab6b40405e0b805c8174665bbcb7c4077ed90202912"}, + {file = "geopandas-0.12.2.tar.gz", hash = "sha256:0acdacddefa176525e4da6d9aeeece225da26055c4becdc6e97cf40fa97c27f4"}, +] gevent = [ {file = "gevent-22.10.1-cp27-cp27m-win32.whl", hash = "sha256:702a51b8f21bad1976b0893f90ade466e8c27039b846b611ad2beb8c6e6ac701"}, {file = "gevent-22.10.1-cp27-cp27m-win_amd64.whl", hash = "sha256:af7baec79a5f8ad1cc132d3b14edd12661c628d8094e501b089b1fe2d3df7f6e"}, @@ -6393,7 +6530,8 @@ nest-asyncio = [ {file = "nest_asyncio-1.5.6.tar.gz", hash = "sha256:d267cc1ff794403f7df692964d1d2a3fa9418ffea2a3f6859a439ff482fef290"}, ] networkx = [ - {file = "networkx-2.3.zip", hash = "sha256:8311ddef63cf5c5c5e7c1d0212dd141d9a1fe3f474915281b73597ed5f1d4e3d"}, + {file = "networkx-2.8.8-py3-none-any.whl", hash = "sha256:e435dfa75b1d7195c7b8378c3859f0445cd88c6b0375c181ed66823a9ceb7524"}, + {file = "networkx-2.8.8.tar.gz", hash = "sha256:230d388117af870fce5647a3c52401fcf753e94720e6ea6b4197a5355648885e"}, ] nodeenv = [ {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, @@ -6521,6 +6659,10 @@ osmium = [ {file = "osmium-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e45b7c54ac756e9cb40e2ba68691df635804eb6aa2023088af66936a9c8e3782"}, {file = "osmium-3.4.1.tar.gz", hash = "sha256:575dad72ab169cf585b9aeefb4f5f99ac250bf7da1986992afcbf169dc70c381"}, ] +osmnx = [ + {file = "osmnx-1.2.2-py2.py3-none-any.whl", hash = "sha256:94f2a3929e857d8c0da39ae552c6da3b1a3f4bcfea6de108696bda5ee3a7689d"}, + {file = "osmnx-1.2.2.tar.gz", hash = "sha256:30924452ca02758ece3301f9fcfb1b80edf31e2be7abe7fa7e0fefddb5050408"}, +] packaging = [ {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, @@ -7362,6 +7504,53 @@ requests-toolbelt = [ reverse-geocoder = [ {file = "reverse_geocoder-1.5.1.tar.gz", hash = "sha256:2a2e781b5f69376d922b78fe8978f1350c84fce0ddb07e02c834ecf98b57c75c"}, ] +rtree = [ + {file = "Rtree-1.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9855b8f11cdad99c56eb361b7b632a4fbd3d8cbe3f2081426b445f0cfb7fdca9"}, + {file = "Rtree-1.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:18ce7e4d04b85c48f2d364835620b3b20e38e199639746e7b12f07a2303e18ff"}, + {file = "Rtree-1.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:784efa6b7be9e99b33613ae8495931032689441eabb6120c9b3eb91188c33794"}, + {file = "Rtree-1.0.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:157207191aebdacbbdbb369e698cfbfebce53bc97114e96c8af5bed3126475f1"}, + {file = "Rtree-1.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5fb3671a8d440c24b1dd29ec621d4345ced7185e26f02abe98e85a6629fcb50"}, + {file = "Rtree-1.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:11d16f51cf9205cd6995af36e24efe8f184270f667fb49bb69b09fc46b97e7d4"}, + {file = "Rtree-1.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6db6a0a93e41594ffc14b053f386dd414ab5a82535bbd9aedafa6ac8dc0650d8"}, + {file = "Rtree-1.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c6e29e5eb3083ad12ac5c1ce6e37465ea3428d894d3466cc9c9e2ee4bf768e53"}, + {file = "Rtree-1.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:656b148589c0b5bab4a7db4d033634329f42a5feaac10ca40aceeca109d83c1f"}, + {file = "Rtree-1.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7b2c15f9373ba314c83a8df5cb6d99b4e3af23c376c6b1317add995432dd0970"}, + {file = "Rtree-1.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93c5e0bf31e76b4f92a6eec3d2891e938408774c75a8ed6ac3d2c8db04a2be33"}, + {file = "Rtree-1.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6792de0e3c2fd3ad7e069445027603bec7a47000432f49c80246886311f4f152"}, + {file = "Rtree-1.0.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:004e131b570dc360a49e7f3b60e7bc6517943a54df056587964d1cb903889e7e"}, + {file = "Rtree-1.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:becd711fe97c2e09b1b7969e83080a3c8012bce2d30f6db879aade255fcba5c1"}, + {file = "Rtree-1.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:015df09e1bc55ddf7c88799bf1515d058cd0ee78eacf4cd443a32876d3b3a863"}, + {file = "Rtree-1.0.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c2973b76f61669a85e160b4ad09879c4089fc0e3f20fd99adf161ca298fe8374"}, + {file = "Rtree-1.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e4335e131a58952635560a003458011d97f9ea6f3c010dc24906050b42ee2c03"}, + {file = "Rtree-1.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:e7ca5d743f6a1dc62653dfac8ee7ce2e1ba91be7cf97916a7f60b7cbe48fb48d"}, + {file = "Rtree-1.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2ee7165e9872a026ccb868c021711eba39cedf7d1820763c9de52d5324691a92"}, + {file = "Rtree-1.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8de99f28af0f1783eefb80918959903b4b18112f6a12b48f296ecb162804e69d"}, + {file = "Rtree-1.0.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a94e2f4bf74bd202ea8b67ea3d7c71e763ad41f79be1d6b72aa2c8d5a8e92c4"}, + {file = "Rtree-1.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5120da3a1b96f3a7a17dd6af0afdd4e6f3cc9baa87e9ee0a272882f01f980bb"}, + {file = "Rtree-1.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7e3d5f0e7b28250afbb290ab88b49aa0f121c9714d0da2080581783690347507"}, + {file = "Rtree-1.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:296203e933b6ec0dd07f6a7456c4f1492def95b6993f20cc61c92b0fee0aecc5"}, + {file = "Rtree-1.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:77908cd7acdd519a731979ebf5baff8afd102109c2f52864c1e6ee75d3ea2d87"}, + {file = "Rtree-1.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:1a213e5d385278ca7668bc5b27083f8d6e39996a9bd59b6528f3a30009dae4ed"}, + {file = "Rtree-1.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cfa8cffec5cb9fed494c4bb335ebdb69b3c26178b0b685f67f79296c6b3d800c"}, + {file = "Rtree-1.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b31fd22d214160859d038da7cb2aaa27acb71efc24a7bcc75c84b5e502721549"}, + {file = "Rtree-1.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d68a81ad419d5c2ea5fecc677e6c178666c057e2c7b24100a6c48392196f1e9"}, + {file = "Rtree-1.0.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62f38020af47b765adc6b0bc7c4e810c6c3d1eab44ba339b592ff25a4c0dc0a7"}, + {file = "Rtree-1.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50b658a6707f215a0056d52e9f83a97148c0af62dea07cf29b3789a2c429e78a"}, + {file = "Rtree-1.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3573cbb0de872f54d0a0c29596a84e8ac3939c47ca3bece4a82e92775730a0d0"}, + {file = "Rtree-1.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5abe5a19d943a88bea14901970e4c53e4579fc2662404cdea6163bf4c04d49a"}, + {file = "Rtree-1.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1e894112cef4de6c518bdea0b43eada65f12888c3645cc437c3a677aa023039f"}, + {file = "Rtree-1.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:582854252b8fd5c8472478af060635434931fb55edd269bac128cbf2eef43620"}, + {file = "Rtree-1.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b54057e8a8ad92c1d8e9eaa5cf32aad70dde454abbf9b638e9d6024520a52c02"}, + {file = "Rtree-1.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:698de8ce6c62e159d93b35bacf64bcf3619077b5367bc88cd2cff5e0bc36169b"}, + {file = "Rtree-1.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:273ee61783de3a1664e5f868feebf5eea4629447137751bfa4087b0f82093082"}, + {file = "Rtree-1.0.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:16900ee02cf5c198a42b03635268a80f606aa102f3f7618b89f75023d406da1c"}, + {file = "Rtree-1.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ce4a6fdb63254a4c1efebe7a4f7a59b1c333c703bde4ae715d9ad88c833e10b"}, + {file = "Rtree-1.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5b20f69e040a05503b22297af223f336fe7047909b57e4b207b98292f33a229f"}, + {file = "Rtree-1.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:57128293dd625cb1f07726f32208097953e8854d70ab1fc55d6858733618b9ed"}, + {file = "Rtree-1.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e898d7409ab645c25e06d4e058f99271182601d70b2887aba3351bf08e09a0c6"}, + {file = "Rtree-1.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:ad9912faeddb1ddcec5e26b33089166d58a107af6862d8b7f1bb2b7c0002ab39"}, + {file = "Rtree-1.0.1.tar.gz", hash = "sha256:222121699c303a64065d849bf7038b1ecabc37b65c7fa340bedb38ef0e805429"}, +] s2sphere = [ {file = "s2sphere-0.2.5-py2.py3-none-any.whl", hash = "sha256:d2340c9cf458ddc9a89afd1d8048a4195ce6fa6b0095ab900d4be5271e537401"}, {file = "s2sphere-0.2.5.tar.gz", hash = "sha256:c2478c1ff7c601a59a7151a57b605435897514578fa6bdb8730721c182adbbaf"}, @@ -7539,6 +7728,50 @@ setuptools-scm = [ {file = "setuptools_scm-7.0.5-py3-none-any.whl", hash = "sha256:7930f720905e03ccd1e1d821db521bff7ec2ac9cf0ceb6552dd73d24a45d3b02"}, {file = "setuptools_scm-7.0.5.tar.gz", hash = "sha256:031e13af771d6f892b941adb6ea04545bbf91ebc5ce68c78aaf3fff6e1fb4844"}, ] +shapely = [ + {file = "Shapely-1.8.5.post1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d048f93e42ba578b82758c15d8ae037d08e69d91d9872bca5a1895b118f4e2b0"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99ab0ddc05e44acabdbe657c599fdb9b2d82e86c5493bdae216c0c4018a82dee"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:99a2f0da0109e81e0c101a2b4cd8412f73f5f299e7b5b2deaf64cd2a100ac118"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6fe855e7d45685926b6ba00aaeb5eba5862611f7465775dacd527e081a8ced6d"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec14ceca36f67cb48b34d02d7f65a9acae15cd72b48e303531893ba4a960f3ea"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a2b2a65fa7f97115c1cd989fe9d6f39281ca2a8a014f1d4904c1a6e34d7f25"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-win32.whl", hash = "sha256:21776184516a16bf82a0c3d6d6a312b3cd15a4cabafc61ee01cf2714a82e8396"}, + {file = "Shapely-1.8.5.post1-cp310-cp310-win_amd64.whl", hash = "sha256:a354199219c8d836f280b88f2c5102c81bb044ccea45bd361dc38a79f3873714"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:783bad5f48e2708a0e2f695a34ed382e4162c795cb2f0368b39528ac1d6db7ed"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a23ef3882d6aa203dd3623a3d55d698f59bfbd9f8a3bfed52c2da05a7f0f8640"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ab38f7b5196ace05725e407cb8cab9ff66edb8e6f7bb36a398e8f73f52a7aaa2"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d086591f744be483b34628b391d741e46f2645fe37594319e0a673cc2c26bcf"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4728666fff8cccc65a07448cae72c75a8773fea061c3f4f139c44adc429b18c3"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-win32.whl", hash = "sha256:84010db15eb364a52b74ea8804ef92a6a930dfc1981d17a369444b6ddec66efd"}, + {file = "Shapely-1.8.5.post1-cp311-cp311-win_amd64.whl", hash = "sha256:48dcfffb9e225c0481120f4bdf622131c8c95f342b00b158cdbe220edbbe20b6"}, + {file = "Shapely-1.8.5.post1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2fd15397638df291c427a53d641d3e6fd60458128029c8c4f487190473a69a91"}, + {file = "Shapely-1.8.5.post1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a74631e511153366c6dbe3229fa93f877e3c87ea8369cd00f1d38c76b0ed9ace"}, + {file = "Shapely-1.8.5.post1-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:66bdac74fbd1d3458fa787191a90fa0ae610f09e2a5ec398c36f968cc0ed743f"}, + {file = "Shapely-1.8.5.post1-cp36-cp36m-win32.whl", hash = "sha256:6d388c0c1bd878ed1af4583695690aa52234b02ed35f93a1c8486ff52a555838"}, + {file = "Shapely-1.8.5.post1-cp36-cp36m-win_amd64.whl", hash = "sha256:be9423d5a3577ac2e92c7e758bd8a2b205f5e51a012177a590bc46fc51eb4834"}, + {file = "Shapely-1.8.5.post1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5d7f85c2d35d39ff53c9216bc76b7641c52326f7e09aaad1789a3611a0f812f2"}, + {file = "Shapely-1.8.5.post1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:adcf8a11b98af9375e32bff91de184f33a68dc48b9cb9becad4f132fa25cfa3c"}, + {file = "Shapely-1.8.5.post1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:753ed0e21ab108bd4282405b9b659f2e985e8502b1a72b978eaa51d3496dee19"}, + {file = "Shapely-1.8.5.post1-cp37-cp37m-win32.whl", hash = "sha256:65b21243d8f6bcd421210daf1fabb9de84de2c04353c5b026173b88d17c1a581"}, + {file = "Shapely-1.8.5.post1-cp37-cp37m-win_amd64.whl", hash = "sha256:370b574c78dc5af3a198a6da5d9b3d7c04654bd2ef7e80e80a3a0992dfb2d9cd"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:532a55ee2a6c52d23d6f7d1567c8f0473635f3b270262c44e1b0c88096827e22"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3480657460e939f45a7d359ef0e172a081f249312557fe9aa78c4fd3a362d993"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b65f5d530ba91e49ffc7c589255e878d2506a8b96ffce69d3b7c4500a9a9eaf8"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:147066da0be41b147a61f8eb805dea3b13709dbc873a431ccd7306e24d712bc0"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c2822111ddc5bcfb116e6c663e403579d0fe3f147d2a97426011a191c43a7458"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b47bb6f9369e8bf3e6dbd33e6a25a47ee02b2874792a529fe04a49bf8bc0df6"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-win32.whl", hash = "sha256:2e0a8c2e55f1be1312b51c92b06462ea89e6bb703fab4b114e7a846d941cfc40"}, + {file = "Shapely-1.8.5.post1-cp38-cp38-win_amd64.whl", hash = "sha256:0d885cb0cf670c1c834df3f371de8726efdf711f18e2a75da5cfa82843a7ab65"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0b4ee3132ee90f07d63db3aea316c4c065ed7a26231458dda0874414a09d6ba3"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:02dd5d7dc6e46515d88874134dc8fcdc65826bca93c3eecee59d1910c42c1b17"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c6a9a4a31cd6e86d0fbe8473ceed83d4fe760b19d949fb557ef668defafea0f6"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:38f0fbbcb8ca20c16451c966c1f527cc43968e121c8a048af19ed3e339a921cd"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:78fb9d929b8ee15cfd424b6c10879ce1907f24e05fb83310fc47d2cd27088e40"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89164e7a9776a19e29f01369a98529321994e2e4d852b92b7e01d4d9804c55bf"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-win32.whl", hash = "sha256:8e59817b0fe63d34baedaabba8c393c0090f061917d18fc0bcc2f621937a8f73"}, + {file = "Shapely-1.8.5.post1-cp39-cp39-win_amd64.whl", hash = "sha256:e9c30b311de2513555ab02464ebb76115d242842b29c412f5a9aa0cac57be9f6"}, + {file = "Shapely-1.8.5.post1.tar.gz", hash = "sha256:ef3be705c3eac282a28058e6c6e5503419b250f482320df2172abcbea642c831"}, +] shellingham = [ {file = "shellingham-1.5.0-py2.py3-none-any.whl", hash = "sha256:a8f02ba61b69baaa13facdba62908ca8690a94b8119b69f5ec5873ea85f7391b"}, {file = "shellingham-1.5.0.tar.gz", hash = "sha256:72fb7f5c63103ca2cb91b23dee0c71fe8ad6fbfd46418ef17dbe40db51592dad"}, diff --git a/pyproject.toml b/pyproject.toml index ed3a821e29..6b4b0bb5fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -145,7 +145,7 @@ jupyterlab = "^3.4.4" jupyterlab-vim = "^0.15.1" Markdown = "^3.4.1" msgpack-python = "^0.5.6" -networkx = "~2.3" +networkx = "~2.8" nvidia-ml-py3 = "^7.352.0" onnx2torch = "^1.5.4" onnxoptimizer = "^0.3.1" @@ -172,6 +172,7 @@ triton = "^1.1.1" Werkzeug = "^2.1.2" zerorpc = { git = "https://github.com/commaai/zerorpc-python.git", branch = "master" } omegaconf = "^2.3.0" +osmnx = "==1.2.2" [build-system] From e49748d571d06a65a4361dde1f2f63c7294da13a Mon Sep 17 00:00:00 2001 From: Vivek Aithal Date: Tue, 14 Feb 2023 16:20:28 -0800 Subject: [PATCH 396/484] Chevrolet Bolt: Non-linear torque tune (#27344) * add non linear tune * update refs * rerun tests --- selfdrive/car/gm/interface.py | 44 ++++++++++++++++++++- selfdrive/car/interfaces.py | 4 +- selfdrive/controls/lib/latcontrol_torque.py | 4 +- selfdrive/test/process_replay/ref_commit | 2 +- 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index 8bfc067b48..9a165cf067 100755 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -1,12 +1,15 @@ #!/usr/bin/env python3 +import numpy as np from cereal import car from math import fabs from panda import Panda +from common.numpy_fast import interp from common.conversions import Conversions as CV from selfdrive.car import STD_CARGO_KG, create_button_event, scale_tire_stiffness, get_safety_config from selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams, EV_CAR, CAMERA_ACC_CAR -from selfdrive.car.interfaces import CarInterfaceBase +from selfdrive.car.interfaces import CarInterfaceBase, TorqueFromLateralAccelCallbackType, FRICTION_THRESHOLD +from selfdrive.controls.lib.drive_helpers import apply_center_deadzone ButtonType = car.CarState.ButtonEvent.Type EventName = car.CarEvent.EventName @@ -43,6 +46,43 @@ class CarInterface(CarInterfaceBase): else: return CarInterfaceBase.get_steer_feedforward_default + @staticmethod + def torque_from_lateral_accel_bolt(lateral_accel_value, torque_params, lateral_accel_error, lateral_accel_deadzone, vego, friction_compensation): + friction_interp = interp( + apply_center_deadzone(lateral_accel_error, lateral_accel_deadzone), + [-FRICTION_THRESHOLD, FRICTION_THRESHOLD], + [-torque_params.friction, torque_params.friction] + ) + friction = friction_interp if friction_compensation else 0.0 + steer_torque = lateral_accel_value / torque_params.latAccelFactor + + # TODO: + # 1. Learn the correction factors from data + # 2. Generalize the logic to other GM torque control platforms + steer_break_pts = np.array([-1.0, -0.9, -0.75, -0.5, 0.0, 0.5, 0.75, 0.9, 1.0]) + steer_lataccel_factors = np.array([1.5, 1.15, 1.02, 1.0, 1.0, 1.0, 1.02, 1.15, 1.5]) + steer_correction_factor = np.interp( + steer_torque, + steer_break_pts, + steer_lataccel_factors + ) + + vego_break_pts = np.array([0.0, 10.0, 15.0, 20.0, 100.0]) + vego_lataccel_factors = np.array([1.5, 1.5, 1.25, 1.0, 1.0]) + vego_correction_factor = np.interp( + vego, + vego_break_pts, + vego_lataccel_factors, + ) + + return float((steer_torque + friction) / (steer_correction_factor * vego_correction_factor)) + + def torque_from_lateral_accel(self) -> TorqueFromLateralAccelCallbackType: + if self.CP.carFingerprint == CAR.BOLT_EUV: + return self.torque_from_lateral_accel_bolt + else: + return self.torque_from_lateral_accel_linear + @staticmethod def _get_params(ret, candidate, fingerprint, car_fw, experimental_long): ret.carName = "gm" @@ -176,7 +216,7 @@ class CarInterface(CarInterfaceBase): ret.steerRatio = 16.8 ret.centerToFront = 2.15 # measured tire_stiffness_factor = 1.0 - ret.steerActuatorDelay = 0.2 + ret.steerActuatorDelay = 0.12 CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) elif candidate == CAR.SILVERADO: diff --git a/selfdrive/car/interfaces.py b/selfdrive/car/interfaces.py index 249818369c..f1e2081d05 100644 --- a/selfdrive/car/interfaces.py +++ b/selfdrive/car/interfaces.py @@ -18,7 +18,7 @@ from selfdrive.controls.lib.vehicle_model import VehicleModel ButtonType = car.CarState.ButtonEvent.Type GearShifter = car.CarState.GearShifter EventName = car.CarEvent.EventName -TorqueFromLateralAccelCallbackType = Callable[[float, car.CarParams.LateralTorqueTuning, float, float, bool], float] +TorqueFromLateralAccelCallbackType = Callable[[float, car.CarParams.LateralTorqueTuning, float, float, float, bool], float] MAX_CTRL_SPEED = (V_CRUISE_MAX + 4) * CV.KPH_TO_MS ACCEL_MAX = 2.0 @@ -131,7 +131,7 @@ class CarInterfaceBase(ABC): return self.get_steer_feedforward_default @staticmethod - def torque_from_lateral_accel_linear(lateral_accel_value, torque_params, lateral_accel_error, lateral_accel_deadzone, friction_compensation): + def torque_from_lateral_accel_linear(lateral_accel_value, torque_params, lateral_accel_error, lateral_accel_deadzone, vego, friction_compensation): # The default is a linear relationship between torque and lateral acceleration (accounting for road roll and steering friction) friction_interp = interp( apply_center_deadzone(lateral_accel_error, lateral_accel_deadzone), diff --git a/selfdrive/controls/lib/latcontrol_torque.py b/selfdrive/controls/lib/latcontrol_torque.py index 2f56094379..9129693e5a 100644 --- a/selfdrive/controls/lib/latcontrol_torque.py +++ b/selfdrive/controls/lib/latcontrol_torque.py @@ -64,10 +64,10 @@ class LatControlTorque(LatControl): error = setpoint - measurement gravity_adjusted_lateral_accel = desired_lateral_accel - params.roll * ACCELERATION_DUE_TO_GRAVITY pid_log.error = self.torque_from_lateral_accel(error, self.torque_params, error, - lateral_accel_deadzone, friction_compensation=False) + lateral_accel_deadzone, CS.vEgo, friction_compensation=False) ff = self.torque_from_lateral_accel(gravity_adjusted_lateral_accel, self.torque_params, desired_lateral_accel - actual_lateral_accel, - lateral_accel_deadzone, friction_compensation=True) + lateral_accel_deadzone, CS.vEgo, friction_compensation=True) freeze_integrator = steer_limited or CS.steeringPressed or CS.vEgo < 5 output_torque = self.pid.update(pid_log.error, diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 85c7d8e80b..ed1875dcbe 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -f4efbb65a7eb9a8f4e23492372e707674f80114e \ No newline at end of file +3e53ce81f1ce26409fdc4479e650ef5626130876 \ No newline at end of file From f29e19cf4238199eec142f9c7eab5936874e04de Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 14 Feb 2023 16:59:20 -0800 Subject: [PATCH 397/484] FPv2: log responses from data collection queries (#27345) * log responses from logging/debugging queries * don't add logging FW to FW dict for fingerprinting * flip? * fine before * log if extra in debug ecus * clean up * test extra ecus too * Revert "test extra ecus too" This reverts commit 8f7867844db95f48631348551551148bf504e37b. * bump to master --- cereal | 2 +- selfdrive/car/fw_query_definitions.py | 2 ++ selfdrive/car/fw_versions.py | 17 +++++++++++------ selfdrive/car/honda/values.py | 2 ++ 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/cereal b/cereal index fa3e77b7c8..162a26ca2d 160000 --- a/cereal +++ b/cereal @@ -1 +1 @@ -Subproject commit fa3e77b7c8eee8752f19427b34adcb1ae5c70ec5 +Subproject commit 162a26ca2d7e5bc9a42bb5ea11e98194f722027b diff --git a/selfdrive/car/fw_query_definitions.py b/selfdrive/car/fw_query_definitions.py index a1695733fa..2ee0e9b4c6 100755 --- a/selfdrive/car/fw_query_definitions.py +++ b/selfdrive/car/fw_query_definitions.py @@ -57,6 +57,8 @@ class Request: whitelist_ecus: List[int] = field(default_factory=list) rx_offset: int = 0x8 bus: int = 1 + # FW responses from these queries will not be used for fingerprinting + logging: bool = False @dataclass diff --git a/selfdrive/car/fw_versions.py b/selfdrive/car/fw_versions.py index 0db260abbd..8bf4b74e0f 100755 --- a/selfdrive/car/fw_versions.py +++ b/selfdrive/car/fw_versions.py @@ -29,10 +29,9 @@ def chunks(l, n=128): def build_fw_dict(fw_versions, filter_brand=None): fw_versions_dict = defaultdict(set) for fw in fw_versions: - if filter_brand is None or fw.brand == filter_brand: - addr = fw.address + if (filter_brand is None or fw.brand == filter_brand) and not fw.logging: sub_addr = fw.subAddress if fw.subAddress != 0 else None - fw_versions_dict[(addr, sub_addr)].add(fw.fwVersion) + fw_versions_dict[(fw.address, sub_addr)].add(fw.fwVersion) return dict(fw_versions_dict) @@ -232,15 +231,19 @@ def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1, # ECUs using a subaddress need be queried one by one, the rest can be done in parallel addrs = [] parallel_addrs = [] + logging_addrs = [] ecu_types = {} for brand, brand_versions in versions.items(): - for c in brand_versions.values(): - for ecu_type, addr, sub_addr in c.keys(): + for candidate, ecu in brand_versions.items(): + for ecu_type, addr, sub_addr in ecu.keys(): a = (brand, addr, sub_addr) if a not in ecu_types: ecu_types[a] = ecu_type + if a not in logging_addrs and candidate == "debug": + logging_addrs.append(a) + if sub_addr is None: if a not in parallel_addrs: parallel_addrs.append(a) @@ -269,13 +272,15 @@ def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1, for (tx_addr, sub_addr), version in query.get_data(timeout).items(): f = car.CarParams.CarFw.new_message() - f.ecu = ecu_types.get((brand, tx_addr, sub_addr), Ecu.unknown) + ecu_key = (brand, tx_addr, sub_addr) + f.ecu = ecu_types.get(ecu_key, Ecu.unknown) f.fwVersion = version f.address = tx_addr f.responseAddress = uds.get_rx_addr_for_tx_addr(tx_addr, r.rx_offset) f.request = r.request f.brand = brand f.bus = r.bus + f.logging = r.logging or ecu_key in logging_addrs if sub_addr is not None: f.subAddress = sub_addr diff --git a/selfdrive/car/honda/values.py b/selfdrive/car/honda/values.py index 3d1c006aaa..a4ddd84ead 100644 --- a/selfdrive/car/honda/values.py +++ b/selfdrive/car/honda/values.py @@ -171,12 +171,14 @@ FW_QUERY_CONFIG = FwQueryConfig( [HONDA_VERSION_REQUEST], [HONDA_VERSION_RESPONSE], bus=1, + logging=True ), # Query Nidec PT bus from camera for data collection Request( [StdQueries.UDS_VERSION_REQUEST], [StdQueries.UDS_VERSION_RESPONSE], bus=0, + logging=True ), ], extra_ecus=[ From c4b84783a003479bc105e2f7a93d5e71c2e1c38f Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 14 Feb 2023 17:55:41 -0800 Subject: [PATCH 398/484] Honda Bosch: gather available ECUs from camera (#27180) * add flag * actually use in fw_versions.py * simpler * not here * Comment * better name * add param and block * keys keys * block * with a value with a value * add query for bosch PT bus * different name * fix * . * fix test fix test * add cloulogs * mark as logging --- common/params.cc | 1 + selfdrive/boardd/boardd.cc | 2 ++ selfdrive/car/car_helpers.py | 1 - selfdrive/car/fw_query_definitions.py | 2 ++ selfdrive/car/fw_versions.py | 22 ++++++++++++++++++++-- selfdrive/car/honda/values.py | 14 +++++++++++--- selfdrive/controls/tests/test_startup.py | 1 + 7 files changed, 37 insertions(+), 6 deletions(-) diff --git a/common/params.cc b/common/params.cc index db5e5e700d..5e3361a70f 100644 --- a/common/params.cc +++ b/common/params.cc @@ -155,6 +155,7 @@ std::unordered_map keys = { {"NavSettingTime24h", PERSISTENT}, {"NavSettingLeftSide", PERSISTENT}, {"NavdRender", PERSISTENT}, + {"ObdMultiplexingDisabled", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON}, {"OpenpilotEnabledToggle", PERSISTENT}, {"PandaHeartbeatLost", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF}, {"PandaSignatures", CLEAR_ON_MANAGER_START}, diff --git a/selfdrive/boardd/boardd.cc b/selfdrive/boardd/boardd.cc index 0473d3488c..5d885c2c79 100644 --- a/selfdrive/boardd/boardd.cc +++ b/selfdrive/boardd/boardd.cc @@ -137,6 +137,8 @@ bool safety_setter_thread(std::vector pandas) { panda->set_safety_model(cereal::CarParams::SafetyModel::ELM327, 1U); } + p.putBool("ObdMultiplexingDisabled", true); + std::string params; LOGW("waiting for params to set safety model"); while (true) { diff --git a/selfdrive/car/car_helpers.py b/selfdrive/car/car_helpers.py index 4ccce979d3..ae19fd6248 100644 --- a/selfdrive/car/car_helpers.py +++ b/selfdrive/car/car_helpers.py @@ -116,7 +116,6 @@ def fingerprint(logcan, sendcan, num_pandas): params = Params() params.put("CarVin", vin) - params.put_bool("FirmwareObdQueryDone", True) finger = gen_empty_fingerprint() candidate_cars = {i: all_legacy_fingerprint_cars() for i in [0, 1]} # attempt fingerprint on both bus 0 and 1 diff --git a/selfdrive/car/fw_query_definitions.py b/selfdrive/car/fw_query_definitions.py index 2ee0e9b4c6..dd3b19f6de 100755 --- a/selfdrive/car/fw_query_definitions.py +++ b/selfdrive/car/fw_query_definitions.py @@ -59,6 +59,8 @@ class Request: bus: int = 1 # FW responses from these queries will not be used for fingerprinting logging: bool = False + # These requests are done once OBD multiplexing is disabled, after all others + non_obd: bool = False @dataclass diff --git a/selfdrive/car/fw_versions.py b/selfdrive/car/fw_versions.py index 8bf4b74e0f..23b0fcc6d1 100755 --- a/selfdrive/car/fw_versions.py +++ b/selfdrive/car/fw_versions.py @@ -5,6 +5,7 @@ from tqdm import tqdm import panda.python.uds as uds from cereal import car +from common.params import Params from selfdrive.car.ecu_addrs import get_ecu_addrs from selfdrive.car.interfaces import get_interface_attr from selfdrive.car.fingerprints import FW_VERSIONS @@ -89,7 +90,7 @@ def match_fw_to_car_fuzzy(fw_versions_dict, log=True, exclude=None): return set() -def match_fw_to_car_exact(fw_versions_dict): +def match_fw_to_car_exact(fw_versions_dict) -> Set[str]: """Do an exact FW match. Returns all cars that match the given FW versions for a list of "essential" ECUs. If an ECU is not considered essential the FW version can be missing to get a fingerprint, but if it's present it @@ -202,6 +203,7 @@ def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, num_pand all_car_fw = [] brand_matches = get_brand_ecu_matches(ecu_rx_addrs) + matched_brand: Optional[str] = None for brand in sorted(brand_matches, key=lambda b: len(brand_matches[b]), reverse=True): car_fw = get_fw_versions(logcan, sendcan, query_brand=brand, timeout=timeout, num_pandas=num_pandas, debug=debug, progress=progress) @@ -209,12 +211,25 @@ def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, num_pand # Try to match using FW returned from this brand only matches = match_fw_to_car_exact(build_fw_dict(car_fw)) if len(matches) == 1: + matched_brand = brand break + # Do non-OBD queries for matched brand, or all if no match is found + params = Params() + params.put_bool("FirmwareObdQueryDone", True) + + cloudlog.warning("Waiting for OBD multiplexing to be disabled") + params.get_bool("ObdMultiplexingDisabled", block=True) + cloudlog.warning("OBD multiplexing disabled") + + for brand in FW_QUERY_CONFIGS.keys(): + if brand == matched_brand or matched_brand is None: + all_car_fw.extend(get_fw_versions(logcan, sendcan, query_brand=brand, timeout=timeout, num_pandas=num_pandas, obd_multiplexed=False, debug=debug, progress=progress)) + return all_car_fw -def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1, num_pandas=1, debug=False, progress=False): +def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1, num_pandas=1, obd_multiplexed=True, debug=False, progress=False): versions = VERSIONS.copy() # Each brand can define extra ECUs to query for data collection @@ -262,6 +277,9 @@ def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1, # Skip query if no panda available if r.bus > num_pandas * 4 - 1: continue + # Or if request is not designated for current multiplexing mode + elif r.non_obd == obd_multiplexed: + continue try: addrs = [(a, s) for (b, a, s) in addr_chunk if b in (brand, 'any') and diff --git a/selfdrive/car/honda/values.py b/selfdrive/car/honda/values.py index a4ddd84ead..c085c3fe80 100644 --- a/selfdrive/car/honda/values.py +++ b/selfdrive/car/honda/values.py @@ -171,14 +171,22 @@ FW_QUERY_CONFIG = FwQueryConfig( [HONDA_VERSION_REQUEST], [HONDA_VERSION_RESPONSE], bus=1, - logging=True + logging=True, ), - # Query Nidec PT bus from camera for data collection + # Nidec PT bus Request( [StdQueries.UDS_VERSION_REQUEST], [StdQueries.UDS_VERSION_RESPONSE], bus=0, - logging=True + logging=True, + ), + # Bosch PT bus + Request( + [StdQueries.UDS_VERSION_REQUEST], + [StdQueries.UDS_VERSION_RESPONSE], + bus=1, + logging=True, + non_obd=True, ), ], extra_ecus=[ diff --git a/selfdrive/controls/tests/test_startup.py b/selfdrive/controls/tests/test_startup.py index ba2d2f5c02..92fc2468bb 100755 --- a/selfdrive/controls/tests/test_startup.py +++ b/selfdrive/controls/tests/test_startup.py @@ -72,6 +72,7 @@ class TestStartup(unittest.TestCase): params.clear_all() params.put_bool("Passive", False) params.put_bool("OpenpilotEnabledToggle", True) + params.put_bool("ObdMultiplexingDisabled", True) # Build capnn version of FW array if fw_versions is not None: From 8c099dd4e5adf040f7f454714cf729725bee7f91 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 14 Feb 2023 22:06:23 -0800 Subject: [PATCH 399/484] GM: cleanup torque feedforward function (#27347) no numpy --- selfdrive/car/gm/interface.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index 9a165cf067..104cb8fbd8 100755 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -import numpy as np from cereal import car from math import fabs from panda import Panda @@ -59,23 +58,23 @@ class CarInterface(CarInterfaceBase): # TODO: # 1. Learn the correction factors from data # 2. Generalize the logic to other GM torque control platforms - steer_break_pts = np.array([-1.0, -0.9, -0.75, -0.5, 0.0, 0.5, 0.75, 0.9, 1.0]) - steer_lataccel_factors = np.array([1.5, 1.15, 1.02, 1.0, 1.0, 1.0, 1.02, 1.15, 1.5]) - steer_correction_factor = np.interp( + steer_break_pts = [-1.0, -0.9, -0.75, -0.5, 0.0, 0.5, 0.75, 0.9, 1.0] + steer_lataccel_factors = [1.5, 1.15, 1.02, 1.0, 1.0, 1.0, 1.02, 1.15, 1.5] + steer_correction_factor = interp( steer_torque, steer_break_pts, steer_lataccel_factors ) - vego_break_pts = np.array([0.0, 10.0, 15.0, 20.0, 100.0]) - vego_lataccel_factors = np.array([1.5, 1.5, 1.25, 1.0, 1.0]) - vego_correction_factor = np.interp( + vego_break_pts = [0.0, 10.0, 15.0, 20.0, 100.0] + vego_lataccel_factors = [1.5, 1.5, 1.25, 1.0, 1.0] + vego_correction_factor = interp( vego, vego_break_pts, vego_lataccel_factors, ) - return float((steer_torque + friction) / (steer_correction_factor * vego_correction_factor)) + return (steer_torque + friction) / (steer_correction_factor * vego_correction_factor) def torque_from_lateral_accel(self) -> TorqueFromLateralAccelCallbackType: if self.CP.carFingerprint == CAR.BOLT_EUV: From d4c5b8d6f4ba7606f6823ce523c66a7e270b924b Mon Sep 17 00:00:00 2001 From: Rob Chouinard Date: Wed, 15 Feb 2023 01:24:13 -0500 Subject: [PATCH 400/484] Allow Honda Nidec vehicles with comma pedal to drive at max speed. (#26902) Without this Honda Nidec vehicles with the comma pedal will drive around 2 mph under the max speed. --- selfdrive/car/honda/interface.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/selfdrive/car/honda/interface.py b/selfdrive/car/honda/interface.py index 66c0ce4275..d3cf9fa891 100755 --- a/selfdrive/car/honda/interface.py +++ b/selfdrive/car/honda/interface.py @@ -21,6 +21,8 @@ class CarInterface(CarInterfaceBase): def get_pid_accel_limits(CP, current_speed, cruise_speed): if CP.carFingerprint in HONDA_BOSCH: return CarControllerParams.BOSCH_ACCEL_MIN, CarControllerParams.BOSCH_ACCEL_MAX + elif CP.enableGasInterceptor: + return CarControllerParams.NIDEC_ACCEL_MIN, CarControllerParams.NIDEC_ACCEL_MAX else: # NIDECs don't allow acceleration near cruise_speed, # so limit limits of pid to prevent windup From e4d0ee8716c382e90fd725998216a2f7deafc592 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 16 Feb 2023 02:59:57 +0800 Subject: [PATCH 401/484] cabana: cleanup code for charts (#27350) --- tools/cabana/chartswidget.cc | 123 ++++++++++++++--------------------- tools/cabana/chartswidget.h | 14 ++-- 2 files changed, 54 insertions(+), 83 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index ec54d7f06a..3f12c55ffc 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -11,9 +11,11 @@ #include #include #include +#include #include #include +const int MAX_COLUMN_COUNT = 4; // ChartsWidget ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { @@ -23,14 +25,17 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { QToolBar *toolbar = new QToolBar(tr("Charts"), this); toolbar->setIconSize({16, 16}); - QAction *new_plot_btn = toolbar->addAction(utils::icon("file-plus"), ""); - new_plot_btn->setToolTip(tr("New Plot")); + QAction *new_plot_btn = toolbar->addAction(utils::icon("file-plus"), tr("New Plot")); toolbar->addWidget(title_label = new QLabel()); title_label->setContentsMargins(0, 0, 12, 0); - columns_cb = new QComboBox(this); - columns_cb->addItems({"1", "2", "3", "4"}); - columns_lb_action = toolbar->addWidget(new QLabel(tr("Columns:"))); - columns_cb_action = toolbar->addWidget(columns_cb); + + QMenu *menu = new QMenu(this); + for (int i = 0; i < MAX_COLUMN_COUNT; ++i) { + menu->addAction(tr("%1").arg(i + 1), [=]() { setColumnCount(i + 1); }); + } + columns_action = toolbar->addAction(""); + columns_action->setMenu(menu); + qobject_cast(toolbar->widgetForAction(columns_action))->setPopupMode(QToolButton::InstantPopup); QLabel *stretch_label = new QLabel(this); stretch_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); @@ -44,13 +49,10 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { range_slider->setPageStep(60); // 1 min range_slider_action = toolbar->addWidget(range_slider); - reset_zoom_action = toolbar->addWidget(reset_zoom_btn = new QToolButton()); - reset_zoom_btn->setIcon(utils::icon("zoom-out")); - reset_zoom_btn->setToolTip(tr("Reset zoom")); - reset_zoom_btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + reset_zoom_action = toolbar->addAction(utils::icon("zoom-out"), tr("Reset Zoom")); + qobject_cast(toolbar->widgetForAction(reset_zoom_action))->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - remove_all_btn = toolbar->addAction(utils::icon("x"), ""); - remove_all_btn->setToolTip(tr("Remove all charts")); + remove_all_btn = toolbar->addAction(utils::icon("x"), tr("Remove all charts")); dock_btn = toolbar->addAction(""); main_layout->addWidget(toolbar); @@ -73,10 +75,9 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { // init settings use_dark_theme = QApplication::style()->standardPalette().color(QPalette::WindowText).value() > QApplication::style()->standardPalette().color(QPalette::Background).value(); - column_count = std::clamp(settings.chart_column_count, 1, columns_cb->count()); + column_count = std::clamp(settings.chart_column_count, 1, MAX_COLUMN_COUNT); max_chart_range = std::clamp(settings.chart_range, 1, settings.max_cached_minutes * 60); display_range = {0, max_chart_range}; - columns_cb->setCurrentIndex(column_count - 1); range_slider->setValue(max_chart_range); updateToolBar(); @@ -86,8 +87,7 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { QObject::connect(range_slider, &QSlider::valueChanged, this, &ChartsWidget::setMaxChartRange); QObject::connect(new_plot_btn, &QAction::triggered, this, &ChartsWidget::newChart); QObject::connect(remove_all_btn, &QAction::triggered, this, &ChartsWidget::removeAll); - QObject::connect(reset_zoom_btn, &QToolButton::clicked, this, &ChartsWidget::zoomReset); - QObject::connect(columns_cb, SIGNAL(activated(int)), SLOT(setColumnCount(int))); + QObject::connect(reset_zoom_action, &QAction::triggered, this, &ChartsWidget::zoomReset); QObject::connect(&settings, &Settings::changed, this, &ChartsWidget::settingChanged); QObject::connect(dock_btn, &QAction::triggered, [this]() { emit dock(!docking); @@ -162,11 +162,12 @@ void ChartsWidget::setMaxChartRange(int value) { void ChartsWidget::updateToolBar() { title_label->setText(tr("Charts: %1").arg(charts.size())); + columns_action->setText(tr("Column: %1").arg(column_count)); range_lb->setText(QString("Range: %1:%2 ").arg(max_chart_range / 60, 2, 10, QLatin1Char('0')).arg(max_chart_range % 60, 2, 10, QLatin1Char('0'))); range_lb_action->setVisible(!is_zoomed); range_slider_action->setVisible(!is_zoomed); reset_zoom_action->setVisible(is_zoomed); - reset_zoom_btn->setText(is_zoomed ? tr("Zoomin: %1-%2").arg(zoomed_range.first, 0, 'f', 1).arg(zoomed_range.second, 0, 'f', 1) : ""); + reset_zoom_action->setText(is_zoomed ? tr("Zoomin: %1-%2").arg(zoomed_range.first, 0, 'f', 1).arg(zoomed_range.second, 0, 'f', 1) : ""); remove_all_btn->setEnabled(!charts.isEmpty()); dock_btn->setIcon(utils::icon(docking ? "arrow-up-right-square" : "arrow-down-left-square")); dock_btn->setToolTip(docking ? tr("Undock charts") : tr("Dock charts")); @@ -211,29 +212,29 @@ void ChartsWidget::showChart(const QString &id, const Signal *sig, bool show, bo chart->addSeries(id, sig); updateState(); } else if (!show && chart) { - chart->removeSeries(id, sig); + chart->removeIf([&](auto &s) { return s.msg_id == id && s.sig == sig; }); } updateToolBar(); setUpdatesEnabled(true); } void ChartsWidget::setColumnCount(int n) { - n = std::clamp(n + 1, 1, columns_cb->count()); + n = std::clamp(n, 1, MAX_COLUMN_COUNT); if (column_count != n) { column_count = settings.chart_column_count = n; + updateToolBar(); updateLayout(); } } void ChartsWidget::updateLayout() { - int n = columns_cb->count(); + int n = MAX_COLUMN_COUNT; for (; n > 1; --n) { if ((n * CHART_MIN_WIDTH + (n - 1) * charts_layout->spacing()) < charts_layout->geometry().width()) break; } bool show_column_cb = n > 1; - columns_lb_action->setVisible(show_column_cb); - columns_cb_action->setVisible(show_column_cb); + columns_action->setVisible(show_column_cb); n = std::min(column_count, n); if (charts.size() != charts_layout->count() || n != current_column_count) { @@ -305,7 +306,6 @@ bool ChartsWidget::eventFilter(QObject *obj, QEvent *event) { ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { series_type = settings.chart_series_type == 0 ? QAbstractSeries::SeriesTypeLine : QAbstractSeries::SeriesTypeScatter; - QChart *chart = new QChart(); chart->setBackgroundVisible(false); axis_x = new QValueAxis(this); @@ -367,9 +367,6 @@ void ChartView::addSeries(const QString &msg_id, const Signal *sig) { if (hasSeries(msg_id, sig)) return; QXYSeries *series = createSeries(series_type, getColor(sig)); - chart()->addSeries(series); - series->attachAxis(axis_x); - series->attachAxis(axis_y); auto [source, address] = DBCManager::parseId(msg_id); sigs.push_back({.msg_id = msg_id, .address = address, .source = source, .sig = sig, .series = series}); updateTitle(); @@ -378,30 +375,29 @@ void ChartView::addSeries(const QString &msg_id, const Signal *sig) { emit seriesAdded(msg_id, sig); } -void ChartView::removeSeries(const QString &msg_id, const Signal *sig) { - auto it = std::find_if(sigs.begin(), sigs.end(), [&](auto &s) { return s.msg_id == msg_id && s.sig == sig; }); - if (it != sigs.end()) { - it = removeItem(it); - } -} - bool ChartView::hasSeries(const QString &msg_id, const Signal *sig) const { return std::any_of(sigs.begin(), sigs.end(), [&](auto &s) { return s.msg_id == msg_id && s.sig == sig; }); } -QList::iterator ChartView::removeItem(const QList::iterator &it) { - chart()->removeSeries(it->series); - it->series->deleteLater(); - QString msg_id = it->msg_id; - const Signal *sig = it->sig; - auto ret = sigs.erase(it); - emit seriesRemoved(msg_id, sig); - if (!sigs.isEmpty()) { - updateAxisY(); - } else { +void ChartView::removeIf(std::function predicate) { + int prev_size = sigs.size(); + for (auto it = sigs.begin(); it != sigs.end(); /**/) { + if (predicate(*it)) { + chart()->removeSeries(it->series); + it->series->deleteLater(); + auto msg_id = it->msg_id; + auto sig = it->sig; + it = sigs.erase(it); + emit seriesRemoved(msg_id, sig); + } else { + ++it; + } + } + if (sigs.empty()) { emit remove(); + } else if (sigs.size() != prev_size) { + updateAxisY(); } - return ret; } void ChartView::signalUpdated(const Signal *sig) { @@ -412,23 +408,11 @@ void ChartView::signalUpdated(const Signal *sig) { } } -void ChartView::signalRemoved(const Signal *sig) { - for (auto it = sigs.begin(); it != sigs.end(); /**/) { - it = (it->sig == sig) ? removeItem(it) : ++it; - } -} - void ChartView::msgUpdated(uint32_t address) { if (std::any_of(sigs.begin(), sigs.end(), [=](auto &s) { return s.address == address; })) updateTitle(); } -void ChartView::msgRemoved(uint32_t address) { - for (auto it = sigs.begin(); it != sigs.end(); /**/) { - it = (it->address == address) ? removeItem(it) : ++it; - } -} - void ChartView::manageSeries() { SeriesSelector dlg(tr("Mange Chart"), this); for (auto &s : sigs) { @@ -436,19 +420,12 @@ void ChartView::manageSeries() { } if (dlg.exec() == QDialog::Accepted) { auto items = dlg.seletedItems(); - if (items.isEmpty()) { - emit remove(); - } else { - for (auto s : items) { - addSeries(s->msg_id, s->sig); - } - for (auto it = sigs.begin(); it != sigs.end(); /**/) { - bool exists = std::any_of(items.cbegin(), items.cend(), [&](auto &s) { - return s->msg_id == it->msg_id && s->sig == it->sig; - }); - it = exists ? ++it : removeItem(it); - } + for (auto s : items) { + addSeries(s->msg_id, s->sig); } + removeIf([&](auto &s) { + return std::none_of(items.cbegin(), items.cend(), [&](auto &it) { return s.msg_id == it->msg_id && s.sig == it->sig; }); + }); } } @@ -503,7 +480,7 @@ void ChartView::updateSeriesPoints() { int pixels_per_point = width() / num_points; if (series_type == QAbstractSeries::SeriesTypeScatter) { - ((QScatterSeries *)s.series)->setMarkerSize(std::clamp(pixels_per_point / 3, 1, 8)); + ((QScatterSeries *)s.series)->setMarkerSize(std::clamp(pixels_per_point / 3, 2, 8)); } else { s.series->setPointsVisible(pixels_per_point > 20); } @@ -698,7 +675,7 @@ void ChartView::mouseMoveEvent(QMouseEvent *ev) { text_list.push_front(QString::number(chart()->mapToValue(pt).x(), 'f', 3)); QPointF tooltip_pt(pt.x() + 12, plot_area.top() - 20); QToolTip::showText(mapToGlobal(tooltip_pt.toPoint()), pt.isNull() ? "" : text_list.join("
"), this, plot_area.toRect()); - scene()->update(); + scene()->invalidate({}, QGraphicsScene::ForegroundLayer); } else { QToolTip::hideText(); } @@ -795,6 +772,9 @@ QXYSeries *ChartView::createSeries(QAbstractSeries::SeriesType type, QColor colo pen.setWidth(2.0 * qApp->devicePixelRatio()); series->setPen(pen); #endif + chart()->addSeries(series); + series->attachAxis(axis_x); + series->attachAxis(axis_y); return series; } @@ -809,9 +789,6 @@ void ChartView::setSeriesType(QAbstractSeries::SeriesType type) { } for (auto &s : sigs) { auto series = createSeries(series_type, getColor(s.sig)); - chart()->addSeries(series); - series->attachAxis(axis_x); - series->attachAxis(axis_y); series->replace(s.vals); s.series = series; } diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index 25949dd654..58ae7e3b4c 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include @@ -8,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -28,7 +26,6 @@ class ChartView : public QChartView { public: ChartView(QWidget *parent = nullptr); void addSeries(const QString &msg_id, const Signal *sig); - void removeSeries(const QString &msg_id, const Signal *sig); bool hasSeries(const QString &msg_id, const Signal *sig) const; void updateSeries(const Signal *sig = nullptr, const std::vector *events = nullptr, bool clear = true); void updatePlot(double cur, double min, double max); @@ -54,15 +51,14 @@ signals: void axisYLabelWidthChanged(int w); private slots: - void msgRemoved(uint32_t address); void msgUpdated(uint32_t address); void signalUpdated(const Signal *sig); - void signalRemoved(const Signal *sig); void manageSeries(); void handleMarkerClicked(); + void msgRemoved(uint32_t address) { removeIf([=](auto &s) { return s.address == address; }); } + void signalRemoved(const Signal *sig) { removeIf([=](auto &s) { return s.sig == sig; }); } private: - QList::iterator removeItem(const QList::iterator &it); void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *ev) override; @@ -78,6 +74,7 @@ private: qreal niceNumber(qreal x, bool ceiling); QXYSeries *createSeries(QAbstractSeries::SeriesType type, QColor color); void updateSeriesPoints(); + void removeIf(std::function predicate); int y_label_width = 0; int align_to = 0; @@ -139,7 +136,6 @@ private: bool docking = true; QAction *dock_btn; QAction *reset_zoom_action; - QToolButton *reset_zoom_btn; QAction *remove_all_btn; QGridLayout *charts_layout; QList charts; @@ -148,9 +144,7 @@ private: std::pair display_range; std::pair zoomed_range; bool use_dark_theme = false; - QAction *columns_lb_action; - QAction *columns_cb_action; - QComboBox *columns_cb; + QAction *columns_action; int column_count = 1; int current_column_count = 0; }; From 00513f10011ad6ad693fbbc9b4b5dc7df36f9a79 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 15 Feb 2023 11:28:47 -0800 Subject: [PATCH 402/484] locationd: adjust GPS alive check for QCOM GPS (#27339) * locationd: adjust GPS alive check for QCOM GPS * update refs * update refs --- selfdrive/locationd/locationd.cc | 2 +- selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/locationd/locationd.cc b/selfdrive/locationd/locationd.cc index 8941b50248..307626506a 100755 --- a/selfdrive/locationd/locationd.cc +++ b/selfdrive/locationd/locationd.cc @@ -619,7 +619,7 @@ kj::ArrayPtr Localizer::get_message_bytes(MessageBuilder& msg_build } bool Localizer::is_gps_ok() { - return (this->kf->get_filter_time() - this->last_gps_msg) < 1.0; + return (this->kf->get_filter_time() - this->last_gps_msg) < 2.0; } bool Localizer::critical_services_valid(std::map critical_services) { diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index ed1875dcbe..85eada072e 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -3e53ce81f1ce26409fdc4479e650ef5626130876 \ No newline at end of file +c7bb411b37ab7ff573402b6e4fa24f796cbb2ee8 \ No newline at end of file From 5078c918056c189a3ec84bc63231fbae75c57eda Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Wed, 15 Feb 2023 21:39:03 +0100 Subject: [PATCH 403/484] cabana: refactor message id from QString to struct (#27352) * cabana: refactor message id from QString to struct * fix tabbar * fix findsimilarbits * optimize hash function * cleanup sorting * use in updateLastMsgsTo --- tools/cabana/binaryview.cc | 12 +++--- tools/cabana/binaryview.h | 8 ++-- tools/cabana/chartswidget.cc | 27 +++++++------- tools/cabana/chartswidget.h | 28 +++++++------- tools/cabana/commands.cc | 18 ++++----- tools/cabana/commands.h | 20 +++++----- tools/cabana/dbcmanager.cc | 34 ++++++++--------- tools/cabana/dbcmanager.h | 44 ++++++++++++++++++---- tools/cabana/detailwidget.cc | 51 +++++++++++++++----------- tools/cabana/detailwidget.h | 9 +++-- tools/cabana/historylog.cc | 17 ++++----- tools/cabana/historylog.h | 8 ++-- tools/cabana/messageswidget.cc | 22 +++++------ tools/cabana/messageswidget.h | 14 ++++--- tools/cabana/signaledit.cc | 10 ++--- tools/cabana/signaledit.h | 12 +++--- tools/cabana/streams/abstractstream.cc | 19 +++++----- tools/cabana/streams/abstractstream.h | 17 +++++---- tools/cabana/tools/findsimilarbits.cc | 8 ++-- tools/cabana/tools/findsimilarbits.h | 4 +- 20 files changed, 209 insertions(+), 173 deletions(-) diff --git a/tools/cabana/binaryview.cc b/tools/cabana/binaryview.cc index 16df867d56..9085bf310a 100644 --- a/tools/cabana/binaryview.cc +++ b/tools/cabana/binaryview.cc @@ -94,7 +94,7 @@ void BinaryView::addShortcuts() { QObject::connect(shortcut_plot_c, &QShortcut::activated, shortcut_plot, &QShortcut::activated); QObject::connect(shortcut_plot, &QShortcut::activated, [=]{ if (hovered_sig != nullptr) { - emit showChart(model->msg_id, hovered_sig, true, false); + emit showChart(*model->msg_id, hovered_sig, true, false); } }); } @@ -189,14 +189,14 @@ void BinaryView::leaveEvent(QEvent *event) { QTableView::leaveEvent(event); } -void BinaryView::setMessage(const QString &message_id) { +void BinaryView::setMessage(const MessageId &message_id) { model->msg_id = message_id; verticalScrollBar()->setValue(0); refresh(); } void BinaryView::refresh() { - if (model->msg_id.isEmpty()) return; + if (!model->msg_id) return; clearSelection(); anchor_index = QModelIndex(); @@ -231,7 +231,7 @@ std::tuple BinaryView::getSelection(QModelIndex index) { void BinaryViewModel::refresh() { beginResetModel(); items.clear(); - if ((dbc_msg = dbc()->msg(msg_id))) { + if ((dbc_msg = dbc()->msg(*msg_id))) { row_count = dbc_msg->size; items.resize(row_count * column_count); for (auto sig : dbc_msg->getSignals()) { @@ -250,7 +250,7 @@ void BinaryViewModel::refresh() { } } } else { - row_count = can->lastMessage(msg_id).dat.size(); + row_count = can->lastMessage(*msg_id).dat.size(); items.resize(row_count * column_count); } endResetModel(); @@ -259,7 +259,7 @@ void BinaryViewModel::refresh() { void BinaryViewModel::updateState() { auto prev_items = items; - const auto &last_msg = can->lastMessage(msg_id); + const auto &last_msg = can->lastMessage(*msg_id); const auto &binary = last_msg.dat; // data size may changed. diff --git a/tools/cabana/binaryview.h b/tools/cabana/binaryview.h index 7cf9a8081c..6743b6cfac 100644 --- a/tools/cabana/binaryview.h +++ b/tools/cabana/binaryview.h @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include @@ -46,7 +48,7 @@ public: }; std::vector items; - QString msg_id; + std::optional msg_id; const DBCMsg *dbc_msg = nullptr; int row_count = 0; const int column_count = 9; @@ -57,7 +59,7 @@ class BinaryView : public QTableView { public: BinaryView(QWidget *parent = nullptr); - void setMessage(const QString &message_id); + void setMessage(const MessageId &message_id); void highlight(const Signal *sig); QSet getOverlappingSignals() const; inline void updateState() { model->updateState(); } @@ -70,7 +72,7 @@ signals: void resizeSignal(const Signal *sig, int from, int size); void removeSignal(const Signal *sig); void editSignal(const Signal *origin_s, Signal &s); - void showChart(const QString &name, const Signal *sig, bool show, bool merge); + void showChart(const MessageId &id, const Signal *sig, bool show, bool merge); private: void addShortcuts(); diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 3f12c55ffc..9a085ec732 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -181,7 +181,7 @@ void ChartsWidget::settingChanged() { } } -ChartView *ChartsWidget::findChart(const QString &id, const Signal *sig) { +ChartView *ChartsWidget::findChart(const MessageId &id, const Signal *sig) { for (auto c : charts) if (c->hasSeries(id, sig)) return c; return nullptr; @@ -204,7 +204,7 @@ ChartView *ChartsWidget::createChart() { return chart; } -void ChartsWidget::showChart(const QString &id, const Signal *sig, bool show, bool merge) { +void ChartsWidget::showChart(const MessageId &id, const Signal *sig, bool show, bool merge) { setUpdatesEnabled(false); ChartView *chart = findChart(id, sig); if (show && !chart) { @@ -363,19 +363,18 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { QObject::connect(remove_btn, &QToolButton::clicked, this, &ChartView::remove); } -void ChartView::addSeries(const QString &msg_id, const Signal *sig) { +void ChartView::addSeries(const MessageId &msg_id, const Signal *sig) { if (hasSeries(msg_id, sig)) return; QXYSeries *series = createSeries(series_type, getColor(sig)); - auto [source, address] = DBCManager::parseId(msg_id); - sigs.push_back({.msg_id = msg_id, .address = address, .source = source, .sig = sig, .series = series}); + sigs.push_back({.msg_id = msg_id, .sig = sig, .series = series}); updateTitle(); updateSeries(sig); updateSeriesPoints(); emit seriesAdded(msg_id, sig); } -bool ChartView::hasSeries(const QString &msg_id, const Signal *sig) const { +bool ChartView::hasSeries(const MessageId &msg_id, const Signal *sig) const { return std::any_of(sigs.begin(), sigs.end(), [&](auto &s) { return s.msg_id == msg_id && s.sig == sig; }); } @@ -409,7 +408,7 @@ void ChartView::signalUpdated(const Signal *sig) { } void ChartView::msgUpdated(uint32_t address) { - if (std::any_of(sigs.begin(), sigs.end(), [=](auto &s) { return s.address == address; })) + if (std::any_of(sigs.begin(), sigs.end(), [=](auto &s) { return s.msg_id.address == address; })) updateTitle(); } @@ -455,7 +454,7 @@ void ChartView::updateTitle() { } for (auto &s : sigs) { auto decoration = s.series->isVisible() ? "none" : "line-through"; - s.series->setName(QString("%2 %3 %4").arg(decoration, s.sig->name.c_str(), msgName(s.msg_id), s.msg_id)); + s.series->setName(QString("%2 %3 %4").arg(decoration, s.sig->name.c_str(), msgName(s.msg_id), s.msg_id.toString())); } } @@ -517,7 +516,7 @@ void ChartView::updateSeries(const Signal *sig, const std::vector *even for (auto it = chunk.first; it != chunk.second; ++it) { if ((*it)->which == cereal::Event::Which::CAN) { for (const auto &c : (*it)->event.getCan()) { - if (s.address == c.getAddress() && s.source == c.getSrc()) { + if (s.msg_id.address == c.getAddress() && s.msg_id.source == c.getSrc()) { auto dat = c.getDat(); double value = get_raw_value((uint8_t *)dat.begin(), dat.size(), *s.sig); double ts = ((*it)->mono_time / (double)1e9) - route_start_time; // seconds @@ -847,7 +846,7 @@ SeriesSelector::SeriesSelector(QString title, QWidget *parent) : QDialog(parent) for (auto it = can->can_msgs.cbegin(); it != can->can_msgs.cend(); ++it) { if (auto m = dbc()->msg(it.key())) { - msgs_combo->addItem(QString("%1 (%2)").arg(m->name).arg(it.key()), it.key()); + msgs_combo->addItem(QString("%1 (%2)").arg(m->name).arg(it.key().toString()), QVariant::fromValue(it.key())); } } msgs_combo->model()->sort(0); @@ -872,7 +871,7 @@ void SeriesSelector::add(QListWidgetItem *item) { void SeriesSelector::remove(QListWidgetItem *item) { auto it = (ListItem *)item; - if (it->msg_id == msgs_combo->currentData().toString()) { + if (it->msg_id == msgs_combo->currentData().value()) { addItemToList(available_list, it->msg_id, it->sig); } delete item; @@ -881,7 +880,7 @@ void SeriesSelector::remove(QListWidgetItem *item) { void SeriesSelector::updateAvailableList(int index) { if (index == -1) return; available_list->clear(); - QString msg_id = msgs_combo->itemData(index).toString(); + MessageId msg_id = msgs_combo->itemData(index).value(); auto selected_items = seletedItems(); for (auto &[name, s] : dbc()->msg(msg_id)->sigs) { bool is_selected = std::any_of(selected_items.begin(), selected_items.end(), [=, sig=&s](auto it) { return it->msg_id == msg_id && it->sig == sig; }); @@ -891,9 +890,9 @@ void SeriesSelector::updateAvailableList(int index) { } } -void SeriesSelector::addItemToList(QListWidget *parent, const QString id, const Signal *sig, bool show_msg_name) { +void SeriesSelector::addItemToList(QListWidget *parent, const MessageId id, const Signal *sig, bool show_msg_name) { QString text = QString(" %1").arg(getColor(sig).name(), sig->name.c_str()); - if (show_msg_name) text += QString(" %0 %1").arg(msgName(id), id); + if (show_msg_name) text += QString(" %0 %1").arg(msgName(id), id.toString()); QLabel *label = new QLabel(text); label->setContentsMargins(5, 0, 5, 0); diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index 58ae7e3b4c..7569155d39 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -25,17 +25,15 @@ class ChartView : public QChartView { public: ChartView(QWidget *parent = nullptr); - void addSeries(const QString &msg_id, const Signal *sig); - bool hasSeries(const QString &msg_id, const Signal *sig) const; + void addSeries(const MessageId &msg_id, const Signal *sig); + bool hasSeries(const MessageId &msg_id, const Signal *sig) const; void updateSeries(const Signal *sig = nullptr, const std::vector *events = nullptr, bool clear = true); void updatePlot(double cur, double min, double max); void setSeriesType(QAbstractSeries::SeriesType type); void updatePlotArea(int left); struct SigItem { - QString msg_id; - uint8_t source = 0; - uint32_t address = 0; + MessageId msg_id; const Signal *sig = nullptr; QXYSeries *series = nullptr; QVector vals; @@ -43,8 +41,8 @@ public: }; signals: - void seriesRemoved(const QString &id, const Signal *sig); - void seriesAdded(const QString &id, const Signal *sig); + void seriesRemoved(const MessageId &id, const Signal *sig); + void seriesAdded(const MessageId &id, const Signal *sig); void zoomIn(double min, double max); void zoomReset(); void remove(); @@ -55,7 +53,7 @@ private slots: void signalUpdated(const Signal *sig); void manageSeries(); void handleMarkerClicked(); - void msgRemoved(uint32_t address) { removeIf([=](auto &s) { return s.address == address; }); } + void msgRemoved(uint32_t address) { removeIf([=](auto &s) { return s.msg_id.address == address; }); } void signalRemoved(const Signal *sig) { removeIf([=](auto &s) { return s.sig == sig; }); } private: @@ -99,8 +97,8 @@ class ChartsWidget : public QWidget { public: ChartsWidget(QWidget *parent = nullptr); - void showChart(const QString &id, const Signal *sig, bool show, bool merge); - inline bool hasSignal(const QString &id, const Signal *sig) { return findChart(id, sig) != nullptr; } + void showChart(const MessageId &id, const Signal *sig, bool show, bool merge); + inline bool hasSignal(const MessageId &id, const Signal *sig) { return findChart(id, sig) != nullptr; } public slots: void setColumnCount(int n); @@ -126,7 +124,7 @@ private: void updateLayout(); void settingChanged(); bool eventFilter(QObject *obj, QEvent *event) override; - ChartView *findChart(const QString &id, const Signal *sig); + ChartView *findChart(const MessageId &id, const Signal *sig); QLabel *title_label; QLabel *range_lb; @@ -152,18 +150,18 @@ private: class SeriesSelector : public QDialog { public: struct ListItem : public QListWidgetItem { - ListItem(const QString &msg_id, const Signal *sig, QListWidget *parent) : msg_id(msg_id), sig(sig), QListWidgetItem(parent) {} - QString msg_id; + ListItem(const MessageId &msg_id, const Signal *sig, QListWidget *parent) : msg_id(msg_id), sig(sig), QListWidgetItem(parent) {} + MessageId msg_id; const Signal *sig; }; SeriesSelector(QString title, QWidget *parent); QList seletedItems(); - inline void addSelected(const QString &id, const Signal *sig) { addItemToList(selected_list, id, sig, true); } + inline void addSelected(const MessageId &id, const Signal *sig) { addItemToList(selected_list, id, sig, true); } private: void updateAvailableList(int index); - void addItemToList(QListWidget *parent, const QString id, const Signal *sig, bool show_msg_name = false); + void addItemToList(QListWidget *parent, const MessageId id, const Signal *sig, bool show_msg_name = false); void add(QListWidgetItem *item); void remove(QListWidgetItem *item); diff --git a/tools/cabana/commands.cc b/tools/cabana/commands.cc index e4bf999062..b03f46b5d2 100644 --- a/tools/cabana/commands.cc +++ b/tools/cabana/commands.cc @@ -4,13 +4,13 @@ // EditMsgCommand -EditMsgCommand::EditMsgCommand(const QString &id, const QString &title, int size, QUndoCommand *parent) +EditMsgCommand::EditMsgCommand(const MessageId &id, const QString &title, int size, QUndoCommand *parent) : id(id), new_title(title), new_size(size), QUndoCommand(parent) { if (auto msg = dbc()->msg(id)) { old_title = msg->name; old_size = msg->size; } - setText(QObject::tr("Edit message %1:%2").arg(DBCManager::parseId(id).second).arg(title)); + setText(QObject::tr("Edit message %1:%2").arg(id.address).arg(title)); } void EditMsgCommand::undo() { @@ -26,10 +26,10 @@ void EditMsgCommand::redo() { // RemoveMsgCommand -RemoveMsgCommand::RemoveMsgCommand(const QString &id, QUndoCommand *parent) : id(id), QUndoCommand(parent) { +RemoveMsgCommand::RemoveMsgCommand(const MessageId &id, QUndoCommand *parent) : id(id), QUndoCommand(parent) { if (auto msg = dbc()->msg(id)) { message = *msg; - setText(QObject::tr("Remove message %1:%2").arg(DBCManager::parseId(id).second).arg(message.name)); + setText(QObject::tr("Remove message %1:%2").arg(id.address).arg(message.name)); } } @@ -48,9 +48,9 @@ void RemoveMsgCommand::redo() { // AddSigCommand -AddSigCommand::AddSigCommand(const QString &id, const Signal &sig, QUndoCommand *parent) +AddSigCommand::AddSigCommand(const MessageId &id, const Signal &sig, QUndoCommand *parent) : id(id), signal(sig), QUndoCommand(parent) { - setText(QObject::tr("Add signal %1 to %2").arg(sig.name.c_str()).arg(DBCManager::parseId(id).second)); + setText(QObject::tr("Add signal %1 to %2").arg(sig.name.c_str()).arg(id.address)); } void AddSigCommand::undo() { dbc()->removeSignal(id, signal.name.c_str()); } @@ -58,9 +58,9 @@ void AddSigCommand::redo() { dbc()->addSignal(id, signal); } // RemoveSigCommand -RemoveSigCommand::RemoveSigCommand(const QString &id, const Signal *sig, QUndoCommand *parent) +RemoveSigCommand::RemoveSigCommand(const MessageId &id, const Signal *sig, QUndoCommand *parent) : id(id), signal(*sig), QUndoCommand(parent) { - setText(QObject::tr("Remove signal %1 from %2").arg(signal.name.c_str()).arg(DBCManager::parseId(id).second)); + setText(QObject::tr("Remove signal %1 from %2").arg(signal.name.c_str()).arg(id.address)); } void RemoveSigCommand::undo() { dbc()->addSignal(id, signal); } @@ -68,7 +68,7 @@ void RemoveSigCommand::redo() { dbc()->removeSignal(id, signal.name.c_str()); } // EditSignalCommand -EditSignalCommand::EditSignalCommand(const QString &id, const Signal *sig, const Signal &new_sig, QUndoCommand *parent) +EditSignalCommand::EditSignalCommand(const MessageId &id, const Signal *sig, const Signal &new_sig, QUndoCommand *parent) : id(id), old_signal(*sig), new_signal(new_sig), QUndoCommand(parent) { setText(QObject::tr("Edit signal %1").arg(old_signal.name.c_str())); } diff --git a/tools/cabana/commands.h b/tools/cabana/commands.h index c07a00b760..46e9f0a030 100644 --- a/tools/cabana/commands.h +++ b/tools/cabana/commands.h @@ -7,57 +7,57 @@ class EditMsgCommand : public QUndoCommand { public: - EditMsgCommand(const QString &id, const QString &title, int size, QUndoCommand *parent = nullptr); + EditMsgCommand(const MessageId &id, const QString &title, int size, QUndoCommand *parent = nullptr); void undo() override; void redo() override; private: - const QString id; + const MessageId id; QString old_title, new_title; int old_size = 0, new_size = 0; }; class RemoveMsgCommand : public QUndoCommand { public: - RemoveMsgCommand(const QString &id, QUndoCommand *parent = nullptr); + RemoveMsgCommand(const MessageId &id, QUndoCommand *parent = nullptr); void undo() override; void redo() override; private: - const QString id; + const MessageId id; DBCMsg message; }; class AddSigCommand : public QUndoCommand { public: - AddSigCommand(const QString &id, const Signal &sig, QUndoCommand *parent = nullptr); + AddSigCommand(const MessageId &id, const Signal &sig, QUndoCommand *parent = nullptr); void undo() override; void redo() override; private: - const QString id; + const MessageId id; Signal signal = {}; }; class RemoveSigCommand : public QUndoCommand { public: - RemoveSigCommand(const QString &id, const Signal *sig, QUndoCommand *parent = nullptr); + RemoveSigCommand(const MessageId &id, const Signal *sig, QUndoCommand *parent = nullptr); void undo() override; void redo() override; private: - const QString id; + const MessageId id; Signal signal = {}; }; class EditSignalCommand : public QUndoCommand { public: - EditSignalCommand(const QString &id, const Signal *sig, const Signal &new_sig, QUndoCommand *parent = nullptr); + EditSignalCommand(const MessageId &id, const Signal *sig, const Signal &new_sig, QUndoCommand *parent = nullptr); void undo() override; void redo() override; private: - const QString id; + const MessageId id; Signal old_signal = {}; Signal new_signal = {}; }; diff --git a/tools/cabana/dbcmanager.cc b/tools/cabana/dbcmanager.cc index 3d565e7067..27f16c71e5 100644 --- a/tools/cabana/dbcmanager.cc +++ b/tools/cabana/dbcmanager.cc @@ -4,6 +4,10 @@ #include #include +uint qHash(const MessageId &item) { + return qHash(item.source) ^ qHash(item.address); +} + DBCManager::DBCManager(QObject *parent) : QObject(parent) {} DBCManager::~DBCManager() {} @@ -56,29 +60,27 @@ QString DBCManager::generateDBC() { return dbc_string; } -void DBCManager::updateMsg(const QString &id, const QString &name, uint32_t size) { - auto [_, address] = parseId(id); - auto &m = msgs[address]; +void DBCManager::updateMsg(const MessageId &id, const QString &name, uint32_t size) { + auto &m = msgs[id.address]; m.name = name; m.size = size; - emit msgUpdated(address); + emit msgUpdated(id.address); } -void DBCManager::removeMsg(const QString &id) { - uint32_t address = parseId(id).second; - msgs.erase(address); - emit msgRemoved(address); +void DBCManager::removeMsg(const MessageId &id) { + msgs.erase(id.address); + emit msgRemoved(id.address); } -void DBCManager::addSignal(const QString &id, const Signal &sig) { - if (auto m = const_cast(msg(id))) { +void DBCManager::addSignal(const MessageId &id, const Signal &sig) { + if (auto m = const_cast(msg(id.address))) { auto &s = m->sigs[sig.name.c_str()]; s = sig; - emit signalAdded(parseId(id).second, &s); + emit signalAdded(id.address, &s); } } -void DBCManager::updateSignal(const QString &id, const QString &sig_name, const Signal &sig) { +void DBCManager::updateSignal(const MessageId &id, const QString &sig_name, const Signal &sig) { if (auto m = const_cast(msg(id))) { // change key name QString new_name = QString::fromStdString(sig.name); @@ -91,7 +93,7 @@ void DBCManager::updateSignal(const QString &id, const QString &sig_name, const } } -void DBCManager::removeSignal(const QString &id, const QString &sig_name) { +void DBCManager::removeSignal(const MessageId &id, const QString &sig_name) { if (auto m = const_cast(msg(id))) { auto it = m->sigs.find(sig_name); if (it != m->sigs.end()) { @@ -101,12 +103,6 @@ void DBCManager::removeSignal(const QString &id, const QString &sig_name) { } } -std::pair DBCManager::parseId(const QString &id) { - const auto list = id.split(':'); - if (list.size() != 2) return {0, 0}; - return {list[0].toInt(), list[1].toUInt(nullptr, 16)}; -} - DBCManager *dbc() { static DBCManager dbc_manager(nullptr); return &dbc_manager; diff --git a/tools/cabana/dbcmanager.h b/tools/cabana/dbcmanager.h index 7571d1c44e..b766c837b6 100644 --- a/tools/cabana/dbcmanager.h +++ b/tools/cabana/dbcmanager.h @@ -5,6 +5,35 @@ #include #include "opendbc/can/common_dbc.h" +struct MessageId { + uint8_t source; + uint32_t address; + + QString toString() const { + return QString("%1:%2").arg(source).arg(address, 1, 16); + } + + bool operator==(const MessageId &other) const { + return source == other.source && address == other.address; + } + + bool operator!=(const MessageId &other) const { + return !(*this == other); + } + + bool operator<(const MessageId &other) const { + return std::pair{source, address} < std::pair{other.source, other.address}; + } + + bool operator>(const MessageId &other) const { + return std::pair{source, address} > std::pair{other.source, other.address}; + } +}; + +Q_DECLARE_METATYPE(MessageId); + +uint qHash(const MessageId &item); + struct DBCMsg { QString name; uint32_t size; @@ -24,17 +53,16 @@ public: void open(const QString &dbc_file_name); bool open(const QString &name, const QString &content, QString *error = nullptr); QString generateDBC(); - void addSignal(const QString &id, const Signal &sig); - void updateSignal(const QString &id, const QString &sig_name, const Signal &sig); - void removeSignal(const QString &id, const QString &sig_name); + void addSignal(const MessageId &id, const Signal &sig); + void updateSignal(const MessageId &id, const QString &sig_name, const Signal &sig); + void removeSignal(const MessageId &id, const QString &sig_name); - static std::pair parseId(const QString &id); inline static std::vector allDBCNames() { return get_dbc_names(); } inline QString name() const { return dbc ? dbc->name.c_str() : ""; } - void updateMsg(const QString &id, const QString &name, uint32_t size); - void removeMsg(const QString &id); + void updateMsg(const MessageId &id, const QString &name, uint32_t size); + void removeMsg(const MessageId &id); inline const std::map &messages() const { return msgs; } - inline const DBCMsg *msg(const QString &id) const { return msg(parseId(id).second); } + inline const DBCMsg *msg(const MessageId &id) const { return msg(id.address); } inline const DBCMsg *msg(uint32_t address) const { auto it = msgs.find(address); return it != msgs.end() ? &it->second : nullptr; @@ -65,7 +93,7 @@ int bigEndianBitIndex(int index); void updateSigSizeParamsFromRange(Signal &s, int start_bit, int size); std::pair getSignalRange(const Signal *s); DBCManager *dbc(); -inline QString msgName(const QString &id) { +inline QString msgName(const MessageId &id) { auto msg = dbc()->msg(id); return msg ? msg->name : UNTITLED; } diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index 2036912344..55ba0b9feb 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -85,11 +85,15 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart QObject::connect(UndoStack::instance(), &QUndoStack::indexChanged, this, &DetailWidget::refresh); QObject::connect(tabbar, &QTabBar::customContextMenuRequested, this, &DetailWidget::showTabBarContextMenu); QObject::connect(tabbar, &QTabBar::currentChanged, [this](int index) { - if (index != -1 && tabbar->tabText(index) != msg_id) { - setMessage(tabbar->tabText(index)); + if (index != -1) { + setMessage(tabbar_ids[index]); } }); - QObject::connect(tabbar, &QTabBar::tabCloseRequested, tabbar, &QTabBar::removeTab); + QObject::connect(tabbar, &QTabBar::tabCloseRequested, [this](int index) { + tabbar_ids.removeAt(index); + tabbar->removeTab(index); + assert(tabbar_ids.size() == tabbar->count()); + }); QObject::connect(charts, &ChartsWidget::seriesChanged, signal_view, &SignalView::updateChartState); } @@ -108,29 +112,32 @@ void DetailWidget::showTabBarContextMenu(const QPoint &pt) { } void DetailWidget::removeAll() { - msg_id = ""; + msg_id = std::nullopt; tabbar->blockSignals(true); while (tabbar->count() > 0) { tabbar->removeTab(0); } + tabbar_ids.clear(); tabbar->blockSignals(false); stacked_layout->setCurrentIndex(0); } -void DetailWidget::setMessage(const QString &message_id) { +void DetailWidget::setMessage(const MessageId &message_id) { msg_id = message_id; - int index = tabbar->count() - 1; - for (/**/; index >= 0 && tabbar->tabText(index) != msg_id; --index) { /**/ } + int index = tabbar_ids.indexOf(*msg_id); + if (index == -1) { - index = tabbar->addTab(message_id); + tabbar_ids.append(*msg_id); + index = tabbar->addTab(message_id.toString()); tabbar->setTabToolTip(index, msgName(message_id)); } + assert(tabbar->count() == tabbar_ids.size()); setUpdatesEnabled(false); - signal_view->setMessage(msg_id); - binary_view->setMessage(msg_id); - history_log->setMessage(msg_id); + signal_view->setMessage(*msg_id); + binary_view->setMessage(*msg_id); + history_log->setMessage(*msg_id); stacked_layout->setCurrentIndex(1); tabbar->setCurrentIndex(index); @@ -141,12 +148,12 @@ void DetailWidget::setMessage(const QString &message_id) { } void DetailWidget::refresh() { - if (msg_id.isEmpty()) return; + if (!msg_id) return; QStringList warnings; - const DBCMsg *msg = dbc()->msg(msg_id); + const DBCMsg *msg = dbc()->msg(*msg_id); if (msg) { - if (msg->size != can->lastMessage(msg_id).dat.size()) { + if (msg->size != can->lastMessage(*msg_id).dat.size()) { warnings.push_back(tr("Message size (%1) is incorrect.").arg(msg->size)); } for (auto s : binary_view->getOverlappingSignals()) { @@ -156,7 +163,7 @@ void DetailWidget::refresh() { warnings.push_back(tr("Drag-Select in binary view to create new signal.")); } remove_msg_act->setEnabled(msg != nullptr); - name_label->setText(msgName(msg_id)); + name_label->setText(msgName(*msg_id)); if (!warnings.isEmpty()) { warning_label->setText(warnings.join('\n')); @@ -165,9 +172,9 @@ void DetailWidget::refresh() { warning_widget->setVisible(!warnings.isEmpty()); } -void DetailWidget::updateState(const QHash *msgs) { +void DetailWidget::updateState(const QHash *msgs) { time_label->setText(QString::number(can->currentSec(), 'f', 3)); - if (msg_id.isEmpty() || (msgs && !msgs->contains(msg_id))) + if (!msg_id || (msgs && !msgs->contains(*msg_id))) return; if (tab_widget->currentIndex() == 0) @@ -177,24 +184,24 @@ void DetailWidget::updateState(const QHash *msgs) { } void DetailWidget::editMsg() { - QString id = msg_id; + MessageId id = *msg_id; auto msg = dbc()->msg(id); int size = msg ? msg->size : can->lastMessage(id).dat.size(); EditMessageDialog dlg(id, msgName(id), size, this); if (dlg.exec()) { - UndoStack::push(new EditMsgCommand(msg_id, dlg.name_edit->text(), dlg.size_spin->value())); + UndoStack::push(new EditMsgCommand(*msg_id, dlg.name_edit->text(), dlg.size_spin->value())); } } void DetailWidget::removeMsg() { - UndoStack::push(new RemoveMsgCommand(msg_id)); + UndoStack::push(new RemoveMsgCommand(*msg_id)); } // EditMessageDialog -EditMessageDialog::EditMessageDialog(const QString &msg_id, const QString &title, int size, QWidget *parent) +EditMessageDialog::EditMessageDialog(const MessageId &msg_id, const QString &title, int size, QWidget *parent) : original_name(title), QDialog(parent) { - setWindowTitle(tr("Edit message: %1").arg(msg_id)); + setWindowTitle(tr("Edit message: %1").arg(msg_id.toString())); QFormLayout *form_layout = new QFormLayout(this); form_layout->addRow("", error_label = new QLabel); diff --git a/tools/cabana/detailwidget.h b/tools/cabana/detailwidget.h index c5d5fe9a4d..949a8c9b8d 100644 --- a/tools/cabana/detailwidget.h +++ b/tools/cabana/detailwidget.h @@ -14,7 +14,7 @@ class EditMessageDialog : public QDialog { public: - EditMessageDialog(const QString &msg_id, const QString &title, int size, QWidget *parent); + EditMessageDialog(const MessageId &msg_id, const QString &title, int size, QWidget *parent); void validateName(const QString &text); QString original_name; @@ -34,7 +34,7 @@ class DetailWidget : public QWidget { public: DetailWidget(ChartsWidget *charts, QWidget *parent); - void setMessage(const QString &message_id); + void setMessage(const MessageId &message_id); void refresh(); void removeAll(); QSize minimumSizeHint() const override { return binary_view->minimumSizeHint(); } @@ -43,13 +43,14 @@ private: void showTabBarContextMenu(const QPoint &pt); void editMsg(); void removeMsg(); - void updateState(const QHash * msgs = nullptr); + void updateState(const QHash * msgs = nullptr); - QString msg_id; + std::optional msg_id; QLabel *time_label, *warning_icon, *warning_label; ElidedLabel *name_label; QWidget *warning_widget; QTabBar *tabbar; + QList tabbar_ids; QTabWidget *tab_widget; QAction *remove_msg_act; LogsWidget *history_log; diff --git a/tools/cabana/historylog.cc b/tools/cabana/historylog.cc index e4ad99758b..f7f02b06d8 100644 --- a/tools/cabana/historylog.cc +++ b/tools/cabana/historylog.cc @@ -22,14 +22,14 @@ QVariant HistoryLogModel::data(const QModelIndex &index, int role) const { return {}; } -void HistoryLogModel::setMessage(const QString &message_id) { +void HistoryLogModel::setMessage(const MessageId &message_id) { msg_id = message_id; } void HistoryLogModel::refresh() { beginResetModel(); sigs.clear(); - if (auto dbc_msg = dbc()->msg(msg_id)) { + if (auto dbc_msg = dbc()->msg(*msg_id)) { sigs = dbc_msg->getSignals(); } last_fetch_time = 0; @@ -78,8 +78,8 @@ void HistoryLogModel::setFilter(int sig_idx, const QString &value, std::function } void HistoryLogModel::updateState() { - if (!msg_id.isEmpty()) { - uint64_t current_time = (can->lastMessage(msg_id).ts + can->routeStartTime()) * 1e9 + 1; + if (msg_id) { + uint64_t current_time = (can->lastMessage(*msg_id).ts + can->routeStartTime()) * 1e9 + 1; auto new_msgs = dynamic_mode ? fetchData(current_time, last_fetch_time) : fetchData(0); if (!new_msgs.empty()) { beginInsertRows({}, 0, new_msgs.size() - 1); @@ -106,12 +106,11 @@ void HistoryLogModel::fetchMore(const QModelIndex &parent) { template std::deque HistoryLogModel::fetchData(InputIt first, InputIt last, uint64_t min_time) { std::deque msgs; - const auto [src, address] = DBCManager::parseId(msg_id); QVector values(sigs.size()); for (auto it = first; it != last && (*it)->mono_time > min_time; ++it) { if ((*it)->which == cereal::Event::Which::CAN) { for (const auto &c : (*it)->event.getCan()) { - if (address == c.getAddress() && src == c.getSrc()) { + if (msg_id->address == c.getAddress() && msg_id->source == c.getSrc()) { const auto dat = c.getDat(); for (int i = 0; i < sigs.size(); ++i) { values[i] = get_raw_value((uint8_t *)dat.begin(), dat.size(), *(sigs[i])); @@ -136,7 +135,7 @@ template std::deque HistoryLogModel::fetchData<>(std:: std::deque HistoryLogModel::fetchData(uint64_t from_time, uint64_t min_time) { auto events = can->events(); - const auto freq = can->lastMessage(msg_id).freq; + const auto freq = can->lastMessage(*msg_id).freq; const bool update_colors = !display_signals_mode || sigs.empty(); if (dynamic_mode) { @@ -241,13 +240,13 @@ LogsWidget::LogsWidget(QWidget *parent) : QWidget(parent) { QObject::connect(can, &AbstractStream::eventsMerged, model, &HistoryLogModel::segmentsMerged); } -void LogsWidget::setMessage(const QString &message_id) { +void LogsWidget::setMessage(const MessageId &message_id) { model->setMessage(message_id); refresh(); } void LogsWidget::refresh() { - if (model->msg_id.isEmpty()) return; + if (!model->msg_id) return; model->setFilter(0, "", nullptr); model->refresh(); diff --git a/tools/cabana/historylog.h b/tools/cabana/historylog.h index 2458fc1c31..00a8f73836 100644 --- a/tools/cabana/historylog.h +++ b/tools/cabana/historylog.h @@ -1,6 +1,8 @@ #pragma once #include +#include + #include #include #include @@ -22,7 +24,7 @@ class HistoryLogModel : public QAbstractTableModel { public: HistoryLogModel(QObject *parent) : QAbstractTableModel(parent) {} - void setMessage(const QString &message_id); + void setMessage(const MessageId &message_id); void updateState(); void setFilter(int sig_idx, const QString &value, std::function cmp); QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; @@ -52,7 +54,7 @@ public: std::deque fetchData(InputIt first, InputIt last, uint64_t min_time); std::deque fetchData(uint64_t from_time, uint64_t min_time = 0); - QString msg_id; + std::optional msg_id; ChangeTracker hex_colors; bool has_more_data = true; const int batch_size = 50; @@ -71,7 +73,7 @@ class LogsWidget : public QWidget { public: LogsWidget(QWidget *parent); - void setMessage(const QString &message_id); + void setMessage(const MessageId &message_id); void updateState() {if (dynamic_mode->isChecked()) model->updateState(); } void showEvent(QShowEvent *event) override { if (dynamic_mode->isChecked()) model->refresh(); } diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index 8279d08a5c..4f6bb92375 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -46,12 +46,12 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { QObject::connect(dbc(), &DBCManager::DBCFileChanged, model, &MessageListModel::sortMessages); QObject::connect(dbc(), &DBCManager::msgUpdated, model, &MessageListModel::sortMessages); QObject::connect(dbc(), &DBCManager::msgRemoved, model, &MessageListModel::sortMessages); - QObject::connect(model, &MessageListModel::modelReset, [this]() { selectMessage(current_msg_id); }); + QObject::connect(model, &MessageListModel::modelReset, [this]() { selectMessage(*current_msg_id); }); QObject::connect(table_widget->selectionModel(), &QItemSelectionModel::currentChanged, [=](const QModelIndex ¤t, const QModelIndex &previous) { if (current.isValid() && current.row() < model->msgs.size()) { - if (model->msgs[current.row()] != current_msg_id) { + if (model->msgs[current.row()] != *current_msg_id) { current_msg_id = model->msgs[current.row()]; - emit msgSelectionChanged(current_msg_id); + emit msgSelectionChanged(*current_msg_id); } } }); @@ -67,7 +67,7 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { updateSuppressedButtons(); } -void MessagesWidget::selectMessage(const QString &msg_id) { +void MessagesWidget::selectMessage(const MessageId &msg_id) { if (int row = model->msgs.indexOf(msg_id); row != -1) { table_widget->selectionModel()->setCurrentIndex(model->index(row, 0), QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect); } @@ -86,7 +86,7 @@ void MessagesWidget::updateSuppressedButtons() { void MessagesWidget::reset() { model->reset(); filter->clear(); - current_msg_id = ""; + current_msg_id = std::nullopt; updateSuppressedButtons(); } @@ -106,7 +106,7 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const { if (role == Qt::DisplayRole) { switch (index.column()) { case 0: return msgName(id); - case 1: return id; + case 1: return id.toString(); // TODO: put source and address in separate columns case 2: return can_data.freq; case 3: return can_data.count; case 4: return toHex(can_data.dat); @@ -128,9 +128,9 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const { } void MessageListModel::setFilterString(const QString &string) { - auto contains = [](const QString &id, const QString &txt) { + auto contains = [](const MessageId &id, const QString &txt) { auto cs = Qt::CaseInsensitive; - if (id.contains(txt, cs) || msgName(id).contains(txt, cs)) return true; + if (id.toString().contains(txt, cs) || msgName(id).contains(txt, cs)) return true; // Search by signal name if (const auto msg = dbc()->msg(id)) { for (auto &signal : msg->getSignals()) { @@ -160,9 +160,7 @@ void MessageListModel::sortMessages() { }); } else if (sort_column == 1) { std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) { - auto ll = DBCManager::parseId(l); - auto rr = DBCManager::parseId(r); - return sort_order == Qt::AscendingOrder ? ll < rr : ll > rr; + return sort_order == Qt::AscendingOrder ? l < r : l > r; }); } else if (sort_column == 2) { std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) { @@ -180,7 +178,7 @@ void MessageListModel::sortMessages() { endResetModel(); } -void MessageListModel::msgsReceived(const QHash *new_msgs) { +void MessageListModel::msgsReceived(const QHash *new_msgs) { int prev_row_count = msgs.size(); if (filter_str.isEmpty() && msgs.size() != can->can_msgs.size()) { msgs = can->can_msgs.keys(); diff --git a/tools/cabana/messageswidget.h b/tools/cabana/messageswidget.h index 81ee36cd6f..562069c3ae 100644 --- a/tools/cabana/messageswidget.h +++ b/tools/cabana/messageswidget.h @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include @@ -20,13 +22,13 @@ public: int rowCount(const QModelIndex &parent = QModelIndex()) const override { return msgs.size(); } void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; void setFilterString(const QString &string); - void msgsReceived(const QHash *new_msgs = nullptr); + void msgsReceived(const QHash *new_msgs = nullptr); void sortMessages(); void suppress(); void clearSuppress(); void reset(); - QStringList msgs; - QSet> suppressed_bytes; + QList msgs; + QSet> suppressed_bytes; private: QString filter_str; @@ -39,18 +41,18 @@ class MessagesWidget : public QWidget { public: MessagesWidget(QWidget *parent); - void selectMessage(const QString &message_id); + void selectMessage(const MessageId &message_id); QByteArray saveHeaderState() const { return table_widget->horizontalHeader()->saveState(); } bool restoreHeaderState(const QByteArray &state) const { return table_widget->horizontalHeader()->restoreState(state); } void updateSuppressedButtons(); void reset(); signals: - void msgSelectionChanged(const QString &message_id); + void msgSelectionChanged(const MessageId &message_id); protected: QTableView *table_widget; - QString current_msg_id; + std::optional current_msg_id; QLineEdit *filter; MessageListModel *model; QPushButton *suppress_add; diff --git a/tools/cabana/signaledit.cc b/tools/cabana/signaledit.cc index 0499b1be8a..98dd39204f 100644 --- a/tools/cabana/signaledit.cc +++ b/tools/cabana/signaledit.cc @@ -31,7 +31,7 @@ void SignalModel::insertItem(SignalModel::Item *parent_item, int pos, const Sign } } -void SignalModel::setMessage(const QString &id) { +void SignalModel::setMessage(const MessageId &id) { msg_id = id; filter_str = ""; refresh(); @@ -56,7 +56,7 @@ void SignalModel::refresh() { endResetModel(); } -void SignalModel::updateState(const QHash *msgs) { +void SignalModel::updateState(const QHash *msgs) { if (!msgs || (msgs->contains(msg_id))) { auto &dat = can->lastMessage(msg_id).dat; int row = 0; @@ -230,13 +230,13 @@ void SignalModel::removeSignal(const Signal *sig) { } void SignalModel::handleMsgChanged(uint32_t address) { - if (address == DBCManager::parseId(msg_id).second) { + if (address == msg_id.address) { refresh(); } } void SignalModel::handleSignalAdded(uint32_t address, const Signal *sig) { - if (address == DBCManager::parseId(msg_id).second) { + if (address == msg_id.address) { int i = 0; for (; i < root->children.size(); ++i) { if (sig->start_bit < root->children[i]->sig->start_bit) break; @@ -367,7 +367,7 @@ SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts), QObject::connect(dbc(), &DBCManager::signalAdded, [this](uint32_t address, const Signal *sig) { expandSignal(sig); }); } -void SignalView::setMessage(const QString &id) { +void SignalView::setMessage(const MessageId &id) { msg_id = id; filter_edit->clear(); model->setMessage(id); diff --git a/tools/cabana/signaledit.h b/tools/cabana/signaledit.h index c0b649209a..7e5015f707 100644 --- a/tools/cabana/signaledit.h +++ b/tools/cabana/signaledit.h @@ -37,7 +37,7 @@ public: QModelIndex parent(const QModelIndex &index) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; - void setMessage(const QString &id); + void setMessage(const MessageId &id); void setFilter(const QString &txt); void addSignal(int start_bit, int size, bool little_endian); bool saveSignal(const Signal *origin_s, Signal &s); @@ -54,9 +54,9 @@ private: void handleSignalRemoved(const Signal *sig); void handleMsgChanged(uint32_t address); void refresh(); - void updateState(const QHash *msgs); + void updateState(const QHash *msgs); - QString msg_id; + MessageId msg_id; QString filter_str; std::unique_ptr root; friend class SignalView; @@ -76,7 +76,7 @@ class SignalView : public QWidget { public: SignalView(ChartsWidget *charts, QWidget *parent); - void setMessage(const QString &id); + void setMessage(const MessageId &id); void signalHovered(const Signal *sig); void updateChartState(); void expandSignal(const Signal *sig); @@ -85,13 +85,13 @@ public: signals: void highlight(const Signal *sig); - void showChart(const QString &name, const Signal *sig, bool show, bool merge); + void showChart(const MessageId &id, const Signal *sig, bool show, bool merge); private: void rowsChanged(); void leaveEvent(QEvent *event); - QString msg_id; + MessageId msg_id; QTreeView *tree; QLineEdit *filter_edit; ChartsWidget *charts; diff --git a/tools/cabana/streams/abstractstream.cc b/tools/cabana/streams/abstractstream.cc index 13b154a7ea..5f12e0c42f 100644 --- a/tools/cabana/streams/abstractstream.cc +++ b/tools/cabana/streams/abstractstream.cc @@ -4,12 +4,12 @@ AbstractStream *can = nullptr; AbstractStream::AbstractStream(QObject *parent, bool is_live_streaming) : is_live_streaming(is_live_streaming), QObject(parent) { can = this; - new_msgs = std::make_unique>(); + new_msgs = std::make_unique>(); QObject::connect(this, &AbstractStream::received, this, &AbstractStream::process, Qt::QueuedConnection); QObject::connect(this, &AbstractStream::seekedTo, this, &AbstractStream::updateLastMsgsTo); } -void AbstractStream::process(QHash *messages) { +void AbstractStream::process(QHash *messages) { for (auto it = messages->begin(); it != messages->end(); ++it) { can_msgs[it.key()] = it.value(); } @@ -25,7 +25,7 @@ bool AbstractStream::updateEvent(const Event *event) { if (event->which == cereal::Event::Which::CAN) { double current_sec = event->mono_time / 1e9 - routeStartTime(); for (const auto &c : event->event.getCan()) { - QString id = QString("%1:%2").arg(c.getSrc()).arg(c.getAddress(), 1, 16); + MessageId id = {.source = c.getSrc(), .address = c.getAddress()}; CanData &data = (*new_msgs)[id]; data.ts = current_sec; data.dat = QByteArray((char *)c.getDat().begin(), c.getDat().size()); @@ -44,21 +44,21 @@ bool AbstractStream::updateEvent(const Event *event) { prev_update_ts = ts; // use pointer to avoid data copy in queued connection. emit received(new_msgs.release()); - new_msgs.reset(new QHash); + new_msgs.reset(new QHash); new_msgs->reserve(100); } } return true; } -const CanData &AbstractStream::lastMessage(const QString &id) { +const CanData &AbstractStream::lastMessage(const MessageId &id) { static CanData empty_data; auto it = can_msgs.find(id); return it != can_msgs.end() ? it.value() : empty_data; } void AbstractStream::updateLastMsgsTo(double sec) { - QHash, CanData> last_msgs; // Much faster than QHash + QHash last_msgs; last_msgs.reserve(can_msgs.size()); double route_start_time = routeStartTime(); uint64_t last_ts = (sec + route_start_time) * 1e9; @@ -66,7 +66,7 @@ void AbstractStream::updateLastMsgsTo(double sec) { for (auto it = last; it != events()->rend(); ++it) { if ((*it)->which == cereal::Event::Which::CAN) { for (const auto &c : (*it)->event.getCan()) { - auto &m = last_msgs[{c.getSrc(), c.getAddress()}]; + auto &m = last_msgs[{.source = c.getSrc(), .address = c.getAddress()}]; if (++m.count == 1) { m.ts = ((*it)->mono_time / 1e9) - route_start_time; m.dat = QByteArray((char *)c.getDat().begin(), c.getDat().size()); @@ -87,9 +87,8 @@ void AbstractStream::updateLastMsgsTo(double sec) { counters.clear(); can_msgs.clear(); for (auto it = last_msgs.cbegin(); it != last_msgs.cend(); ++it) { - QString msg_id = QString("%1:%2").arg(it.key().first).arg(it.key().second, 1, 16); - can_msgs[msg_id] = it.value(); - counters[msg_id] = it.value().count; + can_msgs[it.key()] = it.value(); + counters[it.key()] = it.value().count; } emit updated(); emit msgsReceived(&can_msgs); diff --git a/tools/cabana/streams/abstractstream.h b/tools/cabana/streams/abstractstream.h index 8c10d959cb..e582682971 100644 --- a/tools/cabana/streams/abstractstream.h +++ b/tools/cabana/streams/abstractstream.h @@ -5,6 +5,7 @@ #include #include +#include "tools/cabana/dbcmanager.h" #include "tools/cabana/settings.h" #include "tools/cabana/util.h" #include "tools/replay/replay.h" @@ -33,7 +34,7 @@ public: virtual double routeStartTime() const { return 0; } virtual double currentSec() const = 0; virtual QDateTime currentDateTime() const { return {}; } - virtual const CanData &lastMessage(const QString &id); + virtual const CanData &lastMessage(const MessageId &id); virtual VisionStreamType visionStreamType() const { return VISION_STREAM_ROAD; } virtual const Route *route() const { return nullptr; } virtual const std::vector *events() const = 0; @@ -49,22 +50,22 @@ signals: void streamStarted(); void eventsMerged(); void updated(); - void msgsReceived(const QHash *); - void received(QHash *); + void msgsReceived(const QHash *); + void received(QHash *); public: - QHash can_msgs; + QHash can_msgs; protected: - void process(QHash *); + void process(QHash *); bool updateEvent(const Event *event); void updateLastMsgsTo(double sec); bool is_live_streaming = false; std::atomic processing = false; - QHash counters; - std::unique_ptr> new_msgs; - QHash change_trackers; + QHash counters; + std::unique_ptr> new_msgs; + QHash change_trackers; }; // A global pointer referring to the unique AbstractStream object diff --git a/tools/cabana/tools/findsimilarbits.cc b/tools/cabana/tools/findsimilarbits.cc index 63d01b152d..ffb0e54b0e 100644 --- a/tools/cabana/tools/findsimilarbits.cc +++ b/tools/cabana/tools/findsimilarbits.cc @@ -20,10 +20,10 @@ FindSimilarBitsDlg::FindSimilarBitsDlg(QWidget *parent) : QDialog(parent, Qt::Wi bus_combo = new QComboBox(this); QSet bus_set; for (auto it = can->can_msgs.begin(); it != can->can_msgs.end(); ++it) { - bus_set << DBCManager::parseId(it.key()).first; + bus_set << it.key().source; } for (uint8_t bus : bus_set) { - bus_combo->addItem(QString::number(bus)); + bus_combo->addItem(QString::number(bus), bus); } bus_combo->model()->sort(0); bus_combo->setCurrentIndex(0); @@ -69,9 +69,11 @@ FindSimilarBitsDlg::FindSimilarBitsDlg(QWidget *parent) : QDialog(parent, Qt::Wi setMinimumSize({700, 500}); QObject::connect(search_btn, &QPushButton::clicked, this, &FindSimilarBitsDlg::find); + QObject::connect(table, &QTableWidget::doubleClicked, [this](const QModelIndex &index) { if (index.isValid()) { - emit openMessage(bus_combo->currentText() + ":" + table->item(index.row(), 0)->text()); + MessageId msg_id = {.source = (uint8_t)bus_combo->currentData().toUInt(), .address = table->item(index.row(), 0)->text().toUInt(0, 16)}; + emit openMessage(msg_id); } }); } diff --git a/tools/cabana/tools/findsimilarbits.h b/tools/cabana/tools/findsimilarbits.h index 30d78f0dea..53d7806a8f 100644 --- a/tools/cabana/tools/findsimilarbits.h +++ b/tools/cabana/tools/findsimilarbits.h @@ -6,6 +6,8 @@ #include #include +#include "tools/cabana/dbcmanager.h" + class FindSimilarBitsDlg : public QDialog { Q_OBJECT @@ -13,7 +15,7 @@ public: FindSimilarBitsDlg(QWidget *parent); signals: - void openMessage(const QString &msg_id); + void openMessage(const MessageId &msg_id); private: struct mismatched_struct { From 5dde8f2c8436df9a11bfb11cf86dcfbf3f1ee874 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 15 Feb 2023 15:52:10 -0800 Subject: [PATCH 404/484] Write boardd param to continue startup (#27354) disable at the bottom --- selfdrive/car/car_helpers.py | 3 ++- selfdrive/car/fw_versions.py | 18 +++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/selfdrive/car/car_helpers.py b/selfdrive/car/car_helpers.py index ae19fd6248..370772c902 100644 --- a/selfdrive/car/car_helpers.py +++ b/selfdrive/car/car_helpers.py @@ -8,7 +8,7 @@ from system.version import is_comma_remote, is_tested_branch from selfdrive.car.interfaces import get_interface_attr from selfdrive.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars from selfdrive.car.vin import get_vin, is_valid_vin, VIN_UNKNOWN -from selfdrive.car.fw_versions import get_fw_versions_ordered, match_fw_to_car, get_present_ecus +from selfdrive.car.fw_versions import disable_obd_multiplexing, get_fw_versions_ordered, match_fw_to_car, get_present_ecus from system.swaglog import cloudlog import cereal.messaging as messaging from selfdrive.car import gen_empty_fingerprint @@ -116,6 +116,7 @@ def fingerprint(logcan, sendcan, num_pandas): params = Params() params.put("CarVin", vin) + disable_obd_multiplexing(params) finger = gen_empty_fingerprint() candidate_cars = {i: all_legacy_fingerprint_cars() for i in [0, 1]} # attempt fingerprint on both bus 0 and 1 diff --git a/selfdrive/car/fw_versions.py b/selfdrive/car/fw_versions.py index 23b0fcc6d1..8092ac0b76 100755 --- a/selfdrive/car/fw_versions.py +++ b/selfdrive/car/fw_versions.py @@ -198,6 +198,15 @@ def get_brand_ecu_matches(ecu_rx_addrs): return brand_matches +def disable_obd_multiplexing(params): + if not params.get_bool("ObdMultiplexingDisabled"): + params.put_bool("FirmwareObdQueryDone", True) + + cloudlog.warning("Waiting for OBD multiplexing to be disabled") + params.get_bool("ObdMultiplexingDisabled", block=True) + cloudlog.warning("OBD multiplexing disabled") + + def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, num_pandas=1, debug=False, progress=False): """Queries for FW versions ordering brands by likelihood, breaks when exact match is found""" @@ -214,14 +223,9 @@ def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, num_pand matched_brand = brand break - # Do non-OBD queries for matched brand, or all if no match is found - params = Params() - params.put_bool("FirmwareObdQueryDone", True) - - cloudlog.warning("Waiting for OBD multiplexing to be disabled") - params.get_bool("ObdMultiplexingDisabled", block=True) - cloudlog.warning("OBD multiplexing disabled") + disable_obd_multiplexing(Params()) + # Do non-OBD queries for matched brand, or all if no match is found for brand in FW_QUERY_CONFIGS.keys(): if brand == matched_brand or matched_brand is None: all_car_fw.extend(get_fw_versions(logcan, sendcan, query_brand=brand, timeout=timeout, num_pandas=num_pandas, obd_multiplexed=False, debug=debug, progress=progress)) From 2bc2dbfb700e71588c998cf1dfaef10ac9d852ce Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 15 Feb 2023 15:55:08 -0800 Subject: [PATCH 405/484] process replay: fix hang --- selfdrive/test/process_replay/process_replay.py | 1 + 1 file changed, 1 insertion(+) diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index b531cb3430..28fc9c452c 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -407,6 +407,7 @@ def setup_env(simulation=False, CP=None, cfg=None, controlsState=None): params.put_bool("WideCameraOnly", False) params.put_bool("DisableLogging", False) params.put_bool("UbloxAvailable", True) + params.put_bool("ObdMultiplexingDisabled", True) os.environ["NO_RADAR_SLEEP"] = "1" os.environ["REPLAY"] = "1" From 48b059d3670e8082eae785c850508f88b1181188 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 15 Feb 2023 15:59:23 -0800 Subject: [PATCH 406/484] CAN fingerprint script improvements (#27355) * match online logic * 0 and 2 are different buses. on gm, same address has different lengths * match fingerprint_from_route --- selfdrive/debug/fingerprint_from_route.py | 2 +- selfdrive/debug/get_fingerprint.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/debug/fingerprint_from_route.py b/selfdrive/debug/fingerprint_from_route.py index 326e68f8e7..b3598b105c 100755 --- a/selfdrive/debug/fingerprint_from_route.py +++ b/selfdrive/debug/fingerprint_from_route.py @@ -17,7 +17,7 @@ def get_fingerprint(lr): for c in msg.can: # read also msgs sent by EON on CAN bus 0x80 and filter out the # addr with more than 11 bits - if c.src % 0x80 == 0 and c.address < 0x800: + if c.src % 0x80 == 0 and c.address < 0x800 and c.address not in (0x7df, 0x7e0, 0x7e8): msgs[c.address] = len(c.dat) # show CAN fingerprint diff --git a/selfdrive/debug/get_fingerprint.py b/selfdrive/debug/get_fingerprint.py index e678db4f17..f7f7a1604f 100755 --- a/selfdrive/debug/get_fingerprint.py +++ b/selfdrive/debug/get_fingerprint.py @@ -22,7 +22,7 @@ while True: for c in lc.can: # read also msgs sent by EON on CAN bus 0x80 and filter out the # addr with more than 11 bits - if c.src in [0, 2] and c.address < 0x800: + if c.src % 0x80 == 0 and c.address < 0x800 and c.address not in (0x7df, 0x7e0, 0x7e8): msgs[c.address] = len(c.dat) fingerprint = ', '.join("%d: %d" % v for v in sorted(msgs.items())) From 89934b35d04291384b7a287a41ce49905f321167 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 15 Feb 2023 16:27:00 -0800 Subject: [PATCH 407/484] Car interfaces: assert reasonable centerToFront range (#27356) * assert reasonable centerToFront values * comment * fix Bolt centerToFront * Update ref_commit --- selfdrive/car/gm/interface.py | 2 +- selfdrive/car/tests/test_car_interfaces.py | 3 ++- selfdrive/test/process_replay/ref_commit | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index 104cb8fbd8..71e1ad4018 100755 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -213,7 +213,7 @@ class CarInterface(CarInterfaceBase): ret.mass = 1669. + STD_CARGO_KG ret.wheelbase = 2.63779 ret.steerRatio = 16.8 - ret.centerToFront = 2.15 # measured + ret.centerToFront = ret.wheelbase * 0.4 tire_stiffness_factor = 1.0 ret.steerActuatorDelay = 0.12 CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) diff --git a/selfdrive/car/tests/test_car_interfaces.py b/selfdrive/car/tests/test_car_interfaces.py index 78ecbe425e..ac8213e4c1 100755 --- a/selfdrive/car/tests/test_car_interfaces.py +++ b/selfdrive/car/tests/test_car_interfaces.py @@ -32,7 +32,8 @@ class TestCarInterfaces(unittest.TestCase): self.assertGreater(car_params.mass, 1) self.assertGreater(car_params.wheelbase, 0) - self.assertGreater(car_params.centerToFront, 0) + # centerToFront is center of gravity to front wheels, assert a reasonable range + self.assertTrue(car_params.wheelbase * 0.3 < car_params.centerToFront < car_params.wheelbase * 0.7) self.assertGreater(car_params.maxLateralAccel, 0) # Longitudinal sanity checks diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 85eada072e..b151a6a5ae 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -c7bb411b37ab7ff573402b6e4fa24f796cbb2ee8 \ No newline at end of file +fbb99ee7dc0336062e6785814af82e359dcdd9bf From b76795d7f083c956004fefd07dbb406eeeb20584 Mon Sep 17 00:00:00 2001 From: Ricky Gilleland Date: Wed, 15 Feb 2023 20:07:24 -0500 Subject: [PATCH 408/484] GM: Escalade 2017 support (#27276) * Initial 2017 Escalade values * update docs/CARS.md * revert back to v1 fingerprint & add test route * revert bad merge * update fingerprint * update * update * Update selfdrive/car/gm/values.py * Update selfdrive/car/gm/values.py * no engine * Try global tune * pcm * Driver Assist Package has ACC, and includes LKAS from previous package * update docs * add to releases --------- Co-authored-by: Shane Smiskol --- RELEASES.md | 1 + docs/CARS.md | 3 ++- selfdrive/car/gm/interface.py | 8 ++++++++ selfdrive/car/gm/values.py | 6 ++++++ selfdrive/car/tests/routes.py | 1 + selfdrive/car/torque_data/override.yaml | 1 + 6 files changed, 19 insertions(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index ceae8d10cb..78d2e3b74b 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -7,6 +7,7 @@ Version 0.9.1 (2023-02-23) * Adjust alert volume using ambient noise level * Driver monitoring icon shows driver's head pose * German translation thanks to Vrabetz and CzokNorris! +* Cadillac Escalade 2017 support thanks to rickygilleland! * Chevrolet Bolt EV 2022-23 support thanks to JasonJShuler! * Genesis GV60 2023 support thanks to sunnyhaibin! * Hyundai Tucson 2022-23 support diff --git a/docs/CARS.md b/docs/CARS.md index e6b6bbee7d..8860299360 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,7 +4,7 @@ A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system. -# 236 Supported Cars +# 237 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness|Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| @@ -17,6 +17,7 @@ A supported vehicle is one that just works when you install a comma three. All s |Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| |Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| |Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|| +|Cadillac|Escalade 2017[3](#footnotes)|Driver Assist Package|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|| |Cadillac|Escalade ESV 2016[3](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|| |Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|| |Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|| diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index 71e1ad4018..c28274011e 100755 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -198,6 +198,14 @@ class CarInterface(CarInterfaceBase): ret.steerRatio = 15.3 ret.centerToFront = ret.wheelbase * 0.5 + elif candidate == CAR.ESCALADE: + ret.minEnableSpeed = -1. # engage speed is decided by pcm + ret.mass = 5653. * CV.LB_TO_KG + STD_CARGO_KG # (5552+5815)/2 + ret.wheelbase = 2.95 # 116 inches in meters + ret.steerRatio = 17.3 + ret.centerToFront = ret.wheelbase * 0.5 + CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) + elif candidate == CAR.ESCALADE_ESV: ret.minEnableSpeed = -1. # engage speed is decided by pcm ret.mass = 2739. + STD_CARGO_KG diff --git a/selfdrive/car/gm/values.py b/selfdrive/car/gm/values.py index 1c956192eb..207af6bb05 100644 --- a/selfdrive/car/gm/values.py +++ b/selfdrive/car/gm/values.py @@ -67,6 +67,7 @@ class CAR: MALIBU = "CHEVROLET MALIBU PREMIER 2017" ACADIA = "GMC ACADIA DENALI 2018" BUICK_REGAL = "BUICK REGAL ESSENCE 2018" + ESCALADE = "CADILLAC ESCALADE 2017" ESCALADE_ESV = "CADILLAC ESCALADE ESV 2016" BOLT_EUV = "CHEVROLET BOLT EUV 2022" SILVERADO = "CHEVROLET SILVERADO 1500 2020" @@ -99,6 +100,7 @@ CAR_INFO: Dict[str, Union[GMCarInfo, List[GMCarInfo]]] = { CAR.MALIBU: GMCarInfo("Chevrolet Malibu Premier 2017"), CAR.ACADIA: GMCarInfo("GMC Acadia 2018", video_link="https://www.youtube.com/watch?v=0ZN6DdsBUZo"), CAR.BUICK_REGAL: GMCarInfo("Buick Regal Essence 2018"), + CAR.ESCALADE: GMCarInfo("Cadillac Escalade 2017", "Driver Assist Package"), CAR.ESCALADE_ESV: GMCarInfo("Cadillac Escalade ESV 2016", "Adaptive Cruise Control (ACC) & LKAS"), CAR.BOLT_EUV: [ GMCarInfo("Chevrolet Bolt EUV 2022-23", "Premier or Premier Redline Trim without Super Cruise Package", "https://youtu.be/xvwzGMUA210"), @@ -174,6 +176,10 @@ FINGERPRINTS = { { 190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 289: 8, 298: 8, 304: 1, 309: 8, 313: 8, 320: 3, 322: 7, 328: 1, 338: 6, 340: 6, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 393: 8, 398: 8, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 462: 4, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 567: 5, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1217: 8, 1221: 5, 1225: 8, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1328: 4, 1417: 8, 1601: 8, 1906: 7, 1907: 7, 1912: 7, 1914: 7, 1919: 7, 1920: 7, 1930: 7, 2016: 8, 2024: 8 }], + CAR.ESCALADE: [ + { + 170: 8, 190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 407: 4, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 460: 5, 462: 4, 463: 3, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 532: 6, 534: 2, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 719: 5, 761: 7, 801: 8, 804: 3, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 967: 4, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1217: 8, 1221: 5, 1223: 2, 1225: 7, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1609: 8, 1613: 8, 1649: 8, 1792: 8, 1798: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1858: 8, 1860: 8, 1863: 8, 1872: 8, 1875: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1906: 7, 1907: 7, 1912: 7, 1914: 7, 1917: 7, 1918: 7, 1919: 7, 1920: 7, 1930: 7, 1937: 8, 1953: 8, 1968: 8, 2001: 8, 2017: 8, 2018: 8, 2020: 8, 2026: 8 + }], CAR.ESCALADE_ESV: [ { 309: 1, 848: 8, 849: 8, 850: 8, 851: 8, 852: 8, 853: 8, 854: 3, 1056: 6, 1057: 8, 1058: 8, 1059: 8, 1060: 8, 1061: 8, 1062: 8, 1063: 8, 1064: 8, 1065: 8, 1066: 8, 1067: 8, 1068: 8, 1120: 8, 1121: 8, 1122: 8, 1123: 8, 1124: 8, 1125: 8, 1126: 8, 1127: 8, 1128: 8, 1129: 8, 1130: 8, 1131: 8, 1132: 8, 1133: 8, 1134: 8, 1135: 8, 1136: 8, 1137: 8, 1138: 8, 1139: 8, 1140: 8, 1141: 8, 1142: 8, 1143: 8, 1146: 8, 1147: 8, 1148: 8, 1149: 8, 1150: 8, 1151: 8, 1216: 8, 1217: 8, 1218: 8, 1219: 8, 1220: 8, 1221: 8, 1222: 8, 1223: 8, 1224: 8, 1225: 8, 1226: 8, 1232: 8, 1233: 8, 1234: 8, 1235: 8, 1236: 8, 1237: 8, 1238: 8, 1239: 8, 1240: 8, 1241: 8, 1242: 8, 1787: 8, 1788: 8 diff --git a/selfdrive/car/tests/routes.py b/selfdrive/car/tests/routes.py index a4d2f55581..02e37492d3 100644 --- a/selfdrive/car/tests/routes.py +++ b/selfdrive/car/tests/routes.py @@ -50,6 +50,7 @@ routes = [ CarTestRoute("7cc2a8365b4dd8a9|2018-12-02--12-10-44", GM.ACADIA), CarTestRoute("aa20e335f61ba898|2019-02-05--16-59-04", GM.BUICK_REGAL), + CarTestRoute("ef8f2185104d862e|2023-02-09--18-37-13", GM.ESCALADE), CarTestRoute("46460f0da08e621e|2021-10-26--07-21-46", GM.ESCALADE_ESV), CarTestRoute("c950e28c26b5b168|2018-05-30--22-03-41", GM.VOLT), CarTestRoute("f08912a233c1584f|2022-08-11--18-02-41", GM.BOLT_EUV, segment=1), diff --git a/selfdrive/car/torque_data/override.yaml b/selfdrive/car/torque_data/override.yaml index 52c9e8d547..cc1681bce1 100644 --- a/selfdrive/car/torque_data/override.yaml +++ b/selfdrive/car/torque_data/override.yaml @@ -26,6 +26,7 @@ COMMA BODY: [.nan, 1000, .nan] RAM 1500 5TH GEN: [2.0, 2.0, 0.0] RAM HD 5TH GEN: [1.4, 1.4, 0.0] SUBARU OUTBACK 6TH GEN: [2.3, 2.3, 0.11] +CADILLAC ESCALADE 2017: [1.899999976158142, 1.842270016670227, 0.1120000034570694] CHEVROLET BOLT EUV 2022: [2.0, 2.0, 0.05] CHEVROLET SILVERADO 1500 2020: [1.9, 1.9, 0.112] CHEVROLET EQUINOX 2019: [2.0, 2.0, 0.05] From 90ce7605c159133eaad5ffe6259da513db28e8ff Mon Sep 17 00:00:00 2001 From: Jason Wen <47793918+sunnyhaibin@users.noreply.github.com> Date: Wed, 15 Feb 2023 20:33:04 -0500 Subject: [PATCH 409/484] Hyundai: 2015-16 Genesis resume from standstill fix (#25579) * Hyundai: 2015-16 Genesis resume from standstill fix * Add comment * Fix typo * Merge both methods * Add back the comment * 20Hz not needed according to tester * no new global state variable * PCM's perspective: random counter -> random counter * only send 5 --------- Co-authored-by: Shane Smiskol --- selfdrive/car/hyundai/carcontroller.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/selfdrive/car/hyundai/carcontroller.py b/selfdrive/car/hyundai/carcontroller.py index 516666eff1..1e6f78af20 100644 --- a/selfdrive/car/hyundai/carcontroller.py +++ b/selfdrive/car/hyundai/carcontroller.py @@ -169,7 +169,8 @@ class CarController: if (self.frame - self.last_button_frame) * DT_CTRL > 0.1: # send 25 messages at a time to increases the likelihood of resume being accepted can_sends.extend([hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.RES_ACCEL, self.CP.carFingerprint)] * 25) - self.last_button_frame = self.frame + if (self.frame - self.last_button_frame) * DT_CTRL >= 0.15: + self.last_button_frame = self.frame if self.frame % 2 == 0 and self.CP.openpilotLongitudinalControl: # TODO: unclear if this is needed From 4638d4b1a2e1dd7ad500750e6f8d97b0e66e9e97 Mon Sep 17 00:00:00 2001 From: wowcat1234 <124840678+wowcat1234@users.noreply.github.com> Date: Wed, 15 Feb 2023 21:17:46 -0600 Subject: [PATCH 410/484] Add fingerprint for Hyundai Ioniq 22 limited HEV (#27334) * Update values.py * update for ioniq HEV 22 --------- Co-authored-by: Shane Smiskol --- selfdrive/car/hyundai/values.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 9067299473..086bd1f26b 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -483,6 +483,7 @@ FW_VERSIONS = { CAR.IONIQ_HEV_2022: { (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00AEhe SCC F-CUP 1.00 1.00 99110-G2600 ', + b'\xf1\x00AEhe SCC FHCUP 1.00 1.00 99110-G2600 ', ], (Ecu.eps, 0x7d4, None): [ b'\xf1\x00AE MDPS C 1.00 1.01 56310G2510\x00 4APHC101', @@ -495,6 +496,7 @@ FW_VERSIONS = { ], (Ecu.transmission, 0x7e1, None): [ b'\xf1\x816U3J9051\x00\x00\xf1\x006U3H1_C2\x00\x006U3J9051\x00\x00HAE0G16NL2\x00\x00\x00\x00', + b'\xf1\x006U3H1_C2\x00\x006U3J9051\x00\x00HAE0G16NL2\x96\xda\xd4\xee', ], }, CAR.SONATA: { From d7ddf25899ba480b11a6b9064875392b7d2f68b5 Mon Sep 17 00:00:00 2001 From: gregschueman <124465406+gregschueman@users.noreply.github.com> Date: Wed, 15 Feb 2023 22:21:36 -0500 Subject: [PATCH 411/484] 2021 Hyundai Ioniq PHEV - additional engine fingerprint added (#27221) * Update values.py Addition of Hyundai 2021 Ioniq PHEV modeled on 2019 entry * Update values.py * Update values.py * Update values.py * Update values.py * replace with shorter trans --------- Co-authored-by: Shane Smiskol --- selfdrive/car/hyundai/values.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 086bd1f26b..4a7f257092 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -437,13 +437,14 @@ FW_VERSIONS = { (Ecu.engine, 0x7e0, None): [ b'\xf1\x816H6F6051\x00\x00\x00\x00\x00\x00\x00\x00', b'\xf1\x816H6G6051\x00\x00\x00\x00\x00\x00\x00\x00', + b'\xf1\x816H6G5051\x00\x00\x00\x00\x00\x00\x00\x00', ], (Ecu.transmission, 0x7e1, None): [ b'\xf1\x816U3J9051\000\000\xf1\0006U3H1_C2\000\0006U3J9051\000\000PAE0G16NL0\x82zT\xd2', b'\xf1\x816U3J8051\x00\x00\xf1\x006U3H1_C2\x00\x006U3J8051\x00\x00PAETG16UL0\x00\x00\x00\x00', - b'\xf1\x816U3J9051\x00\x00\xf1\x006U3H1_C2\x00\x006U3J9051\x00\x00PAE0G16NL2\xad\xeb\xabt', b'\xf1\x816U3J9051\x00\x00\xf1\x006U3H1_C2\x00\x006U3J9051\x00\x00PAE0G16NL2\x00\x00\x00\x00', b'\xf1\x006U3H1_C2\x00\x006U3J9051\x00\x00PAE0G16NL0\x00\x00\x00\x00', + b'\xf1\x006U3H1_C2\x00\x006U3J9051\x00\x00PAE0G16NL2\xad\xeb\xabt', ], }, CAR.IONIQ_EV_2020: { From 2769b110e0b0ea82f96471d964b05499441b6d73 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 15 Feb 2023 19:42:56 -0800 Subject: [PATCH 412/484] Fingerprint for Hyundai Santa Fe 2019 Europe (#26952) * Fingerpint fur Hyundai Santa Fe 2019 Europe * add version for transmission segment 092ef1cc055c6354|2023-01-05--17-49-21 * remove long --------- Co-authored-by: sevdokim --- selfdrive/car/hyundai/values.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 4a7f257092..15c239506b 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -685,12 +685,14 @@ FW_VERSIONS = { }, CAR.SANTA_FE: { (Ecu.fwdRadar, 0x7d0, None): [ + b'\xf1\x00TM__ SCC F-CUP 1.00 1.00 99110-S1210 ', b'\xf1\x00TM__ SCC F-CUP 1.00 1.01 99110-S2000 ', b'\xf1\x00TM__ SCC F-CUP 1.00 1.02 99110-S2000 ', b'\xf1\x00TM__ SCC F-CUP 1.00 1.03 99110-S2000 ', ], (Ecu.abs, 0x7d1, None): [ b'\xf1\x00TM ESC \r 100\x18\x031 58910-S2650', + b'\xf1\x00TM ESC \r 105\x19\x05# 58910-S1500', b'\xf1\x00TM ESC \r 103\x18\x11\x08 58910-S2650', b'\xf1\x00TM ESC \r 104\x19\x07\x08 58910-S2650', b'\xf1\x00TM ESC \x02 100\x18\x030 58910-S2600', @@ -709,11 +711,14 @@ FW_VERSIONS = { b'\xf1\x00TM MDPS C 1.00 1.00 56340-S2000 8409', b'\xf1\x00TM MDPS C 1.00 1.00 56340-S2000 8A12', b'\xf1\x00TM MDPS C 1.00 1.01 56340-S2000 9129', + b'\xf1\x00TM MDPS R 1.00 1.02 57700-S1100 4TMDP102' ], (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00TM MFC AT EUR LHD 1.00 1.01 99211-S1010 181207', b'\xf1\x00TM MFC AT USA LHD 1.00 1.00 99211-S2000 180409', ], (Ecu.transmission, 0x7e1, None): [ + b'\xf1\x00bcsh8p54 U833\x00\x00\x00\x00\x00\x00TTM4V22US3_<]\xf1', b'\xf1\x006W351_C2\x00\x006W3E1051\x00\x00TTM4T20NS5\x00\x00\x00\x00', b'\xf1\x87LBJSGA7082574HG0\x87www\x98\x88\x88\x88\x99\xaa\xb9\x9afw\x86gx\x99\xa7\x89co\xf8\xffvU_\xffR\xaf\xf1\x816W3C2051\x00\x00\xf1\x006W351_C2\x00\x006W3C2051\x00\x00TTM2T20NS1\x00\xa6\xe0\x91', b'\xf1\x87LBKSGA0458404HG0vfvg\x87www\x89\x99\xa8\x99y\xaa\xa7\x9ax\x88\xa7\x88t_\xf9\xff\x86w\x8f\xff\x15x\xf1\x816W3C2051\x00\x00\xf1\x006W351_C2\x00\x006W3C2051\x00\x00TTM2T20NS1\x00\x00\x00\x00', From e6c02027d3d7c6cd850fb13b948bd010b15b5308 Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Thu, 16 Feb 2023 19:34:18 +0100 Subject: [PATCH 413/484] cabana: fix updating tabbar_ids on "Close Other Tabs" (#27362) --- tools/cabana/detailwidget.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index 55ba0b9feb..bf4c1e44f8 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -103,10 +103,14 @@ void DetailWidget::showTabBarContextMenu(const QPoint &pt) { QMenu menu(this); menu.addAction(tr("Close Other Tabs")); if (menu.exec(tabbar->mapToGlobal(pt))) { + tabbar_ids.move(index, 0); tabbar->moveTab(index, 0); tabbar->setCurrentIndex(0); - while (tabbar->count() > 1) + while (tabbar->count() > 1) { + tabbar_ids.removeAt(1); tabbar->removeTab(1); + } + assert(tabbar_ids.size() == tabbar->count()); } } } From 8772306467630df66716e47ec34fa36935152bf6 Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Thu, 16 Feb 2023 19:34:42 +0100 Subject: [PATCH 414/484] cabana: fix events() call in AbstractStream::updateLastMsgsTo (#27360) --- tools/cabana/streams/abstractstream.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/cabana/streams/abstractstream.cc b/tools/cabana/streams/abstractstream.cc index 5f12e0c42f..8fdfbd5c1b 100644 --- a/tools/cabana/streams/abstractstream.cc +++ b/tools/cabana/streams/abstractstream.cc @@ -62,8 +62,9 @@ void AbstractStream::updateLastMsgsTo(double sec) { last_msgs.reserve(can_msgs.size()); double route_start_time = routeStartTime(); uint64_t last_ts = (sec + route_start_time) * 1e9; - auto last = std::upper_bound(events()->rbegin(), events()->rend(), last_ts, [](uint64_t ts, auto &e) { return e->mono_time < ts; }); - for (auto it = last; it != events()->rend(); ++it) { + auto evs = events(); + auto last = std::upper_bound(evs->rbegin(), evs->rend(), last_ts, [](uint64_t ts, auto &e) { return e->mono_time < ts; }); + for (auto it = last; it != evs->rend(); ++it) { if ((*it)->which == cereal::Event::Which::CAN) { for (const auto &c : (*it)->event.getCan()) { auto &m = last_msgs[{.source = c.getSrc(), .address = c.getAddress()}]; From 2510f78126b0a457b5e8b42f1f32e5bf24a2c045 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 17 Feb 2023 06:47:36 +0800 Subject: [PATCH 415/484] cabana: add online-help (#27349) * add online helps * typo * remove duplicate 'too' * show idle message:For Help, Press F1 * improve drawHelp * fix color desc --- tools/cabana/binaryview.cc | 15 ++++++++ tools/cabana/chartswidget.cc | 5 +++ tools/cabana/detailwidget.cc | 5 +-- tools/cabana/mainwin.cc | 66 ++++++++++++++++++++++++++++++++-- tools/cabana/mainwin.h | 14 ++++++++ tools/cabana/messageswidget.cc | 9 +++++ tools/cabana/signaledit.cc | 5 +++ tools/cabana/videowidget.cc | 7 ++++ 8 files changed, 122 insertions(+), 4 deletions(-) diff --git a/tools/cabana/binaryview.cc b/tools/cabana/binaryview.cc index 9085bf310a..20edfd06ff 100644 --- a/tools/cabana/binaryview.cc +++ b/tools/cabana/binaryview.cc @@ -42,6 +42,21 @@ BinaryView::BinaryView(QWidget *parent) : QTableView(parent) { QObject::connect(UndoStack::instance(), &QUndoStack::indexChanged, this, &BinaryView::refresh); addShortcuts(); + setWhatsThis(R"( + Binary View
+ + Shortcuts:
+ Delete Signal: + x , + Backspace , + Delete
+ Change endianness: e
+ Change singedness: s
+ Open chart: + c , + p , + g
+ )"); } void BinaryView::addShortcuts() { diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 9a085ec732..9c5ed097c6 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -94,6 +94,11 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { docking = !docking; updateToolBar(); }); + + setWhatsThis(tr(R"( + Chart view
+ + )")); } void ChartsWidget::eventsMerged() { diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index bf4c1e44f8..95ef975dd1 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -264,11 +264,12 @@ WelcomeWidget::WelcomeWidget(QWidget *parent) : QWidget(parent) { return hlayout; }; - auto lb = new QLabel(tr("<-Select a message to to view details")); + auto lb = new QLabel(tr("<-Select a message to view details")); lb->setAlignment(Qt::AlignHCenter); main_layout->addWidget(lb); main_layout->addLayout(newShortcutRow("Pause", "Space")); - main_layout->addLayout(newShortcutRow("Help", "Alt + H")); + main_layout->addLayout(newShortcutRow("Help", "F1")); + main_layout->addLayout(newShortcutRow("WhatsThis", "Shift+F1")); main_layout->addStretch(0); setStyleSheet("QLabel{color:darkGray;}"); diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index bce6d313c8..07d37df4e4 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -9,7 +9,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -20,7 +22,7 @@ static MainWindow *main_win = nullptr; void qLogMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { if (type == QtDebugMsg) std::cout << msg.toStdString() << std::endl; - if (main_win) emit main_win->showMessage(msg, 0); + if (main_win) emit main_win->showMessage(msg, 2000); } MainWindow::MainWindow() : QMainWindow() { @@ -43,7 +45,7 @@ MainWindow::MainWindow() : QMainWindow() { qRegisterMetaType("ReplyMsgType"); installMessageHandler([this](ReplyMsgType type, const std::string msg) { // use queued connection to recv the log messages from replay. - emit showMessage(QString::fromStdString(msg), 3000); + emit showMessage(QString::fromStdString(msg), 2000); }); installDownloadProgressHandler([this](uint64_t cur, uint64_t total, bool success) { emit updateProgressBar(cur, total, success); @@ -125,6 +127,7 @@ void MainWindow::createActions() { tools_menu->addAction(tr("Find &Similar Bits"), this, &MainWindow::findSimilarBits); QMenu *help_menu = menuBar()->addMenu(tr("&Help")); + help_menu->addAction(tr("Help"), this, &MainWindow::onlineHelp)->setShortcuts(QKeySequence::HelpContents); help_menu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt); } @@ -173,6 +176,7 @@ void MainWindow::createStatusBar() { progress_bar->setTextVisible(true); progress_bar->setFixedSize({230, 16}); progress_bar->setVisible(false); + statusBar()->addWidget(new QLabel(tr("For Help,Press F1"))); statusBar()->addPermanentWidget(progress_bar); } @@ -422,3 +426,61 @@ void MainWindow::findSimilarBits() { QObject::connect(dlg, &FindSimilarBitsDlg::openMessage, messages_widget, &MessagesWidget::selectMessage); dlg->show(); } + +void MainWindow::onlineHelp() { + if (auto help = findChild()) { + help->close(); + } else { + help = new HelpOverlay(this); + help->setGeometry(rect()); + help->show(); + help->raise(); + } +} + +// HelpOverlay +HelpOverlay::HelpOverlay(MainWindow *parent) : QWidget(parent) { + setAttribute(Qt::WA_NoSystemBackground, true); + setAttribute(Qt::WA_TranslucentBackground, true); + setAttribute(Qt::WA_DeleteOnClose); + parent->installEventFilter(this); +} + +void HelpOverlay::paintEvent(QPaintEvent *event) { + QPainter painter(this); + painter.fillRect(rect(), QColor(0, 0, 0, 50)); + MainWindow *parent = (MainWindow *)parentWidget(); + drawHelpForWidget(painter, parent->findChild()); + drawHelpForWidget(painter, parent->findChild()); + drawHelpForWidget(painter, parent->findChild()); + drawHelpForWidget(painter, parent->findChild()); + drawHelpForWidget(painter, parent->findChild()); +} + +void HelpOverlay::drawHelpForWidget(QPainter &painter, QWidget *w) { + if (w && w->isVisible() && !w->whatsThis().isEmpty()) { + QPoint pt = mapFromGlobal(w->mapToGlobal(w->rect().center())); + if (rect().contains(pt)) { + QTextDocument document; + document.setHtml(w->whatsThis()); + QSize doc_size = document.size().toSize(); + QPoint topleft = {pt.x() - doc_size.width() / 2, pt.y() - doc_size.height() / 2}; + painter.translate(topleft); + painter.fillRect(QRect{{0, 0}, doc_size}, palette().toolTipBase()); + document.drawContents(&painter); + painter.translate(-topleft); + } + } +} + +bool HelpOverlay::eventFilter(QObject *obj, QEvent *event) { + if (obj == parentWidget() && event->type() == QEvent::Resize) { + QResizeEvent *resize_event = (QResizeEvent *)(event); + setGeometry(QRect{QPoint(0, 0), resize_event->size()}); + } + return false; +} + +void HelpOverlay::mouseReleaseEvent(QMouseEvent *event) { + close(); +} diff --git a/tools/cabana/mainwin.h b/tools/cabana/mainwin.h index 5e627df58b..e7a39adab7 100644 --- a/tools/cabana/mainwin.h +++ b/tools/cabana/mainwin.h @@ -54,6 +54,7 @@ protected: void setOption(); void findSimilarBits(); void undoStackCleanChanged(bool clean); + void onlineHelp(); VideoWidget *video_widget = nullptr; QDockWidget *video_dock; @@ -69,4 +70,17 @@ protected: enum { MAX_RECENT_FILES = 15 }; QAction *recent_files_acts[MAX_RECENT_FILES] = {}; QMenu *open_recent_menu = nullptr; + friend class OnlineHelp; +}; + +class HelpOverlay : public QWidget { + Q_OBJECT +public: + HelpOverlay(MainWindow *parent); + +protected: + void drawHelpForWidget(QPainter &painter, QWidget *w); + void paintEvent(QPaintEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + bool eventFilter(QObject *obj, QEvent *event) override; }; diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index 4f6bb92375..1cf85ed740 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -65,6 +65,15 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { }); updateSuppressedButtons(); + + setWhatsThis(tr(R"( + Message View
+ + Byte color:
+ constant changing
+ increasing
+ decreasing
+ )")); } void MessagesWidget::selectMessage(const MessageId &msg_id) { diff --git a/tools/cabana/signaledit.cc b/tools/cabana/signaledit.cc index 98dd39204f..e2be5c85d0 100644 --- a/tools/cabana/signaledit.cc +++ b/tools/cabana/signaledit.cc @@ -365,6 +365,11 @@ SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts), QObject::connect(model, &QAbstractItemModel::rowsInserted, this, &SignalView::rowsChanged); QObject::connect(model, &QAbstractItemModel::rowsRemoved, this, &SignalView::rowsChanged); QObject::connect(dbc(), &DBCManager::signalAdded, [this](uint32_t address, const Signal *sig) { expandSignal(sig); }); + + setWhatsThis(tr(R"( + Signal view
+ + )")); } void SignalView::setMessage(const MessageId &id) { diff --git a/tools/cabana/videowidget.cc b/tools/cabana/videowidget.cc index cd3dc0b516..f77f7c306d 100644 --- a/tools/cabana/videowidget.cc +++ b/tools/cabana/videowidget.cc @@ -53,6 +53,13 @@ VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent) { QObject::connect(can, &AbstractStream::paused, this, &VideoWidget::updatePlayBtnState); QObject::connect(can, &AbstractStream::resume, this, &VideoWidget::updatePlayBtnState); updatePlayBtnState(); + + setWhatsThis(tr(R"( + Video
+ + Shortcuts:
+ Pause/Resume: space
+ )")); } QWidget *VideoWidget::createCameraWidget() { From 202eb06a05e43ce77e5be19540fb427b5a060cfc Mon Sep 17 00:00:00 2001 From: grekiki <96022003+GregorKikelj@users.noreply.github.com> Date: Thu, 16 Feb 2023 23:51:25 +0100 Subject: [PATCH 416/484] Carla simulator: set driver orientation (#27363) --- tools/sim/bridge.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/sim/bridge.py b/tools/sim/bridge.py index f0694078e2..7bb12badb2 100755 --- a/tools/sim/bridge.py +++ b/tools/sim/bridge.py @@ -211,7 +211,10 @@ def fake_driver_monitoring(exit_event: threading.Event): while not exit_event.is_set(): # dmonitoringmodeld output dat = messaging.new_message('driverStateV2') + dat.driverStateV2.leftDriverData.faceOrientation = [0., 0., 0.] dat.driverStateV2.leftDriverData.faceProb = 1.0 + dat.driverStateV2.rightDriverData.faceOrientation = [0., 0., 0.] + dat.driverStateV2.rightDriverData.faceProb = 1.0 pm.send('driverStateV2', dat) # dmonitoringd output From 5e0b28111902f9d8565a5bae50369185ad1cbe01 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 16 Feb 2023 16:09:11 -0800 Subject: [PATCH 417/484] HKG: log AEB/FCW for Kia Ceed (#27364) Ceed uses FCA --- selfdrive/car/hyundai/values.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 15c239506b..422431a718 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -1660,7 +1660,7 @@ FEATURES = { "use_elect_gears": {CAR.KIA_NIRO_EV, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_OPTIMA_H, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.IONIQ, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.KONA_EV_2022, CAR.KIA_K5_HEV_2020}, # these cars use the FCA11 message for the AEB and FCW signals, all others use SCC12 - "use_fca": {CAR.SONATA, CAR.SONATA_HYBRID, CAR.ELANTRA, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.KIA_STINGER, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_FORTE, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV_2021, CAR.PALISADE, CAR.GENESIS_G70, CAR.GENESIS_G70_2020, CAR.KONA, CAR.SANTA_FE, CAR.KIA_SELTOS, CAR.KONA_HEV, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.TUCSON, CAR.KONA_EV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020, CAR.KIA_OPTIMA_G4_FL}, + "use_fca": {CAR.SONATA, CAR.SONATA_HYBRID, CAR.ELANTRA, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.KIA_STINGER, CAR.KIA_CEED, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_FORTE, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV_2021, CAR.PALISADE, CAR.GENESIS_G70, CAR.GENESIS_G70_2020, CAR.KONA, CAR.SANTA_FE, CAR.KIA_SELTOS, CAR.KONA_HEV, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.TUCSON, CAR.KONA_EV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020, CAR.KIA_OPTIMA_G4_FL}, } CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.TUCSON_4TH_GEN, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, CAR.SANTA_CRUZ_1ST_GEN, CAR.KIA_SPORTAGE_5TH_GEN, CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.GENESIS_GV60_EV_1ST_GEN, CAR.KIA_SORENTO_4TH_GEN, CAR.KIA_NIRO_HEV_2ND_GEN} From 37adf5d3a681daac2542270a24897ae00d42e4b8 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 17 Feb 2023 10:53:12 +0800 Subject: [PATCH 418/484] cabana: simplify conversions between QVariant and QVector (#27367) --- tools/cabana/historylog.cc | 2 +- tools/cabana/messageswidget.cc | 16 +++++++--------- tools/cabana/util.cc | 25 ++++++------------------- tools/cabana/util.h | 1 - 4 files changed, 14 insertions(+), 30 deletions(-) diff --git a/tools/cabana/historylog.cc b/tools/cabana/historylog.cc index f7f02b06d8..87968d495c 100644 --- a/tools/cabana/historylog.cc +++ b/tools/cabana/historylog.cc @@ -17,7 +17,7 @@ QVariant HistoryLogModel::data(const QModelIndex &index, int role) const { } return show_signals ? QString::number(m.sig_values[index.column() - 1]) : toHex(m.data); } else if (role == Qt::UserRole && index.column() == 1 && !show_signals) { - return ChangeTracker::toVariantList(m.colors); + return QVariant::fromValue(m.colors); } return {}; } diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index 1cf85ed740..81ebc6af20 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -121,17 +121,15 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const { case 4: return toHex(can_data.dat); } } else if (role == Qt::UserRole && index.column() == 4) { - QList colors; - colors.reserve(can_data.dat.size()); - for (int i = 0; i < can_data.dat.size(); i++){ - if (suppressed_bytes.contains({id, i})) { - colors.append(QColor(255, 255, 255, 0)); - } else { - colors.append(i < can_data.colors.size() ? can_data.colors[i] : QColor(255, 255, 255, 0)); + QVector colors = can_data.colors; + if (!suppressed_bytes.empty()) { + for (int i = 0; i < colors.size(); i++) { + if (suppressed_bytes.contains({id, i})) { + colors[i] = QColor(255, 255, 255, 0); + } } } - return colors; - + return QVariant::fromValue(colors); } return {}; } diff --git a/tools/cabana/util.cc b/tools/cabana/util.cc index 454dd29b87..9843a3f00c 100644 --- a/tools/cabana/util.cc +++ b/tools/cabana/util.cc @@ -66,13 +66,6 @@ void ChangeTracker::clear() { colors.clear(); } -QList ChangeTracker::toVariantList(const QVector &colors) { - QList ret; - ret.reserve(colors.size()); - for (auto &c : colors) ret.append(c); - return ret; -} - // MessageBytesDelegate MessageBytesDelegate::MessageBytesDelegate(QObject *parent) : QStyledItemDelegate(parent) { @@ -89,12 +82,8 @@ void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem & return; } - if ((option.state & QStyle::State_Selected) && (option.state & QStyle::State_Active)) { - painter->setPen(option.palette.color(QPalette::HighlightedText)); - } else { - painter->setPen(option.palette.color(QPalette::Text)); - } - + auto color_role = option.state & QStyle::State_Selected ? QPalette::HighlightedText: QPalette::Text; + painter->setPen(option.palette.color(color_role)); painter->setFont(fixed_font); QRect space = painter->boundingRect(opt.rect, opt.displayAlignment, " "); QRect pos = painter->boundingRect(opt.rect, opt.displayAlignment, "00"); @@ -103,15 +92,13 @@ void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem & int m = space.width() / 2; const QMargins margins(m, m, m, m); - QList colors = index.data(Qt::UserRole).toList(); - int i = 0; - for (auto &byte : byte_list) { + auto colors = index.data(Qt::UserRole).value>(); + for (int i = 0; i < byte_list.size(); ++i) { if (i < colors.size()) { - painter->fillRect(pos.marginsAdded(margins), colors[i].value()); + painter->fillRect(pos.marginsAdded(margins), colors[i]); } - painter->drawText(pos, opt.displayAlignment, byte); + painter->drawText(pos, opt.displayAlignment, byte_list[i]); pos.moveLeft(pos.right() + space.width()); - i++; } } diff --git a/tools/cabana/util.h b/tools/cabana/util.h index 8ec4cda90c..eb5203fb0b 100644 --- a/tools/cabana/util.h +++ b/tools/cabana/util.h @@ -14,7 +14,6 @@ class ChangeTracker { public: void compute(const QByteArray &dat, double ts, uint32_t freq); - static QList toVariantList(const QVector &colors); void clear(); QVector last_change_t; From b2453d55c12e4e6e8e5115ef7c8daf0f60205fdb Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 16 Feb 2023 21:52:32 -0800 Subject: [PATCH 419/484] Hyundai: detect FCA feature (#27322) * detect fca * use value * it will be from camera on camera acc cars * Update ref_commit --- selfdrive/car/hyundai/carstate.py | 8 ++++---- selfdrive/car/hyundai/interface.py | 4 ++++ selfdrive/car/hyundai/values.py | 4 +--- selfdrive/test/process_replay/ref_commit | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/selfdrive/car/hyundai/carstate.py b/selfdrive/car/hyundai/carstate.py index 0bab188790..22934c05b2 100644 --- a/selfdrive/car/hyundai/carstate.py +++ b/selfdrive/car/hyundai/carstate.py @@ -135,8 +135,8 @@ class CarState(CarStateBase): ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear)) if not self.CP.openpilotLongitudinalControl: - aeb_src = "FCA11" if self.CP.carFingerprint in FEATURES["use_fca"] else "SCC12" - aeb_sig = "FCA_CmdAct" if self.CP.carFingerprint in FEATURES["use_fca"] else "AEB_CmdAct" + aeb_src = "FCA11" if self.CP.flags & HyundaiFlags.USE_FCA.value else "SCC12" + aeb_sig = "FCA_CmdAct" if self.CP.flags & HyundaiFlags.USE_FCA.value else "AEB_CmdAct" aeb_warning = cp_cruise.vl[aeb_src]["CF_VSM_Warn"] != 0 aeb_braking = cp_cruise.vl[aeb_src]["CF_VSM_DecCmdAct"] != 0 or cp_cruise.vl[aeb_src][aeb_sig] != 0 ret.stockFcw = aeb_warning and not aeb_braking @@ -317,7 +317,7 @@ class CarState(CarStateBase): ("SCC12", 50), ] - if CP.carFingerprint in FEATURES["use_fca"]: + if CP.flags & HyundaiFlags.USE_FCA.value: signals += [ ("FCA_CmdAct", "FCA11"), ("CF_VSM_Warn", "FCA11"), @@ -408,7 +408,7 @@ class CarState(CarStateBase): ("SCC12", 50), ] - if CP.carFingerprint in FEATURES["use_fca"]: + if CP.flags & HyundaiFlags.USE_FCA.value: signals += [ ("FCA_CmdAct", "FCA11"), ("CF_VSM_Warn", "FCA11"), diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py index ae0f26274c..d2cc5b4ec0 100644 --- a/selfdrive/car/hyundai/interface.py +++ b/selfdrive/car/hyundai/interface.py @@ -48,6 +48,10 @@ class CarInterface(CarInterfaceBase): if 0x485 in fingerprint[2]: ret.flags |= HyundaiFlags.SEND_LFA.value + # These cars use the FCA11 message for the AEB and FCW signals, all others use SCC12 + if 0x38d in fingerprint[0] or 0x38d in fingerprint[2]: + ret.flags |= HyundaiFlags.USE_FCA.value + ret.steerActuatorDelay = 0.1 # Default delay ret.steerLimitTimer = 0.4 tire_stiffness_factor = 1. diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 422431a718..d0a4e4dd1a 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -61,6 +61,7 @@ class HyundaiFlags(IntFlag): ENABLE_BLINKERS = 32 CANFD_ALT_GEARS_2 = 64 SEND_LFA = 128 + USE_FCA = 256 class CAR: @@ -1658,9 +1659,6 @@ FEATURES = { "use_cluster_gears": {CAR.ELANTRA, CAR.KONA}, "use_tcu_gears": {CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.SONATA_LF, CAR.VELOSTER, CAR.TUCSON}, "use_elect_gears": {CAR.KIA_NIRO_EV, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_OPTIMA_H, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.IONIQ, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.KONA_EV_2022, CAR.KIA_K5_HEV_2020}, - - # these cars use the FCA11 message for the AEB and FCW signals, all others use SCC12 - "use_fca": {CAR.SONATA, CAR.SONATA_HYBRID, CAR.ELANTRA, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.KIA_STINGER, CAR.KIA_CEED, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_FORTE, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV_2021, CAR.PALISADE, CAR.GENESIS_G70, CAR.GENESIS_G70_2020, CAR.KONA, CAR.SANTA_FE, CAR.KIA_SELTOS, CAR.KONA_HEV, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.TUCSON, CAR.KONA_EV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020, CAR.KIA_OPTIMA_G4_FL}, } CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.TUCSON_4TH_GEN, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, CAR.SANTA_CRUZ_1ST_GEN, CAR.KIA_SPORTAGE_5TH_GEN, CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.GENESIS_GV60_EV_1ST_GEN, CAR.KIA_SORENTO_4TH_GEN, CAR.KIA_NIRO_HEV_2ND_GEN} diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index b151a6a5ae..6884eb4660 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -fbb99ee7dc0336062e6785814af82e359dcdd9bf +8883c476d5abc12b4b2949e04c6d7c0cd7c8b9fa From 524f7079a9a51631bef042f28bfc339ecdf6d7d3 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 18 Feb 2023 02:35:50 +0800 Subject: [PATCH 420/484] cabana: add description for timeline color in help (#27375) --- tools/cabana/videowidget.cc | 41 +++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/tools/cabana/videowidget.cc b/tools/cabana/videowidget.cc index f77f7c306d..2b5374d85f 100644 --- a/tools/cabana/videowidget.cc +++ b/tools/cabana/videowidget.cc @@ -13,6 +13,15 @@ #include #include +static const QColor timeline_colors[] = { + [(int)TimelineType::None] = QColor(111, 143, 175), + [(int)TimelineType::Engaged] = QColor(0, 163, 108), + [(int)TimelineType::UserFlag] = Qt::magenta, + [(int)TimelineType::AlertInfo] = Qt::green, + [(int)TimelineType::AlertWarning] = QColor(255, 195, 0), + [(int)TimelineType::AlertCritical] = QColor(199, 0, 57), +}; + inline QString formatTime(int seconds) { return QDateTime::fromTime_t(seconds).toString(seconds > 60 * 60 ? "hh:mm:ss" : "mm:ss"); } @@ -57,9 +66,23 @@ VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent) { setWhatsThis(tr(R"( Video
- Shortcuts:
- Pause/Resume: space
- )")); + Timeline color + + + + + + + +
Disengaged Engaged
User Flag Info
Warning Critical
+ Shortcuts
+ Pause/Resume:  space  + )").arg(timeline_colors[(int)TimelineType::None].name(), + timeline_colors[(int)TimelineType::Engaged].name(), + timeline_colors[(int)TimelineType::UserFlag].name(), + timeline_colors[(int)TimelineType::AlertInfo].name(), + timeline_colors[(int)TimelineType::AlertWarning].name(), + timeline_colors[(int)TimelineType::AlertCritical].name())); } QWidget *VideoWidget::createCameraWidget() { @@ -212,17 +235,9 @@ void Slider::sliderChange(QAbstractSlider::SliderChange change) { } void Slider::paintEvent(QPaintEvent *ev) { - static const QColor colors[] = { - [(int)TimelineType::None] = QColor(111, 143, 175), - [(int)TimelineType::Engaged] = QColor(0, 163, 108), - [(int)TimelineType::UserFlag] = Qt::white, - [(int)TimelineType::AlertInfo] = Qt::green, - [(int)TimelineType::AlertWarning] = QColor(255, 195, 0), - [(int)TimelineType::AlertCritical] = QColor(199, 0, 57)}; - QPainter p(this); QRect r = rect().adjusted(0, 4, 0, -4); - p.fillRect(r, colors[(int)TimelineType::None]); + p.fillRect(r, timeline_colors[(int)TimelineType::None]); double min = minimum() / 1000.0; double max = maximum() / 1000.0; for (auto [begin, end, type] : timeline) { @@ -230,7 +245,7 @@ void Slider::paintEvent(QPaintEvent *ev) { continue; r.setLeft(((std::max(min, (double)begin) - min) / (max - min)) * width()); r.setRight(((std::min(max, (double)end) - min) / (max - min)) * width()); - p.fillRect(r, colors[(int)type]); + p.fillRect(r, timeline_colors[(int)type]); } QStyleOptionSlider opt; From 2c5e55ccdca597857208fc2fbb6bbc58972dbc4b Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 18 Feb 2023 02:53:47 +0800 Subject: [PATCH 421/484] cabana: attach messageid to tabdata (#27368) --- tools/cabana/detailwidget.cc | 20 +++++++++----------- tools/cabana/detailwidget.h | 1 - 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index 95ef975dd1..f290b3b706 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -86,13 +86,11 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart QObject::connect(tabbar, &QTabBar::customContextMenuRequested, this, &DetailWidget::showTabBarContextMenu); QObject::connect(tabbar, &QTabBar::currentChanged, [this](int index) { if (index != -1) { - setMessage(tabbar_ids[index]); + setMessage(tabbar->tabData(index).value()); } }); QObject::connect(tabbar, &QTabBar::tabCloseRequested, [this](int index) { - tabbar_ids.removeAt(index); tabbar->removeTab(index); - assert(tabbar_ids.size() == tabbar->count()); }); QObject::connect(charts, &ChartsWidget::seriesChanged, signal_view, &SignalView::updateChartState); } @@ -103,14 +101,11 @@ void DetailWidget::showTabBarContextMenu(const QPoint &pt) { QMenu menu(this); menu.addAction(tr("Close Other Tabs")); if (menu.exec(tabbar->mapToGlobal(pt))) { - tabbar_ids.move(index, 0); tabbar->moveTab(index, 0); tabbar->setCurrentIndex(0); while (tabbar->count() > 1) { - tabbar_ids.removeAt(1); tabbar->removeTab(1); } - assert(tabbar_ids.size() == tabbar->count()); } } } @@ -121,21 +116,25 @@ void DetailWidget::removeAll() { while (tabbar->count() > 0) { tabbar->removeTab(0); } - tabbar_ids.clear(); tabbar->blockSignals(false); stacked_layout->setCurrentIndex(0); } void DetailWidget::setMessage(const MessageId &message_id) { msg_id = message_id; - int index = tabbar_ids.indexOf(*msg_id); + tabbar->blockSignals(true); + int index = tabbar->count() - 1; + for (/**/; index >= 0; --index) { + if (tabbar->tabData(index).value() == message_id) break; + } if (index == -1) { - tabbar_ids.append(*msg_id); index = tabbar->addTab(message_id.toString()); + tabbar->setTabData(index, QVariant::fromValue(message_id)); tabbar->setTabToolTip(index, msgName(message_id)); } - assert(tabbar->count() == tabbar_ids.size()); + tabbar->setCurrentIndex(index); + tabbar->blockSignals(false); setUpdatesEnabled(false); @@ -144,7 +143,6 @@ void DetailWidget::setMessage(const MessageId &message_id) { history_log->setMessage(*msg_id); stacked_layout->setCurrentIndex(1); - tabbar->setCurrentIndex(index); refresh(); splitter->setSizes({1, 2}); diff --git a/tools/cabana/detailwidget.h b/tools/cabana/detailwidget.h index 949a8c9b8d..b3e353d539 100644 --- a/tools/cabana/detailwidget.h +++ b/tools/cabana/detailwidget.h @@ -50,7 +50,6 @@ private: ElidedLabel *name_label; QWidget *warning_widget; QTabBar *tabbar; - QList tabbar_ids; QTabWidget *tab_widget; QAction *remove_msg_act; LogsWidget *history_log; From 89f68bf0cbf53a81b0553d3816fdbe522f941fa1 Mon Sep 17 00:00:00 2001 From: martinl Date: Fri, 17 Feb 2023 21:08:41 +0200 Subject: [PATCH 422/484] cleanup: remove nonexisting third_party includes (#27372) --- SConstruct | 4 ---- 1 file changed, 4 deletions(-) diff --git a/SConstruct b/SConstruct index 31aa6ecced..007931b7f5 100644 --- a/SConstruct +++ b/SConstruct @@ -197,10 +197,6 @@ env = Environment( "#third_party/libyuv/include", "#third_party/json11", "#third_party/curl/include", - "#third_party/libgralloc/include", - "#third_party/android_frameworks_native/include", - "#third_party/android_hardware_libhardware/include", - "#third_party/android_system_core/include", "#third_party/linux/include", "#third_party/snpe/include", "#third_party/mapbox-gl-native-qt/include", From b9a01752fc88e12eb3375aca1492d0cedae17946 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 17 Feb 2023 14:13:22 -0800 Subject: [PATCH 423/484] bump version to 0.9.2 --- RELEASES.md | 4 ++++ common/version.h | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index 78d2e3b74b..ff5b686e0c 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,7 @@ +Version 0.9.2 (2023-03-XX) +======================== + + Version 0.9.1 (2023-02-23) ======================== * New driving model diff --git a/common/version.h b/common/version.h index 7b5764785a..5637029cf4 100644 --- a/common/version.h +++ b/common/version.h @@ -1 +1 @@ -#define COMMA_VERSION "0.9.1" +#define COMMA_VERSION "0.9.2" From dad948983a7ff26ffc073c536bd1e99b5d826cec Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 17 Feb 2023 15:04:21 -0800 Subject: [PATCH 424/484] chyrsler: add comment about radar parsing --- selfdrive/car/chrysler/interface.py | 1 + 1 file changed, 1 insertion(+) diff --git a/selfdrive/car/chrysler/interface.py b/selfdrive/car/chrysler/interface.py index 961684f398..303f563c90 100755 --- a/selfdrive/car/chrysler/interface.py +++ b/selfdrive/car/chrysler/interface.py @@ -12,6 +12,7 @@ class CarInterface(CarInterfaceBase): ret.carName = "chrysler" ret.dashcamOnly = candidate in RAM_HD + # radar parsing needs some work, see https://github.com/commaai/openpilot/issues/26842 ret.radarUnavailable = True # DBC[candidate]['radar'] is None ret.steerActuatorDelay = 0.1 ret.steerLimitTimer = 0.4 From 04fe6c4ec72dfcf89456a484d03f85502c237701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Fri, 17 Feb 2023 15:28:26 -0800 Subject: [PATCH 425/484] Expand lateral MPC to 10s (#27343) * 10s lat * Full length MPC * redfine N * Leave controls the same for now * Updates * use long plan in lat plan * interp plan * add new interp * simplergit add selfdrive/controls/plannerd.py selfdrive/controls/ * expand to 10s * revert this * fix linter * Update sconscripts * fix test * fix test * fix test * Revert "Update sconscripts" This reverts commit 6e23c69dcebd5ed003e37e01921f6af7c31de0db. * Dont import drive helpers * better compile deps * fix compile * comment * update replay * Update plannerd time --- selfdrive/controls/lib/drive_helpers.py | 2 -- .../controls/lib/lateral_mpc_lib/SConscript | 1 + .../controls/lib/lateral_mpc_lib/lat_mpc.py | 10 ++++----- selfdrive/controls/lib/lateral_planner.py | 21 +++++++++++-------- .../lib/longitudinal_mpc_lib/SConscript | 1 + .../lib/longitudinal_mpc_lib/long_mpc.py | 1 + selfdrive/controls/tests/test_lateral_mpc.py | 3 ++- selfdrive/test/process_replay/ref_commit | 2 +- selfdrive/test/test_onroad.py | 2 +- 9 files changed, 24 insertions(+), 19 deletions(-) diff --git a/selfdrive/controls/lib/drive_helpers.py b/selfdrive/controls/lib/drive_helpers.py index a332d06765..05c3897335 100644 --- a/selfdrive/controls/lib/drive_helpers.py +++ b/selfdrive/controls/lib/drive_helpers.py @@ -17,8 +17,6 @@ V_CRUISE_INITIAL_EXPERIMENTAL_MODE = 105 IMPERIAL_INCREMENT = 1.6 # should be CV.MPH_TO_KPH, but this causes rounding errors MIN_SPEED = 1.0 -LAT_MPC_N = 16 -LON_MPC_N = 32 CONTROL_N = 17 CAR_ROTATION_RADIUS = 0.0 diff --git a/selfdrive/controls/lib/lateral_mpc_lib/SConscript b/selfdrive/controls/lib/lateral_mpc_lib/SConscript index 868b5a873c..745ed99d10 100644 --- a/selfdrive/controls/lib/lateral_mpc_lib/SConscript +++ b/selfdrive/controls/lib/lateral_mpc_lib/SConscript @@ -47,6 +47,7 @@ acados_dir = '#third_party/acados' acados_templates_dir = '#third_party/acados/acados_template/c_templates_tera' source_list = ['lat_mpc.py', + '#/selfdrive/modeld/constants.py', f'{acados_dir}/include/acados_c/ocp_nlp_interface.h', f'{acados_dir}/x86_64/lib/libacados.so', f'{acados_dir}/larch64/lib/libacados.so', diff --git a/selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py b/selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py index 536f436fce..ca7b991e69 100755 --- a/selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py +++ b/selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py @@ -3,8 +3,8 @@ import os import numpy as np from casadi import SX, vertcat, sin, cos - from common.realtime import sec_since_boot +# WARNING: imports outside of constants will not trigger a rebuild from selfdrive.modeld.constants import T_IDXS if __name__ == '__main__': # generating code @@ -17,12 +17,12 @@ EXPORT_DIR = os.path.join(LAT_MPC_DIR, "c_generated_code") JSON_FILE = os.path.join(LAT_MPC_DIR, "acados_ocp_lat.json") X_DIM = 4 P_DIM = 2 -N = 16 COST_E_DIM = 3 COST_DIM = COST_E_DIM + 2 SPEED_OFFSET = 10.0 MODEL_NAME = 'lat' ACADOS_SOLVER_TYPE = 'SQP_RTI' +N = 32 def gen_lat_model(): model = AcadosModel() @@ -168,14 +168,14 @@ class LateralMpc(): self.solver.constraints_set(0, "lbx", x0_cp) self.solver.constraints_set(0, "ubx", x0_cp) self.yref[:,0] = y_pts - v_ego = p_cp[0] + v_ego = p_cp[0, 0] # rotation_radius = p_cp[1] self.yref[:,1] = heading_pts * (v_ego + SPEED_OFFSET) self.yref[:,2] = yaw_rate_pts * (v_ego + SPEED_OFFSET) for i in range(N): self.solver.cost_set(i, "yref", self.yref[i]) - self.solver.set(i, "p", p_cp) - self.solver.set(N, "p", p_cp) + self.solver.set(i, "p", p_cp[i]) + self.solver.set(N, "p", p_cp[N]) self.solver.cost_set(N, "yref", self.yref[N][:COST_E_DIM]) t = sec_since_boot() diff --git a/selfdrive/controls/lib/lateral_planner.py b/selfdrive/controls/lib/lateral_planner.py index 932ad49535..7230b5ad14 100644 --- a/selfdrive/controls/lib/lateral_planner.py +++ b/selfdrive/controls/lib/lateral_planner.py @@ -16,12 +16,12 @@ CAMERA_OFFSET = 0.04 PATH_COST = 1.0 LATERAL_MOTION_COST = 0.11 LATERAL_ACCEL_COST = 0.0 -LATERAL_JERK_COST = 0.05 +LATERAL_JERK_COST = 0.04 # Extreme steering rate is unpleasant, even # when it does not cause bad jerk. # TODO this cost should be lowered when low # speed lateral control is stable on all cars -STEERING_RATE_COST = 800.0 +STEERING_RATE_COST = 700.0 class LateralPlanner: @@ -35,6 +35,7 @@ class LateralPlanner: self.solution_invalid_cnt = 0 self.path_xyz = np.zeros((TRAJECTORY_SIZE, 3)) + self.velocity_xyz = np.zeros((TRAJECTORY_SIZE, 3)) self.plan_yaw = np.zeros((TRAJECTORY_SIZE,)) self.plan_yaw_rate = np.zeros((TRAJECTORY_SIZE,)) self.t_idxs = np.arange(TRAJECTORY_SIZE) @@ -49,7 +50,6 @@ class LateralPlanner: def update(self, sm): # clip speed , lateral planning is not possible at 0 speed - self.v_ego = max(MIN_SPEED, sm['carState'].vEgo) measured_curvature = sm['controlsState'].curvature # Parse model predictions @@ -59,6 +59,10 @@ class LateralPlanner: self.t_idxs = np.array(md.position.t) self.plan_yaw = np.array(md.orientation.z) self.plan_yaw_rate = np.array(md.orientationRate.z) + self.velocity_xyz = np.column_stack([md.velocity.x, md.velocity.y, md.velocity.z]) + car_speed = np.linalg.norm(self.velocity_xyz, axis=1) + self.v_plan = np.clip(car_speed, MIN_SPEED, np.inf) + self.v_ego = self.v_plan[0] # Lane change logic desire_state = md.meta.desireState @@ -68,21 +72,20 @@ class LateralPlanner: lane_change_prob = self.l_lane_change_prob + self.r_lane_change_prob self.DH.update(sm['carState'], sm['carControl'].latActive, lane_change_prob) - d_path_xyz = self.path_xyz self.lat_mpc.set_weights(PATH_COST, LATERAL_MOTION_COST, LATERAL_ACCEL_COST, LATERAL_JERK_COST, STEERING_RATE_COST) - y_pts = np.interp(self.v_ego * self.t_idxs[:LAT_MPC_N + 1], np.linalg.norm(d_path_xyz, axis=1), d_path_xyz[:, 1]) - heading_pts = np.interp(self.v_ego * self.t_idxs[:LAT_MPC_N + 1], np.linalg.norm(self.path_xyz, axis=1), self.plan_yaw) - yaw_rate_pts = np.interp(self.v_ego * self.t_idxs[:LAT_MPC_N + 1], np.linalg.norm(self.path_xyz, axis=1), self.plan_yaw_rate) + y_pts = self.path_xyz[:LAT_MPC_N+1, 1] + heading_pts = self.plan_yaw[:LAT_MPC_N+1] + yaw_rate_pts = self.plan_yaw_rate[:LAT_MPC_N+1] self.y_pts = y_pts assert len(y_pts) == LAT_MPC_N + 1 assert len(heading_pts) == LAT_MPC_N + 1 assert len(yaw_rate_pts) == LAT_MPC_N + 1 - lateral_factor = max(0, self.factor1 - (self.factor2 * self.v_ego**2)) - p = np.array([self.v_ego, lateral_factor]) + lateral_factor = np.clip(self.factor1 - (self.factor2 * self.v_plan**2), 0.0, np.inf) + p = np.column_stack([self.v_plan, lateral_factor]) self.lat_mpc.run(self.x0, p, y_pts, diff --git a/selfdrive/controls/lib/longitudinal_mpc_lib/SConscript b/selfdrive/controls/lib/longitudinal_mpc_lib/SConscript index e5b2360607..7f5daf157c 100644 --- a/selfdrive/controls/lib/longitudinal_mpc_lib/SConscript +++ b/selfdrive/controls/lib/longitudinal_mpc_lib/SConscript @@ -54,6 +54,7 @@ acados_dir = '#third_party/acados' acados_templates_dir = '#third_party/acados/acados_template/c_templates_tera' source_list = ['long_mpc.py', + '#/selfdrive/modeld/constants.py', f'{acados_dir}/include/acados_c/ocp_nlp_interface.h', f'{acados_dir}/x86_64/lib/libacados.so', f'{acados_dir}/larch64/lib/libacados.so', diff --git a/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py b/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py index c017951232..660002691a 100644 --- a/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py +++ b/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py @@ -5,6 +5,7 @@ import numpy as np from common.realtime import sec_since_boot from common.numpy_fast import clip from system.swaglog import cloudlog +# WARNING: imports outside of constants will not trigger a rebuild from selfdrive.modeld.constants import index_function from selfdrive.controls.lib.radar_helpers import _LEAD_ACCEL_TAU diff --git a/selfdrive/controls/tests/test_lateral_mpc.py b/selfdrive/controls/tests/test_lateral_mpc.py index df5154b2b4..b569da09b4 100644 --- a/selfdrive/controls/tests/test_lateral_mpc.py +++ b/selfdrive/controls/tests/test_lateral_mpc.py @@ -17,7 +17,8 @@ def run_mpc(lat_mpc=None, v_ref=30., x_init=0., y_init=0., psi_init=0., curvatur curv_rate_pts = np.zeros(LAT_MPC_N + 1) x0 = np.array([x_init, y_init, psi_init, curvature_init]) - p = np.array([v_ref, CAR_ROTATION_RADIUS]) + p = np.column_stack([v_ref * np.ones(LAT_MPC_N + 1), + CAR_ROTATION_RADIUS * np.ones(LAT_MPC_N + 1)]) # converge in no more than 10 iterations for _ in range(10): diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 6884eb4660..4a670b11ff 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -8883c476d5abc12b4b2949e04c6d7c0cd7c8b9fa +086896986a3fcdb1a03d9afd00a9abc928f9ef25 diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index 4021e27de3..84511771f7 100755 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -26,7 +26,7 @@ PROCS = { "./encoderd": 17.0, "./camerad": 14.5, "./locationd": 9.1, - "selfdrive.controls.plannerd": 11.7, + "selfdrive.controls.plannerd": 16.5, "./_ui": 19.2, "selfdrive.locationd.paramsd": 9.0, "./_sensord": 12.0, From b6fd39856c2862132f00ca48269f2e851cda62f3 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 17 Feb 2023 18:39:27 -0800 Subject: [PATCH 426/484] HKG: simplify Ioniq car params (#27371) * move PHEV to other ioniqs * reverse if * order * order --- selfdrive/car/hyundai/interface.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py index d2cc5b4ec0..2a909b6e2e 100644 --- a/selfdrive/car/hyundai/interface.py +++ b/selfdrive/car/hyundai/interface.py @@ -103,18 +103,13 @@ class CarInterface(CarInterfaceBase): ret.wheelbase = 2.6 ret.steerRatio = 13.42 # Spec tire_stiffness_factor = 0.385 - elif candidate in (CAR.IONIQ, CAR.IONIQ_EV_LTD, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.IONIQ_HEV_2022): + elif candidate in (CAR.IONIQ, CAR.IONIQ_EV_LTD, CAR.IONIQ_PHEV_2019, CAR.IONIQ_HEV_2022, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV): ret.mass = 1490. + STD_CARGO_KG # weight per hyundai site https://www.hyundaiusa.com/ioniq-electric/specifications.aspx ret.wheelbase = 2.7 ret.steerRatio = 13.73 # Spec tire_stiffness_factor = 0.385 - if candidate not in (CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.IONIQ_HEV_2022): + if candidate in (CAR.IONIQ, CAR.IONIQ_EV_LTD, CAR.IONIQ_PHEV_2019): ret.minSteerSpeed = 32 * CV.MPH_TO_MS - elif candidate == CAR.IONIQ_PHEV_2019: - ret.mass = 1550. + STD_CARGO_KG # weight per hyundai site https://www.hyundaiusa.com/us/en/vehicles/2019-ioniq-plug-in-hybrid/compare-specs - ret.wheelbase = 2.7 - ret.steerRatio = 13.73 - ret.minSteerSpeed = 32 * CV.MPH_TO_MS elif candidate == CAR.VELOSTER: ret.mass = 3558. * CV.LB_TO_KG ret.wheelbase = 2.80 From 7330466dffe4b1142c64d99d328654e7109e8073 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Sat, 18 Feb 2023 03:12:15 +0000 Subject: [PATCH 427/484] Ford Escape PHEV 2022 Fingerprint (#27382) * Ford Escape PHEV 2022 Fingerprint 6cffc47c251aa36c|2023-02-17--18-17-14 VIN: 1FMCU0EZ0NUB76728 * include MY2022 in car info * Update selfdrive/car/ford/values.py Co-authored-by: Shane Smiskol --------- Co-authored-by: koffee-bean Co-authored-by: Shane Smiskol --- selfdrive/car/ford/values.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index 526b74b16c..24415c74be 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -72,7 +72,8 @@ class FordCarInfo(CarInfo): CAR_INFO: Dict[str, Union[CarInfo, List[CarInfo]]] = { CAR.BRONCO_SPORT_MK1: FordCarInfo("Ford Bronco Sport 2021-22"), CAR.ESCAPE_MK4: [ - FordCarInfo("Ford Escape 2020-21"), + FordCarInfo("Ford Escape 2020-22"), + FordCarInfo("Ford Escape Plug-in Hybrid 2020-22"), FordCarInfo("Ford Kuga 2020-21", "Driver Assistance Pack"), ], CAR.EXPLORER_MK6: [ @@ -128,6 +129,7 @@ FW_VERSIONS = { (Ecu.eps, 0x730, None): [ b'LX6C-14D003-AF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'LX6C-14D003-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + b'LX6C-14D003-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', ], (Ecu.abs, 0x760, None): [ b'LX6C-2D053-NS\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', @@ -145,10 +147,12 @@ FW_VERSIONS = { b'LX6A-14C204-BJV\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'LX6A-14C204-ESG\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'MX6A-14C204-BEF\x00\x00\x00\x00\x00\x00\x00\x00\x00', + b'NX6A-14C204-BLE\x00\x00\x00\x00\x00\x00\x00\x00\x00', ], (Ecu.shiftByWire, 0x732, None): [ b'LX6P-14G395-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'LX6P-14G395-AD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + b'PZ1P-14G395-AC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', ], }, CAR.EXPLORER_MK6: { From ce9fd785d55f5bee8e49d035b13548709fbe989c Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Fri, 17 Feb 2023 19:30:06 -0800 Subject: [PATCH 428/484] Ford: add CarInfo for Plug-in Hybrids --- selfdrive/car/ford/values.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index 24415c74be..951cd09ba0 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -75,10 +75,12 @@ CAR_INFO: Dict[str, Union[CarInfo, List[CarInfo]]] = { FordCarInfo("Ford Escape 2020-22"), FordCarInfo("Ford Escape Plug-in Hybrid 2020-22"), FordCarInfo("Ford Kuga 2020-21", "Driver Assistance Pack"), + FordCarInfo("Ford Kuga Plug-in Hybrid 2020-22", "Driver Assistance Pack"), ], CAR.EXPLORER_MK6: [ FordCarInfo("Ford Explorer 2020-22"), FordCarInfo("Lincoln Aviator 2021", "Co-Pilot360 Plus"), + FordCarInfo("Lincoln Aviator Plug-in Hybrid 2021", "Co-Pilot360 Plus"), ], CAR.FOCUS_MK4: FordCarInfo("Ford Focus EU 2019", "Driver Assistance Pack"), CAR.MAVERICK_MK1: FordCarInfo("Ford Maverick 2022", "Co-Pilot360 Assist"), From ac0dbf74bc835faa46beab4bbb948c95597e4e67 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 18 Feb 2023 11:31:54 +0800 Subject: [PATCH 429/484] cabana: add support for load&save extra dbc info (#27203) * support extra info * support undo/redo * fix undo/redo * cleanup * fix regexp * refactor dbcmanager * replace text in headerview * fix binary::refresh * cleanup * use QRegularExpression * add desc validation * edit val description in table * cleanup --- tools/cabana/SConscript | 2 + tools/cabana/binaryview.cc | 21 ++- tools/cabana/binaryview.h | 3 +- tools/cabana/chartswidget.cc | 8 +- tools/cabana/chartswidget.h | 2 +- tools/cabana/commands.cc | 16 +- tools/cabana/commands.h | 4 +- tools/cabana/dbcmanager.cc | 218 +++++++++++++++++--------- tools/cabana/dbcmanager.h | 56 ++++--- tools/cabana/detailwidget.cc | 6 +- tools/cabana/historylog.cc | 19 +-- tools/cabana/historylog.h | 3 +- tools/cabana/messageswidget.cc | 6 +- tools/cabana/messageswidget.h | 2 + tools/cabana/signaledit.cc | 122 ++++++++++++-- tools/cabana/signaledit.h | 18 ++- tools/cabana/streams/replaystream.cc | 2 - tools/cabana/streams/replaystream.h | 1 - tools/cabana/tests/test_cabana.cc | 19 ++- tools/cabana/tools/findsimilarbits.cc | 1 + tools/cabana/tools/findsimilarbits.h | 1 + tools/cabana/util.cc | 2 +- tools/cabana/util.h | 6 +- tools/cabana/videowidget.h | 2 + 24 files changed, 375 insertions(+), 165 deletions(-) diff --git a/tools/cabana/SConscript b/tools/cabana/SConscript index a9922ba9be..166b3d5548 100644 --- a/tools/cabana/SConscript +++ b/tools/cabana/SConscript @@ -17,6 +17,8 @@ qt_libs = ['qt_util'] + base_libs cabana_libs = [widgets, cereal, messaging, visionipc, replay_lib, opendbc,'avutil', 'avcodec', 'avformat', 'bz2', 'curl', 'yuv'] + qt_libs cabana_env = qt_env.Clone() +opendbc_path = '-DOPENDBC_FILE_PATH=\'"%s"\'' % (cabana_env.Dir("../../opendbc").abspath) +cabana_env['CXXFLAGS'] += [opendbc_path] prev_moc_path = cabana_env['QT_MOCHPREFIX'] cabana_env['QT_MOCHPREFIX'] = os.path.dirname(prev_moc_path) + '/cabana/moc_' diff --git a/tools/cabana/binaryview.cc b/tools/cabana/binaryview.cc index 20edfd06ff..5ea5ea2d99 100644 --- a/tools/cabana/binaryview.cc +++ b/tools/cabana/binaryview.cc @@ -12,7 +12,6 @@ #include "tools/cabana/commands.h" #include "tools/cabana/signaledit.h" -#include "tools/cabana/streams/abstractstream.h" // BinaryView @@ -170,7 +169,7 @@ void BinaryView::highlightPosition(const QPoint &pos) { auto item = (BinaryViewModel::Item *)index.internalPointer(); const Signal *sig = item->sigs.isEmpty() ? nullptr : item->sigs.back(); highlight(sig); - QToolTip::showText(pos, sig ? sig->name.c_str() : "", this, rect()); + QToolTip::showText(pos, sig ? sig->name : "", this, rect()); } } @@ -246,22 +245,22 @@ std::tuple BinaryView::getSelection(QModelIndex index) { void BinaryViewModel::refresh() { beginResetModel(); items.clear(); - if ((dbc_msg = dbc()->msg(*msg_id))) { + if (auto dbc_msg = dbc()->msg(*msg_id)) { row_count = dbc_msg->size; items.resize(row_count * column_count); - for (auto sig : dbc_msg->getSignals()) { - auto [start, end] = getSignalRange(sig); + for (auto &sig : dbc_msg->sigs) { + auto [start, end] = getSignalRange(&sig); for (int j = start; j <= end; ++j) { - int bit_index = sig->is_little_endian ? bigEndianBitIndex(j) : j; + int bit_index = sig.is_little_endian ? bigEndianBitIndex(j) : j; int idx = column_count * (bit_index / 8) + bit_index % 8; if (idx >= items.size()) { - qWarning() << "signal " << sig->name.c_str() << "out of bounds.start_bit:" << sig->start_bit << "size:" << sig->size; + qWarning() << "signal " << sig.name << "out of bounds.start_bit:" << sig.start_bit << "size:" << sig.size; break; } - if (j == start) sig->is_little_endian ? items[idx].is_lsb = true : items[idx].is_msb = true; - if (j == end) sig->is_little_endian ? items[idx].is_msb = true : items[idx].is_lsb = true; - items[idx].bg_color = getColor(sig); - items[idx].sigs.push_back(sig); + if (j == start) sig.is_little_endian ? items[idx].is_lsb = true : items[idx].is_msb = true; + if (j == end) sig.is_little_endian ? items[idx].is_msb = true : items[idx].is_lsb = true; + items[idx].bg_color = getColor(&sig); + items[idx].sigs.push_back(&sig); } } } else { diff --git a/tools/cabana/binaryview.h b/tools/cabana/binaryview.h index 6743b6cfac..1d6d5d0b07 100644 --- a/tools/cabana/binaryview.h +++ b/tools/cabana/binaryview.h @@ -9,6 +9,8 @@ #include #include "tools/cabana/dbcmanager.h" +#include "tools/cabana/streams/abstractstream.h" +using namespace dbcmanager; class BinaryItemDelegate : public QStyledItemDelegate { public: @@ -49,7 +51,6 @@ public: std::vector items; std::optional msg_id; - const DBCMsg *dbc_msg = nullptr; int row_count = 0; const int column_count = 9; }; diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 9c5ed097c6..8515ffcfe7 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -459,7 +459,7 @@ void ChartView::updateTitle() { } for (auto &s : sigs) { auto decoration = s.series->isVisible() ? "none" : "line-through"; - s.series->setName(QString("%2 %3 %4").arg(decoration, s.sig->name.c_str(), msgName(s.msg_id), s.msg_id.toString())); + s.series->setName(QString("%2 %3 %4").arg(decoration, s.sig->name, msgName(s.msg_id), s.msg_id.toString())); } } @@ -672,7 +672,7 @@ void ChartView::mouseMoveEvent(QMouseEvent *ev) { value = QString::number(it->y()); track_pts[i] = chart()->mapToPosition(*it); } - text_list.push_back(QString("%2: %3").arg(sigs[i].series->color().name(), sigs[i].sig->name.c_str(), value)); + text_list.push_back(QString("%2: %3").arg(sigs[i].series->color().name(), sigs[i].sig->name, value)); } auto max = std::max_element(track_pts.begin(), track_pts.end(), [](auto &a, auto &b) { return a.x() < b.x(); }); auto pt = (max == track_pts.end()) ? ev->pos() : *max; @@ -887,7 +887,7 @@ void SeriesSelector::updateAvailableList(int index) { available_list->clear(); MessageId msg_id = msgs_combo->itemData(index).value(); auto selected_items = seletedItems(); - for (auto &[name, s] : dbc()->msg(msg_id)->sigs) { + for (auto &s : dbc()->msg(msg_id)->sigs) { bool is_selected = std::any_of(selected_items.begin(), selected_items.end(), [=, sig=&s](auto it) { return it->msg_id == msg_id && it->sig == sig; }); if (!is_selected) { addItemToList(available_list, msg_id, &s); @@ -896,7 +896,7 @@ void SeriesSelector::updateAvailableList(int index) { } void SeriesSelector::addItemToList(QListWidget *parent, const MessageId id, const Signal *sig, bool show_msg_name) { - QString text = QString(" %1").arg(getColor(sig).name(), sig->name.c_str()); + QString text = QString(" %1").arg(getColor(sig).name(), sig->name); if (show_msg_name) text += QString(" %0 %1").arg(msgName(id), id.toString()); QLabel *label = new QLabel(text); diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index 7569155d39..7089b4eaee 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -15,7 +15,7 @@ #include "tools/cabana/dbcmanager.h" #include "tools/cabana/streams/abstractstream.h" - +using namespace dbcmanager; using namespace QtCharts; const int CHART_MIN_WIDTH = 300; diff --git a/tools/cabana/commands.cc b/tools/cabana/commands.cc index b03f46b5d2..e03d6d1b11 100644 --- a/tools/cabana/commands.cc +++ b/tools/cabana/commands.cc @@ -36,7 +36,7 @@ RemoveMsgCommand::RemoveMsgCommand(const MessageId &id, QUndoCommand *parent) : void RemoveMsgCommand::undo() { if (!message.name.isEmpty()) { dbc()->updateMsg(id, message.name, message.size); - for (auto &[name, s] : message.sigs) + for (auto &s : message.sigs) dbc()->addSignal(id, s); } } @@ -50,31 +50,31 @@ void RemoveMsgCommand::redo() { AddSigCommand::AddSigCommand(const MessageId &id, const Signal &sig, QUndoCommand *parent) : id(id), signal(sig), QUndoCommand(parent) { - setText(QObject::tr("Add signal %1 to %2").arg(sig.name.c_str()).arg(id.address)); + setText(QObject::tr("Add signal %1 to %2").arg(sig.name).arg(id.address)); } -void AddSigCommand::undo() { dbc()->removeSignal(id, signal.name.c_str()); } +void AddSigCommand::undo() { dbc()->removeSignal(id, signal.name); } void AddSigCommand::redo() { dbc()->addSignal(id, signal); } // RemoveSigCommand RemoveSigCommand::RemoveSigCommand(const MessageId &id, const Signal *sig, QUndoCommand *parent) : id(id), signal(*sig), QUndoCommand(parent) { - setText(QObject::tr("Remove signal %1 from %2").arg(signal.name.c_str()).arg(id.address)); + setText(QObject::tr("Remove signal %1 from %2").arg(signal.name).arg(id.address)); } void RemoveSigCommand::undo() { dbc()->addSignal(id, signal); } -void RemoveSigCommand::redo() { dbc()->removeSignal(id, signal.name.c_str()); } +void RemoveSigCommand::redo() { dbc()->removeSignal(id, signal.name); } // EditSignalCommand EditSignalCommand::EditSignalCommand(const MessageId &id, const Signal *sig, const Signal &new_sig, QUndoCommand *parent) : id(id), old_signal(*sig), new_signal(new_sig), QUndoCommand(parent) { - setText(QObject::tr("Edit signal %1").arg(old_signal.name.c_str())); + setText(QObject::tr("Edit signal %1").arg(old_signal.name)); } -void EditSignalCommand::undo() { dbc()->updateSignal(id, new_signal.name.c_str(), old_signal); } -void EditSignalCommand::redo() { dbc()->updateSignal(id, old_signal.name.c_str(), new_signal); } +void EditSignalCommand::undo() { dbc()->updateSignal(id, new_signal.name, old_signal); } +void EditSignalCommand::redo() { dbc()->updateSignal(id, old_signal.name, new_signal); } namespace UndoStack { diff --git a/tools/cabana/commands.h b/tools/cabana/commands.h index 46e9f0a030..57df664974 100644 --- a/tools/cabana/commands.h +++ b/tools/cabana/commands.h @@ -4,6 +4,8 @@ #include #include "tools/cabana/dbcmanager.h" +#include "tools/cabana/streams/abstractstream.h" +using namespace dbcmanager; class EditMsgCommand : public QUndoCommand { public: @@ -25,7 +27,7 @@ public: private: const MessageId id; - DBCMsg message; + Msg message; }; class AddSigCommand : public QUndoCommand { diff --git a/tools/cabana/dbcmanager.cc b/tools/cabana/dbcmanager.cc index 27f16c71e5..ae3a65a99b 100644 --- a/tools/cabana/dbcmanager.cc +++ b/tools/cabana/dbcmanager.cc @@ -1,63 +1,114 @@ #include "tools/cabana/dbcmanager.h" +#include +#include +#include +#include +#include #include #include -#include -uint qHash(const MessageId &item) { - return qHash(item.source) ^ qHash(item.address); -} +namespace dbcmanager { -DBCManager::DBCManager(QObject *parent) : QObject(parent) {} - -DBCManager::~DBCManager() {} - -void DBCManager::open(const QString &dbc_file_name) { - dbc = const_cast(dbc_lookup(dbc_file_name.toStdString())); - initMsgMap(); +void sortSignalsByAddress(QList &sigs) { + std::sort(sigs.begin(), sigs.end(), [](auto &a, auto &b) { return a.start_bit < b.start_bit; }); } -bool DBCManager::open(const QString &name, const QString &content, QString *error) { - try { - std::istringstream stream(content.toStdString()); - dbc = const_cast(dbc_parse_from_stream(name.toStdString(), stream)); - initMsgMap(); - return true; - } catch (std::exception &e) { - if (error) *error = e.what(); +bool DBCManager::open(const QString &dbc_file_name, QString *error) { + QString opendbc_file_path = QString("%1/%2.dbc").arg(OPENDBC_FILE_PATH, dbc_file_name); + QFile file(opendbc_file_path); + if (file.open(QIODevice::ReadOnly)) { + return open(dbc_file_name, file.readAll(), error); } return false; } -void DBCManager::initMsgMap() { - msgs.clear(); - for (auto &msg : dbc->msgs) { - auto &m = msgs[msg.address]; - m.name = msg.name.c_str(); - m.size = msg.size; - for (auto &s : msg.sigs) - m.sigs[QString::fromStdString(s.name)] = s; +void DBCManager::parseExtraInfo(const QString &content) { + static QRegularExpression bo_regexp(R"(^BO_ (\w+) (\w+) *: (\w+) (\w+))"); + static QRegularExpression sg_regexp(R"(^SG_ (\w+) : (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*))"); + static QRegularExpression sgm_regexp(R"(^SG_ (\w+) (\w+) *: (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*))"); + static QRegularExpression sg_comment_regexp(R"(^CM_ SG_ *(\w+) *(\w+) *\"(.*)\";)"); + static QRegularExpression val_regexp(R"(VAL_ (\w+) (\w+) (.*);)"); + auto get_sig = [this](uint32_t address, const QString &name) -> Signal * { + auto m = (Msg *)msg(address); + return m ? (Signal *)m->sig(name) : nullptr; + }; + + QTextStream stream((QString *)&content); + uint32_t address = 0; + while (!stream.atEnd()) { + QString line = stream.readLine().trimmed(); + if (line.startsWith("BO_ ")) { + if (auto match = bo_regexp.match(line); match.hasMatch()) { + address = match.captured(1).toUInt(); + } + } else if (line.startsWith("SG_ ")) { + int offset = 0; + auto match = sg_regexp.match(line); + if (!match.hasMatch()) { + match = sgm_regexp.match(line); + offset = 1; + } + if (match.hasMatch()) { + if (auto s = get_sig(address, match.captured(1))) { + s->min = match.captured(8 + offset); + s->max = match.captured(9 + offset); + s->unit = match.captured(10 + offset); + } + } + } else if (line.startsWith("VAL_ ")) { + if (auto match = val_regexp.match(line); match.hasMatch()) { + if (auto s = get_sig(match.captured(1).toUInt(), match.captured(2))) { + QStringList desc_list = match.captured(3).trimmed().split('"'); + for (int i = 0; i < desc_list.size(); i += 2) { + auto val = desc_list[i].trimmed(); + if (!val.isEmpty() && (i + 1) < desc_list.size()) { + auto desc = desc_list[i+1].trimmed(); + s->val_desc.push_back({val, desc}); + } + } + } + } + } else if (line.startsWith("CM_ SG_ ")) { + if (auto match = sg_comment_regexp.match(line); match.hasMatch()) { + if (auto s = get_sig(match.captured(1).toUInt(), match.captured(2))) { + s->comment = match.captured(3).trimmed(); + } + } + } } - emit DBCFileChanged(); } QString DBCManager::generateDBC() { - QString dbc_string; + QString dbc_string, signal_comment, val_desc; for (auto &[address, m] : msgs) { dbc_string += QString("BO_ %1 %2: %3 XXX\n").arg(address).arg(m.name).arg(m.size); - for (auto &[name, sig] : m.sigs) { - dbc_string += QString(" SG_ %1 : %2|%3@%4%5 (%6,%7) [0|0] \"\" XXX\n") - .arg(name) + for (auto &sig : m.sigs) { + dbc_string += QString(" SG_ %1 : %2|%3@%4%5 (%6,%7) [%8|%9] \"%10\" XXX\n") + .arg(sig.name) .arg(sig.start_bit) .arg(sig.size) .arg(sig.is_little_endian ? '1' : '0') .arg(sig.is_signed ? '-' : '+') .arg(sig.factor, 0, 'g', std::numeric_limits::digits10) - .arg(sig.offset, 0, 'g', std::numeric_limits::digits10); + .arg(sig.offset, 0, 'g', std::numeric_limits::digits10) + .arg(sig.min) + .arg(sig.max) + .arg(sig.unit); + if (!sig.comment.isEmpty()) { + signal_comment += QString("CM_ SG_ %1 %2 \"%3\";\n").arg(address).arg(sig.name).arg(sig.comment); + } + if (!sig.val_desc.isEmpty()) { + QString text; + for (auto &[val, desc] : sig.val_desc) { + text += QString("%1 \"%2\"").arg(val, desc); + } + val_desc += QString("VAL_ %1 %2 %3;\n").arg(address).arg(sig.name).arg(text); + } } dbc_string += "\n"; } - return dbc_string; + return dbc_string + signal_comment + val_desc; } void DBCManager::updateMsg(const MessageId &id, const QString &name, uint32_t size) { @@ -73,31 +124,29 @@ void DBCManager::removeMsg(const MessageId &id) { } void DBCManager::addSignal(const MessageId &id, const Signal &sig) { - if (auto m = const_cast(msg(id.address))) { - auto &s = m->sigs[sig.name.c_str()]; - s = sig; - emit signalAdded(id.address, &s); + if (auto m = const_cast(msg(id.address))) { + m->sigs.push_back(sig); + auto s = &m->sigs.last(); + sortSignalsByAddress(m->sigs); + emit signalAdded(id.address, s); } } void DBCManager::updateSignal(const MessageId &id, const QString &sig_name, const Signal &sig) { - if (auto m = const_cast(msg(id))) { - // change key name - QString new_name = QString::fromStdString(sig.name); - auto node = m->sigs.extract(sig_name); - node.key() = new_name; - auto it = m->sigs.insert(std::move(node)); - auto &s = m->sigs[new_name]; - s = sig; - emit signalUpdated(&s); + if (auto m = const_cast(msg(id))) { + if (auto s = (Signal *)m->sig(sig_name)) { + *s = sig; + sortSignalsByAddress(m->sigs); + emit signalUpdated(s); + } } } void DBCManager::removeSignal(const MessageId &id, const QString &sig_name) { - if (auto m = const_cast(msg(id))) { - auto it = m->sigs.find(sig_name); + if (auto m = const_cast(msg(id))) { + auto it = std::find_if(m->sigs.begin(), m->sigs.end(), [&](auto &s) { return s.name == sig_name; }); if (it != m->sigs.end()) { - emit signalRemoved(&(it->second)); + emit signalRemoved(&(*it)); m->sigs.erase(it); } } @@ -108,16 +157,6 @@ DBCManager *dbc() { return &dbc_manager; } -// DBCMsg - -std::vector DBCMsg::getSignals() const { - std::vector ret; - ret.reserve(sigs.size()); - for (auto &[_, sig] : sigs) ret.push_back(&sig); - std::sort(ret.begin(), ret.end(), [](auto l, auto r) { return l->start_bit < r->start_bit; }); - return ret; -} - // helper functions static QVector BIG_ENDIAN_START_BITS = []() { @@ -128,13 +167,8 @@ static QVector BIG_ENDIAN_START_BITS = []() { return ret; }(); -int bigEndianStartBitsIndex(int start_bit) { - return BIG_ENDIAN_START_BITS[start_bit]; -} - -int bigEndianBitIndex(int index) { - return BIG_ENDIAN_START_BITS.indexOf(index); -} +int bigEndianStartBitsIndex(int start_bit) { return BIG_ENDIAN_START_BITS[start_bit]; } +int bigEndianBitIndex(int index) { return BIG_ENDIAN_START_BITS.indexOf(index); } double get_raw_value(uint8_t *data, size_t data_size, const Signal &sig) { int64_t val = 0; @@ -155,8 +189,7 @@ double get_raw_value(uint8_t *data, size_t data_size, const Signal &sig) { if (sig.is_signed) { val -= ((val >> (sig.size - 1)) & 0x1) ? (1ULL << sig.size) : 0; } - double value = val * sig.factor + sig.offset; - return value; + return val * sig.factor + sig.offset; } void updateSigSizeParamsFromRange(Signal &s, int start_bit, int size) { @@ -182,5 +215,50 @@ bool operator==(const Signal &l, const Signal &r) { l.start_bit == r.start_bit && l.msb == r.msb && l.lsb == r.lsb && l.is_signed == r.is_signed && l.is_little_endian == r.is_little_endian && - l.factor == r.factor && l.offset == r.offset; + l.factor == r.factor && l.offset == r.offset && + l.min == r.min && l.max == r.max && l.comment == r.comment && l.unit == r.unit && l.val_desc == r.val_desc; +} + +} // namespace dbcmanager + +#include "opendbc/can/common_dbc.h" +std::vector dbcmanager::DBCManager::allDBCNames() { return get_dbc_names(); } + +bool dbcmanager::DBCManager::open(const QString &name, const QString &content, QString *error) { + try { + std::istringstream stream(content.toStdString()); + auto dbc = const_cast(dbc_parse_from_stream(name.toStdString(), stream)); + msgs.clear(); + for (auto &msg : dbc->msgs) { + auto &m = msgs[msg.address]; + m.name = msg.name.c_str(); + m.size = msg.size; + for (auto &s : msg.sigs) { + m.sigs.push_back({}); + auto &sig = m.sigs.last(); + sig.name = s.name.c_str(); + sig.start_bit = s.start_bit; + sig.msb = s.msb; + sig.lsb = s.lsb; + sig.size = s.size; + sig.is_signed = s.is_signed; + sig.factor = s.factor; + sig.offset = s.offset; + sig.is_little_endian = s.is_little_endian; + } + sortSignalsByAddress(m.sigs); + } + parseExtraInfo(content); + name_ = name; + emit DBCFileChanged(); + delete dbc; + } catch (std::exception &e) { + if (error) *error = e.what(); + return false; + } + return true; +} + +uint qHash(const MessageId &item) { + return qHash(item.source) ^ qHash(item.address); } diff --git a/tools/cabana/dbcmanager.h b/tools/cabana/dbcmanager.h index b766c837b6..b438b3e1c2 100644 --- a/tools/cabana/dbcmanager.h +++ b/tools/cabana/dbcmanager.h @@ -1,9 +1,8 @@ #pragma once #include -#include +#include #include -#include "opendbc/can/common_dbc.h" struct MessageId { uint8_t source; @@ -30,40 +29,55 @@ struct MessageId { } }; +uint qHash(const MessageId &item); Q_DECLARE_METATYPE(MessageId); -uint qHash(const MessageId &item); +namespace dbcmanager { + +typedef QList> ValueDescription; + +struct Signal { + QString name; + int start_bit, msb, lsb, size; + bool is_signed; + double factor, offset; + bool is_little_endian; + QString min, max, unit; + QString comment; + ValueDescription val_desc; +}; -struct DBCMsg { +struct Msg { QString name; uint32_t size; - // signal must be saved as value in map to make undo stack work properly. - std::map sigs; - // return vector, sort by start_bits - std::vector getSignals() const; + QList sigs; + + const Signal *sig(const QString &sig_name) const { + auto it = std::find_if(sigs.begin(), sigs.end(), [&](auto &s) { return s.name == sig_name; }); + return it != sigs.end() ? &(*it) : nullptr; + } }; class DBCManager : public QObject { Q_OBJECT public: - DBCManager(QObject *parent); - ~DBCManager(); - - void open(const QString &dbc_file_name); + DBCManager(QObject *parent) {} + ~DBCManager() {} + bool open(const QString &dbc_file_name, QString *error = nullptr); bool open(const QString &name, const QString &content, QString *error = nullptr); QString generateDBC(); void addSignal(const MessageId &id, const Signal &sig); void updateSignal(const MessageId &id, const QString &sig_name, const Signal &sig); void removeSignal(const MessageId &id, const QString &sig_name); - inline static std::vector allDBCNames() { return get_dbc_names(); } - inline QString name() const { return dbc ? dbc->name.c_str() : ""; } + static std::vector allDBCNames(); + inline QString name() const { return name_; } void updateMsg(const MessageId &id, const QString &name, uint32_t size); void removeMsg(const MessageId &id); - inline const std::map &messages() const { return msgs; } - inline const DBCMsg *msg(const MessageId &id) const { return msg(id.address); } - inline const DBCMsg *msg(uint32_t address) const { + inline const std::map &messages() const { return msgs; } + inline const Msg *msg(const MessageId &id) const { return msg(id.address); } + inline const Msg *msg(uint32_t address) const { auto it = msgs.find(address); return it != msgs.end() ? &it->second : nullptr; } @@ -77,9 +91,9 @@ signals: void DBCFileChanged(); private: - void initMsgMap(); - DBC *dbc = nullptr; - std::map msgs; + void parseExtraInfo(const QString &content); + std::map msgs; + QString name_; }; const QString UNTITLED = "untitled"; @@ -97,3 +111,5 @@ inline QString msgName(const MessageId &id) { auto msg = dbc()->msg(id); return msg ? msg->name : UNTITLED; } + +} // namespace dbcmanager diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index f290b3b706..882ea932c6 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -6,8 +6,6 @@ #include #include "tools/cabana/commands.h" -#include "tools/cabana/dbcmanager.h" -#include "tools/cabana/streams/abstractstream.h" // DetailWidget @@ -153,13 +151,13 @@ void DetailWidget::refresh() { if (!msg_id) return; QStringList warnings; - const DBCMsg *msg = dbc()->msg(*msg_id); + auto msg = dbc()->msg(*msg_id); if (msg) { if (msg->size != can->lastMessage(*msg_id).dat.size()) { warnings.push_back(tr("Message size (%1) is incorrect.").arg(msg->size)); } for (auto s : binary_view->getOverlappingSignals()) { - warnings.push_back(tr("%1 has overlapping bits.").arg(s->name.c_str())); + warnings.push_back(tr("%1 has overlapping bits.").arg(s->name)); } } else { warnings.push_back(tr("Drag-Select in binary view to create new signal.")); diff --git a/tools/cabana/historylog.cc b/tools/cabana/historylog.cc index 87968d495c..a1c671b68d 100644 --- a/tools/cabana/historylog.cc +++ b/tools/cabana/historylog.cc @@ -5,6 +5,7 @@ #include #include "tools/cabana/commands.h" +#include "tools/cabana/util.h" // HistoryLogModel @@ -30,7 +31,7 @@ void HistoryLogModel::refresh() { beginResetModel(); sigs.clear(); if (auto dbc_msg = dbc()->msg(*msg_id)) { - sigs = dbc_msg->getSignals(); + sigs = dbc_msg->sigs; } last_fetch_time = 0; has_more_data = true; @@ -47,9 +48,9 @@ QVariant HistoryLogModel::headerData(int section, Qt::Orientation orientation, i if (section == 0) { return "Time"; } - return show_signals ? QString::fromStdString(sigs[section - 1]->name).replace('_', ' ') : "Data"; + return show_signals ? sigs[section - 1].name : "Data"; } else if (role == Qt::BackgroundRole && section > 0 && show_signals) { - return QBrush(getColor(sigs[section - 1])); + return QBrush(getColor(&sigs[section - 1])); } } return {}; @@ -113,7 +114,7 @@ std::deque HistoryLogModel::fetchData(InputIt first, I if (msg_id->address == c.getAddress() && msg_id->source == c.getSrc()) { const auto dat = c.getDat(); for (int i = 0; i < sigs.size(); ++i) { - values[i] = get_raw_value((uint8_t *)dat.begin(), dat.size(), *(sigs[i])); + values[i] = get_raw_value((uint8_t *)dat.begin(), dat.size(), sigs[i]); } if (!filter_cmp || filter_cmp(values[filter_sig_idx], filter_value)) { auto &m = msgs.emplace_back(); @@ -170,8 +171,8 @@ QSize HeaderView::sectionSizeFromContents(int logicalIndex) const { return time_col_size; } else { int default_size = qMax(100, (rect().width() - time_col_size.width()) / (model()->columnCount() - 1)); - const QString text = model()->headerData(logicalIndex, this->orientation(), Qt::DisplayRole).toString(); - const QRect rect = fontMetrics().boundingRect({0, 0, default_size, 2000}, defaultAlignment(), text); + QString text = model()->headerData(logicalIndex, this->orientation(), Qt::DisplayRole).toString(); + const QRect rect = fontMetrics().boundingRect({0, 0, default_size, 2000}, defaultAlignment(), text.replace(QChar('_'), ' ')); QSize size = rect.size() + QSize{10, 6}; return QSize{qMax(size.width(), default_size), size.height()}; } @@ -183,7 +184,7 @@ void HeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalI painter->fillRect(rect, bg_role.value()); } QString text = model()->headerData(logicalIndex, Qt::Horizontal, Qt::DisplayRole).toString(); - painter->drawText(rect.adjusted(5, 3, -5, -3), defaultAlignment(), text); + painter->drawText(rect.adjusted(5, 3, -5, -3), defaultAlignment(), text.replace(QChar('_'), ' ')); } // LogsWidget @@ -253,8 +254,8 @@ void LogsWidget::refresh() { bool has_signal = model->sigs.size(); if (has_signal) { signals_cb->clear(); - for (auto s : model->sigs) { - signals_cb->addItem(s->name.c_str()); + for (auto &s : model->sigs) { + signals_cb->addItem(s.name); } } value_edit->clear(); diff --git a/tools/cabana/historylog.h b/tools/cabana/historylog.h index 00a8f73836..a1b7bc0098 100644 --- a/tools/cabana/historylog.h +++ b/tools/cabana/historylog.h @@ -11,6 +11,7 @@ #include "tools/cabana/dbcmanager.h" #include "tools/cabana/streams/abstractstream.h" +using namespace dbcmanager; class HeaderView : public QHeaderView { public: @@ -63,7 +64,7 @@ public: uint64_t last_fetch_time = 0; std::function filter_cmp = nullptr; std::deque messages; - std::vector sigs; + QList sigs; bool dynamic_mode = true; bool display_signals_mode = true; }; diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index 81ebc6af20..42ac22865c 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -7,8 +7,6 @@ #include #include -#include "tools/cabana/dbcmanager.h" - MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { QVBoxLayout *main_layout = new QVBoxLayout(this); @@ -140,8 +138,8 @@ void MessageListModel::setFilterString(const QString &string) { if (id.toString().contains(txt, cs) || msgName(id).contains(txt, cs)) return true; // Search by signal name if (const auto msg = dbc()->msg(id)) { - for (auto &signal : msg->getSignals()) { - if (QString::fromStdString(signal->name).contains(txt, cs)) return true; + for (auto &signal : msg->sigs) { + if (signal.name.contains(txt, cs)) return true; } } return false; diff --git a/tools/cabana/messageswidget.h b/tools/cabana/messageswidget.h index 562069c3ae..d88def3acb 100644 --- a/tools/cabana/messageswidget.h +++ b/tools/cabana/messageswidget.h @@ -9,7 +9,9 @@ #include #include +#include "tools/cabana/dbcmanager.h" #include "tools/cabana/streams/abstractstream.h" +using namespace dbcmanager; class MessageListModel : public QAbstractTableModel { Q_OBJECT diff --git a/tools/cabana/signaledit.cc b/tools/cabana/signaledit.cc index e2be5c85d0..d5099da6f4 100644 --- a/tools/cabana/signaledit.cc +++ b/tools/cabana/signaledit.cc @@ -1,5 +1,6 @@ #include "tools/cabana/signaledit.h" +#include #include #include #include @@ -23,9 +24,9 @@ SignalModel::SignalModel(QObject *parent) : root(new Item), QAbstractItemModel(p } void SignalModel::insertItem(SignalModel::Item *parent_item, int pos, const Signal *sig) { - Item *item = new Item{.sig = sig, .parent = parent_item, .title = sig->name.c_str(), .type = Item::Sig}; + Item *item = new Item{.sig = sig, .parent = parent_item, .title = sig->name, .type = Item::Sig}; parent_item->children.insert(pos, item); - QString titles[]{"Name", "Size", "Little Endian", "Signed", "Offset", "Factor", "Extra Info", "Unit", "Comment", "Minimum", "Maximum", "Description"}; + QString titles[]{"Name", "Size", "Little Endian", "Signed", "Offset", "Factor", "Extra Info", "Unit", "Comment", "Minimum Value", "Maximum Value", "Value Descriptions"}; for (int i = 0; i < std::size(titles); ++i) { item->children.push_back(new Item{.sig = sig, .parent = item, .title = titles[i], .type = (Item::Type)(i + Item::Name)}); } @@ -47,9 +48,9 @@ void SignalModel::refresh() { beginResetModel(); root.reset(new SignalModel::Item); if (auto msg = dbc()->msg(msg_id)) { - for (auto &s : msg->getSignals()) { - if (filter_str.isEmpty() || QString::fromStdString(s->name).contains(filter_str, Qt::CaseInsensitive)) { - insertItem(root.get(), root->children.size(), s); + for (auto &s : msg->sigs) { + if (filter_str.isEmpty() || s.name.contains(filter_str, Qt::CaseInsensitive)) { + insertItem(root.get(), root->children.size(), &s); } } } @@ -115,14 +116,25 @@ QVariant SignalModel::data(const QModelIndex &index, int role) const { const Item *item = getItem(index); if (role == Qt::DisplayRole || role == Qt::EditRole) { if (index.column() == 0) { - return item->type == Item::Sig ? QString::fromStdString(item->sig->name) : item->title; + return item->type == Item::Sig ? item->sig->name : item->title; } else { switch (item->type) { case Item::Sig: return item->sig_val; - case Item::Name: return QString::fromStdString(item->sig->name); + case Item::Name: return item->sig->name; case Item::Size: return item->sig->size; case Item::Offset: return QString::number(item->sig->offset, 'f', 6); case Item::Factor: return QString::number(item->sig->factor, 'f', 6); + case Item::Unit: return item->sig->unit; + case Item::Comment: return item->sig->comment; + case Item::Min: return item->sig->min; + case Item::Max: return item->sig->max; + case Item::Desc: { + QString val_desc; + for (auto &[val, desc] : item->sig->val_desc) { + val_desc += QString("%1 \"%2\"").arg(val, desc); + } + return val_desc; + } default: break; } } @@ -142,12 +154,17 @@ bool SignalModel::setData(const QModelIndex &index, const QVariant &value, int r Item *item = getItem(index); Signal s = *item->sig; switch (item->type) { - case Item::Name: s.name = value.toString().toStdString(); break; + case Item::Name: s.name = value.toString(); break; case Item::Size: s.size = value.toInt(); break; case Item::Endian: s.is_little_endian = value.toBool(); break; case Item::Signed: s.is_signed = value.toBool(); break; case Item::Offset: s.offset = value.toDouble(); break; case Item::Factor: s.factor = value.toDouble(); break; + case Item::Unit: s.unit = value.toString(); break; + case Item::Comment: s.comment = value.toString(); break; + case Item::Min: s.min = value.toString(); break; + case Item::Max: s.max = value.toString(); break; + case Item::Desc: s.val_desc = value.value(); break; default: return false; } bool ret = saveSignal(item->sig, s); @@ -172,8 +189,8 @@ void SignalModel::showExtraInfo(const QModelIndex &index) { bool SignalModel::saveSignal(const Signal *origin_s, Signal &s) { auto msg = dbc()->msg(msg_id); - if (s.name != origin_s->name && msg->sigs.count(s.name.c_str()) != 0) { - QString text = tr("There is already a signal with the same name '%1'").arg(s.name.c_str()); + if (s.name != origin_s->name && msg->sig(s.name) != nullptr) { + QString text = tr("There is already a signal with the same name '%1'").arg(s.name); QMessageBox::warning(nullptr, tr("Failed to save signal"), text); return false; } @@ -212,8 +229,8 @@ void SignalModel::addSignal(int start_bit, int size, bool little_endian) { Signal sig = {.is_little_endian = little_endian, .factor = 1}; for (int i = 1; /**/; ++i) { - sig.name = "NEW_SIGNAL_" + std::to_string(i); - if (msg->sigs.count(sig.name.c_str()) == 0) break; + sig.name = QString("NEW_SIGNAL_%1").arg(i); + if (msg->sig(sig.name) == nullptr) break; } updateSigSizeParamsFromRange(sig, start_bit, size); UndoStack::push(new AddSigCommand(msg_id, sig)); @@ -266,8 +283,8 @@ void SignalModel::handleSignalRemoved(const Signal *sig) { SignalItemDelegate::SignalItemDelegate(QObject *parent) { name_validator = new NameValidator(this); double_validator = new QDoubleValidator(this); - small_font.setPointSize(8); double_validator->setLocale(QLocale::C); // Match locale of QString::toDouble() instead of system + small_font.setPointSize(8); } void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { @@ -304,7 +321,8 @@ void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op QWidget *SignalItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { auto item = (SignalModel::Item *)index.internalPointer(); - if (item->type == SignalModel::Item::Name || item->type == SignalModel::Item::Offset || item->type == SignalModel::Item::Factor) { + if (item->type == SignalModel::Item::Name || item->type == SignalModel::Item::Offset || + item->type == SignalModel::Item::Factor || item->type == SignalModel::Item::Min || item->type == SignalModel::Item::Max) { QLineEdit *e = new QLineEdit(parent); e->setFrame(false); e->setValidator(index.row() == 0 ? name_validator : double_validator); @@ -314,6 +332,13 @@ QWidget *SignalItemDelegate::createEditor(QWidget *parent, const QStyleOptionVie spin->setFrame(false); spin->setRange(1, 64); return spin; + } else if (item->type == SignalModel::Item::Desc) { + ValueDescriptionDlg dlg(item->sig->val_desc, parent); + dlg.setWindowTitle(item->sig->name); + if (dlg.exec()) { + ((QAbstractItemModel *)index.model())->setData(index, QVariant::fromValue(dlg.val_desc)); + } + return nullptr; } return QStyledItemDelegate::createEditor(parent, option, index); } @@ -437,7 +462,7 @@ void SignalView::expandSignal(const Signal *sig) { void SignalView::updateChartState() { int i = 0; for (auto item : model->root->children) { - auto plot_btn = tree->indexWidget(model->index(i, 1))->findChildren()[0]; + auto plot_btn = tree->indexWidget(model->index(i, 1))->findChildren()[0]; bool chart_opened = charts->hasSignal(msg_id, item->sig); plot_btn->setChecked(chart_opened); plot_btn->setToolTip(chart_opened ? tr("Close Plot") : tr("Show Plot\nSHIFT click to add to previous opened plot")); @@ -459,3 +484,70 @@ void SignalView::leaveEvent(QEvent *event) { emit highlight(nullptr); QWidget::leaveEvent(event); } + +// ValueDescriptionDlg + +ValueDescriptionDlg::ValueDescriptionDlg(const ValueDescription &descriptions, QWidget *parent) : QDialog(parent) { + QHBoxLayout *toolbar_layout = new QHBoxLayout(); + QPushButton *add = new QPushButton(utils::icon("plus"), ""); + QPushButton *remove = new QPushButton(utils::icon("dash"), ""); + remove->setEnabled(false); + toolbar_layout->addWidget(add); + toolbar_layout->addWidget(remove); + toolbar_layout->addStretch(0); + + table = new QTableWidget(descriptions.size(), 2, this); + table->setItemDelegate(new Delegate(this)); + table->setHorizontalHeaderLabels({"Value", "Description"}); + table->horizontalHeader()->setStretchLastSection(true); + table->setSelectionBehavior(QAbstractItemView::SelectRows); + table->setSelectionMode(QAbstractItemView::SingleSelection); + table->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed); + table->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + int row = 0; + for (auto &[val, desc] : descriptions) { + table->setItem(row, 0, new QTableWidgetItem(val)); + table->setItem(row, 1, new QTableWidgetItem(desc)); + ++row; + } + + auto btn_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->addLayout(toolbar_layout); + main_layout->addWidget(table); + main_layout->addWidget(btn_box); + setMinimumWidth(500); + + QObject::connect(btn_box, &QDialogButtonBox::accepted, this, &ValueDescriptionDlg::save); + QObject::connect(btn_box, &QDialogButtonBox::rejected, this, &QDialog::reject); + QObject::connect(add, &QPushButton::clicked, [this]() { + table->setRowCount(table->rowCount() + 1); + table->setItem(table->rowCount() - 1, 0, new QTableWidgetItem); + table->setItem(table->rowCount() - 1, 1, new QTableWidgetItem); + }); + QObject::connect(remove, &QPushButton::clicked, [this]() { table->removeRow(table->currentRow()); }); + QObject::connect(table, &QTableWidget::itemSelectionChanged, [=]() { + remove->setEnabled(table->currentRow() != -1); + }); +} + +void ValueDescriptionDlg::save() { + for (int i = 0; i < table->rowCount(); ++i) { + QString val = table->item(i, 0)->text().trimmed(); + QString desc = table->item(i, 1)->text().trimmed(); + if (!val.isEmpty() && !desc.isEmpty()) { + val_desc.push_back({val, desc}); + } + } + QDialog::accept(); +} + +QWidget *ValueDescriptionDlg::Delegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { + QLineEdit *edit = new QLineEdit(parent); + edit->setFrame(false); + if (index.column() == 0) { + edit->setValidator(new QIntValidator(edit)); + } + return edit; +} diff --git a/tools/cabana/signaledit.h b/tools/cabana/signaledit.h index 7e5015f707..efdc653584 100644 --- a/tools/cabana/signaledit.h +++ b/tools/cabana/signaledit.h @@ -4,11 +4,10 @@ #include #include #include +#include #include #include "tools/cabana/chartswidget.h" -#include "tools/cabana/dbcmanager.h" -#include "tools/cabana/streams/abstractstream.h" class SignalModel : public QAbstractItemModel { Q_OBJECT @@ -62,6 +61,21 @@ private: friend class SignalView; }; +class ValueDescriptionDlg : public QDialog { +public: + ValueDescriptionDlg(const ValueDescription &descriptions, QWidget *parent); + ValueDescription val_desc; + +private: + struct Delegate : public QStyledItemDelegate { + Delegate(QWidget *parent) : QStyledItemDelegate(parent) {} + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + }; + + void save(); + QTableWidget *table; +}; + class SignalItemDelegate : public QStyledItemDelegate { public: SignalItemDelegate(QObject *parent); diff --git a/tools/cabana/streams/replaystream.cc b/tools/cabana/streams/replaystream.cc index b768b94327..ebd969be61 100644 --- a/tools/cabana/streams/replaystream.cc +++ b/tools/cabana/streams/replaystream.cc @@ -1,7 +1,5 @@ #include "tools/cabana/streams/replaystream.h" -#include "tools/cabana/dbcmanager.h" - ReplayStream::ReplayStream(uint32_t replay_flags, QObject *parent) : replay_flags(replay_flags), AbstractStream(parent, false) { QObject::connect(&settings, &Settings::changed, [this]() { if (replay) replay->setSegmentCacheLimit(settings.max_cached_minutes); diff --git a/tools/cabana/streams/replaystream.h b/tools/cabana/streams/replaystream.h index 69fb738ab8..3505f6abf4 100644 --- a/tools/cabana/streams/replaystream.h +++ b/tools/cabana/streams/replaystream.h @@ -1,6 +1,5 @@ #pragma once -#include "opendbc/can/common_dbc.h" #include "tools/cabana/streams/abstractstream.h" #include "tools/cabana/settings.h" diff --git a/tools/cabana/tests/test_cabana.cc b/tools/cabana/tests/test_cabana.cc index 586422ffc8..9a8ab710bc 100644 --- a/tools/cabana/tests/test_cabana.cc +++ b/tools/cabana/tests/test_cabana.cc @@ -2,8 +2,10 @@ #include "opendbc/can/common.h" #undef INFO #include "catch2/catch.hpp" -#include "tools/cabana/dbcmanager.h" #include "tools/replay/logreader.h" +#include "tools/cabana/dbcmanager.h" +#include "tools/cabana/streams/abstractstream.h" +using namespace dbcmanager; // demo route, first segment const std::string TEST_RLOG_URL = "https://commadata2.blob.core.windows.net/commadata2/4cf7a6ad03080c90/2021-09-29--13-46-36/0/rlog.bz2"; @@ -18,12 +20,13 @@ TEST_CASE("DBCManager::generateDBC") { auto &new_msgs = dbc_from_generated.messages(); REQUIRE(msgs.size() == new_msgs.size()); for (auto &[address, m] : msgs) { - auto new_m = new_msgs.at(address); + auto &new_m = new_msgs.at(address); REQUIRE(m.name == new_m.name); REQUIRE(m.size == new_m.size); REQUIRE(m.sigs.size() == new_m.sigs.size()); - for (auto &[name, sig] : m.sigs) - REQUIRE(sig == new_m.sigs[name]); + for (int i = 0; i < m.sigs.size(); ++i) { + REQUIRE(m.sigs[i] == new_m.sigs[i]); + } } } @@ -37,13 +40,13 @@ TEST_CASE("Parse can messages") { REQUIRE(log.events.size() > 0); for (auto e : log.events) { if (e->which == cereal::Event::Which::CAN) { - std::map, std::vector> values_1; + std::map, std::vector> values_1; for (const auto &c : e->event.getCan()) { const auto msg = dbc.msg(c.getAddress()); if (c.getSrc() == 0 && msg) { - for (auto &[name, sig] : msg->sigs) { + for (auto &sig : msg->sigs) { double val = get_raw_value((uint8_t *)c.getDat().begin(), c.getDat().size(), sig); - values_1[{c.getAddress(), name.toStdString()}].push_back(val); + values_1[{c.getAddress(), sig.name}].push_back(val); } } } @@ -53,7 +56,7 @@ TEST_CASE("Parse can messages") { for (auto &[key, v1] : values_1) { bool found = false; for (auto &v2 : values_2) { - if (v2.address == key.first && v2.name == key.second) { + if (v2.address == key.first && key.second == v2.name.c_str()) { REQUIRE(v2.all_values.size() == v1.size()); REQUIRE(v2.all_values == v1); found = true; diff --git a/tools/cabana/tools/findsimilarbits.cc b/tools/cabana/tools/findsimilarbits.cc index ffb0e54b0e..27cb7bced8 100644 --- a/tools/cabana/tools/findsimilarbits.cc +++ b/tools/cabana/tools/findsimilarbits.cc @@ -9,6 +9,7 @@ #include "tools/cabana/dbcmanager.h" #include "tools/cabana/streams/abstractstream.h" +using namespace dbcmanager; FindSimilarBitsDlg::FindSimilarBitsDlg(QWidget *parent) : QDialog(parent, Qt::WindowFlags() | Qt::Window) { setWindowTitle(tr("Find similar bits")); diff --git a/tools/cabana/tools/findsimilarbits.h b/tools/cabana/tools/findsimilarbits.h index 53d7806a8f..ba9b063baf 100644 --- a/tools/cabana/tools/findsimilarbits.h +++ b/tools/cabana/tools/findsimilarbits.h @@ -7,6 +7,7 @@ #include #include "tools/cabana/dbcmanager.h" +using namespace dbcmanager; class FindSimilarBitsDlg : public QDialog { Q_OBJECT diff --git a/tools/cabana/util.cc b/tools/cabana/util.cc index 9843a3f00c..6415cc8e16 100644 --- a/tools/cabana/util.cc +++ b/tools/cabana/util.cc @@ -106,7 +106,7 @@ QColor getColor(const Signal *sig) { float h = 19 * (float)sig->lsb / 64.0; h = fmod(h, 1.0); - size_t hash = qHash(QString::fromStdString(sig->name)); + size_t hash = qHash(sig->name); float s = 0.25 + 0.25 * (float)(hash & 0xff) / 255.0; float v = 0.75 + 0.25 * (float)((hash >> 8) & 0xff) / 255.0; diff --git a/tools/cabana/util.h b/tools/cabana/util.h index eb5203fb0b..2717451061 100644 --- a/tools/cabana/util.h +++ b/tools/cabana/util.h @@ -9,7 +9,9 @@ #include #include -#include "opendbc/can/common_dbc.h" +#include "tools/cabana/dbcmanager.h" +using namespace dbcmanager; + class ChangeTracker { public: @@ -37,7 +39,7 @@ public: inline QString toHex(const QByteArray &dat) { return dat.toHex(' ').toUpper(); } inline char toHex(uint value) { return "0123456789ABCDEF"[value & 0xF]; } -QColor getColor(const Signal *sig); +QColor getColor(const dbcmanager::Signal *sig); class NameValidator : public QRegExpValidator { Q_OBJECT diff --git a/tools/cabana/videowidget.h b/tools/cabana/videowidget.h index 51197eedd6..1a7e27f1b3 100644 --- a/tools/cabana/videowidget.h +++ b/tools/cabana/videowidget.h @@ -11,7 +11,9 @@ #include "selfdrive/ui/qt/widgets/cameraview.h" #include "selfdrive/ui/qt/widgets/controls.h" +#include "tools/cabana/dbcmanager.h" #include "tools/cabana/streams/abstractstream.h" +using namespace dbcmanager; class Slider : public QSlider { Q_OBJECT From eb8bdc0026b49de2fc5107746baeadbd42f58550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Fri, 17 Feb 2023 19:58:30 -0800 Subject: [PATCH 430/484] MPC path in UI (#27380) * 10s lat * Full length MPC * redfine N * Leave controls the same for now * Updates * use long plan in lat plan * interp plan * simplergit add selfdrive/controls/plannerd.py selfdrive/controls/ * expand to 10s * revert this * fix linter * vizualize * fix long test * typo * cleanup * compiles * unused * unused * bump cereal * bump cereal * use model if no uiplanm * update replay * update ref commit * bump cereal to master --- cereal | 2 +- .../controls/lib/longitudinal_planner.py | 3 ++- selfdrive/controls/plannerd.py | 20 +++++++++++++++-- selfdrive/modeld/models/driving.cc | 4 ++-- .../test/longitudinal_maneuvers/plant.py | 6 ++--- .../test/process_replay/process_replay.py | 2 +- selfdrive/test/process_replay/ref_commit | 2 +- selfdrive/ui/qt/maps/map_helpers.cc | 2 +- selfdrive/ui/qt/maps/map_helpers.h | 2 +- selfdrive/ui/qt/onroad.cc | 2 +- selfdrive/ui/ui.cc | 22 ++++++++++++------- selfdrive/ui/ui.h | 10 +++++---- 12 files changed, 51 insertions(+), 26 deletions(-) diff --git a/cereal b/cereal index 162a26ca2d..b88523f05a 160000 --- a/cereal +++ b/cereal @@ -1 +1 @@ -Subproject commit 162a26ca2d7e5bc9a42bb5ea11e98194f722027b +Subproject commit b88523f05ac958f87a8f6d57c3f4bb20da55f216 diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index 0febfbafd9..2ef9051122 100755 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -121,7 +121,8 @@ class LongitudinalPlanner: x, v, a, j = self.parse_model(sm['modelV2'], self.v_model_error) self.mpc.update(sm['radarState'], v_cruise, x, v, a, j) - self.v_desired_trajectory = np.interp(T_IDXS[:CONTROL_N], T_IDXS_MPC, self.mpc.v_solution) + self.v_desired_trajectory_full = np.interp(T_IDXS, T_IDXS_MPC, self.mpc.v_solution) + self.v_desired_trajectory = self.v_desired_trajectory_full[:CONTROL_N] self.a_desired_trajectory = np.interp(T_IDXS[:CONTROL_N], T_IDXS_MPC, self.mpc.a_solution) self.j_desired_trajectory = np.interp(T_IDXS[:CONTROL_N], T_IDXS_MPC[:-1], self.mpc.j_solution) diff --git a/selfdrive/controls/plannerd.py b/selfdrive/controls/plannerd.py index 93d0c80dac..543274d841 100755 --- a/selfdrive/controls/plannerd.py +++ b/selfdrive/controls/plannerd.py @@ -1,12 +1,28 @@ #!/usr/bin/env python3 +import numpy as np from cereal import car from common.params import Params from common.realtime import Priority, config_realtime_process from system.swaglog import cloudlog +from selfdrive.modeld.constants import T_IDXS from selfdrive.controls.lib.longitudinal_planner import LongitudinalPlanner from selfdrive.controls.lib.lateral_planner import LateralPlanner import cereal.messaging as messaging +def cumtrapz(x, t): + return np.concatenate([[0], np.cumsum(((x[0:-1] + x[1:])/2) * np.diff(t))]) + +def publish_ui_plan(sm, pm, lateral_planner, longitudinal_planner): + plan_odo = cumtrapz(longitudinal_planner.v_desired_trajectory_full, T_IDXS) + model_odo = cumtrapz(lateral_planner.v_plan, T_IDXS) + + ui_send = messaging.new_message('uiPlan') + ui_send.valid = sm.all_checks(service_list=['carState', 'controlsState', 'modelV2']) + uiPlan = ui_send.uiPlan + uiPlan.position.x = np.interp(plan_odo, model_odo, lateral_planner.lat_mpc.x_sol[:,0]).tolist() + uiPlan.position.y = np.interp(plan_odo, model_odo, lateral_planner.lat_mpc.x_sol[:,1]).tolist() + uiPlan.position.z = np.interp(plan_odo, model_odo, lateral_planner.path_xyz[:,2]).tolist() + pm.send('uiPlan', ui_send) def plannerd_thread(sm=None, pm=None): config_realtime_process(5, Priority.CTRL_LOW) @@ -24,7 +40,7 @@ def plannerd_thread(sm=None, pm=None): poll=['radarState', 'modelV2'], ignore_avg_freq=['radarState']) if pm is None: - pm = messaging.PubMaster(['longitudinalPlan', 'lateralPlan']) + pm = messaging.PubMaster(['longitudinalPlan', 'lateralPlan', 'uiPlan']) while True: sm.update() @@ -34,7 +50,7 @@ def plannerd_thread(sm=None, pm=None): lateral_planner.publish(sm, pm) longitudinal_planner.update(sm) longitudinal_planner.publish(sm, pm) - + publish_ui_plan(sm, pm, lateral_planner, longitudinal_planner) def main(sm=None, pm=None): plannerd_thread(sm, pm) diff --git a/selfdrive/modeld/models/driving.cc b/selfdrive/modeld/models/driving.cc index ac101bfee7..5538d6ff9b 100644 --- a/selfdrive/modeld/models/driving.cc +++ b/selfdrive/modeld/models/driving.cc @@ -200,7 +200,7 @@ void fill_meta(cereal::ModelDataV2::MetaData::Builder meta, const ModelOutputMet } template -void fill_xyzt(cereal::ModelDataV2::XYZTData::Builder xyzt, const std::array &t, +void fill_xyzt(cereal::XYZTData::Builder xyzt, const std::array &t, const std::array &x, const std::array &y, const std::array &z) { xyzt.setT(to_kj_array_ptr(t)); xyzt.setX(to_kj_array_ptr(x)); @@ -209,7 +209,7 @@ void fill_xyzt(cereal::ModelDataV2::XYZTData::Builder xyzt, const std::array -void fill_xyzt(cereal::ModelDataV2::XYZTData::Builder xyzt, const std::array &t, +void fill_xyzt(cereal::XYZTData::Builder xyzt, const std::array &t, const std::array &x, const std::array &y, const std::array &z, const std::array &x_std, const std::array &y_std, const std::array &z_std) { fill_xyzt(xyzt, t, x, y, z); diff --git a/selfdrive/test/longitudinal_maneuvers/plant.py b/selfdrive/test/longitudinal_maneuvers/plant.py index bd0556aa07..8febbf4022 100755 --- a/selfdrive/test/longitudinal_maneuvers/plant.py +++ b/selfdrive/test/longitudinal_maneuvers/plant.py @@ -99,13 +99,13 @@ class Plant: # Simulate model predicting slightly faster speed # this is to ensure lead policy is effective when model # does not predict slowdown in e2e mode - position = log.ModelDataV2.XYZTData.new_message() + position = log.XYZTData.new_message() position.x = [float(x) for x in (self.speed + 0.5) * np.array(T_IDXS)] model.modelV2.position = position - velocity = log.ModelDataV2.XYZTData.new_message() + velocity = log.XYZTData.new_message() velocity.x = [float(x) for x in (self.speed + 0.5) * np.ones_like(T_IDXS)] model.modelV2.velocity = velocity - acceleration = log.ModelDataV2.XYZTData.new_message() + acceleration = log.XYZTData.new_message() acceleration.x = [float(x) for x in np.zeros_like(T_IDXS)] model.modelV2.acceleration = acceleration diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 28fc9c452c..7dc0b139a3 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -292,7 +292,7 @@ CONFIGS = [ ProcessConfig( proc_name="plannerd", pub_sub={ - "modelV2": ["lateralPlan", "longitudinalPlan"], + "modelV2": ["lateralPlan", "longitudinalPlan", "uiPlan"], "carControl": [], "carState": [], "controlsState": [], "radarState": [], }, ignore=["logMonoTime", "valid", "longitudinalPlan.processingDelay", "longitudinalPlan.solverExecutionTime", "lateralPlan.solverExecutionTime"], diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 4a670b11ff..e9016cc27c 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -086896986a3fcdb1a03d9afd00a9abc928f9ef25 +70753c5f491de7432ff22f4ef560820ce9919b2e diff --git a/selfdrive/ui/qt/maps/map_helpers.cc b/selfdrive/ui/qt/maps/map_helpers.cc index 8d5d4e1715..95db4f2bbd 100644 --- a/selfdrive/ui/qt/maps/map_helpers.cc +++ b/selfdrive/ui/qt/maps/map_helpers.cc @@ -31,7 +31,7 @@ QGeoCoordinate to_QGeoCoordinate(const QMapbox::Coordinate &in) { QMapbox::CoordinatesCollections model_to_collection( const cereal::LiveLocationKalman::Measurement::Reader &calibratedOrientationECEF, const cereal::LiveLocationKalman::Measurement::Reader &positionECEF, - const cereal::ModelDataV2::XYZTData::Reader &line){ + const cereal::XYZTData::Reader &line){ Eigen::Vector3d ecef(positionECEF.getValue()[0], positionECEF.getValue()[1], positionECEF.getValue()[2]); Eigen::Vector3d orient(calibratedOrientationECEF.getValue()[0], calibratedOrientationECEF.getValue()[1], calibratedOrientationECEF.getValue()[2]); diff --git a/selfdrive/ui/qt/maps/map_helpers.h b/selfdrive/ui/qt/maps/map_helpers.h index 6bd5b0f067..f9c56107e3 100644 --- a/selfdrive/ui/qt/maps/map_helpers.h +++ b/selfdrive/ui/qt/maps/map_helpers.h @@ -20,7 +20,7 @@ QGeoCoordinate to_QGeoCoordinate(const QMapbox::Coordinate &in); QMapbox::CoordinatesCollections model_to_collection( const cereal::LiveLocationKalman::Measurement::Reader &calibratedOrientationECEF, const cereal::LiveLocationKalman::Measurement::Reader &positionECEF, - const cereal::ModelDataV2::XYZTData::Reader &line); + const cereal::XYZTData::Reader &line); QMapbox::CoordinatesCollections coordinate_to_collection(const QMapbox::Coordinate &c); QMapbox::CoordinatesCollections capnp_coordinate_list_to_collection(const capnp::List::Reader &coordinate_list); QMapbox::CoordinatesCollections coordinate_list_to_collection(const QList &coordinate_list); diff --git a/selfdrive/ui/qt/onroad.cc b/selfdrive/ui/qt/onroad.cc index 33b1ea8e27..98c636b0a4 100644 --- a/selfdrive/ui/qt/onroad.cc +++ b/selfdrive/ui/qt/onroad.cc @@ -674,7 +674,7 @@ void AnnotatedCameraWidget::paintGL() { if (s->worldObjectsVisible()) { if (sm.rcv_frame("modelV2") > s->scene.started_frame) { - update_model(s, sm["modelV2"].getModelV2()); + update_model(s, sm["modelV2"].getModelV2(), sm["uiPlan"].getUiPlan()); if (sm.rcv_frame("radarState") > s->scene.started_frame) { update_leads(s, radar_state, sm["modelV2"].getModelV2().getPosition()); } diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index 6c850b8ca4..50843de68e 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -35,7 +35,7 @@ static bool calib_frame_to_full_frame(const UIState *s, float in_x, float in_y, return false; } -int get_path_length_idx(const cereal::ModelDataV2::XYZTData::Reader &line, const float path_height) { +int get_path_length_idx(const cereal::XYZTData::Reader &line, const float path_height) { const auto line_x = line.getX(); int max_idx = 0; for (int i = 1; i < TRAJECTORY_SIZE && line_x[i] <= path_height; ++i) { @@ -44,7 +44,7 @@ int get_path_length_idx(const cereal::ModelDataV2::XYZTData::Reader &line, const return max_idx; } -void update_leads(UIState *s, const cereal::RadarState::Reader &radar_state, const cereal::ModelDataV2::XYZTData::Reader &line) { +void update_leads(UIState *s, const cereal::RadarState::Reader &radar_state, const cereal::XYZTData::Reader &line) { for (int i = 0; i < 2; ++i) { auto lead_data = (i == 0) ? radar_state.getLeadOne() : radar_state.getLeadTwo(); if (lead_data.getStatus()) { @@ -54,7 +54,7 @@ void update_leads(UIState *s, const cereal::RadarState::Reader &radar_state, con } } -void update_line_data(const UIState *s, const cereal::ModelDataV2::XYZTData::Reader &line, +void update_line_data(const UIState *s, const cereal::XYZTData::Reader &line, float y_off, float z_off, QPolygonF *pvd, int max_idx, bool allow_invert=true) { const auto line_x = line.getX(), line_y = line.getY(), line_z = line.getZ(); QPolygonF left_points, right_points; @@ -79,10 +79,15 @@ void update_line_data(const UIState *s, const cereal::ModelDataV2::XYZTData::Rea *pvd = left_points + right_points; } -void update_model(UIState *s, const cereal::ModelDataV2::Reader &model) { +void update_model(UIState *s, + const cereal::ModelDataV2::Reader &model, + const cereal::UiPlan::Reader &plan) { UIScene &scene = s->scene; - auto model_position = model.getPosition(); - float max_distance = std::clamp(model_position.getX()[TRAJECTORY_SIZE - 1], + auto plan_position = plan.getPosition(); + if (plan_position.getX().size() < TRAJECTORY_SIZE){ + plan_position = model.getPosition(); + } + float max_distance = std::clamp(plan_position.getX()[TRAJECTORY_SIZE - 1], MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE); // update lane lines @@ -108,8 +113,8 @@ void update_model(UIState *s, const cereal::ModelDataV2::Reader &model) { const float lead_d = lead_one.getDRel() * 2.; max_distance = std::clamp((float)(lead_d - fmin(lead_d * 0.35, 10.)), 0.0f, max_distance); } - max_idx = get_path_length_idx(model_position, max_distance); - update_line_data(s, model_position, 0.9, 1.22, &scene.track_vertices, max_idx, false); + max_idx = get_path_length_idx(plan_position, max_distance); + update_line_data(s, plan_position, 0.9, 1.22, &scene.track_vertices, max_idx, false); } void update_dmonitoring(UIState *s, const cereal::DriverStateV2::Reader &driverstate, float dm_fade_state, bool is_rhd) { @@ -248,6 +253,7 @@ UIState::UIState(QObject *parent) : QObject(parent) { "modelV2", "controlsState", "liveCalibration", "radarState", "deviceState", "roadCameraState", "pandaStates", "carParams", "driverMonitoringState", "carState", "liveLocationKalman", "driverStateV2", "wideRoadCameraState", "managerState", "navInstruction", "navRoute", "gnssMeasurements", + "uiPlan", }); Params params; diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index e3eb97a762..ad2a1fe1f4 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -198,9 +198,11 @@ public slots: }; void ui_update_params(UIState *s); -int get_path_length_idx(const cereal::ModelDataV2::XYZTData::Reader &line, const float path_height); -void update_model(UIState *s, const cereal::ModelDataV2::Reader &model); +int get_path_length_idx(const cereal::XYZTData::Reader &line, const float path_height); +void update_model(UIState *s, + const cereal::ModelDataV2::Reader &model, + const cereal::UiPlan::Reader &plan); void update_dmonitoring(UIState *s, const cereal::DriverStateV2::Reader &driverstate, float dm_fade_state, bool is_rhd); -void update_leads(UIState *s, const cereal::RadarState::Reader &radar_state, const cereal::ModelDataV2::XYZTData::Reader &line); -void update_line_data(const UIState *s, const cereal::ModelDataV2::XYZTData::Reader &line, +void update_leads(UIState *s, const cereal::RadarState::Reader &radar_state, const cereal::XYZTData::Reader &line); +void update_line_data(const UIState *s, const cereal::XYZTData::Reader &line, float y_off, float z_off, QPolygonF *pvd, int max_idx, bool allow_invert); From 7acb4a94cc77a5d2b19b86fccdf373489bcb6f8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Fri, 17 Feb 2023 20:12:31 -0800 Subject: [PATCH 431/484] Update RELEASES.md --- RELEASES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index ff5b686e0c..0ea4241a35 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,6 +1,6 @@ Version 0.9.2 (2023-03-XX) ======================== - +* Draw MPC path instead of model predicted path, this is a more accurate representation of what the car will do. Version 0.9.1 (2023-02-23) ======================== From 8d9f99b835aeb0d317337d1f87a961e75f8d6468 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 18 Feb 2023 21:48:13 -0800 Subject: [PATCH 432/484] cabana: add space to help message (#27384) add space --- tools/cabana/mainwin.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index 07d37df4e4..2cba29bf5e 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -176,7 +176,7 @@ void MainWindow::createStatusBar() { progress_bar->setTextVisible(true); progress_bar->setFixedSize({230, 16}); progress_bar->setVisible(false); - statusBar()->addWidget(new QLabel(tr("For Help,Press F1"))); + statusBar()->addWidget(new QLabel(tr("For Help, Press F1"))); statusBar()->addPermanentWidget(progress_bar); } From 8e3cb0fb2075cc0e21f0362dad467cd992291c3a Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 20 Feb 2023 07:14:13 +0800 Subject: [PATCH 433/484] cabana: add qrc resource file (#27386) * add qrc resource file * remove export --- selfdrive/ui/SConscript | 1 - tools/cabana/SConscript | 10 ++++++++-- tools/cabana/assets/.gitignore | 1 + tools/cabana/assets/assets.qrc | 5 +++++ 4 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 tools/cabana/assets/.gitignore create mode 100644 tools/cabana/assets/assets.qrc diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 0f28f5ccc3..a8c8463bd7 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -42,7 +42,6 @@ assets_src = "#selfdrive/assets/assets.qrc" qt_env.Command(assets, assets_src, f"rcc $SOURCES -o $TARGET") qt_env.Depends(assets, Glob('#selfdrive/assets/*', exclude=[assets, assets_src, "#selfdrive/assets/assets.o"])) asset_obj = qt_env.Object("assets", assets) -Export('asset_obj') # build soundd qt_env.Program("soundd/_soundd", ["soundd/main.cc", "soundd/sound.cc"], LIBS=qt_libs) diff --git a/tools/cabana/SConscript b/tools/cabana/SConscript index 166b3d5548..ddd6208c07 100644 --- a/tools/cabana/SConscript +++ b/tools/cabana/SConscript @@ -1,6 +1,6 @@ import os Import('env', 'qt_env', 'arch', 'common', 'messaging', 'visionipc', 'replay_lib', - 'cereal', 'transformations', 'widgets', 'opendbc', 'asset_obj') + 'cereal', 'transformations', 'widgets', 'opendbc') base_frameworks = qt_env['FRAMEWORKS'] base_libs = [common, messaging, cereal, visionipc, transformations, 'zmq', @@ -20,11 +20,17 @@ cabana_env = qt_env.Clone() opendbc_path = '-DOPENDBC_FILE_PATH=\'"%s"\'' % (cabana_env.Dir("../../opendbc").abspath) cabana_env['CXXFLAGS'] += [opendbc_path] +# build assets +assets = "assets/assets.cc" +assets_src = "assets/assets.qrc" +cabana_env.Command(assets, assets_src, f"rcc $SOURCES -o $TARGET") +cabana_env.Depends(assets, Glob('/assets/*', exclude=[assets, assets_src, "assets/assets.o"])) + prev_moc_path = cabana_env['QT_MOCHPREFIX'] cabana_env['QT_MOCHPREFIX'] = os.path.dirname(prev_moc_path) + '/cabana/moc_' cabana_lib = cabana_env.Library("cabana_lib", ['mainwin.cc', 'streams/livestream.cc', 'streams/abstractstream.cc', 'streams/replaystream.cc', 'binaryview.cc', 'chartswidget.cc', 'historylog.cc', 'videowidget.cc', 'signaledit.cc', 'dbcmanager.cc', 'commands.cc', 'messageswidget.cc', 'route.cc', 'settings.cc', 'util.cc', 'detailwidget.cc', 'tools/findsimilarbits.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) -cabana_env.Program('_cabana', ['cabana.cc', cabana_lib, asset_obj], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) +cabana_env.Program('_cabana', ['cabana.cc', cabana_lib, assets], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) if arch == "Darwin": cabana_env.Execute('install_name_tool -change opendbc/can/libdbc.dylib @loader_path/../../opendbc/can/libdbc.dylib ./_cabana') diff --git a/tools/cabana/assets/.gitignore b/tools/cabana/assets/.gitignore new file mode 100644 index 0000000000..283034ca8b --- /dev/null +++ b/tools/cabana/assets/.gitignore @@ -0,0 +1 @@ +*.cc diff --git a/tools/cabana/assets/assets.qrc b/tools/cabana/assets/assets.qrc new file mode 100644 index 0000000000..b42f2a82b1 --- /dev/null +++ b/tools/cabana/assets/assets.qrc @@ -0,0 +1,5 @@ + + + ../../../third_party/bootstrap/bootstrap-icons.svg + + From c7cc36d9ee22aea2e07aab61d51f0f949b64bcf5 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 20 Feb 2023 13:15:59 -0800 Subject: [PATCH 434/484] cabana: add window icon (#27109) * add logos * most zoomed in * add alt * slightly zoomed more * add images * add draft images * best image * not working * 96px icon image to reduce compiled size * remove from selfdrive assets * fixes --- tools/cabana/assets/assets.qrc | 1 + tools/cabana/assets/cabana-icon.png | Bin 0 -> 18008 bytes tools/cabana/cabana.cc | 1 + 3 files changed, 2 insertions(+) create mode 100644 tools/cabana/assets/cabana-icon.png diff --git a/tools/cabana/assets/assets.qrc b/tools/cabana/assets/assets.qrc index b42f2a82b1..6a8e5a3414 100644 --- a/tools/cabana/assets/assets.qrc +++ b/tools/cabana/assets/assets.qrc @@ -1,5 +1,6 @@ ../../../third_party/bootstrap/bootstrap-icons.svg + cabana-icon.png diff --git a/tools/cabana/assets/cabana-icon.png b/tools/cabana/assets/cabana-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..86add806fd1cabcd145ceef74a3995bc609d34fb GIT binary patch literal 18008 zcmV)CK*GO?P)PyA07*naRCr#^T?LpO<<)*iuIpY|_l+kcAwmLy1PB(S6iIO{MOw7Dv_%Wli&Lz4 za0`|YG(d>EZnCb|-R92x&pC59rTu|~mO=+MyLzg%c;yj0#)^6M0 z_Q9HM8a z#bb~}8KNj6ole7Mvq6$1IGs-PbanH0Nfe=K8edj89OmyPqft{8HL$65>st>$^}+*- zSFBhGpzyzN?LVjj#`yjAlYV;o&-UMY>ZJmNdY8+ETrP)1JOPi_14S*ePzMJCsHiAM zHk(0F(ICq*1VMlXjTPW_yOE41kV+}P-7GdEV%qCbY7Nk-sWV2Z)xja-&g)ECOnar?S zY$y~8{5KK$h*cDvLaZ9NO`di0@-7cX72_50ox-=_k^pPllPb5A<@sNV^I z!)PWU%ZNpzFv=3_4m+EgLa_*k!--5L4X@XOSS*TQFo=eRMub9PR903Z7E7Rz&%@<$ zK`APbL<6FcC@gj>3)ybBBQQ7sS(1>?=b$JGR8@u5>OdkMN0C0m=|Dc8MX{(tQvpeo zkjZ7cAA08bv!8wC^@ZP;BEAm=2-jb7;ROdDuG6Ap8$A%`K zFQB`#6T?T2Vz836UWR>XI%fL~p9$;opL-1iBC(Ih34323SUi-{0O2z7OJY`Z&Jx5Hmm1y8vj zW{VB!REmY?u-jQ!Mxzm3-JPhese##IK`xs`JQim=U0z;}LP0?|6hd`f4OF#=&dv_x zvss3vUay~>wW?}}Cz22}4TXG;g(@3N2m}YgyYkh{d6)Dl+LbOja|j zHajw@G@~HNAhUq1W)rXYd|rWp%#Fj2-tI1T@PqyRXlZIfAQ0s9NawPsuBnA48z9RP ztX3P+w3C7Z5H%Hg)g+Y?=@v`N2+;-2S*M3(@_)ZisdGzS2 zNALg3dVfWQhs=$lww;d(6H!Tz<)if*?qA!ep-ryVq7!A{vb# zl}@o!Ba=Z`-060~;c_7zjU$;#!tF1Ir_6^;Cdn5siV_Pvok^jxx)w&+fLJs}CV&H} zk)uapFc5&N%tH|_`Z~MW7o;;;6bgA%R#l>ZV33iL&1z?VU@{re-rmkiHd!njQPcZ& z_jF^@o|BNyrjbsk(ACw2VNJ~pYZVQ!*sLJ9+`eNwa;Y>#XpF)}j2Z=_(HuSM?9)fB zU*9(HpD5u!Qow~jJ@e$tFFfz*0!6RMBs?AuN2G&;{ivy`=2c4pmZBCQsC4pChytJq zK!v{?9o=2<_{*UxDhFe#svw)rFpA0M^K6R=YAX3Wl7xPs!DhF!qc(^}c>G?Zk}1ec zRghCH@_V>lE)4V!urDxMt^ByjXvSb)Kd)?)#e`TW!sdvaxXkx~@7~YK-IRfEe5Zzs!sI95RKwyB) z0ugFvgA73g67duSNrb<=9N|cm1G4^}9yB&Ipr@~&cThAIgV|_-)9vQnL@@;^hJre$ z!-jY)#!+r{bv45lQb;D9gWKsuHlG98CN-CbY&J4FB7ag=R>l!D`4d89sbm_WEW_jT zKr3o68jK7Ag24c6Rx6A)3!A-cHlzOT?gyqm_T0UQrHBEn;wB z0A7y=k}Sb&weZd&lSR9V0yDeSf^Z}ZNtR(U6KYbK;LD~mFq&lkoqz%DKzeTq+-TBUg&b*|g++E<$>i7!&<-MnmRDDzZQFL{ zaSBSF&BvCu4lMd?83N%Lws&+QkxH|GNH`SL`aET*uc^kNv-U%CLmk6egDfMT$-wD$ z@*)Wb11PT~N3SuWA_Nr(Qqbk#z|L;7vNP}Q>|!2@;tq=1%gS8n>+R*$PJtl#F*b{e z0%#f>b_X_X-HiIWM)iVU-#B{Bn$6#ePx@90IQyiz`~LF0vp*qwUteF3L?Xos7etZm zbCI^S(ZIHsPzDLf;c`MP7Lm*7urZU%B9qQAw9E0&Sq`d_sWcjg4MW?G?ab>C|MS7J zRd{W|BE*yW=j_fwbo7XpCj9oAD;ZU}%iJ&+4KT1AFqzQa*2X)t zzOjK`RnT~UmT~}L&&W=ugUq_h>W|JA+-JSWL{o=~14c*;3 z55616ZYyB)=+Ra4?)^iT+vS!*;V|rW2dq{Lb27A2%vLj-4yU_}p&i*>@&g8|3DHmp zF1LFp^dxv#U0I2(+qR;vwjN4RMPGj(n;U|u+d8{(%U$;)8c*!TC4BW$XqC>KHU($R zJ%*!R3hMY`RSk(m43!m?>|f|Qylyv}rBEcyE1eWj*VKf9T107yXd=i%5Rk&IKE6P$7MiB-Fs0NPyz$g393A26Fu9+kGR1|3ooybMQSu~}d? z8KjA2a;sI94CN1V&1nWiRMf>t=eXR_(ML(^$SR1_-^tx;87%k;Ns z5D^K7;BwlTKA|1l-Q9`$x_ZbaBMe3}1_lS1YszHvpUgUX?#$h}h}}}aN3TBr?ucQ{ zhxGOJGNDC?!)P$Eqa#~M*qaapIb(8kbdaMW0*!*0d@%>7vy1~QQcffsL{D2gY8vaI zXd>)R7h=gc&bsIdtlhYIch37;Kj++&=VHd>JyGUz@{S-IPQakKsR@E8p|YwP;ZT@? zf}_lhqN<{|tB27T?QnWcUvD3ifV7a*qKf|he)!A%fGk52B#x?+$rv<2!t*b^c;&C} zyz39UI?-J!;Oa{*IQg{Wj($4Hj#Po!W@RB0zIQoX$mg;gd|53PRsvDHw4w-6P+UNM zfN(#p@JuGfFfSSoQC0*tmy?4lRTJ>@8*ah2wvJt$?|=07Q-6FE4xPCVC~nE6GpMbt zfzf2-T!d2881+QLVbs^vF&U>8@|`;=(Y%(%WWh3*LNI-*%P;iiAg`Tb+)YR3Y zKmfzx)ajH&8reb)R;!uKDEYOe%a>!!_%Z#>lV??P#P>DFH!7f~b>nBbbZT$6OV7(v za)V5c*-Q*&j?If8XrS=6sDOn}KL-cT9nlcv7dE$U z$BAbX)BF7$fBo%E7(H?X;_(EUo0}mMb)CsVmIPGR)nl-?6SguZ!hr}zj2(+eFo;wv z%ruE48If1=Fq+KJ1QC^l2BTqE%yy}IIDW5*UGA>DlP+2{Y}TPxt4(@s0^ zj@y2{D4R?%Tr-;qS}GtvFl<;0`g?i_cruJ5p_f$>#_M1+o8WRfnbNkJOt6}baCys_ z#r4-zAsz@Z8A#Fh%P+lvDO08*lgVM^$Puu)%GhZq31}pu3~$XwGrBsvkWXjO*w}#O zpL~R2w0_0>xs1`49?9C@(8X0^+dGfe-d&ei|{OXRcL`7ezfD2DQ z>E{PcpL{O`CR}?X0uc(^1+XnoB;trh!(2LIFWsxROmU?jg5#0 z!*F`aIFM->F%ru@{REwDZK$ko#BC2hf%fj6A4CE4p=Td93m%sPCPFp&JhJIHYO1R+ zddwcoC&gn?5bQ3ms>GU=D=>WAcvwwFeE#`o*l+)Xkf6V-Dw|Ux=j>J+K3TMgfy&GS zXQO@FW_0anW8iV`OAE$yghHGC^AP@xkW?~wNix-U&E5qt= zGAF-j>o&mU$3w5Y^@A#aKKz);qhXRo*i2?PttLn%;6S#Vpf%Cw1fhwHBNC7jOH{wq z+uh5kNH&>ZFdGpG>iG^TpXurD;Q*E3X;*JIOrn5LID+@qcZ~?-a@)Rq(f_&wgkKys zBbF=V9ppTT9Gfz2I)Z)uoRlCki};Xr>(-&5s2DlC8J(SN7(RRiwrtsgrp6|;wzfhx znPD~>JG4v!we_`VZ|g*Lg^v+@PhTIJn}@MqAl6nYs))rx$mMhJR@NXAiNa(s zaC)?*Wf+2?0Qv_5$QKmMoH+}v+qUv^C|AC;E#L|R@u3UctFiWHlg8kv$H zG(}r9lg%TO%j1*Y=*}YAA6g3sk7)+MZ9-?`hBt6guK*c!ResLKb@vazY_Y>(w{Y;4 zj>q8lm7%A%4;8ib*xI@Ssz7XT2E&_%VQXtEa(T*)7j?uaima^QU=U`1`JD?lwO{j9 z3b3EQ{~ih1Xhm<|AVQ%SR6*eMTTN{ZJWdya!G354BMUehPr)DvXsYp}vA%&5CInHb zb|)N(Q_UsRVu5*mm(Ro01f>aScS(`~?|1$1=4VI<%`OY7Tz166VT@?5LugS?IhDU(5&+XBgA zzn_3`=l zyd@l>h7sj%Wa4pD`8`O*<1m>m)P#U^D#=A&ieNxTEzI)jydH=pX23Q@yk&GbWVJGbG)qfY~+94v1PkaT}xn!u|v| zDfLG*1aUutp@2|41&h;%o`DDir2vOnhTrRfld?{M09TfgNy_FjFdAf}iC-}okt--r zxllxd%jv?$-C?dg_+L0|l7I%g5l)K{`E(Wn2VYhwRC{Egw4I8qD5I&mg0GKq+-6zi z`lL`Q3z<@wiVE2P)YsHwpl<;6bv2x1vY1PidTIgbRGN|DU_ABMvQXjdFDt;Zuh)>K zNQ)drE?eLf9l=aWev?@ljD{f_HN+!n1hWNXxcEwgMbzLe({rbyq9PSb!eErSoI}eK zubzd=?Ls(4CQan@UuC5qpLItN%l=RFNI(_^OsaIkAZUF4#0|OK4k${F>qZ0p15gYm zI86qor3I)QIVUoCB$OhT zh|n?xjsfVMvw0O(qr_Eek!YMN6x5=EmZmyv3B<8(;NQ=9eV03jgfQAEz)oq>WEv8{ z)fHqusGKDh4kIrbVH7k7q6S$KVX~T#%@&YUR9FonnMD?ifJK}7ze_p(NAESI&cleONNg=s-T_v#6JC!Sn>VbZDlLdADL_S4Wd$U&iD{N_ zG7XC;Le`3Kcs_Aa=u-&(qtQa?W96aSUShaW&0)YrD1Sj%2 zxXOL_$&HUdt{V-bnnvp<@4)3N!^bZ@ib$vv4woMVO@WI{)v#&r-Fe3!4kiVd?w$Wh zHn@2?gk%td9j$N~0IS)IUZR(qhC_6D5$WkbF5ZW2D^?}AIwW12fq5*x~RB53=iFac0iw|JTp3{&s*ichlhqPdgUpV(rmoRz% zqefkR|GZ84L=^dW0Af*r5bZ-QrdKw{Qwg{#8jue6qi5;6*!1bAh-ETJf)tQMwbugK zY)9kRaWIGm?AW-D1DZ0A5B;Gi#!TA>!>1pC*Y3WF12C8^IPj#iFl_1}_~g}R@Z_WS z{!hTsORu>ZgYBEpx_A*JEsF`Wk3n^%58iSgUVZ#NWK)E41g5ITPoILC#%8R1_f7P6 z^#R#5v}^{hno690&zms%8X#x_(uD&1RIOM<-?kO-)C@x;l7f%QF(Kf%iB6Mn_RaU6 zIs3#59;1?RRm*ex5GWs-&q5LfWYZ}~CJTg23@abI3GGWiK{$~@S~R1OieW@^IlSI- z)Q=pAWFm*nYgQs1i^5Bha3G4k4w{47$+Pj&@2_K;+Fad;Q-AYUsGuJ39=wHzji6NcK{h&&?`qsNJ zxOoM3eD*G&INm7LeIvHhz6)Cpd9_7Bqkqx zBu4IYIG(!sV$K`bt6K2Wzs}?6c;y>UEu?_u*zx%S zh=nZnJoYR&%o@slm00}p6BvwUVUUWP#NTtj{owZbvGM(PFc65KNVP<%6ub>}IQ5S& z!r`ZK8z94VqY|%Z3e*%$<9La_NT?sa@!SoV8C@_dZI)=V2o4E9w@~FXR29pjs9DD44 z3?94T0;YUz<;^(v-WRy+W91u9;*|&gi1MakIOwd)@%yWOhDh)~D!2c))JHJ|K~jG^sp?0Iq(J3O;}PwP%H=HmM02f?Z)@lhAb&L|>GgFhGPs zP+>q3pWSsa?|?)ii>$?pL?ntmr%u4|y$|4ogpwFUf9qx>LUB0U4g_OaOgeZD#?AUM zp8U;4OwU?;b-4I%uOUMUc=KtzeDCj2(KH-KUV1BcvCO;YcC26a$-gDYfA>Yi`&@D3 zpU|`JQ*}gK6cY(S}SSiP3wE#<>0GAVMvpi692IZ$>d z#LA8shpGD?&b(7k`&N8B|GC|JEr6<;I-Gmyuh72gLv()e0pwy4v(LW@g=jyVWj?G~ z@B;dJLV!`?rooXDCv#4_bLA&kyK*%g1`$Fg2YW?1esufuaMX|C&*n}|LF7`-q4^b5 zra80)Qjy>m;gxNwTGTXwsOLhFY`;dI1B)ndl7gU@q^ih4#qI+-E&u=^07*naRFc15 zg6;3UfkZlk2#hEcvKT#f48|XLI70nB9C`=awjdEo!tS;sm`G#F{zsr`#$kB*_N$P| z6(Be~xbms@QBX8&{P<1y(Iv0jDeinGEceTCE~2iKXfV(#JhQ359#;oS0*ugLHj3QINRX83^GRz2saD>1 zCE6Cgj6^zxxL`$I$za&9;h1vR5xfIpJv|7tZRLcv!)3={B8dt69Ewqg%*BhhUZ&U9 z+kLqFiT6=}gzZaS!!x&C4SQt`&i%tY_6yle7P(A{1x^js*=(A7zo<5es?G>g)5%NF zAWAZ{5~?6;OGcC^5u(QLx#2waIlFW0chKROamcagUGo8YmMny%s5tE6Un3prg3al{ z+66Bn6pTWo{I(=v%+wihmscUaV=cCASdUCF1hJ5Zt)d)9-|;M5_2c#4=F$?NMnU!l zBu`D_ia54rG0q(hysyS!NS_Vb9xQ{4NY zNfO3R+ZS%HADQlL*tBL9vY{}vTn>(M9}c_aNjMw!WOJ@C+98uZWY~3d$%|bD;?co? z@WdunfuIo$%333)PxsyQB$hLQae~2HsVhK}(7XOqJapYTaQMn`!F}_gr~=pF z(27?IIW8Ti#yDzwr5Ys)>#1>C-_1Ilqb;q`843ayk5M0THkrU}=l_@q!QDB2deN1r zYMFqZ&)-1z@(&@Xc^r7buaOCKz**+Us<&T2I-BR+Cz_2Iwbu-e^s_zNv3c!kq^bWn zpJSyQc+216YSEkgNOlZr_=JaIEIlW}4T7Oyhw%8uVpb4f`ll1+=awKSdu$bgR>b-T zuR!~UFCm#MAZxN99Z#WocrEt%(N7Q<>_fb_2f-a%P)KKCFLPrs9LJc0jz;6OgE9ZN zS0PUYtd+Gm=YhAN3PucW_zaI+b2hAHeq8v-yHJWEhwikBN!TPz>K`MMLgs{&z|z;3 z2QLe?qyREK6zCKRIi#Xt-2BtScSrUd7@c$3b*O9{i_TA9Mfb7~A!-T^IR6Hu`#a$D z`LTAv%SdJNP$|o2k}-Pk{kS7P-Ms^w)~rNoFu)3Mc)d92#=pZod>Y$)imPb#bN%d) z@745baoQKraB#iw$Oa`Q34)W-hb;DgSv6#9=?y3-TXWyVX#en4Br`>1BqK73G%H~G zkv~Bw*pFCW7ed>&LdoXgpkW%3D8|nIG3uuujMr|toCRvDs>9j$y$MZ_5!|vA4_|R6 zEM-1i^w>fun#4AoObI1E_&fiuLZj7BCttb{{+Xi{Hju0wR#rBRSfCfbIcxV6U@;hQ z_7%T|zhM-*K6(k=OW)^i`B~>(kMuw%oL(y;m%aIhIgL{3ku-N(hb)%FB556z_^^?9HhsR%ClK)QJK)M^s zSJK-^C}QpX7o+3j*O5pSkd@6y#gk|mUWaK%o`B$B4-$i&2(`ANm?s7J&=-kd!okO( zdeQ-SV%t~Xq07&J+3m%JtN>#PH?NB&+VJQxC8ZtEJ(aVIVLEb z4I!c-*R**4vyYJiKDK-O`wb~z=&XmPOX)U@f-(hb@4pb8AJ0c3tsrkOBNjXkFZm5zb;Ho{?o;Sr_92_wz0SGb;-I|EfKb79-N+C0=Tcl6t&-UQ|26pP=7y?fi8sF^c_G7=nMog zWzJkwPMnF?Z@ZMYu(`4kXWsh;6j4TI$4dO|m#4D=e*VY@P(%|W94?Dwb2xNHUqUm2 zrXw|Sz`UAN&axGXy7|ecQ@Hy0eUM7*y5ybeK`YCgIN|bJ;i?;swznR`;L4An5gB*J z70}Wl9&NDs{a2C88xT(?VKRuAaqs~!njF}+Y6-%w%U`uk`Hyv>1F zLXf$eytcxQ#&HvoiKUQAgpmpcdDWV%PDJu~G*8(FQw~21FVPNAa*+IWIQM~f0Z~G{ za}6H6=qIo^%W&}%A0sEw4jAG>^pzknX4b(QUkdkw(240m7uBz#s6r{wJfH-Z&|iAw zgxx|w_2qs@s)AFl`xETdjc9%2VGJyP4=5;@dd6iKXkCMRIEqN;M#Ks-cl%gm0i#9@ zgJzI1uys2xlq}7-P;;<2JedBA8_~4?2_-W_D2-Zp1$OMZFV@*nlIbrGpHeQgOr2Rb z_m;;x8P>73$imWSNDT%-MzAMwA2>MTlofHF*TazHF3PV?l4+yg6^C@&~UW>CSBeZ!1ikUQ~9CH@(u^{YL1J*5lA9>Y`R3-(B1dM2DhS_OD-=VmpW}39nRX`|e3h?($xTURib8~_-MDY9ftHqh>%!K&AP52> zo!%7-fJiT-Tn0iWiflRoMUy%EmFd|6DI7vd6_62(Q1dj!N`Td5hhQ_IkV-+xW|3Dk zoN9;=W zypvA3>^E5P+(XFc6FBA8zro_G#fB$-kHIyI82}u5;u+YmbTNW~0XR)E(y$_yioq(1 zm@s2+tY5km#$pDUtcGMJ4P$|(J(@7`z(cWO@u#rZ9Z*dUu9hATj^1?Br3$q_q6 z$X)AJy!e+h4xh^k_~nc5J=QRK!kIa$?SYKMhi|~4#PP|SFF+JzOx$C<2sRl6j+fcmEk5zWK^76|l!1dtlP!saUh{4Px%_qw_9;z08NzZ#;{5M?2T; zjGsCUy{%heqynr&2nE54a6Af|Y{aDLdt%GVl~C0JQmG>1nJmmyNoX-*-0Xv}=Ce;> za(bYc?5L%cHP=QP#k1w`#VacXl zH+qk$@u1P~f{FTp4M3T@3^o%9OW@r=4J0(JE7{eFsA}LU?=p`annpY^;Tb|-3Icrt zaCtp22u5V{8IHoaO;-T7@=|H0%jHLwijxg0j5a^&C+!RGnDOwB9zS?goxkGC&>w%> z*uF+;UpM}h8!m>xgY~iy6dHt05@ot;X8=LxN`9Q8P#u*FQ8WTi#$rWL`c2b=@d}#- zA*BFWGICOz2I!~^Xt6TX6lEQ{#glQEXylFA%mEnV z3l)f_GI)0B)?M}+*i8mZsrQ5GZ4I)(L!xLA7)m>j3X+M^AlBAiC}JR;<7=b_6hiV$ zQt0xT>A=J)j!B3&<~ADO%qb#)P&tP%|^$;e9<9DmE*=#6D=zy9Q7uKOni9Dn8Y zb5B42+~+%9`a52||BrB+3`h}wFY4(toz)-(>w;%KgR3S;_&RZ+se0r}f^m7=JTWDi zj55~N3vF`_?A1zmRdoK{%P61sb7QlW0Tn8GE6t1A1Y zqEv{zB(Ob>5euAb1NRYDj= zsIXZb++j$~7&KFd1V%v-mfb8(XRn-88Q9*ax4pdiHz+y6# z5-a+ECNgbo=jn4)4J)_zV!@i$T{_-6w;iJ!>S426pd>;N^I4e5XBlKrn-bYH5+qG? z;T2abqM#LEpvYAaF&GMPQFq>8n zjg;4SLo$}BKQ287dTL<_*a2hM@j?uM3+ zV##NVxf{i37P$qskjwL|HtN`+oJ?K=V(9|j-`umy#G_RfQSY{(wyG9Yl{N6%4OqS8 zLs$$lvz(kJB%aAc*Gi^F)GIj1L>7zOaFU_1HnOD4fK3aTBT@*(0VVsugqjM2q>p8z z3@Xi@$P^XWy?)g0xi=Dyx}X2;&YSQ5N(D5JY94Xm@Tx6Js29a#8hNNZtd8OX%7Ss$ zj134S8z`R^2L&J*k{mlbzakH5MVY@mq`k~+6FkP zN3pYS-?AFjRh6(itWb&>&ONgWV$&ya`%f&D#v|{o;GOtQ2X(<8IAJ7;`2s?LAV!Sb z19r0!b|Hbz^&5D8G)bS$EG7Tw%t{F#J(mlP#&fE|07D{wL_%kitdC08&7!VF-DK-K zoD@JvO)wf^v^r3bTqA#(H+PP( zoumSz(s$i9tL)75JdW-w=w=L5>P}{ehT@TJq^o4UiA)I%{h(E6wV*GY!i4<}Lb4#j zX{M@%?HD(H6sQz{6i*nR%niLZo=R~8#FHPa;EVfa$SLB8y(e(lJeii>z5vqMB1VlE z2fs^3=bFV_ghdXMh0m5&=DrBp0hF1edRIb5MM2`e0U8=ei%J&&`HqrBAV*A#Pn20c z)Wj{QB09UfpcsrOP|<_g_00Umt55mzIsZQejB2VrWI~nWUF1@nl_Hxta`YIU60&;L zDvo|>Fp+)|y6K@z6ya-K0Xj=bwKXQOWbgTJ`Ibw-#rW$`#T$pSU`OH8N*iE^NoE{SaOX+cpF9!=6^D9u(t zWgjx+Si1B0e+r<#9JANRK?CxB=0VwH=q83v zl2dJ8%1Y@)Mk1G;Q!0^uCc!EKxB!*f`z=-~?KbFCb193(rYw1<0QngDzZu* z^WS<0cmMH^@RfVn_R{<|p7o#2aR>g-Zn&0N$!|Ky1e|sBF=(i*h1G0fvX5Gd1|l)s z_`Ba@;>c0hd&+cJoi^0la`@=om-J+ho?vFWfG&}CAMyWrItS6Lv%{ryqHcin+?JkA zBw6WaC5r}*lQ?rh6Bz|jhs{3ys;l0k7ZUDfY)e~^QIh@4q0=-5=29j3t3jkKk|2uZ9c^86cg+K#PZC0#Zv=UBxC5E+(!H&Ux zy#4V|@=y$VZ0*Jkxa+Y;(AC$wOW`-w*5cSh4&#+>F@V~$K zJU(5qd*dsoO_+$8Q}^Owg4A+KriX@&P#i(`eY4P=mUu^)xm=vcvn9)yW5@RGXx*|E zXPSbt~u_6+kk7vSTnRL2%G=Cs?Yl{ZfSj!-XNilZy0cb*KQ9ofiGQn1wXZk633H z*1q)`27*0E7X^&<_)%9;iT*$jwnXA+sjWv(D2SKeeVb9wt`57!in(*&H7yIuu1s!1(*{ZQ9$`Xtw z5w5B-VjT7SG?`U4t$MUig3x;>XmOH{Bn+iHal(5%rJ|G9G~ zUi`=VSCKu-km!pF5%_S`R+4uPwX{e0?#3(d3Gdx8!4dlv&BfnpBV;| zL7#k1ciRwYwx@m*IJ&RFWAmQDi?7Yc{*x!;!ZS|77pph3!{#oQd;xu(+cD|1bKssd z6-806#vsA&JQx+}zYm#bTJ#oPDVfA%#L5pwUg zrXi~ox#NTI4+)-c_9eKf*=IH5jMGlX zk54~|tFL$GpdlH@9(pL>8IQPp${U-FCbsqDba}tDoek&qvm z_cZ3cJ|Aw26~DXgYApPG6;lVa)Ts(Cv}q-dJnup@AAd4CR2_2u!|LwrcHx~d^miSg z-u3IN4|?&z2X}cJtld%o3Ffg?n^%?j{1bIUucMm+IoVPGLQE)aL&gZDYHLCR(Oe!q zPdwCoLBt7mjoOr}hY-%VFBJ;uu zFc@Gq=xB$KkBROJBgqhDiGwc^^w7f4IW~<42GVioy?@2#wlzCFTMgz#~!a=^H9LWOMVDKmae;W?WBm$7rgVAYfeAmvfWuwyR85!Qkd5j z7$_E1H$hP9_~sTgiWUVCCQ4=s;7SVO^@*d&78S$`Mf5%W5C+z)fXm_F5yhLkdT`QD zPDN8wGvB}Kua7*4kCuG)|IU}3YI9vZX6-eN?K;;+@U5z3jz8!-1#9#?GBf(hmvwVf zx?vnc5JmE5@3p4kc zj>+TqC|xF%p%bE_RZXj&gk0JMtOQ*VWQK->PgDu5W~K{x;3}TZt$ z`&J5|6P|YT(PPfM>u>8s6?9`+JuoRz)wKGlRD+zffl5pS3H`|&dR}@8Yud9xoSzgW!8Bf0aJeI9qg~{W_W7@=tI{lnic(#yUh()xnZp(=hCgC!!ClkY_ zMpp>=1Fq~S*?K)fr2$bA?+!v%1Q2A|07257F)bW=|6pJcOO~xhGMhtD6i~=y5ZbsL zr~m10SoWC0rj-Ly;$tZnQKA}j^Tr@Oe$9xECrp|)Yu0k| zHbeJanbJHAT7?gWVZ)i!qKY=!83Tz7+MaqCAHDDt%A5{3Y<5(Xxv>AN+3;7_Ft5Xv z&4mJwJEIbIDm5HRX0QNB0bjD|yoy=)y5Q+|R>qK0SXuwzW^@x&a=;}LO;>=(K7)pG zrxOXRUA>0Wh=b`QPr3@NU5?Z5dJtmEsL~D)7+f%=s1?z*_)|__rc%lD6Mwn0sie0DAM`{+fvsM~_$`$|4;&Y~NYvvfJT5{uEA465pdq8saGhp|@VbdyhYi zN}mgZ!4MiNEAiu_jzMipi+=xKnO3~s2S8u6@`w7t=(A{j(W)n56H3v|iyr>-4rDW< zn;gzYl{#kFN^=cq37t{lREeP_{+SV5$w$y_4Hv!jHWPR4gCTsld>M?fAg+DpWuT@W z(5O5fpnf6>DnkSPc<$WOpeUL6`rd)Ma5(&}gp;0UHM7`Fjknzw+9*Cf5J}1^P)uK~xgzDt&z4MvvW& zW9Iw_jiW|l`}Xaqs;+`UDZql>1ICRRl+7sh1F!;kr4!2HP>5rUQu_m&Trw*pc!Hlg z^wiH+7ry=)+A%{gl%6o;oayg`-><#px7;EXPp0{9ejbB_-`xK=gpngq5L6iHd{iJ+ zETXNw1MgpdY45_%7Y_&Ut<27sCHZgffT1^WI2_*b4Go)5I_xN4uiFjR$v;JoIdTE^ zyawOra6E(B@(P@M>~UxwK8&Yv>8rHVdc;)~^fNVR3^ixy{Q;bh zBl3%%L%U?C8iQ`&OsuT#aCL#}P?I?V9yHWsu&X0a_%2j*Z z_=h{GCTR4kk5L!sMMRO%kx1dM4?V>9?4p%Srr_{{55_(d$8+jZxAUcJSt*IZJEGL} z$m!=&z&F&k#DvID#J96XXQ-o-6i_lZB}1$8HznSRoif!X>B8q7C`$O{uWsV`rg??N z%&71=tho0tf945b+ZMhDt!bF@*tV^QEqmnNT}HUO68zBLzf%R!n>Ngx*>w5ESARC5 zZF^-#G9XA5a+w?+p7#_Z9?Fc_Ep{9#pxrtD({U=(3%?;P*l)7U`5hZ&= zir^|bUiEbP9E6l^CQ(WbF%;FEHOqf!;z-FnaUfZ0@}NEl-YuexoB#YLL}E$3KSEPs zl{H*+?s*tBY7`A0|Kh^yubI6oZfAD||6fx;>5YXuF1h5!#^K|CRVWk${mvSiezzhD zmgu&HoV3v6NmbEj%yB$X$~fur)*sh9l=L0JO?%wFRO042a4MOR5+B2r2}DPfRz2Bx z&RY*f$fO`9(e(Bc+L3x1QdczH7l$|{x{vXO4IA>GE?<7i%M0dzEv5Wzh5ns7;cxoz z+S*#r&6i#Ks;|tmpUGIH?1)R%a3pvNnuc}&*YcEd z(_Eu7R2EWVD9NPgw^-33Dt|8tekV0iDs3bM4^bFQHPB5(vsta9r~A>LU4H2WUzbez zPXzzp-U0t~Zj;7M8hztcSG`c?c8}$YFRfnYR7$p8U*Ytnr}2tvVW(0^@Dvwtxk!l+ z(<=k?FP#$5oiV3MS>U?RY3I?XNcx%PVGKpeJ85KkZV3quDW;>V>#eJ9zva}mGAC95Wcz@Rx2{Xr2Pue$FI)+5WS#^na*IjeWtF8lOuA ztE?!F27d+6dXO0!j&R-Q<@N+(R$k3Ho)%tosk$hVx*yQq((y`59S}Zz!1_ z`kffip-6bBF}9Iew0{%%~ZSH z#7UE7NgO+-Wq6$i%@2TvUmHrVp;n(H1PrX%xVb|Tq;(&B{@KbvUu0uze=FTucDMQY fT}tdbe!~9&ZRyor+0M!j00000NkvXXu0mjfA#`jm literal 0 HcmV?d00001 diff --git a/tools/cabana/cabana.cc b/tools/cabana/cabana.cc index e028e383c2..5c182f435f 100644 --- a/tools/cabana/cabana.cc +++ b/tools/cabana/cabana.cc @@ -14,6 +14,7 @@ int main(int argc, char *argv[]) { initApp(argc, argv); QApplication app(argc, argv); app.setApplicationDisplayName("Cabana"); + app.setWindowIcon(QIcon(":cabana-icon.png")); QCommandLineParser cmd_parser; cmd_parser.addHelpOption(); From a20f07144beb26e4ade7b3c5dc6e4be40ff5a12d Mon Sep 17 00:00:00 2001 From: Jason Wen <47793918+sunnyhaibin@users.noreply.github.com> Date: Mon, 20 Feb 2023 16:19:17 -0500 Subject: [PATCH 435/484] Hyundai CAN-FD: common PT bus function cleanup (#27397) * Hyundai CAN-FD: common PT bus function cleanup * use CP * Update selfdrive/car/hyundai/hyundaicanfd.py --------- Co-authored-by: Shane Smiskol --- selfdrive/car/hyundai/interface.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py index 2a909b6e2e..1dd91ac888 100644 --- a/selfdrive/car/hyundai/interface.py +++ b/selfdrive/car/hyundai/interface.py @@ -2,6 +2,7 @@ from cereal import car from panda import Panda from common.conversions import Conversions as CV +from selfdrive.car.hyundai.hyundaicanfd import get_e_can_bus from selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CANFD_CAR, CAMERA_SCC_CAR, CANFD_RADAR_SCC_CAR, EV_CAR, HYBRID_CAR, LEGACY_SAFETY_MODE_CAR, Buttons from selfdrive.car.hyundai.radar_interface import RADAR_START_ADDR from selfdrive.car import STD_CARGO_KG, create_button_event, scale_tire_stiffness, get_safety_config @@ -247,8 +248,7 @@ class CarInterface(CarInterfaceBase): # *** feature detection *** if candidate in CANFD_CAR: - bus = 5 if ret.flags & HyundaiFlags.CANFD_HDA2 else 4 - ret.enableBsm = 0x1e5 in fingerprint[bus] + ret.enableBsm = 0x1e5 in fingerprint[get_e_can_bus(ret)] else: ret.enableBsm = 0x58b in fingerprint[0] From 4918daaf5d8578c467f9626afc0ae0d6dafd9422 Mon Sep 17 00:00:00 2001 From: Matt Crum Date: Mon, 20 Feb 2023 15:25:46 -0600 Subject: [PATCH 436/484] Added FW for 2023 Hyundai Santa Cruz Limited (#27395) * Update values.py Added additional firmware version for Santa Cruz 1st Gen forward camera Updated CAR.SANTA_CRUZ_1ST_GEN's information to include 2023 models * Update values.py added fwdRadar firmware version * Update CARS.md Updated to include 2023 Hyundai Santa Cruz --- docs/CARS.md | 2 +- selfdrive/car/hyundai/values.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index 8860299360..5a29a63f45 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -79,7 +79,7 @@ A supported vehicle is one that just works when you install a comma three. All s |Hyundai|Kona Electric 2022|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai O|| |Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai I|| |Hyundai|Palisade 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| -|Hyundai|Santa Cruz 2021-22[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|| +|Hyundai|Santa Cruz 2021-23[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|| |Hyundai|Santa Fe 2019-20|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai D|| |Hyundai|Santa Fe 2021-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| |Hyundai|Santa Fe Hybrid 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index d0a4e4dd1a..7a16b47a4d 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -191,7 +191,7 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { HyundaiCarInfo("Hyundai Tucson 2023", "All", harness=Harness.hyundai_n), ], CAR.TUCSON_HYBRID_4TH_GEN: HyundaiCarInfo("Hyundai Tucson Hybrid 2022", "All", harness=Harness.hyundai_n), - CAR.SANTA_CRUZ_1ST_GEN: HyundaiCarInfo("Hyundai Santa Cruz 2021-22", harness=Harness.hyundai_n), + CAR.SANTA_CRUZ_1ST_GEN: HyundaiCarInfo("Hyundai Santa Cruz 2021-23", harness=Harness.hyundai_n), # Kia CAR.KIA_FORTE: HyundaiCarInfo("Kia Forte 2019-21", harness=Harness.hyundai_g), @@ -1597,9 +1597,11 @@ FW_VERSIONS = { CAR.SANTA_CRUZ_1ST_GEN: { (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.00 99211-CW000 14M', + b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.00 99211-CW010 14X', ], (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00NX4__ 1.00 1.00 99110-K5000 ', + b'\xf1\x00NX4__ 1.01 1.00 99110-K5000 ', ], }, CAR.KIA_SPORTAGE_5TH_GEN: { From 927289cda8cd8465c1161e64026d7839cc4fc938 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Mon, 20 Feb 2023 13:25:52 -0800 Subject: [PATCH 437/484] increase fetch depth for release submodule check --- release/check-submodules.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/check-submodules.sh b/release/check-submodules.sh index bc85a43c57..2585db6bd7 100755 --- a/release/check-submodules.sh +++ b/release/check-submodules.sh @@ -1,7 +1,7 @@ #!/bin/bash while read hash submodule ref; do - git -C $submodule fetch --depth 200 origin master + git -C $submodule fetch --depth 300 origin master git -C $submodule branch -r --contains $hash | grep "origin/master" if [ "$?" -eq 0 ]; then echo "$submodule ok" From 55da5175aea1a4d206001f2c51d66f4cdf6799c2 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 20 Feb 2023 13:29:37 -0800 Subject: [PATCH 438/484] HKG: update Santa Cruz model years (#27401) 2021 Santa Cruz doesn't exist --- docs/CARS.md | 2 +- selfdrive/car/hyundai/values.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index 5a29a63f45..1b2ec8a2c8 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -79,7 +79,7 @@ A supported vehicle is one that just works when you install a comma three. All s |Hyundai|Kona Electric 2022|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai O|| |Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai I|| |Hyundai|Palisade 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| -|Hyundai|Santa Cruz 2021-23[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|| +|Hyundai|Santa Cruz 2022-23[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|| |Hyundai|Santa Fe 2019-20|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai D|| |Hyundai|Santa Fe 2021-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| |Hyundai|Santa Fe Hybrid 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|| diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 7a16b47a4d..49645fb0b1 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -191,7 +191,7 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { HyundaiCarInfo("Hyundai Tucson 2023", "All", harness=Harness.hyundai_n), ], CAR.TUCSON_HYBRID_4TH_GEN: HyundaiCarInfo("Hyundai Tucson Hybrid 2022", "All", harness=Harness.hyundai_n), - CAR.SANTA_CRUZ_1ST_GEN: HyundaiCarInfo("Hyundai Santa Cruz 2021-23", harness=Harness.hyundai_n), + CAR.SANTA_CRUZ_1ST_GEN: HyundaiCarInfo("Hyundai Santa Cruz 2022-23", harness=Harness.hyundai_n), # Kia CAR.KIA_FORTE: HyundaiCarInfo("Kia Forte 2019-21", harness=Harness.hyundai_g), From 1f89afd1401176641443090fde6ca7a787c890a9 Mon Sep 17 00:00:00 2001 From: ntegan1 Date: Mon, 20 Feb 2023 16:31:30 -0500 Subject: [PATCH 439/484] cabana: missing includes (#27390) --- tools/cabana/dbcmanager.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/cabana/dbcmanager.h b/tools/cabana/dbcmanager.h index b438b3e1c2..267157b31a 100644 --- a/tools/cabana/dbcmanager.h +++ b/tools/cabana/dbcmanager.h @@ -2,6 +2,8 @@ #include #include +#include +#include #include struct MessageId { From bd469d7c54a0cf32b0d038e1d2e72b737071eb94 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Mon, 20 Feb 2023 13:32:52 -0800 Subject: [PATCH 440/484] bump panda --- panda | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda b/panda index 0d2ee00921..d2c2d5f926 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 0d2ee009218009173f26c73a16aefa12dd169de8 +Subproject commit d2c2d5f926537a132a253277998ef350fe866d27 From 10ed70d95293375a36d57d103b7173dc637e861a Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 21 Feb 2023 05:52:16 +0800 Subject: [PATCH 441/484] cabana: show command's text in statusbar (#27398) --- tools/cabana/commands.cc | 24 +++++++++++++----------- tools/cabana/commands.h | 4 ++-- tools/cabana/mainwin.cc | 22 ++++++++++++++++++++++ tools/cabana/mainwin.h | 3 +++ 4 files changed, 40 insertions(+), 13 deletions(-) diff --git a/tools/cabana/commands.cc b/tools/cabana/commands.cc index e03d6d1b11..70700b33b9 100644 --- a/tools/cabana/commands.cc +++ b/tools/cabana/commands.cc @@ -4,24 +4,26 @@ // EditMsgCommand -EditMsgCommand::EditMsgCommand(const MessageId &id, const QString &title, int size, QUndoCommand *parent) - : id(id), new_title(title), new_size(size), QUndoCommand(parent) { +EditMsgCommand::EditMsgCommand(const MessageId &id, const QString &name, int size, QUndoCommand *parent) + : id(id), new_name(name), new_size(size), QUndoCommand(parent) { if (auto msg = dbc()->msg(id)) { - old_title = msg->name; + old_name = msg->name; old_size = msg->size; + setText(QObject::tr("edit message %1:%2").arg(name).arg(id.address)); + } else { + setText(QObject::tr("new message %1:%2").arg(name).arg(id.address)); } - setText(QObject::tr("Edit message %1:%2").arg(id.address).arg(title)); } void EditMsgCommand::undo() { - if (old_title.isEmpty()) + if (old_name.isEmpty()) dbc()->removeMsg(id); else - dbc()->updateMsg(id, old_title, old_size); + dbc()->updateMsg(id, old_name, old_size); } void EditMsgCommand::redo() { - dbc()->updateMsg(id, new_title, new_size); + dbc()->updateMsg(id, new_name, new_size); } // RemoveMsgCommand @@ -29,7 +31,7 @@ void EditMsgCommand::redo() { RemoveMsgCommand::RemoveMsgCommand(const MessageId &id, QUndoCommand *parent) : id(id), QUndoCommand(parent) { if (auto msg = dbc()->msg(id)) { message = *msg; - setText(QObject::tr("Remove message %1:%2").arg(id.address).arg(message.name)); + setText(QObject::tr("remove message %1:%2").arg(message.name).arg(id.address)); } } @@ -50,7 +52,7 @@ void RemoveMsgCommand::redo() { AddSigCommand::AddSigCommand(const MessageId &id, const Signal &sig, QUndoCommand *parent) : id(id), signal(sig), QUndoCommand(parent) { - setText(QObject::tr("Add signal %1 to %2").arg(sig.name).arg(id.address)); + setText(QObject::tr("add signal %1 to %2:%3").arg(sig.name).arg(msgName(id)).arg(id.address)); } void AddSigCommand::undo() { dbc()->removeSignal(id, signal.name); } @@ -60,7 +62,7 @@ void AddSigCommand::redo() { dbc()->addSignal(id, signal); } RemoveSigCommand::RemoveSigCommand(const MessageId &id, const Signal *sig, QUndoCommand *parent) : id(id), signal(*sig), QUndoCommand(parent) { - setText(QObject::tr("Remove signal %1 from %2").arg(signal.name).arg(id.address)); + setText(QObject::tr("remove signal %1 from %2:%3").arg(signal.name).arg(msgName(id)).arg(id.address)); } void RemoveSigCommand::undo() { dbc()->addSignal(id, signal); } @@ -70,7 +72,7 @@ void RemoveSigCommand::redo() { dbc()->removeSignal(id, signal.name); } EditSignalCommand::EditSignalCommand(const MessageId &id, const Signal *sig, const Signal &new_sig, QUndoCommand *parent) : id(id), old_signal(*sig), new_signal(new_sig), QUndoCommand(parent) { - setText(QObject::tr("Edit signal %1").arg(old_signal.name)); + setText(QObject::tr("edit signal %1 in %2:%3").arg(old_signal.name).arg(msgName(id)).arg(id.address)); } void EditSignalCommand::undo() { dbc()->updateSignal(id, new_signal.name, old_signal); } diff --git a/tools/cabana/commands.h b/tools/cabana/commands.h index 57df664974..3565211eb9 100644 --- a/tools/cabana/commands.h +++ b/tools/cabana/commands.h @@ -9,13 +9,13 @@ using namespace dbcmanager; class EditMsgCommand : public QUndoCommand { public: - EditMsgCommand(const MessageId &id, const QString &title, int size, QUndoCommand *parent = nullptr); + EditMsgCommand(const MessageId &id, const QString &name, int size, QUndoCommand *parent = nullptr); void undo() override; void redo() override; private: const MessageId id; - QString old_title, new_title; + QString old_name, new_name; int old_size = 0, new_size = 0; }; diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index 2cba29bf5e..47ad3a4a6d 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -65,6 +65,7 @@ MainWindow::MainWindow() : QMainWindow() { QObject::connect(can, &AbstractStream::streamStarted, this, &MainWindow::loadDBCFromFingerprint); QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &MainWindow::DBCFileChanged); QObject::connect(UndoStack::instance(), &QUndoStack::cleanChanged, this, &MainWindow::undoStackCleanChanged); + QObject::connect(UndoStack::instance(), &QUndoStack::indexChanged, this, &MainWindow::undoStackIndexChanged); } void MainWindow::createActions() { @@ -186,7 +187,28 @@ void MainWindow::createShortcuts() { // TODO: add more shortcuts here. } +void MainWindow::undoStackIndexChanged(int index) { + int count = UndoStack::instance()->count(); + if (count >= 0) { + QString command_text; + if (index == count) { + command_text = (count == prev_undostack_count ? "Redo " : "") + UndoStack::instance()->text(index - 1); + } else if (index < prev_undostack_index) { + command_text = tr("Undo %1").arg(UndoStack::instance()->text(index)); + } else if (index > prev_undostack_index) { + command_text = tr("Redo %1").arg(UndoStack::instance()->text(index - 1)); + } + statusBar()->showMessage(command_text, 2000); + } + prev_undostack_index = index; + prev_undostack_count = count; +} + void MainWindow::undoStackCleanChanged(bool clean) { + if (clean) { + prev_undostack_index = 0; + prev_undostack_count = 0; + } setWindowModified(!clean); } diff --git a/tools/cabana/mainwin.h b/tools/cabana/mainwin.h index e7a39adab7..85d221a231 100644 --- a/tools/cabana/mainwin.h +++ b/tools/cabana/mainwin.h @@ -54,6 +54,7 @@ protected: void setOption(); void findSimilarBits(); void undoStackCleanChanged(bool clean); + void undoStackIndexChanged(int index); void onlineHelp(); VideoWidget *video_widget = nullptr; @@ -70,6 +71,8 @@ protected: enum { MAX_RECENT_FILES = 15 }; QAction *recent_files_acts[MAX_RECENT_FILES] = {}; QMenu *open_recent_menu = nullptr; + int prev_undostack_index = 0; + int prev_undostack_count = 0; friend class OnlineHelp; }; From ba3d3bd940d1796c863b80a7d27291c9c6249440 Mon Sep 17 00:00:00 2001 From: Chris Hart Date: Mon, 20 Feb 2023 14:03:42 -0800 Subject: [PATCH 442/484] SANTA_FE_2022: add missing transmission FW (#27369) * Added new fingerprints for updated SANTAFE_2022 * Update values.py fixed missing comma that was breaking build * Revert "Update values.py" This reverts commit 58837eadd4cfb57ec56a45592c21b251e6c52957. * Revert "Added new fingerprints for updated SANTAFE_2022" This reverts commit 3bec6e0196e9fcac83a34f79b7b860f2e24aa763. * add FW, remove radar FW from long query --------- Co-authored-by: Chris Hart Co-authored-by: Shane Smiskol --- selfdrive/car/hyundai/values.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 49645fb0b1..4b7a295246 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -751,7 +751,6 @@ FW_VERSIONS = { (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00TM__ SCC F-CUP 1.00 1.00 99110-S1500 ', b'\xf1\x8799110S1500\xf1\x00TM__ SCC F-CUP 1.00 1.00 99110-S1500 ', - b'\xf1\x8799110S1500\xf1\x00TM__ SCC FHCUP 1.00 1.00 99110-S1500 ', b'\xf1\x00TM__ SCC FHCUP 1.00 1.00 99110-S1500 ', ], (Ecu.abs, 0x7d1, None): [ @@ -785,6 +784,7 @@ FW_VERSIONS = { b'\xf1\x00TMA MFC AT USA LHD 1.00 1.01 99211-S2500 210205', ], (Ecu.transmission, 0x7e1, None): [ + b'\xf1\x00T02601BL T02900A1 VTMPT25XXX900NSA\xf3\xf4Uj', b'\xf1\x87SDMXCA9087684GN1VfvgUUeVwwgwwwwwffffU?\xfb\xff\x97\x88\x7f\xff+\xa4\xf1\x89HT6WAD00A1\xf1\x82STM4G25NH1\x00\x00\x00\x00\x00\x00', b'\xf1\x00T02601BL T02730A1 VTMPT25XXX730NS2\xa6\x06\x88\xf7', b'\xf1\x87SDMXCA8653204GN1EVugEUuWwwwwww\x87wwwwwv/\xfb\xff\xa8\x88\x9f\xff\xa5\x9c\xf1\x89HT6WAD00A1\xf1\x82STM4G25NH1\x00\x00\x00\x00\x00\x00', From 1bd15f00a70b72caa99e7be87571595adb5ea074 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 20 Feb 2023 14:07:22 -0800 Subject: [PATCH 443/484] Toyota: expand C-HR Hybrid model years (#27402) we probably support 2021 too --- docs/CARS.md | 2 +- selfdrive/car/toyota/values.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index 1b2ec8a2c8..59eece1e4b 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -179,7 +179,7 @@ A supported vehicle is one that just works when you install a comma three. All s |Toyota|C-HR 2017-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| |Toyota|C-HR 2021|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| |Toyota|C-HR Hybrid 2017-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Toyota|C-HR Hybrid 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|C-HR Hybrid 2021-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| |Toyota|Camry 2018-20|All|Stock|0 mph[6](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| |Toyota|Camry 2021-22|All|openpilot|0 mph[6](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| |Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index b6a556438d..8e118293ee 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -120,7 +120,7 @@ CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = { CAR.CHR: ToyotaCarInfo("Toyota C-HR 2017-20"), CAR.CHR_TSS2: ToyotaCarInfo("Toyota C-HR 2021"), CAR.CHRH: ToyotaCarInfo("Toyota C-HR Hybrid 2017-19"), - CAR.CHRH_TSS2: ToyotaCarInfo("Toyota C-HR Hybrid 2022"), + CAR.CHRH_TSS2: ToyotaCarInfo("Toyota C-HR Hybrid 2021-22"), CAR.COROLLA: ToyotaCarInfo("Toyota Corolla 2017-19"), CAR.COROLLA_TSS2: [ ToyotaCarInfo("Toyota Corolla 2020-22", video_link="https://www.youtube.com/watch?v=_66pXk0CBYA"), From e1051e5e5c52267407cca864f984fee2328c2cce Mon Sep 17 00:00:00 2001 From: kaischiefer-srd <106266385+kaischiefer-srd@users.noreply.github.com> Date: Mon, 20 Feb 2023 17:11:46 -0500 Subject: [PATCH 444/484] Toyota: add engine FW for 2021 NX300 (#27326) added engine fw for our 2021 NX300 F sport --- selfdrive/car/toyota/values.py | 1 + 1 file changed, 1 insertion(+) diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index 8e118293ee..0fcabe163c 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -1726,6 +1726,7 @@ FW_VERSIONS = { (Ecu.engine, 0x700, None): [ b'\x018966378B2100\x00\x00\x00\x00', b'\x018966378B3000\x00\x00\x00\x00', + b'\x018966378B4100\x00\x00\x00\x00', b'\x018966378G3000\x00\x00\x00\x00', b'\x018966378B2000\x00\x00\x00\x00', ], From 4efd246bac004f3b6f1f7d733e6e285b3fdc8322 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 21 Feb 2023 06:16:30 +0800 Subject: [PATCH 445/484] cabana: improve UI & fix bugs (#27387) * improve ui * keep splitter size after msg changed * no leading spaces allowed in msg filter and signal filter * draw color byte AlignCenter * always set as current index * reduce chart flickers while resizing * dispaly more info in tooltip for signal * narrow combobox * use   * typo * private sigs,fix bugs * merge #27383 * no expanding after undo/redo * gray color in tooltip * clear current_msg_id before reset model * dont call setmeesage if id is the same * fix bugs * cleanup * dont fetch logs if invisible * add new CenterWidget, make sure msg_id is always valid * cache icons * cleanup paint byte color * merge #27385 implement sizeHint * cleanup code * fillrect if alpha>0 --- tools/cabana/binaryview.cc | 90 +++++++++--------- tools/cabana/binaryview.h | 4 +- tools/cabana/chartswidget.cc | 29 +++--- tools/cabana/chartswidget.h | 2 +- tools/cabana/commands.cc | 4 +- tools/cabana/dbcmanager.cc | 53 ++++++----- tools/cabana/dbcmanager.h | 6 +- tools/cabana/detailwidget.cc | 122 +++++++++++++------------ tools/cabana/detailwidget.h | 27 +++--- tools/cabana/historylog.cc | 76 ++++++++------- tools/cabana/historylog.h | 17 ++-- tools/cabana/mainwin.cc | 10 +- tools/cabana/mainwin.h | 2 +- tools/cabana/messageswidget.cc | 28 +++--- tools/cabana/messageswidget.h | 3 - tools/cabana/signaledit.cc | 83 ++++++++--------- tools/cabana/signaledit.h | 6 +- tools/cabana/streams/abstractstream.cc | 10 +- tools/cabana/tests/test_cabana.cc | 14 +-- tools/cabana/util.cc | 49 +++++----- tools/cabana/util.h | 4 + tools/cabana/videowidget.cc | 4 +- 22 files changed, 344 insertions(+), 299 deletions(-) diff --git a/tools/cabana/binaryview.cc b/tools/cabana/binaryview.cc index 5ea5ea2d99..d9b00f6882 100644 --- a/tools/cabana/binaryview.cc +++ b/tools/cabana/binaryview.cc @@ -32,7 +32,6 @@ BinaryView::BinaryView(QWidget *parent) : QTableView(parent) { verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); verticalHeader()->setDefaultSectionSize(CELL_HEIGHT); horizontalHeader()->hide(); - setFrameShape(QFrame::NoFrame); setShowGrid(false); setMouseTracking(true); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); @@ -44,17 +43,17 @@ BinaryView::BinaryView(QWidget *parent) : QTableView(parent) { setWhatsThis(R"( Binary View
- Shortcuts:
+ Shortcuts
Delete Signal: - x , - Backspace , - Delete
- Change endianness: e
- Change singedness: s
+  x , +  Backspace , +  Delete 
+ Change endianness:  e 
+ Change singedness:  s 
Open chart: - c , - p , - g
+  c , +  p , +  g  )"); } @@ -108,14 +107,14 @@ void BinaryView::addShortcuts() { QObject::connect(shortcut_plot_c, &QShortcut::activated, shortcut_plot, &QShortcut::activated); QObject::connect(shortcut_plot, &QShortcut::activated, [=]{ if (hovered_sig != nullptr) { - emit showChart(*model->msg_id, hovered_sig, true, false); + emit showChart(model->msg_id, hovered_sig, true, false); } }); } QSize BinaryView::minimumSizeHint() const { - return {(horizontalHeader()->minimumSectionSize() + 1) * 9 + VERTICAL_HEADER_WIDTH, - CELL_HEIGHT * std::min(model->rowCount(), 10)}; + return {(horizontalHeader()->minimumSectionSize() + 1) * 9 + VERTICAL_HEADER_WIDTH + 2, + CELL_HEIGHT * std::min(model->rowCount(), 10) + 2}; } void BinaryView::highlight(const Signal *sig) { @@ -127,6 +126,16 @@ void BinaryView::highlight(const Signal *sig) { emit model->dataChanged(index, index, {Qt::DisplayRole}); } } + + if (sig && underMouse()) { + QString tooltip = tr(R"(%1
+ Size:%2 LE:%3 SGD:%4 + )").arg(sig->name).arg(sig->size).arg(sig->is_little_endian ? "Y" : "N").arg(sig->is_signed ? "Y" : "N"); + QToolTip::showText(QCursor::pos(), tooltip, this, rect()); + } else { + QToolTip::showText(QCursor::pos(), "", this, rect()); + } + hovered_sig = sig; emit signalHovered(hovered_sig); } @@ -169,7 +178,6 @@ void BinaryView::highlightPosition(const QPoint &pos) { auto item = (BinaryViewModel::Item *)index.internalPointer(); const Signal *sig = item->sigs.isEmpty() ? nullptr : item->sigs.back(); highlight(sig); - QToolTip::showText(pos, sig ? sig->name : "", this, rect()); } } @@ -210,8 +218,6 @@ void BinaryView::setMessage(const MessageId &message_id) { } void BinaryView::refresh() { - if (!model->msg_id) return; - clearSelection(); anchor_index = QModelIndex(); resize_sig = nullptr; @@ -245,26 +251,26 @@ std::tuple BinaryView::getSelection(QModelIndex index) { void BinaryViewModel::refresh() { beginResetModel(); items.clear(); - if (auto dbc_msg = dbc()->msg(*msg_id)) { + if (auto dbc_msg = dbc()->msg(msg_id)) { row_count = dbc_msg->size; items.resize(row_count * column_count); - for (auto &sig : dbc_msg->sigs) { - auto [start, end] = getSignalRange(&sig); + for (auto sig : dbc_msg->getSignals()) { + auto [start, end] = getSignalRange(sig); for (int j = start; j <= end; ++j) { - int bit_index = sig.is_little_endian ? bigEndianBitIndex(j) : j; + int bit_index = sig->is_little_endian ? bigEndianBitIndex(j) : j; int idx = column_count * (bit_index / 8) + bit_index % 8; if (idx >= items.size()) { - qWarning() << "signal " << sig.name << "out of bounds.start_bit:" << sig.start_bit << "size:" << sig.size; + qWarning() << "signal " << sig->name << "out of bounds.start_bit:" << sig->start_bit << "size:" << sig->size; break; } - if (j == start) sig.is_little_endian ? items[idx].is_lsb = true : items[idx].is_msb = true; - if (j == end) sig.is_little_endian ? items[idx].is_msb = true : items[idx].is_lsb = true; - items[idx].bg_color = getColor(&sig); - items[idx].sigs.push_back(&sig); + if (j == start) sig->is_little_endian ? items[idx].is_lsb = true : items[idx].is_msb = true; + if (j == end) sig->is_little_endian ? items[idx].is_msb = true : items[idx].is_lsb = true; + items[idx].bg_color = getColor(sig); + items[idx].sigs.push_back(sig); } } } else { - row_count = can->lastMessage(*msg_id).dat.size(); + row_count = can->lastMessage(msg_id).dat.size(); items.resize(row_count * column_count); } endResetModel(); @@ -273,7 +279,7 @@ void BinaryViewModel::refresh() { void BinaryViewModel::updateState() { auto prev_items = items; - const auto &last_msg = can->lastMessage(*msg_id); + const auto &last_msg = can->lastMessage(msg_id); const auto &binary = last_msg.dat; // data size may changed. @@ -283,33 +289,29 @@ void BinaryViewModel::updateState() { items.resize(row_count * column_count); endInsertRows(); } + + double max_f = 255.0; + double factor = 0.25; + double scaler = max_f / log2(1.0 + factor); char hex[3] = {'\0'}; for (int i = 0; i < binary.size(); ++i) { for (int j = 0; j < 8; ++j) { - items[i * column_count + j].val = ((binary[i] >> (7 - j)) & 1) != 0 ? '1' : '0'; - + auto &item = items[i * column_count + j]; + item.val = ((binary[i] >> (7 - j)) & 1) != 0 ? '1' : '0'; // Bit update frequency based highlighting - bool has_signal = items[i * column_count + j].sigs.size() > 0; - double offset = has_signal ? 50 : 0; - - double min_f = last_msg.bit_change_counts[i][7 - j] == 0 ? offset : offset + 25; - double max_f = 255.0; - - double factor = 0.25; - double scaler = max_f / log2(1.0 + factor); - - double alpha = std::clamp(offset + log2(1.0 + factor * (double)last_msg.bit_change_counts[i][7 - j] / (double)last_msg.count) * scaler, min_f, max_f); - items[i * column_count + j].bg_color.setAlpha(alpha); + double offset = !item.sigs.empty() ? 50 : 0; + auto n = last_msg.bit_change_counts[i][7 - j]; + double min_f = n == 0 ? offset : offset + 25; + double alpha = std::clamp(offset + log2(1.0 + factor * (double)n / (double)last_msg.count) * scaler, min_f, max_f); + item.bg_color.setAlpha(alpha); } hex[0] = toHex(binary[i] >> 4); hex[1] = toHex(binary[i] & 0xf); items[i * column_count + 8].val = hex; items[i * column_count + 8].bg_color = last_msg.colors[i]; } - for (int i = binary.size(); i < row_count; ++i) { - for (int j = 0; j < column_count; ++j) { - items[i * column_count + j].val = "-"; - } + for (int i = binary.size() * column_count; i < items.size(); ++i) { + items[i].val = "-"; } for (int i = 0; i < items.size(); ++i) { diff --git a/tools/cabana/binaryview.h b/tools/cabana/binaryview.h index 1d6d5d0b07..681ac1fbf3 100644 --- a/tools/cabana/binaryview.h +++ b/tools/cabana/binaryview.h @@ -1,7 +1,5 @@ #pragma once -#include - #include #include #include @@ -50,7 +48,7 @@ public: }; std::vector items; - std::optional msg_id; + MessageId msg_id; int row_count = 0; const int column_count = 9; }; diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 8515ffcfe7..e315f82398 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -11,15 +11,16 @@ #include #include #include -#include #include #include const int MAX_COLUMN_COUNT = 4; // ChartsWidget -ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { +ChartsWidget::ChartsWidget(QWidget *parent) : QFrame(parent) { + setFrameStyle(QFrame::StyledPanel | QFrame::Plain); QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(0, 0, 0, 0); // toolbar QToolBar *toolbar = new QToolBar(tr("Charts"), this); @@ -43,6 +44,7 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { range_lb_action = toolbar->addWidget(range_lb = new QLabel(this)); range_slider = new QSlider(Qt::Horizontal, this); + range_slider->setMaximumWidth(200); range_slider->setToolTip(tr("Set the chart range")); range_slider->setRange(1, settings.max_cached_minutes * 60); range_slider->setSingleStep(1); @@ -67,6 +69,7 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) { charts_main_layout->addStretch(0); QScrollArea *charts_scroll = new QScrollArea(this); + charts_scroll->setFrameStyle(QFrame::NoFrame); charts_scroll->setWidgetResizable(true); charts_scroll->setWidget(charts_container); charts_scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); @@ -329,18 +332,12 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { move_icon = new QGraphicsPixmapItem(utils::icon("grip-horizontal"), chart); move_icon->setToolTip(tr("Drag and drop to combine charts")); - QToolButton *remove_btn = new QToolButton(); - remove_btn->setIcon(utils::icon("x")); - remove_btn->setAutoRaise(true); - remove_btn->setToolTip(tr("Remove Chart")); + QToolButton *remove_btn = toolButton("x", tr("Remove Chart")); close_btn_proxy = new QGraphicsProxyWidget(chart); close_btn_proxy->setWidget(remove_btn); close_btn_proxy->setZValue(chart->zValue() + 11); - QToolButton *manage_btn = new QToolButton(); - manage_btn->setToolButtonStyle(Qt::ToolButtonIconOnly); - manage_btn->setIcon(utils::icon("list")); - manage_btn->setAutoRaise(true); + QToolButton *manage_btn = toolButton("list", ""); QMenu *menu = new QMenu(this); line_series_action = menu->addAction(tr("Line"), [this]() { setSeriesType(QAbstractSeries::SeriesTypeLine); }); line_series_action->setCheckable(true); @@ -434,12 +431,12 @@ void ChartView::manageSeries() { } void ChartView::resizeEvent(QResizeEvent *event) { - QChartView::resizeEvent(event); updatePlotArea(align_to); int x = event->size().width() - close_btn_proxy->size().width() - 11; close_btn_proxy->setPos(x, 8); manage_btn_proxy->setPos(x - manage_btn_proxy->size().width() - 5, 8); move_icon->setPos(11, 8); + QChartView::resizeEvent(event); } void ChartView::updatePlotArea(int left) { @@ -448,7 +445,7 @@ void ChartView::updatePlotArea(int left) { align_to = left; background->setRect(r); chart()->legend()->setGeometry(QRect(r.left(), r.top(), r.width(), 45)); - chart()->setPlotArea(QRect(align_to, r.top() + 45, r.width() - align_to - 22, r.height() - 80)); + chart()->setPlotArea(QRect(align_to, r.top() + 45, r.width() - align_to - 36, r.height() - 80)); chart()->layout()->invalidate(); } } @@ -570,7 +567,7 @@ void ChartView::updateAxisY() { QFontMetrics fm(axis_y->labelsFont()); int n = qMax(int(-qFloor(std::log10((max_y - min_y) / (tick_count - 1)))), 0) + 1; - y_label_width = qMax(fm.width(QString::number(min_y, 'f', n)), fm.width(QString::number(max_y, 'f', n))) + 20; // left margin 20 + y_label_width = qMax(fm.width(QString::number(min_y, 'f', n)), fm.width(QString::number(max_y, 'f', n))) + 15; // left margin 15 emit axisYLabelWidthChanged(y_label_width); } } @@ -887,10 +884,10 @@ void SeriesSelector::updateAvailableList(int index) { available_list->clear(); MessageId msg_id = msgs_combo->itemData(index).value(); auto selected_items = seletedItems(); - for (auto &s : dbc()->msg(msg_id)->sigs) { - bool is_selected = std::any_of(selected_items.begin(), selected_items.end(), [=, sig=&s](auto it) { return it->msg_id == msg_id && it->sig == sig; }); + for (auto s : dbc()->msg(msg_id)->getSignals()) { + bool is_selected = std::any_of(selected_items.begin(), selected_items.end(), [=, sig=s](auto it) { return it->msg_id == msg_id && it->sig == sig; }); if (!is_selected) { - addItemToList(available_list, msg_id, &s); + addItemToList(available_list, msg_id, s); } } } diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index 7089b4eaee..a1dcf32513 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -92,7 +92,7 @@ private: friend class ChartsWidget; }; -class ChartsWidget : public QWidget { +class ChartsWidget : public QFrame { Q_OBJECT public: diff --git a/tools/cabana/commands.cc b/tools/cabana/commands.cc index 70700b33b9..9b9724aada 100644 --- a/tools/cabana/commands.cc +++ b/tools/cabana/commands.cc @@ -38,8 +38,8 @@ RemoveMsgCommand::RemoveMsgCommand(const MessageId &id, QUndoCommand *parent) : void RemoveMsgCommand::undo() { if (!message.name.isEmpty()) { dbc()->updateMsg(id, message.name, message.size); - for (auto &s : message.sigs) - dbc()->addSignal(id, s); + for (auto s : message.getSignals()) + dbc()->addSignal(id, *s); } } diff --git a/tools/cabana/dbcmanager.cc b/tools/cabana/dbcmanager.cc index ae3a65a99b..32113c2f22 100644 --- a/tools/cabana/dbcmanager.cc +++ b/tools/cabana/dbcmanager.cc @@ -10,10 +10,6 @@ namespace dbcmanager { -void sortSignalsByAddress(QList &sigs) { - std::sort(sigs.begin(), sigs.end(), [](auto &a, auto &b) { return a.start_bit < b.start_bit; }); -} - bool DBCManager::open(const QString &dbc_file_name, QString *error) { QString opendbc_file_path = QString("%1/%2.dbc").arg(OPENDBC_FILE_PATH, dbc_file_name); QFile file(opendbc_file_path); @@ -83,27 +79,27 @@ QString DBCManager::generateDBC() { QString dbc_string, signal_comment, val_desc; for (auto &[address, m] : msgs) { dbc_string += QString("BO_ %1 %2: %3 XXX\n").arg(address).arg(m.name).arg(m.size); - for (auto &sig : m.sigs) { + for (auto sig : m.getSignals()) { dbc_string += QString(" SG_ %1 : %2|%3@%4%5 (%6,%7) [%8|%9] \"%10\" XXX\n") - .arg(sig.name) - .arg(sig.start_bit) - .arg(sig.size) - .arg(sig.is_little_endian ? '1' : '0') - .arg(sig.is_signed ? '-' : '+') - .arg(sig.factor, 0, 'g', std::numeric_limits::digits10) - .arg(sig.offset, 0, 'g', std::numeric_limits::digits10) - .arg(sig.min) - .arg(sig.max) - .arg(sig.unit); - if (!sig.comment.isEmpty()) { - signal_comment += QString("CM_ SG_ %1 %2 \"%3\";\n").arg(address).arg(sig.name).arg(sig.comment); + .arg(sig->name) + .arg(sig->start_bit) + .arg(sig->size) + .arg(sig->is_little_endian ? '1' : '0') + .arg(sig->is_signed ? '-' : '+') + .arg(sig->factor, 0, 'g', std::numeric_limits::digits10) + .arg(sig->offset, 0, 'g', std::numeric_limits::digits10) + .arg(sig->min) + .arg(sig->max) + .arg(sig->unit); + if (!sig->comment.isEmpty()) { + signal_comment += QString("CM_ SG_ %1 %2 \"%3\";\n").arg(address).arg(sig->name).arg(sig->comment); } - if (!sig.val_desc.isEmpty()) { - QString text; - for (auto &[val, desc] : sig.val_desc) { - text += QString("%1 \"%2\"").arg(val, desc); + if (!sig->val_desc.isEmpty()) { + QStringList text; + for (auto &[val, desc] : sig->val_desc) { + text << QString("%1 \"%2\"").arg(val, desc); } - val_desc += QString("VAL_ %1 %2 %3;\n").arg(address).arg(sig.name).arg(text); + val_desc += QString("VAL_ %1 %2 %3;\n").arg(address).arg(sig->name).arg(text.join(" ")); } } dbc_string += "\n"; @@ -127,7 +123,6 @@ void DBCManager::addSignal(const MessageId &id, const Signal &sig) { if (auto m = const_cast(msg(id.address))) { m->sigs.push_back(sig); auto s = &m->sigs.last(); - sortSignalsByAddress(m->sigs); emit signalAdded(id.address, s); } } @@ -136,7 +131,6 @@ void DBCManager::updateSignal(const MessageId &id, const QString &sig_name, cons if (auto m = const_cast(msg(id))) { if (auto s = (Signal *)m->sig(sig_name)) { *s = sig; - sortSignalsByAddress(m->sigs); emit signalUpdated(s); } } @@ -157,6 +151,16 @@ DBCManager *dbc() { return &dbc_manager; } +// Msg + +std::vector Msg::getSignals() const { + std::vector ret; + ret.reserve(sigs.size()); + for (auto &sig : sigs) ret.push_back(&sig); + std::sort(ret.begin(), ret.end(), [](auto l, auto r) { return l->start_bit < r->start_bit; }); + return ret; +} + // helper functions static QVector BIG_ENDIAN_START_BITS = []() { @@ -246,7 +250,6 @@ bool dbcmanager::DBCManager::open(const QString &name, const QString &content, Q sig.offset = s.offset; sig.is_little_endian = s.is_little_endian; } - sortSignalsByAddress(m.sigs); } parseExtraInfo(content); name_ = name; diff --git a/tools/cabana/dbcmanager.h b/tools/cabana/dbcmanager.h index 267157b31a..d877347796 100644 --- a/tools/cabana/dbcmanager.h +++ b/tools/cabana/dbcmanager.h @@ -52,12 +52,16 @@ struct Signal { struct Msg { QString name; uint32_t size; - QList sigs; + std::vector getSignals() const; const Signal *sig(const QString &sig_name) const { auto it = std::find_if(sigs.begin(), sigs.end(), [&](auto &s) { return s.name == sig_name; }); return it != sigs.end() ? &(*it) : nullptr; } + +private: + QList sigs; + friend class DBCManager; }; class DBCManager : public QObject { diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index 882ea932c6..01fbccf7db 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -3,15 +3,13 @@ #include #include #include -#include #include "tools/cabana/commands.h" // DetailWidget DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(charts), QWidget(parent) { - QWidget *main_widget = new QWidget(this); - QVBoxLayout *main_layout = new QVBoxLayout(main_widget); + QVBoxLayout *main_layout = new QVBoxLayout(this); main_layout->setContentsMargins(0, 0, 0, 0); // tabbar @@ -23,22 +21,22 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart main_layout->addWidget(tabbar); // message title - QToolBar *toolbar = new QToolBar(this); - toolbar->setIconSize({16, 16}); - toolbar->addWidget(new QLabel("time:")); + QHBoxLayout *title_layout = new QHBoxLayout(); + title_layout->setContentsMargins(0, 6, 0, 0); time_label = new QLabel(this); - time_label->setStyleSheet("font-weight:bold"); - toolbar->addWidget(time_label); + time_label->setToolTip(tr("Current time")); + time_label->setStyleSheet("QLabel{font-weight:bold;}"); + title_layout->addWidget(time_label); name_label = new ElidedLabel(this); - name_label->setContentsMargins(5, 0, 5, 0); - name_label->setStyleSheet("font-weight:bold;"); + name_label->setStyleSheet("QLabel{font-weight:bold;}"); name_label->setAlignment(Qt::AlignCenter); name_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - toolbar->addWidget(name_label); - toolbar->addAction(utils::icon("pencil"), "", this, &DetailWidget::editMsg)->setToolTip(tr("Edit Message")); - remove_msg_act = toolbar->addAction(utils::icon("x-lg"), "", this, &DetailWidget::removeMsg); - remove_msg_act->setToolTip(tr("Remove Message")); - main_layout->addWidget(toolbar); + title_layout->addWidget(name_label); + auto edit_btn = toolButton("pencil", tr("Edit Message")); + title_layout->addWidget(edit_btn); + remove_btn = toolButton("x-lg", tr("Remove Message")); + title_layout->addWidget(remove_btn); + main_layout->addLayout(title_layout); // warning warning_widget = new QWidget(this); @@ -59,19 +57,18 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart splitter->setStretchFactor(1, 1); tab_widget = new QTabWidget(this); + tab_widget->setStyleSheet("QTabWidget::pane {border: none; margin-bottom: -2px;}"); tab_widget->setTabPosition(QTabWidget::South); tab_widget->addTab(splitter, utils::icon("file-earmark-ruled"), "&Msg"); tab_widget->addTab(history_log = new LogsWidget(this), utils::icon("stopwatch"), "&Logs"); main_layout->addWidget(tab_widget); - stacked_layout = new QStackedLayout(this); - stacked_layout->addWidget(new WelcomeWidget(this)); - stacked_layout->addWidget(main_widget); - + QObject::connect(edit_btn, &QToolButton::clicked, this, &DetailWidget::editMsg); + QObject::connect(remove_btn, &QToolButton::clicked, this, &DetailWidget::removeMsg); QObject::connect(binary_view, &BinaryView::resizeSignal, signal_view->model, &SignalModel::resizeSignal); QObject::connect(binary_view, &BinaryView::addSignal, signal_view->model, &SignalModel::addSignal); QObject::connect(binary_view, &BinaryView::signalHovered, signal_view, &SignalView::signalHovered); - QObject::connect(binary_view, &BinaryView::signalClicked, signal_view, &SignalView::expandSignal); + QObject::connect(binary_view, &BinaryView::signalClicked, [this](const Signal *s) { signal_view->selectSignal(s, true); }); QObject::connect(binary_view, &BinaryView::editSignal, signal_view->model, &SignalModel::saveSignal); QObject::connect(binary_view, &BinaryView::removeSignal, signal_view->model, &SignalModel::removeSignal); QObject::connect(binary_view, &BinaryView::showChart, charts, &ChartsWidget::showChart); @@ -87,9 +84,7 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart setMessage(tabbar->tabData(index).value()); } }); - QObject::connect(tabbar, &QTabBar::tabCloseRequested, [this](int index) { - tabbar->removeTab(index); - }); + QObject::connect(tabbar, &QTabBar::tabCloseRequested, tabbar, &QTabBar::removeTab); QObject::connect(charts, &ChartsWidget::seriesChanged, signal_view, &SignalView::updateChartState); } @@ -108,18 +103,8 @@ void DetailWidget::showTabBarContextMenu(const QPoint &pt) { } } -void DetailWidget::removeAll() { - msg_id = std::nullopt; - tabbar->blockSignals(true); - while (tabbar->count() > 0) { - tabbar->removeTab(0); - } - tabbar->blockSignals(false); - stacked_layout->setCurrentIndex(0); -} - void DetailWidget::setMessage(const MessageId &message_id) { - msg_id = message_id; + if (std::exchange(msg_id, message_id) == message_id) return; tabbar->blockSignals(true); int index = tabbar->count() - 1; @@ -135,25 +120,18 @@ void DetailWidget::setMessage(const MessageId &message_id) { tabbar->blockSignals(false); setUpdatesEnabled(false); - - signal_view->setMessage(*msg_id); - binary_view->setMessage(*msg_id); - history_log->setMessage(*msg_id); - - stacked_layout->setCurrentIndex(1); + signal_view->setMessage(msg_id); + binary_view->setMessage(msg_id); + history_log->setMessage(msg_id); refresh(); - splitter->setSizes({1, 2}); - setUpdatesEnabled(true); } void DetailWidget::refresh() { - if (!msg_id) return; - QStringList warnings; - auto msg = dbc()->msg(*msg_id); + auto msg = dbc()->msg(msg_id); if (msg) { - if (msg->size != can->lastMessage(*msg_id).dat.size()) { + if (msg->size != can->lastMessage(msg_id).dat.size()) { warnings.push_back(tr("Message size (%1) is incorrect.").arg(msg->size)); } for (auto s : binary_view->getOverlappingSignals()) { @@ -162,8 +140,8 @@ void DetailWidget::refresh() { } else { warnings.push_back(tr("Drag-Select in binary view to create new signal.")); } - remove_msg_act->setEnabled(msg != nullptr); - name_label->setText(msgName(*msg_id)); + remove_btn->setEnabled(msg != nullptr); + name_label->setText(msgName(msg_id)); if (!warnings.isEmpty()) { warning_label->setText(warnings.join('\n')); @@ -174,7 +152,7 @@ void DetailWidget::refresh() { void DetailWidget::updateState(const QHash *msgs) { time_label->setText(QString::number(can->currentSec(), 'f', 3)); - if (!msg_id || (msgs && !msgs->contains(*msg_id))) + if ((msgs && !msgs->contains(msg_id))) return; if (tab_widget->currentIndex() == 0) @@ -184,17 +162,16 @@ void DetailWidget::updateState(const QHash *msgs) { } void DetailWidget::editMsg() { - MessageId id = *msg_id; - auto msg = dbc()->msg(id); - int size = msg ? msg->size : can->lastMessage(id).dat.size(); - EditMessageDialog dlg(id, msgName(id), size, this); + auto msg = dbc()->msg(msg_id); + int size = msg ? msg->size : can->lastMessage(msg_id).dat.size(); + EditMessageDialog dlg(msg_id, msgName(msg_id), size, this); if (dlg.exec()) { - UndoStack::push(new EditMsgCommand(*msg_id, dlg.name_edit->text(), dlg.size_spin->value())); + UndoStack::push(new EditMsgCommand(msg_id, dlg.name_edit->text(), dlg.size_spin->value())); } } void DetailWidget::removeMsg() { - UndoStack::push(new RemoveMsgCommand(*msg_id)); + UndoStack::push(new RemoveMsgCommand(msg_id)); } // EditMessageDialog @@ -240,10 +217,34 @@ void EditMessageDialog::validateName(const QString &text) { btn_box->button(QDialogButtonBox::Ok)->setEnabled(valid); } -// WelcomeWidget +// CenterWidget -WelcomeWidget::WelcomeWidget(QWidget *parent) : QWidget(parent) { +CenterWidget::CenterWidget(ChartsWidget *charts, QWidget *parent) : charts(charts), QWidget(parent) { QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(0, 0, 0, 0); + main_layout->addWidget(welcome_widget = createWelcomeWidget()); +} + +void CenterWidget::setMessage(const MessageId &msg_id) { + if (!detail_widget) { + delete welcome_widget; + welcome_widget = nullptr; + layout()->addWidget(detail_widget = new DetailWidget(charts, this)); + } + detail_widget->setMessage(msg_id); +} + +void CenterWidget::clear() { + delete detail_widget; + detail_widget = nullptr; + if (!welcome_widget) { + layout()->addWidget(welcome_widget = createWelcomeWidget()); + } +} + +QWidget *CenterWidget::createWelcomeWidget() { + QWidget *w = new QWidget(this); + QVBoxLayout *main_layout = new QVBoxLayout(w); main_layout->addStretch(0); QLabel *logo = new QLabel("CABANA"); logo->setAlignment(Qt::AlignCenter); @@ -268,7 +269,8 @@ WelcomeWidget::WelcomeWidget(QWidget *parent) : QWidget(parent) { main_layout->addLayout(newShortcutRow("WhatsThis", "Shift+F1")); main_layout->addStretch(0); - setStyleSheet("QLabel{color:darkGray;}"); - setBackgroundRole(QPalette::Base); - setAutoFillBackground(true); + w->setStyleSheet("QLabel{color:darkGray;}"); + w->setBackgroundRole(QPalette::Base); + w->setAutoFillBackground(true); + return w; } diff --git a/tools/cabana/detailwidget.h b/tools/cabana/detailwidget.h index b3e353d539..0bfd8a74af 100644 --- a/tools/cabana/detailwidget.h +++ b/tools/cabana/detailwidget.h @@ -2,9 +2,7 @@ #include #include -#include #include -#include #include "selfdrive/ui/qt/widgets/controls.h" #include "tools/cabana/binaryview.h" @@ -24,11 +22,6 @@ public: QSpinBox *size_spin; }; -class WelcomeWidget : public QWidget { -public: - WelcomeWidget(QWidget *parent); -}; - class DetailWidget : public QWidget { Q_OBJECT @@ -36,7 +29,6 @@ public: DetailWidget(ChartsWidget *charts, QWidget *parent); void setMessage(const MessageId &message_id); void refresh(); - void removeAll(); QSize minimumSizeHint() const override { return binary_view->minimumSizeHint(); } private: @@ -45,17 +37,30 @@ private: void removeMsg(); void updateState(const QHash * msgs = nullptr); - std::optional msg_id; + MessageId msg_id; QLabel *time_label, *warning_icon, *warning_label; ElidedLabel *name_label; QWidget *warning_widget; QTabBar *tabbar; QTabWidget *tab_widget; - QAction *remove_msg_act; + QToolButton *remove_btn; LogsWidget *history_log; BinaryView *binary_view; SignalView *signal_view; ChartsWidget *charts; QSplitter *splitter; - QStackedLayout *stacked_layout; +}; + +class CenterWidget : public QWidget { + Q_OBJECT +public: + CenterWidget(ChartsWidget* charts, QWidget *parent); + void setMessage(const MessageId &msg_id); + void clear(); + +private: + QWidget *createWelcomeWidget(); + DetailWidget *detail_widget = nullptr; + QWidget *welcome_widget = nullptr; + ChartsWidget *charts; }; diff --git a/tools/cabana/historylog.cc b/tools/cabana/historylog.cc index a1c671b68d..abf0b53af8 100644 --- a/tools/cabana/historylog.cc +++ b/tools/cabana/historylog.cc @@ -5,8 +5,6 @@ #include #include "tools/cabana/commands.h" -#include "tools/cabana/util.h" - // HistoryLogModel QVariant HistoryLogModel::data(const QModelIndex &index, int role) const { @@ -27,17 +25,19 @@ void HistoryLogModel::setMessage(const MessageId &message_id) { msg_id = message_id; } -void HistoryLogModel::refresh() { +void HistoryLogModel::refresh(bool fetch_message) { beginResetModel(); sigs.clear(); - if (auto dbc_msg = dbc()->msg(*msg_id)) { - sigs = dbc_msg->sigs; + if (auto dbc_msg = dbc()->msg(msg_id)) { + sigs = dbc_msg->getSignals(); } last_fetch_time = 0; has_more_data = true; messages.clear(); hex_colors.clear(); - updateState(); + if (fetch_message) { + updateState(); + } endResetModel(); } @@ -48,9 +48,9 @@ QVariant HistoryLogModel::headerData(int section, Qt::Orientation orientation, i if (section == 0) { return "Time"; } - return show_signals ? sigs[section - 1].name : "Data"; + return show_signals ? sigs[section - 1]->name : "Data"; } else if (role == Qt::BackgroundRole && section > 0 && show_signals) { - return QBrush(getColor(&sigs[section - 1])); + return QBrush(getColor(sigs[section - 1])); } } return {}; @@ -79,17 +79,15 @@ void HistoryLogModel::setFilter(int sig_idx, const QString &value, std::function } void HistoryLogModel::updateState() { - if (msg_id) { - uint64_t current_time = (can->lastMessage(*msg_id).ts + can->routeStartTime()) * 1e9 + 1; - auto new_msgs = dynamic_mode ? fetchData(current_time, last_fetch_time) : fetchData(0); - if (!new_msgs.empty()) { - beginInsertRows({}, 0, new_msgs.size() - 1); - messages.insert(messages.begin(), std::move_iterator(new_msgs.begin()), std::move_iterator(new_msgs.end())); - endInsertRows(); - } - has_more_data = new_msgs.size() >= batch_size; - last_fetch_time = current_time; + uint64_t current_time = (can->lastMessage(msg_id).ts + can->routeStartTime()) * 1e9 + 1; + auto new_msgs = dynamic_mode ? fetchData(current_time, last_fetch_time) : fetchData(0); + if (!new_msgs.empty()) { + beginInsertRows({}, 0, new_msgs.size() - 1); + messages.insert(messages.begin(), std::move_iterator(new_msgs.begin()), std::move_iterator(new_msgs.end())); + endInsertRows(); } + has_more_data = new_msgs.size() >= batch_size; + last_fetch_time = current_time; } void HistoryLogModel::fetchMore(const QModelIndex &parent) { @@ -111,10 +109,10 @@ std::deque HistoryLogModel::fetchData(InputIt first, I for (auto it = first; it != last && (*it)->mono_time > min_time; ++it) { if ((*it)->which == cereal::Event::Which::CAN) { for (const auto &c : (*it)->event.getCan()) { - if (msg_id->address == c.getAddress() && msg_id->source == c.getSrc()) { + if (msg_id.address == c.getAddress() && msg_id.source == c.getSrc()) { const auto dat = c.getDat(); for (int i = 0; i < sigs.size(); ++i) { - values[i] = get_raw_value((uint8_t *)dat.begin(), dat.size(), sigs[i]); + values[i] = get_raw_value((uint8_t *)dat.begin(), dat.size(), *sigs[i]); } if (!filter_cmp || filter_cmp(values[filter_sig_idx], filter_value)) { auto &m = msgs.emplace_back(); @@ -136,7 +134,7 @@ template std::deque HistoryLogModel::fetchData<>(std:: std::deque HistoryLogModel::fetchData(uint64_t from_time, uint64_t min_time) { auto events = can->events(); - const auto freq = can->lastMessage(*msg_id).freq; + const auto freq = can->lastMessage(msg_id).freq; const bool update_colors = !display_signals_mode || sigs.empty(); if (dynamic_mode) { @@ -189,7 +187,8 @@ void HeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalI // LogsWidget -LogsWidget::LogsWidget(QWidget *parent) : QWidget(parent) { +LogsWidget::LogsWidget(QWidget *parent) : QFrame(parent) { + setFrameStyle(QFrame::StyledPanel | QFrame::Plain); QVBoxLayout *main_layout = new QVBoxLayout(this); main_layout->setContentsMargins(0, 0, 0, 0); main_layout->setSpacing(0); @@ -209,7 +208,8 @@ LogsWidget::LogsWidget(QWidget *parent) : QWidget(parent) { h->addStretch(0); h->addWidget(dynamic_mode = new QCheckBox(tr("Dynamic")), 0, Qt::AlignRight); - display_type_cb->addItems({"Signal Value", "Hex Value"}); + display_type_cb->addItems({"Signal", "Hex"}); + display_type_cb->setToolTip(tr("Display signal value or raw hex value")); comp_box->addItems({">", "=", "!=", "<"}); value_edit->setClearButtonEnabled(true); value_edit->setValidator(new QDoubleValidator(-500000, 500000, 6, this)); @@ -219,10 +219,10 @@ LogsWidget::LogsWidget(QWidget *parent) : QWidget(parent) { main_layout->addWidget(toolbar); QFrame *line = new QFrame(this); line->setFrameStyle(QFrame::HLine | QFrame::Sunken); - main_layout->addWidget(line);; - + main_layout->addWidget(line); main_layout->addWidget(logs = new QTableView(this)); logs->setModel(model = new HistoryLogModel(this)); + delegate = new MessageBytesDelegate(this); logs->setItemDelegateForColumn(1, new MessageBytesDelegate(this)); logs->setHorizontalHeader(new HeaderView(Qt::Horizontal, this)); logs->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | (Qt::Alignment)Qt::TextWordWrap); @@ -230,7 +230,10 @@ LogsWidget::LogsWidget(QWidget *parent) : QWidget(parent) { logs->verticalHeader()->setVisible(false); logs->setFrameShape(QFrame::NoFrame); - QObject::connect(display_type_cb, SIGNAL(activated(int)), model, SLOT(setDisplayType(int))); + QObject::connect(display_type_cb, qOverload(&QComboBox::activated), [this](int index) { + logs->setItemDelegateForColumn(1, index == 1 ? delegate : nullptr); + model->setDisplayType(index); + }); QObject::connect(dynamic_mode, &QCheckBox::stateChanged, model, &HistoryLogModel::setDynamicMode); QObject::connect(signals_cb, SIGNAL(activated(int)), this, SLOT(setFilter())); QObject::connect(comp_box, SIGNAL(activated(int)), this, SLOT(setFilter())); @@ -247,17 +250,16 @@ void LogsWidget::setMessage(const MessageId &message_id) { } void LogsWidget::refresh() { - if (!model->msg_id) return; - model->setFilter(0, "", nullptr); - model->refresh(); + model->refresh(isVisible()); bool has_signal = model->sigs.size(); if (has_signal) { signals_cb->clear(); - for (auto &s : model->sigs) { - signals_cb->addItem(s.name); + for (auto s : model->sigs) { + signals_cb->addItem(s->name); } } + logs->setItemDelegateForColumn(1, !has_signal || display_type_cb->currentIndex() == 1 ? delegate : nullptr); value_edit->clear(); comp_box->setCurrentIndex(0); filters_widget->setVisible(has_signal); @@ -276,3 +278,15 @@ void LogsWidget::setFilter() { model->setFilter(signals_cb->currentIndex(), value_edit->text(), cmp); model->refresh(); } + +void LogsWidget::updateState() { + if (isVisible() && dynamic_mode->isChecked()) { + model->updateState(); + } +} + +void LogsWidget::showEvent(QShowEvent *event) { + if (dynamic_mode->isChecked() || model->canFetchMore({}) && model->rowCount() == 0) { + model->refresh(); + } +} diff --git a/tools/cabana/historylog.h b/tools/cabana/historylog.h index a1b7bc0098..206d53bc8d 100644 --- a/tools/cabana/historylog.h +++ b/tools/cabana/historylog.h @@ -1,8 +1,6 @@ #pragma once #include -#include - #include #include #include @@ -11,6 +9,8 @@ #include "tools/cabana/dbcmanager.h" #include "tools/cabana/streams/abstractstream.h" +#include "tools/cabana/util.h" + using namespace dbcmanager; class HeaderView : public QHeaderView { @@ -36,7 +36,7 @@ public: int columnCount(const QModelIndex &parent = QModelIndex()) const override { return display_signals_mode && !sigs.empty() ? sigs.size() + 1 : 2; } - void refresh(); + void refresh(bool fetch_message = true); public slots: void setDisplayType(int type); @@ -55,7 +55,7 @@ public: std::deque fetchData(InputIt first, InputIt last, uint64_t min_time); std::deque fetchData(uint64_t from_time, uint64_t min_time = 0); - std::optional msg_id; + MessageId msg_id; ChangeTracker hex_colors; bool has_more_data = true; const int batch_size = 50; @@ -64,19 +64,19 @@ public: uint64_t last_fetch_time = 0; std::function filter_cmp = nullptr; std::deque messages; - QList sigs; + std::vector sigs; bool dynamic_mode = true; bool display_signals_mode = true; }; -class LogsWidget : public QWidget { +class LogsWidget : public QFrame { Q_OBJECT public: LogsWidget(QWidget *parent); void setMessage(const MessageId &message_id); - void updateState() {if (dynamic_mode->isChecked()) model->updateState(); } - void showEvent(QShowEvent *event) override { if (dynamic_mode->isChecked()) model->refresh(); } + void updateState(); + void showEvent(QShowEvent *event) override; private slots: void setFilter(); @@ -90,4 +90,5 @@ private: QComboBox *signals_cb, *comp_box, *display_type_cb; QLineEdit *value_edit; QWidget *filters_widget; + MessageBytesDelegate *delegate; }; diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index 47ad3a4a6d..bc090b2cc0 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -27,8 +27,8 @@ void qLogMessageHandler(QtMsgType type, const QMessageLogContext &context, const MainWindow::MainWindow() : QMainWindow() { createDockWindows(); - detail_widget = new DetailWidget(charts_widget, this); - setCentralWidget(detail_widget); + center_widget = new CenterWidget(charts_widget, this); + setCentralWidget(center_widget); createActions(); createStatusBar(); createShortcuts(); @@ -60,7 +60,7 @@ MainWindow::MainWindow() : QMainWindow() { QObject::connect(this, &MainWindow::showMessage, statusBar(), &QStatusBar::showMessage); QObject::connect(this, &MainWindow::updateProgressBar, this, &MainWindow::updateDownloadProgress); - QObject::connect(messages_widget, &MessagesWidget::msgSelectionChanged, detail_widget, &DetailWidget::setMessage); + QObject::connect(messages_widget, &MessagesWidget::msgSelectionChanged, center_widget, &CenterWidget::setMessage); QObject::connect(charts_widget, &ChartsWidget::dock, this, &MainWindow::dockCharts); QObject::connect(can, &AbstractStream::streamStarted, this, &MainWindow::loadDBCFromFingerprint); QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &MainWindow::DBCFileChanged); @@ -175,7 +175,7 @@ void MainWindow::createStatusBar() { progress_bar = new QProgressBar(); progress_bar->setRange(0, 100); progress_bar->setTextVisible(true); - progress_bar->setFixedSize({230, 16}); + progress_bar->setFixedSize({300, 16}); progress_bar->setVisible(false); statusBar()->addWidget(new QLabel(tr("For Help, Press F1"))); statusBar()->addPermanentWidget(progress_bar); @@ -220,7 +220,7 @@ void MainWindow::DBCFileChanged() { void MainWindow::openRoute() { OpenRouteDialog dlg(this); if (dlg.exec()) { - detail_widget->removeAll(); + center_widget->clear(); charts_widget->removeAll(); statusBar()->showMessage(tr("Route %1 loaded").arg(can->routeName()), 2000); } else if (dlg.failedToLoad()) { diff --git a/tools/cabana/mainwin.h b/tools/cabana/mainwin.h index 85d221a231..dd53b1f213 100644 --- a/tools/cabana/mainwin.h +++ b/tools/cabana/mainwin.h @@ -60,7 +60,7 @@ protected: VideoWidget *video_widget = nullptr; QDockWidget *video_dock; MessagesWidget *messages_widget; - DetailWidget *detail_widget; + CenterWidget *center_widget; ChartsWidget *charts_widget; QWidget *floating_window = nullptr; QVBoxLayout *charts_layout; diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index 42ac22865c..c880c61058 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -1,17 +1,17 @@ #include "tools/cabana/messageswidget.h" -#include -#include #include -#include #include #include MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(0 ,0, 0, 0); // message filter filter = new QLineEdit(this); + QRegularExpression re("\\S+"); + filter->setValidator(new QRegularExpressionValidator(re, this)); filter->setClearButtonEnabled(true); filter->setPlaceholderText(tr("filter messages")); main_layout->addWidget(filter); @@ -44,11 +44,16 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { QObject::connect(dbc(), &DBCManager::DBCFileChanged, model, &MessageListModel::sortMessages); QObject::connect(dbc(), &DBCManager::msgUpdated, model, &MessageListModel::sortMessages); QObject::connect(dbc(), &DBCManager::msgRemoved, model, &MessageListModel::sortMessages); - QObject::connect(model, &MessageListModel::modelReset, [this]() { selectMessage(*current_msg_id); }); + QObject::connect(model, &MessageListModel::modelReset, [this]() { + if (current_msg_id) { + selectMessage(*current_msg_id); + } + }); QObject::connect(table_widget->selectionModel(), &QItemSelectionModel::currentChanged, [=](const QModelIndex ¤t, const QModelIndex &previous) { if (current.isValid() && current.row() < model->msgs.size()) { - if (model->msgs[current.row()] != *current_msg_id) { - current_msg_id = model->msgs[current.row()]; + auto &id = model->msgs[current.row()]; + if (!current_msg_id || id != *current_msg_id) { + current_msg_id = id; emit msgSelectionChanged(*current_msg_id); } } @@ -67,10 +72,10 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { setWhatsThis(tr(R"( Message View
- Byte color:
+ Byte color
constant changing
increasing
- decreasing
+ decreasing )")); } @@ -91,9 +96,10 @@ void MessagesWidget::updateSuppressedButtons() { } void MessagesWidget::reset() { + current_msg_id = std::nullopt; + table_widget->selectionModel()->clear(); model->reset(); filter->clear(); - current_msg_id = std::nullopt; updateSuppressedButtons(); } @@ -138,8 +144,8 @@ void MessageListModel::setFilterString(const QString &string) { if (id.toString().contains(txt, cs) || msgName(id).contains(txt, cs)) return true; // Search by signal name if (const auto msg = dbc()->msg(id)) { - for (auto &signal : msg->sigs) { - if (signal.name.contains(txt, cs)) return true; + for (auto s : msg->getSignals()) { + if (s->name.contains(txt, cs)) return true; } } return false; diff --git a/tools/cabana/messageswidget.h b/tools/cabana/messageswidget.h index d88def3acb..926d131c8d 100644 --- a/tools/cabana/messageswidget.h +++ b/tools/cabana/messageswidget.h @@ -1,12 +1,9 @@ #pragma once -#include - #include #include #include #include -#include #include #include "tools/cabana/dbcmanager.h" diff --git a/tools/cabana/signaledit.cc b/tools/cabana/signaledit.cc index d5099da6f4..e862439b68 100644 --- a/tools/cabana/signaledit.cc +++ b/tools/cabana/signaledit.cc @@ -6,7 +6,6 @@ #include #include #include -#include #include #include "tools/cabana/commands.h" @@ -48,9 +47,9 @@ void SignalModel::refresh() { beginResetModel(); root.reset(new SignalModel::Item); if (auto msg = dbc()->msg(msg_id)) { - for (auto &s : msg->sigs) { - if (filter_str.isEmpty() || s.name.contains(filter_str, Qt::CaseInsensitive)) { - insertItem(root.get(), root->children.size(), &s); + for (auto s : msg->getSignals()) { + if (filter_str.isEmpty() || s->name.contains(filter_str, Qt::CaseInsensitive)) { + insertItem(root.get(), root->children.size(), s); } } } @@ -58,7 +57,7 @@ void SignalModel::refresh() { } void SignalModel::updateState(const QHash *msgs) { - if (!msgs || (msgs->contains(msg_id))) { + if (!msgs || msgs->contains(msg_id)) { auto &dat = can->lastMessage(msg_id).dat; int row = 0; for (auto item : root->children) { @@ -93,9 +92,8 @@ Qt::ItemFlags SignalModel::flags(const QModelIndex &index) const { } int SignalModel::signalRow(const Signal *sig) const { - auto &children = root->children; - for (int i = 0; i < children.size(); ++i) { - if (children[i]->sig == sig) return i; + for (int i = 0; i < root->children.size(); ++i) { + if (root->children[i]->sig == sig) return i; } return -1; } @@ -129,11 +127,11 @@ QVariant SignalModel::data(const QModelIndex &index, int role) const { case Item::Min: return item->sig->min; case Item::Max: return item->sig->max; case Item::Desc: { - QString val_desc; + QStringList val_desc; for (auto &[val, desc] : item->sig->val_desc) { - val_desc += QString("%1 \"%2\"").arg(val, desc); + val_desc << QString("%1 \"%2\"").arg(val, desc); } - return val_desc; + return val_desc.join(" "); } default: break; } @@ -280,16 +278,24 @@ void SignalModel::handleSignalRemoved(const Signal *sig) { // SignalItemDelegate -SignalItemDelegate::SignalItemDelegate(QObject *parent) { +SignalItemDelegate::SignalItemDelegate(QObject *parent) : QStyledItemDelegate(parent) { name_validator = new NameValidator(this); double_validator = new QDoubleValidator(this); double_validator->setLocale(QLocale::C); // Match locale of QString::toDouble() instead of system small_font.setPointSize(8); } +QSize SignalItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { + QSize size = QStyledItemDelegate::sizeHint(option, index); + if (!index.parent().isValid() && index.column() == 0) { + size.rwidth() = std::min(((QWidget*)parent())->size().width() / 2, size.width() + color_label_width + 8); + } + return size; +} + void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { auto item = (SignalModel::Item *)index.internalPointer(); - if (item && !index.parent().isValid() && index.column() == 0) { + if (index.column() == 0 && item && item->type == SignalModel::Item::Sig) { painter->save(); painter->setRenderHint(QPainter::Antialiasing); if (option.state & QStyle::State_Selected) { @@ -298,7 +304,7 @@ void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op // color label auto bg_color = getColor(item->sig); - QRect rc{option.rect.left(), option.rect.top(), 18, option.rect.height()}; + QRect rc{option.rect.left(), option.rect.top(), color_label_width, option.rect.height()}; painter->setPen(Qt::NoPen); painter->setBrush(item->highlight ? bg_color.darker(125) : bg_color); painter->drawRoundedRect(rc.adjusted(0, 2, 0, -2), 3, 3); @@ -345,22 +351,22 @@ QWidget *SignalItemDelegate::createEditor(QWidget *parent, const QStyleOptionVie // SignalView -SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts), QWidget(parent) { +SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts), QFrame(parent) { + setFrameStyle(QFrame::StyledPanel | QFrame::Plain); // title bar QWidget *title_bar = new QWidget(this); title_bar->setAutoFillBackground(true); QHBoxLayout *hl = new QHBoxLayout(title_bar); hl->addWidget(signal_count_lb = new QLabel()); filter_edit = new QLineEdit(this); + QRegularExpression re("\\S+"); + filter_edit->setValidator(new QRegularExpressionValidator(re, this)); filter_edit->setClearButtonEnabled(true); - filter_edit->setPlaceholderText(tr("filter signals by name")); + filter_edit->setPlaceholderText(tr("filter signals")); hl->addWidget(filter_edit); hl->addStretch(1); - auto collapse_btn = new QToolButton(); - collapse_btn->setIcon(utils::icon("dash-square")); + auto collapse_btn = toolButton("dash-square", tr("Collapse All")); collapse_btn->setIconSize({12, 12}); - collapse_btn->setAutoRaise(true); - collapse_btn->setToolTip(tr("Collapse All")); hl->addWidget(collapse_btn); // tree view @@ -371,7 +377,8 @@ SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts), tree->setHeaderHidden(true); tree->setMouseTracking(true); tree->setExpandsOnDoubleClick(false); - tree->header()->setSectionResizeMode(QHeaderView::Stretch); + tree->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); + tree->header()->setStretchLastSection(true); tree->setMinimumHeight(300); tree->setStyleSheet("QSpinBox{background-color:white;border:none;} QLineEdit{background-color:white;}"); @@ -389,7 +396,7 @@ SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts), QObject::connect(model, &QAbstractItemModel::modelReset, this, &SignalView::rowsChanged); QObject::connect(model, &QAbstractItemModel::rowsInserted, this, &SignalView::rowsChanged); QObject::connect(model, &QAbstractItemModel::rowsRemoved, this, &SignalView::rowsChanged); - QObject::connect(dbc(), &DBCManager::signalAdded, [this](uint32_t address, const Signal *sig) { expandSignal(sig); }); + QObject::connect(dbc(), &DBCManager::signalAdded, [this](uint32_t address, const Signal *sig) { selectSignal(sig); }); setWhatsThis(tr(R"( Signal view
@@ -404,16 +411,6 @@ void SignalView::setMessage(const MessageId &id) { } void SignalView::rowsChanged() { - auto create_btn = [](const QString &id, const QString &tooltip) { - auto btn = new QToolButton(); - btn->setIcon(utils::icon(id)); - btn->setToolTip(tooltip); - btn->setAutoRaise(true); - return btn; - }; - - signal_count_lb->setText(tr("Signals: %1").arg(model->rowCount())); - for (int i = 0; i < model->rowCount(); ++i) { auto index = model->index(i, 1); if (!tree->indexWidget(index)) { @@ -422,8 +419,8 @@ void SignalView::rowsChanged() { h->setContentsMargins(0, 2, 0, 2); h->addStretch(1); - auto remove_btn = create_btn("x", tr("Remove signal")); - auto plot_btn = create_btn("graph-up", ""); + auto remove_btn = toolButton("x", tr("Remove signal")); + auto plot_btn = toolButton("graph-up", ""); plot_btn->setCheckable(true); h->addWidget(plot_btn); h->addWidget(remove_btn); @@ -436,6 +433,7 @@ void SignalView::rowsChanged() { }); } } + signal_count_lb->setText(tr("Signals: %1").arg(model->rowCount())); updateChartState(); } @@ -449,23 +447,26 @@ void SignalView::rowClicked(const QModelIndex &index) { } } -void SignalView::expandSignal(const Signal *sig) { +void SignalView::selectSignal(const Signal *sig, bool expand) { if (int row = model->signalRow(sig); row != -1) { auto idx = model->index(row, 0); - bool expand = !tree->isExpanded(idx); - tree->setExpanded(idx, expand); + if (expand) { + tree->setExpanded(idx, !tree->isExpanded(idx)); + } tree->scrollTo(idx, QAbstractItemView::PositionAtTop); - if (expand) tree->setCurrentIndex(idx); + tree->setCurrentIndex(idx); } } void SignalView::updateChartState() { int i = 0; for (auto item : model->root->children) { - auto plot_btn = tree->indexWidget(model->index(i, 1))->findChildren()[0]; bool chart_opened = charts->hasSignal(msg_id, item->sig); - plot_btn->setChecked(chart_opened); - plot_btn->setToolTip(chart_opened ? tr("Close Plot") : tr("Show Plot\nSHIFT click to add to previous opened plot")); + auto buttons = tree->indexWidget(model->index(i, 1))->findChildren(); + if (buttons.size() > 0) { + buttons[0]->setChecked(chart_opened); + buttons[0]->setToolTip(chart_opened ? tr("Close Plot") : tr("Show Plot\nSHIFT click to add to previous opened plot")); + } ++i; } } diff --git a/tools/cabana/signaledit.h b/tools/cabana/signaledit.h index efdc653584..3cb193b62f 100644 --- a/tools/cabana/signaledit.h +++ b/tools/cabana/signaledit.h @@ -80,12 +80,14 @@ class SignalItemDelegate : public QStyledItemDelegate { public: SignalItemDelegate(QObject *parent); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QValidator *name_validator, *double_validator; QFont small_font; + const int color_label_width = 18; }; -class SignalView : public QWidget { +class SignalView : public QFrame { Q_OBJECT public: @@ -93,7 +95,7 @@ public: void setMessage(const MessageId &id); void signalHovered(const Signal *sig); void updateChartState(); - void expandSignal(const Signal *sig); + void selectSignal(const Signal *sig, bool expand = false); void rowClicked(const QModelIndex &index); SignalModel *model = nullptr; diff --git a/tools/cabana/streams/abstractstream.cc b/tools/cabana/streams/abstractstream.cc index 8fdfbd5c1b..3e631d3709 100644 --- a/tools/cabana/streams/abstractstream.cc +++ b/tools/cabana/streams/abstractstream.cc @@ -31,10 +31,12 @@ bool AbstractStream::updateEvent(const Event *event) { data.dat = QByteArray((char *)c.getDat().begin(), c.getDat().size()); data.count = ++counters[id]; data.freq = data.count / std::max(1.0, current_sec); - change_trackers[id].compute(data.dat, data.ts, data.freq); - data.colors = change_trackers[id].colors; - data.last_change_t = change_trackers[id].last_change_t; - data.bit_change_counts = change_trackers[id].bit_change_counts; + + auto &tracker = change_trackers[id]; + tracker.compute(data.dat, data.ts, data.freq); + data.colors = tracker.colors; + data.last_change_t = tracker.last_change_t; + data.bit_change_counts = tracker.bit_change_counts; } double ts = millis_since_boot(); diff --git a/tools/cabana/tests/test_cabana.cc b/tools/cabana/tests/test_cabana.cc index 9a8ab710bc..6f9f2d016b 100644 --- a/tools/cabana/tests/test_cabana.cc +++ b/tools/cabana/tests/test_cabana.cc @@ -23,9 +23,11 @@ TEST_CASE("DBCManager::generateDBC") { auto &new_m = new_msgs.at(address); REQUIRE(m.name == new_m.name); REQUIRE(m.size == new_m.size); - REQUIRE(m.sigs.size() == new_m.sigs.size()); - for (int i = 0; i < m.sigs.size(); ++i) { - REQUIRE(m.sigs[i] == new_m.sigs[i]); + REQUIRE(m.getSignals().size() == new_m.getSignals().size()); + auto sigs = m.getSignals(); + auto new_sigs = new_m.getSignals(); + for (int i = 0; i < sigs.size(); ++i) { + REQUIRE(*sigs[i] == *new_sigs[i]); } } } @@ -44,9 +46,9 @@ TEST_CASE("Parse can messages") { for (const auto &c : e->event.getCan()) { const auto msg = dbc.msg(c.getAddress()); if (c.getSrc() == 0 && msg) { - for (auto &sig : msg->sigs) { - double val = get_raw_value((uint8_t *)c.getDat().begin(), c.getDat().size(), sig); - values_1[{c.getAddress(), sig.name}].push_back(val); + for (auto sig : msg->getSignals()) { + double val = get_raw_value((uint8_t *)c.getDat().begin(), c.getDat().size(), *sig); + values_1[{c.getAddress(), sig->name}].push_back(val); } } } diff --git a/tools/cabana/util.cc b/tools/cabana/util.cc index 6415cc8e16..4f79f9a3ac 100644 --- a/tools/cabana/util.cc +++ b/tools/cabana/util.cc @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -73,32 +74,23 @@ MessageBytesDelegate::MessageBytesDelegate(QObject *parent) : QStyledItemDelegat } void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { - QStyleOptionViewItemV4 opt = option; - initStyleOption(&opt, index); - - auto byte_list = opt.text.split(" "); - if (byte_list.size() <= 1) { - QStyledItemDelegate::paint(painter, option, index); - return; - } - auto color_role = option.state & QStyle::State_Selected ? QPalette::HighlightedText: QPalette::Text; painter->setPen(option.palette.color(color_role)); painter->setFont(fixed_font); - QRect space = painter->boundingRect(opt.rect, opt.displayAlignment, " "); - QRect pos = painter->boundingRect(opt.rect, opt.displayAlignment, "00"); - pos.moveLeft(pos.x() + space.width()); - - int m = space.width() / 2; + int space = painter->boundingRect(option.rect, option.displayAlignment, " ").width(); + QRect pos = painter->boundingRect(option.rect, option.displayAlignment, "00").adjusted(0, 0, 2, 0); + pos.moveLeft(pos.x() + space); + int m = space / 2; const QMargins margins(m, m, m, m); auto colors = index.data(Qt::UserRole).value>(); + auto byte_list = index.data(Qt::DisplayRole).toString().split(" "); for (int i = 0; i < byte_list.size(); ++i) { - if (i < colors.size()) { + if (i < colors.size() && colors[i].alpha() > 0) { painter->fillRect(pos.marginsAdded(margins), colors[i]); } - painter->drawText(pos, opt.displayAlignment, byte_list[i]); - pos.moveLeft(pos.right() + space.width()); + painter->drawText(pos, Qt::AlignCenter, byte_list[i]); + pos.moveLeft(pos.right() + space); } } @@ -124,12 +116,25 @@ namespace utils { QPixmap icon(const QString &id) { static bool dark_theme = QApplication::style()->standardPalette().color(QPalette::WindowText).value() > QApplication::style()->standardPalette().color(QPalette::Background).value(); - QPixmap pm = bootstrapPixmap(id); - if (dark_theme) { - QPainter p(&pm); - p.setCompositionMode(QPainter::CompositionMode_SourceIn); - p.fillRect(pm.rect(), Qt::lightGray); + QPixmap pm; + QString key = "bootstrap_" % id % (dark_theme ? "1" : "0"); + if (!QPixmapCache::find(key, &pm)) { + pm = bootstrapPixmap(id); + if (dark_theme) { + QPainter p(&pm); + p.setCompositionMode(QPainter::CompositionMode_SourceIn); + p.fillRect(pm.rect(), Qt::lightGray); + } + QPixmapCache::insert(key, pm); } return pm; } } // namespace utils + +QToolButton *toolButton(const QString &icon, const QString &tooltip) { + auto btn = new QToolButton(); + btn->setIcon(utils::icon(icon)); + btn->setToolTip(tooltip); + btn->setAutoRaise(true); + return btn; +}; diff --git a/tools/cabana/util.h b/tools/cabana/util.h index 2717451061..b72ddfcaa0 100644 --- a/tools/cabana/util.h +++ b/tools/cabana/util.h @@ -6,7 +6,9 @@ #include #include #include +#include #include +#include #include #include "tools/cabana/dbcmanager.h" @@ -52,3 +54,5 @@ public: namespace utils { QPixmap icon(const QString &id); } + +QToolButton *toolButton(const QString &icon, const QString &tooltip); diff --git a/tools/cabana/videowidget.cc b/tools/cabana/videowidget.cc index 2b5374d85f..536ae7ba97 100644 --- a/tools/cabana/videowidget.cc +++ b/tools/cabana/videowidget.cc @@ -28,9 +28,9 @@ inline QString formatTime(int seconds) { VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent) { QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(0, 0, 0, 0); QFrame *frame = new QFrame(this); - frame->setFrameShape(QFrame::StyledPanel); - frame->setFrameShadow(QFrame::Sunken); + frame->setFrameStyle(QFrame::StyledPanel | QFrame::Plain); main_layout->addWidget(frame); QVBoxLayout *frame_layout = new QVBoxLayout(frame); From 500b1b4a5c7e5a1402325092a92774805b95b324 Mon Sep 17 00:00:00 2001 From: Webomcar <124065911+Webomcar@users.noreply.github.com> Date: Tue, 21 Feb 2023 00:23:42 +0100 Subject: [PATCH 446/484] Add missing FW for Toyota C-HR Hybrid 2020 (#27366) * Update values.py * Verified C-HR 2020 is TSS-P --------- Co-authored-by: Shane Smiskol --- docs/CARS.md | 2 +- selfdrive/car/toyota/values.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index 59eece1e4b..3bc918f784 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -178,7 +178,7 @@ A supported vehicle is one that just works when you install a comma three. All s |Toyota|Avalon Hybrid 2022|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| |Toyota|C-HR 2017-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| |Toyota|C-HR 2021|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| -|Toyota|C-HR Hybrid 2017-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Toyota|C-HR Hybrid 2017-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| |Toyota|C-HR Hybrid 2021-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| |Toyota|Camry 2018-20|All|Stock|0 mph[6](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| |Toyota|Camry 2021-22|All|openpilot|0 mph[6](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index 0fcabe163c..0639d46ec0 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -119,7 +119,7 @@ CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = { CAR.CAMRYH_TSS2: ToyotaCarInfo("Toyota Camry Hybrid 2021-23"), CAR.CHR: ToyotaCarInfo("Toyota C-HR 2017-20"), CAR.CHR_TSS2: ToyotaCarInfo("Toyota C-HR 2021"), - CAR.CHRH: ToyotaCarInfo("Toyota C-HR Hybrid 2017-19"), + CAR.CHRH: ToyotaCarInfo("Toyota C-HR Hybrid 2017-20"), CAR.CHRH_TSS2: ToyotaCarInfo("Toyota C-HR Hybrid 2021-22"), CAR.COROLLA: ToyotaCarInfo("Toyota Corolla 2017-19"), CAR.COROLLA_TSS2: [ @@ -712,6 +712,7 @@ FW_VERSIONS = { b'8646FF404000 ', b'8646FF406000 ', b'8646FF407000 ', + b'8646FF407100 ', ], }, CAR.CHRH_TSS2: { From c3ba2a025ba96684cd0371ae46b3847589b5c773 Mon Sep 17 00:00:00 2001 From: Eric Brown Date: Mon, 20 Feb 2023 16:28:32 -0700 Subject: [PATCH 447/484] GM: detect unplugged or missing radar (#27245) * Add flashed Volt fingerprint * Add another fingerprint * Detect radar header message * dashcamOnly if ASCM vehicle with radar unavailable May be able to change this in the future to OP long off with experimental long available * no submodule update * Revert minEnableSpeed * combine into one fingerprint * Don't use GM radar DBC Co-authored-by: Shane Smiskol * unused * add new FP * update docs * use_off_car_defaults use_off_car_defaults * update docs --------- Co-authored-by: Shane Smiskol --- selfdrive/car/gm/interface.py | 9 ++++++--- selfdrive/car/gm/values.py | 4 ++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index c28274011e..032a49e761 100755 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -6,7 +6,8 @@ from panda import Panda from common.numpy_fast import interp from common.conversions import Conversions as CV from selfdrive.car import STD_CARGO_KG, create_button_event, scale_tire_stiffness, get_safety_config -from selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams, EV_CAR, CAMERA_ACC_CAR +from selfdrive.car.gm.radar_interface import RADAR_HEADER_MSG +from selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams, EV_CAR, CAMERA_ACC_CAR, CanBus from selfdrive.car.interfaces import CarInterfaceBase, TorqueFromLateralAccelCallbackType, FRICTION_THRESHOLD from selfdrive.controls.lib.drive_helpers import apply_center_deadzone @@ -87,6 +88,7 @@ class CarInterface(CarInterfaceBase): ret.carName = "gm" ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.gm)] ret.autoResumeSng = False + use_off_car_defaults = len(fingerprint[0]) == 0 # Pick sensible carParams during offline doc generation/CI jobs if candidate in EV_CAR: ret.transmissionType = TransmissionType.direct @@ -125,7 +127,7 @@ class CarInterface(CarInterfaceBase): else: # ASCM, OBD-II harness ret.openpilotLongitudinalControl = True ret.networkLocation = NetworkLocation.gateway - ret.radarUnavailable = False + ret.radarUnavailable = RADAR_HEADER_MSG not in fingerprint[CanBus.OBSTACLE] and not use_off_car_defaults ret.pcmCruise = False # stock non-adaptive cruise control is kept off # supports stop and go, but initial engage must (conservatively) be above 18mph ret.minEnableSpeed = 18 * CV.MPH_TO_MS @@ -138,7 +140,8 @@ class CarInterface(CarInterfaceBase): # These cars have been put into dashcam only due to both a lack of users and test coverage. # These cars likely still work fine. Once a user confirms each car works and a test route is # added to selfdrive/car/tests/routes.py, we can remove it from this list. - ret.dashcamOnly = candidate in {CAR.CADILLAC_ATS, CAR.HOLDEN_ASTRA, CAR.MALIBU, CAR.BUICK_REGAL, CAR.EQUINOX} + ret.dashcamOnly = candidate in {CAR.CADILLAC_ATS, CAR.HOLDEN_ASTRA, CAR.MALIBU, CAR.BUICK_REGAL, CAR.EQUINOX} or \ + (ret.networkLocation == NetworkLocation.gateway and ret.radarUnavailable) # Start with a baseline tuning for all GM vehicles. Override tuning as needed in each model section below. ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] diff --git a/selfdrive/car/gm/values.py b/selfdrive/car/gm/values.py index 207af6bb05..c4791738b8 100644 --- a/selfdrive/car/gm/values.py +++ b/selfdrive/car/gm/values.py @@ -151,6 +151,10 @@ FINGERPRINTS = { # Volt Premier w/ ACC 2018 { 170: 8, 171: 8, 189: 7, 190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 288: 5, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 389: 2, 390: 7, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 528: 4, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 5, 567: 3, 568: 1, 573: 1, 577: 8, 578: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 715: 8, 717: 5, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1273: 3, 1275: 3, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1516: 8, 1601: 8, 1618: 8, 1905: 7, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1922: 7, 1927: 7, 1930: 7, 2016: 8, 2018: 8, 2020: 8, 2024: 8, 2028: 8 + }, + # Volt Premier 2018 w/ flashed firmware, no radar + { + 170: 8, 171: 8, 189: 7, 190: 6, 192: 5, 193: 8, 197: 8, 199: 4, 201: 6, 209: 7, 211: 2, 241: 6, 288: 5, 289: 1, 290: 1, 298: 2, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 368: 8, 381: 2, 384: 8, 386: 5, 388: 8, 389: 2, 390: 7, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 8, 479: 3, 481: 7, 485: 8, 489: 5, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 3, 508: 8, 512: 3, 528: 4, 530: 8, 532: 6, 537: 5, 539: 8, 542: 7, 546: 7, 550: 8, 554: 3, 558: 8, 560: 6, 562: 4, 563: 5, 564: 5, 565: 5, 566: 5, 567: 3, 568: 1, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 761: 7, 810: 8, 821: 4, 823: 7, 832: 8, 840: 5, 842: 5, 844: 8, 853: 8, 866: 4, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 988: 6, 989: 8, 995: 7, 1001: 5, 1003: 5, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1273: 3, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1905: 7, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1922: 7, 1927: 7 }], CAR.BUICK_REGAL : [ # Regal TourX Essence w/ ACC 2018 From 14d44e444418e1b6bd23afe5a371db886e8273e2 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 20 Feb 2023 15:33:15 -0800 Subject: [PATCH 448/484] GM: remove redundant CarController class variable (#27280) * clean up * fix check * only set ts_nanos on update --- selfdrive/car/gm/carcontroller.py | 9 +++------ selfdrive/car/gm/carstate.py | 3 ++- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/selfdrive/car/gm/carcontroller.py b/selfdrive/car/gm/carcontroller.py index 73085d30b0..42eaf7e88a 100644 --- a/selfdrive/car/gm/carcontroller.py +++ b/selfdrive/car/gm/carcontroller.py @@ -30,7 +30,6 @@ class CarController: self.cancel_counter = 0 self.lka_steering_cmd_counter = 0 - self.sent_lka_steering_cmd = False self.lka_icon_status_last = (False, False) self.params = CarControllerParams(self.CP) @@ -59,19 +58,17 @@ class CarController: # - until we're in sync with camera so counters align when relay closes, preventing a fault. # openpilot can subtly drift, so this is activated throughout a drive to stay synced out_of_sync = self.lka_steering_cmd_counter % 4 != (CS.cam_lka_steering_cmd_counter + 1) % 4 - if not self.sent_lka_steering_cmd or out_of_sync: + if CS.loopback_lka_steering_cmd_ts_nanos == 0 or out_of_sync: steer_step = self.params.STEER_STEP - if CS.loopback_lka_steering_cmd_updated: - self.lka_steering_cmd_counter += 1 - self.sent_lka_steering_cmd = True + self.lka_steering_cmd_counter += 1 if CS.loopback_lka_steering_cmd_updated else 0 # Avoid GM EPS faults when transmitting messages too close together: skip this transmit if we # received the ASCMLKASteeringCmd loopback confirmation too recently last_lka_steer_msg_ms = (now_nanos - CS.loopback_lka_steering_cmd_ts_nanos) * 1e-6 if (self.frame - self.last_steer_frame) >= steer_step and last_lka_steer_msg_ms > MIN_STEER_MSG_INTERVAL_MS: # Initialize ASCMLKASteeringCmd counter using the camera until we get a msg on the bus - if not self.sent_lka_steering_cmd: + if CS.loopback_lka_steering_cmd_ts_nanos == 0: self.lka_steering_cmd_counter = CS.pt_lka_steering_cmd_counter + 1 if CC.latActive: diff --git a/selfdrive/car/gm/carstate.py b/selfdrive/car/gm/carstate.py index 3c7d35f2dc..2f9c952876 100644 --- a/selfdrive/car/gm/carstate.py +++ b/selfdrive/car/gm/carstate.py @@ -37,7 +37,8 @@ class CarState(CarStateBase): # Variables used for avoiding LKAS faults self.loopback_lka_steering_cmd_updated = len(loopback_cp.vl_all["ASCMLKASteeringCmd"]["RollingCounter"]) > 0 - self.loopback_lka_steering_cmd_ts_nanos = loopback_cp.ts_nanos["ASCMLKASteeringCmd"]["RollingCounter"] + if self.loopback_lka_steering_cmd_updated: + self.loopback_lka_steering_cmd_ts_nanos = loopback_cp.ts_nanos["ASCMLKASteeringCmd"]["RollingCounter"] if self.CP.networkLocation == NetworkLocation.fwdCamera: self.pt_lka_steering_cmd_counter = pt_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"] self.cam_lka_steering_cmd_counter = cam_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"] From 79749a306c7621e047d1a57fae40493b40c25954 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 20 Feb 2023 16:12:11 -0800 Subject: [PATCH 449/484] GM: add longitudinal delay to Escalade (#27406) * add delay to escalade too * order --- selfdrive/car/gm/interface.py | 1 + 1 file changed, 1 insertion(+) diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index 032a49e761..44ece1d415 100755 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -207,6 +207,7 @@ class CarInterface(CarInterfaceBase): ret.wheelbase = 2.95 # 116 inches in meters ret.steerRatio = 17.3 ret.centerToFront = ret.wheelbase * 0.5 + ret.longitudinalActuatorDelayUpperBound = 0.5 # large delay to initially start braking CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) elif candidate == CAR.ESCALADE_ESV: From dff054d4dfb6c08a471315778dadf6c0dc24207d Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 20 Feb 2023 16:13:01 -0800 Subject: [PATCH 450/484] Ford: filter steering pressed (#27123) * ford steering pressed filter * raise to 1.0 * Update ref_commit --- selfdrive/car/ford/carstate.py | 2 +- selfdrive/car/ford/values.py | 2 +- selfdrive/test/process_replay/ref_commit | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/selfdrive/car/ford/carstate.py b/selfdrive/car/ford/carstate.py index f97225e9f2..21af1062f3 100644 --- a/selfdrive/car/ford/carstate.py +++ b/selfdrive/car/ford/carstate.py @@ -37,7 +37,7 @@ class CarState(CarStateBase): # steering wheel ret.steeringAngleDeg = cp.vl["SteeringPinion_Data"]["StePinComp_An_Est"] ret.steeringTorque = cp.vl["EPAS_INFO"]["SteeringColumnTorque"] - ret.steeringPressed = abs(ret.steeringTorque) > CarControllerParams.STEER_DRIVER_ALLOWANCE + ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > CarControllerParams.STEER_DRIVER_ALLOWANCE, 5) ret.steerFaultTemporary = cp.vl["EPAS_INFO"]["EPAS_Failure"] == 1 ret.steerFaultPermanent = cp.vl["EPAS_INFO"]["EPAS_Failure"] in (2, 3) # ret.espDisabled = False # TODO: find traction control signal diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index 951cd09ba0..6be93269c4 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -24,7 +24,7 @@ class CarControllerParams: BUTTONS_STEP = 10 / 2 CURVATURE_MAX = 0.02 # Max curvature for steering command, m^-1 - STEER_DRIVER_ALLOWANCE = 0.8 # Driver intervention threshold, Nm + STEER_DRIVER_ALLOWANCE = 1.0 # Driver intervention threshold, Nm # Curvature rate limits # TODO: unify field names used by curvature and angle control cars diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index e9016cc27c..c158382fe8 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -70753c5f491de7432ff22f4ef560820ce9919b2e +f7ff7e7d37c276c31d8d44c73a0091b5996650b1 From ea419c5764739555673d5545f16cff7c05f9e6c0 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Mon, 20 Feb 2023 16:29:17 -0800 Subject: [PATCH 451/484] add 'remount device' to permanent calibration invalid alert (#27407) --- selfdrive/controls/lib/events.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/controls/lib/events.py b/selfdrive/controls/lib/events.py index a85e48649f..4e664d8cf0 100644 --- a/selfdrive/controls/lib/events.py +++ b/selfdrive/controls/lib/events.py @@ -292,7 +292,7 @@ def calibration_invalid_alert(CP: car.CarParams, CS: car.CarState, sm: messaging rpy = sm['liveCalibration'].rpyCalib yaw = math.degrees(rpy[2] if len(rpy) == 3 else math.nan) pitch = math.degrees(rpy[1] if len(rpy) == 3 else math.nan) - angles = f"Pitch: {pitch:.1f}°, Yaw: {yaw:.1f}°" + angles = f"Remount Device (Pitch: {pitch:.1f}°, Yaw: {yaw:.1f}°)" return NormalPermanentAlert("Calibration Invalid", angles) From 3c66106539e5aad272c967009e23e4ff8508157c Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 20 Feb 2023 18:02:52 -0800 Subject: [PATCH 452/484] Chrysler Pacifica Hybrid 2017: add missing CAN FP (#27408) add missing 840: 8 --- selfdrive/car/chrysler/values.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/car/chrysler/values.py b/selfdrive/car/chrysler/values.py index 16ebb4fa11..2a5daf440c 100644 --- a/selfdrive/car/chrysler/values.py +++ b/selfdrive/car/chrysler/values.py @@ -93,7 +93,7 @@ CAR_INFO: Dict[str, Optional[Union[ChryslerCarInfo, List[ChryslerCarInfo]]]] = { FINGERPRINTS = { CAR.PACIFICA_2017_HYBRID: [{ - 168: 8, 257: 5, 258: 8, 264: 8, 268: 8, 270: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 291: 8, 292: 8, 294: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 368: 8, 376: 3, 384: 8, 388: 4, 448: 6, 456: 4, 464: 8, 469: 8, 480: 8, 500: 8, 501: 8, 512: 8, 514: 8, 515: 7, 516: 7, 517: 7, 518: 7, 520: 8, 528: 8, 532: 8, 542: 8, 544: 8, 557: 8, 559: 8, 560: 4, 564: 4, 571: 3, 584: 8, 608: 8, 624: 8, 625: 8, 632: 8, 639: 8, 653: 8, 654: 8, 655: 8, 658: 6, 660: 8, 669: 3, 671: 8, 672: 8, 678: 8, 680: 8, 701: 8, 704: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 737: 8, 746: 5, 760: 8, 764: 8, 766: 8, 770: 8, 773: 8, 779: 8, 782: 8, 784: 8, 788:3, 792: 8, 799: 8, 800: 8, 804: 8, 808: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 832: 8, 838: 2, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 878: 8, 882: 8, 897: 8, 908: 8, 924: 3, 926: 3, 929: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 956: 8, 958: 8, 959: 8, 969: 4, 974: 5, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1082: 8, 1083: 8, 1098: 8, 1100: 8, 1216: 8, 1218: 8, 1220: 8, 1225: 8, 1235: 8, 1242: 8, 1246: 8, 1250: 8, 1284: 8, 1537: 8, 1538: 8, 1562: 8, 1568: 8, 1856: 8, 1858: 8, 1860: 8, 1865: 8, 1875: 8, 1882: 8, 1886: 8, 1890: 8, 1892: 8, 2016: 8, 2024: 8 + 168: 8, 257: 5, 258: 8, 264: 8, 268: 8, 270: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 291: 8, 292: 8, 294: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 368: 8, 376: 3, 384: 8, 388: 4, 448: 6, 456: 4, 464: 8, 469: 8, 480: 8, 500: 8, 501: 8, 512: 8, 514: 8, 515: 7, 516: 7, 517: 7, 518: 7, 520: 8, 528: 8, 532: 8, 542: 8, 544: 8, 557: 8, 559: 8, 560: 4, 564: 4, 571: 3, 584: 8, 608: 8, 624: 8, 625: 8, 632: 8, 639: 8, 653: 8, 654: 8, 655: 8, 658: 6, 660: 8, 669: 3, 671: 8, 672: 8, 678: 8, 680: 8, 701: 8, 704: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 737: 8, 746: 5, 760: 8, 764: 8, 766: 8, 770: 8, 773: 8, 779: 8, 782: 8, 784: 8, 788: 3, 792: 8, 799: 8, 800: 8, 804: 8, 808: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 832: 8, 838: 2, 840: 8, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 878: 8, 882: 8, 897: 8, 908: 8, 924: 3, 926: 3, 929: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 956: 8, 958: 8, 959: 8, 969: 4, 974: 5, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1082: 8, 1083: 8, 1098: 8, 1100: 8, 1216: 8, 1218: 8, 1220: 8, 1225: 8, 1235: 8, 1242: 8, 1246: 8, 1250: 8, 1284: 8, 1537: 8, 1538: 8, 1562: 8, 1568: 8, 1856: 8, 1858: 8, 1860: 8, 1865: 8, 1875: 8, 1882: 8, 1886: 8, 1890: 8, 1892: 8, 2016: 8, 2024: 8 }], CAR.PACIFICA_2018: [{ 55: 8, 257: 5, 258: 8, 264: 8, 268: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 292: 8, 294: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 368: 8, 376: 3, 384: 8, 388: 4, 416: 7, 448: 6, 456: 4, 464: 8, 469: 8, 480: 8, 500: 8, 501: 8, 512: 8, 514: 8, 516: 7, 517: 7, 520: 8, 524: 8, 526: 6, 528: 8, 532: 8, 542: 8, 544: 8, 557: 8, 559: 8, 560: 4, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 624: 8, 625: 8, 632: 8, 639: 8, 656: 4, 658: 6, 660: 8, 669: 3, 671: 8, 672: 8, 678: 8, 680: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 746: 5, 752: 2, 760: 8, 764: 8, 766: 8, 770: 8, 773: 8, 779: 8, 784: 8, 792: 8, 799: 8, 800: 8, 804: 8, 808: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 832: 8, 838: 2, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 882: 8, 897: 8, 924: 8, 926: 3, 937: 8, 947: 8, 948: 8, 969: 4, 974: 5, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1098: 8, 1100: 8, 1537: 8, 1538: 8, 1562: 8 From 2008332bd42528ae56d937a2ec0e9a1dd244b2ea Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 21 Feb 2023 15:27:48 -0800 Subject: [PATCH 453/484] GM: revert custom Bolt torque tune (#27403) * Revert "GM: cleanup torque feedforward function (#27347)" This reverts commit 8c099dd4e5adf040f7f454714cf729725bee7f91. * Revert "Chevrolet Bolt: Non-linear torque tune (#27344)" This reverts commit e49748d571d06a65a4361dde1f2f63c7294da13a. * update refs --- selfdrive/car/gm/interface.py | 43 +-------------------- selfdrive/car/interfaces.py | 4 +- selfdrive/controls/lib/latcontrol_torque.py | 4 +- 3 files changed, 6 insertions(+), 45 deletions(-) diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index 44ece1d415..7e6e71c642 100755 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -3,13 +3,11 @@ from cereal import car from math import fabs from panda import Panda -from common.numpy_fast import interp from common.conversions import Conversions as CV from selfdrive.car import STD_CARGO_KG, create_button_event, scale_tire_stiffness, get_safety_config from selfdrive.car.gm.radar_interface import RADAR_HEADER_MSG from selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams, EV_CAR, CAMERA_ACC_CAR, CanBus -from selfdrive.car.interfaces import CarInterfaceBase, TorqueFromLateralAccelCallbackType, FRICTION_THRESHOLD -from selfdrive.controls.lib.drive_helpers import apply_center_deadzone +from selfdrive.car.interfaces import CarInterfaceBase ButtonType = car.CarState.ButtonEvent.Type EventName = car.CarEvent.EventName @@ -46,43 +44,6 @@ class CarInterface(CarInterfaceBase): else: return CarInterfaceBase.get_steer_feedforward_default - @staticmethod - def torque_from_lateral_accel_bolt(lateral_accel_value, torque_params, lateral_accel_error, lateral_accel_deadzone, vego, friction_compensation): - friction_interp = interp( - apply_center_deadzone(lateral_accel_error, lateral_accel_deadzone), - [-FRICTION_THRESHOLD, FRICTION_THRESHOLD], - [-torque_params.friction, torque_params.friction] - ) - friction = friction_interp if friction_compensation else 0.0 - steer_torque = lateral_accel_value / torque_params.latAccelFactor - - # TODO: - # 1. Learn the correction factors from data - # 2. Generalize the logic to other GM torque control platforms - steer_break_pts = [-1.0, -0.9, -0.75, -0.5, 0.0, 0.5, 0.75, 0.9, 1.0] - steer_lataccel_factors = [1.5, 1.15, 1.02, 1.0, 1.0, 1.0, 1.02, 1.15, 1.5] - steer_correction_factor = interp( - steer_torque, - steer_break_pts, - steer_lataccel_factors - ) - - vego_break_pts = [0.0, 10.0, 15.0, 20.0, 100.0] - vego_lataccel_factors = [1.5, 1.5, 1.25, 1.0, 1.0] - vego_correction_factor = interp( - vego, - vego_break_pts, - vego_lataccel_factors, - ) - - return (steer_torque + friction) / (steer_correction_factor * vego_correction_factor) - - def torque_from_lateral_accel(self) -> TorqueFromLateralAccelCallbackType: - if self.CP.carFingerprint == CAR.BOLT_EUV: - return self.torque_from_lateral_accel_bolt - else: - return self.torque_from_lateral_accel_linear - @staticmethod def _get_params(ret, candidate, fingerprint, car_fw, experimental_long): ret.carName = "gm" @@ -227,7 +188,7 @@ class CarInterface(CarInterfaceBase): ret.steerRatio = 16.8 ret.centerToFront = ret.wheelbase * 0.4 tire_stiffness_factor = 1.0 - ret.steerActuatorDelay = 0.12 + ret.steerActuatorDelay = 0.2 CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) elif candidate == CAR.SILVERADO: diff --git a/selfdrive/car/interfaces.py b/selfdrive/car/interfaces.py index f1e2081d05..249818369c 100644 --- a/selfdrive/car/interfaces.py +++ b/selfdrive/car/interfaces.py @@ -18,7 +18,7 @@ from selfdrive.controls.lib.vehicle_model import VehicleModel ButtonType = car.CarState.ButtonEvent.Type GearShifter = car.CarState.GearShifter EventName = car.CarEvent.EventName -TorqueFromLateralAccelCallbackType = Callable[[float, car.CarParams.LateralTorqueTuning, float, float, float, bool], float] +TorqueFromLateralAccelCallbackType = Callable[[float, car.CarParams.LateralTorqueTuning, float, float, bool], float] MAX_CTRL_SPEED = (V_CRUISE_MAX + 4) * CV.KPH_TO_MS ACCEL_MAX = 2.0 @@ -131,7 +131,7 @@ class CarInterfaceBase(ABC): return self.get_steer_feedforward_default @staticmethod - def torque_from_lateral_accel_linear(lateral_accel_value, torque_params, lateral_accel_error, lateral_accel_deadzone, vego, friction_compensation): + def torque_from_lateral_accel_linear(lateral_accel_value, torque_params, lateral_accel_error, lateral_accel_deadzone, friction_compensation): # The default is a linear relationship between torque and lateral acceleration (accounting for road roll and steering friction) friction_interp = interp( apply_center_deadzone(lateral_accel_error, lateral_accel_deadzone), diff --git a/selfdrive/controls/lib/latcontrol_torque.py b/selfdrive/controls/lib/latcontrol_torque.py index 9129693e5a..2f56094379 100644 --- a/selfdrive/controls/lib/latcontrol_torque.py +++ b/selfdrive/controls/lib/latcontrol_torque.py @@ -64,10 +64,10 @@ class LatControlTorque(LatControl): error = setpoint - measurement gravity_adjusted_lateral_accel = desired_lateral_accel - params.roll * ACCELERATION_DUE_TO_GRAVITY pid_log.error = self.torque_from_lateral_accel(error, self.torque_params, error, - lateral_accel_deadzone, CS.vEgo, friction_compensation=False) + lateral_accel_deadzone, friction_compensation=False) ff = self.torque_from_lateral_accel(gravity_adjusted_lateral_accel, self.torque_params, desired_lateral_accel - actual_lateral_accel, - lateral_accel_deadzone, CS.vEgo, friction_compensation=True) + lateral_accel_deadzone, friction_compensation=True) freeze_integrator = steer_limited or CS.steeringPressed or CS.vEgo < 5 output_torque = self.pid.update(pid_log.error, From 1504c10760627406ce26478bcb98957c64deafac Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 21 Feb 2023 15:48:10 -0800 Subject: [PATCH 454/484] GM: revert steering limits update (#27413) * Revert " GM: update steering limits (#27331)" This reverts commit 1cdf80003dcaea79375cf398ec65849424f2f44b. * update refs * bump panda --- panda | 2 +- selfdrive/car/gm/values.py | 4 ++-- selfdrive/test/process_replay/ref_commit | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/panda b/panda index d2c2d5f926..9fe24bd368 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit d2c2d5f926537a132a253277998ef350fe866d27 +Subproject commit 9fe24bd3683377146b04c68ab94d0935f0bd9d78 diff --git a/selfdrive/car/gm/values.py b/selfdrive/car/gm/values.py index c4791738b8..2ac418b629 100644 --- a/selfdrive/car/gm/values.py +++ b/selfdrive/car/gm/values.py @@ -14,8 +14,8 @@ class CarControllerParams: STEER_STEP = 3 # Active control frames per command (~33hz) INACTIVE_STEER_STEP = 10 # Inactive control frames per command (10hz) STEER_DELTA_UP = 10 # Delta rates require review due to observed EPS weakness - STEER_DELTA_DOWN = 15 - STEER_DRIVER_ALLOWANCE = 65 + STEER_DELTA_DOWN = 25 + STEER_DRIVER_ALLOWANCE = 50 STEER_DRIVER_MULTIPLIER = 4 STEER_DRIVER_FACTOR = 100 NEAR_STOP_BRAKE_PHASE = 0.5 # m/s diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index c158382fe8..07fe22b022 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -f7ff7e7d37c276c31d8d44c73a0091b5996650b1 +4ad02871365f4cbd83c6b4c4f8ad6bb73015a626 \ No newline at end of file From f381b694013ef662c5d7638fe7b706315ce9547d Mon Sep 17 00:00:00 2001 From: huifan0114 <108561744+huifan0114@users.noreply.github.com> Date: Wed, 22 Feb 2023 11:14:19 +0800 Subject: [PATCH 455/484] =?UTF-8?q?=C5=A0koda:=20add=20missing=20FW=20vers?= =?UTF-8?q?ions=20for=202019=20Kodiaq=20(#27410)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update values.py * remove duplicate --------- Co-authored-by: Shane Smiskol --- selfdrive/car/volkswagen/values.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py index cd30bef73b..158a178b03 100755 --- a/selfdrive/car/volkswagen/values.py +++ b/selfdrive/car/volkswagen/values.py @@ -1050,6 +1050,7 @@ FW_VERSIONS = { b'\xf1\x8704E906027DD\xf1\x893123', b'\xf1\x8704L906026DE\xf1\x895418', b'\xf1\x875NA907115E \xf1\x890003', + b'\xf1\x875NA907115E \xf1\x890005', ], (Ecu.transmission, 0x7e1, None): [ b'\xf1\x870D9300043 \xf1\x895202', @@ -1058,8 +1059,8 @@ FW_VERSIONS = { b'\xf1\x870DL300013G \xf1\x892119', ], (Ecu.srs, 0x715, None): [ - b'\xf1\x873Q0959655BJ\xf1\x890703\xf1\x82\0161213001211001205212111052100', - b'\xf1\x873Q0959655CN\xf1\x890720\xf1\x82\0161213001211001205212112052100', + b'\xf1\x873Q0959655BJ\xf1\x890703\xf1\x82\x0e1213001211001205212111052100', + b'\xf1\x873Q0959655CN\xf1\x890720\xf1\x82\x0e1213001211001205212112052100', b'\xf1\x873Q0959655CQ\xf1\x890720\xf1\x82\x0e1213111211001205212112052111', ], (Ecu.eps, 0x712, None): [ From 72539fa177a4e3f320daf3257038ef3f2e4893ac Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 21 Feb 2023 21:34:02 -0800 Subject: [PATCH 456/484] AGNOS system reset: handle ABL reset (#27418) * AGNOS system reset: handle ABL reset * no more tapping * eta --- selfdrive/ui/qt/setup/reset.cc | 49 ++++++++++++++++-------- selfdrive/ui/qt/setup/reset.h | 11 +++++- selfdrive/ui/translations/main_de.ts | 21 +++++----- selfdrive/ui/translations/main_ja.ts | 21 +++++----- selfdrive/ui/translations/main_ko.ts | 21 +++++----- selfdrive/ui/translations/main_pt-BR.ts | 21 +++++----- selfdrive/ui/translations/main_zh-CHS.ts | 21 +++++----- selfdrive/ui/translations/main_zh-CHT.ts | 21 +++++----- 8 files changed, 107 insertions(+), 79 deletions(-) diff --git a/selfdrive/ui/qt/setup/reset.cc b/selfdrive/ui/qt/setup/reset.cc index 582217c1d7..ccf9bf8053 100644 --- a/selfdrive/ui/qt/setup/reset.cc +++ b/selfdrive/ui/qt/setup/reset.cc @@ -11,14 +11,11 @@ #define NVME "/dev/nvme0n1" #define USERDATA "/dev/disk/by-partlabel/userdata" -void Reset::doReset() { - // best effort to wipe nvme and sd card +void Reset::doErase() { + // best effort to wipe nvme std::system("sudo umount " NVME); std::system("yes | sudo mkfs.ext4 " NVME); - // we handle two cases here - // * user-prompted factory reset - // * recovering from a corrupt userdata by formatting int rm = std::system("sudo rm -rf /data/*"); std::system("sudo umount " USERDATA); int fmt = std::system("yes | sudo mkfs.ext4 " USERDATA); @@ -30,22 +27,26 @@ void Reset::doReset() { rebootBtn->show(); } +void Reset::startReset() { + body->setText(tr("Resetting device...\nThis may take up to a minute.")); + rejectBtn->hide(); + rebootBtn->hide(); + confirmBtn->hide(); +#ifdef __aarch64__ + QTimer::singleShot(100, this, &Reset::doErase); +#endif +} + void Reset::confirm() { const QString confirm_txt = tr("Are you sure you want to reset your device?"); if (body->text() != confirm_txt) { body->setText(confirm_txt); } else { - body->setText(tr("Resetting device...")); - rejectBtn->hide(); - rebootBtn->hide(); - confirmBtn->hide(); -#ifdef __aarch64__ - QTimer::singleShot(100, this, &Reset::doReset); -#endif + startReset(); } } -Reset::Reset(bool recover, QWidget *parent) : QWidget(parent) { +Reset::Reset(ResetMode mode, QWidget *parent) : QWidget(parent) { QVBoxLayout *main_layout = new QVBoxLayout(this); main_layout->setContentsMargins(45, 220, 45, 45); main_layout->setSpacing(0); @@ -56,7 +57,7 @@ Reset::Reset(bool recover, QWidget *parent) : QWidget(parent) { main_layout->addSpacing(60); - body = new QLabel(tr("System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot.")); + body = new QLabel(tr("Press confirm to erase all content and settings. Press cancel to resume boot.")); body->setWordWrap(true); body->setStyleSheet("font-size: 80px; font-weight: light;"); main_layout->addWidget(body, 1, Qt::AlignTop | Qt::AlignLeft); @@ -82,10 +83,16 @@ Reset::Reset(bool recover, QWidget *parent) : QWidget(parent) { blayout->addWidget(confirmBtn); QObject::connect(confirmBtn, &QPushButton::clicked, this, &Reset::confirm); + bool recover = mode == ResetMode::RECOVER; rejectBtn->setVisible(!recover); rebootBtn->setVisible(recover); if (recover) { - body->setText(tr("Unable to mount data partition. Press confirm to reset your device.")); + body->setText(tr("Unable to mount data partition. Partition may be corrupted. Press confirm to erase and reset your device.")); + } + + // automatically start if we're just finishing up an ABL reset + if (mode == ResetMode::FORMAT) { + startReset(); } setStyleSheet(R"( @@ -108,9 +115,17 @@ Reset::Reset(bool recover, QWidget *parent) : QWidget(parent) { } int main(int argc, char *argv[]) { - bool recover = argc > 1 && strcmp(argv[1], "--recover") == 0; + ResetMode mode = ResetMode::USER_RESET; + if (argc > 1) { + if (strcmp(argv[1], "--recover") == 0) { + mode = ResetMode::RECOVER; + } else if (strcmp(argv[1], "--format") == 0) { + mode = ResetMode::FORMAT; + } + } + QApplication a(argc, argv); - Reset reset(recover); + Reset reset(mode); setMainWindow(&reset); return a.exec(); } diff --git a/selfdrive/ui/qt/setup/reset.h b/selfdrive/ui/qt/setup/reset.h index 3a4994077c..04a191d829 100644 --- a/selfdrive/ui/qt/setup/reset.h +++ b/selfdrive/ui/qt/setup/reset.h @@ -2,18 +2,25 @@ #include #include +enum ResetMode { + USER_RESET, // user initiated a factory reset from openpilot + RECOVER, // userdata is corrupt for some reason, give a chance to recover + FORMAT, // finish up an ABL factory reset +}; + class Reset : public QWidget { Q_OBJECT public: - explicit Reset(bool recover = false, QWidget *parent = 0); + explicit Reset(ResetMode mode, QWidget *parent = 0); private: QLabel *body; QPushButton *rejectBtn; QPushButton *rebootBtn; QPushButton *confirmBtn; - void doReset(); + void doErase(); + void startReset(); private slots: void confirm(); diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index b40812af7c..77670ede3b 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -576,18 +576,10 @@ location set Are you sure you want to reset your device? Bist du sicher, dass du das Gerät auf Werkseinstellungen zurücksetzen möchtest?
- - Resetting device... - Gerät wird zurückgesetzt... - System Reset System auf Werkseinstellungen zurücksetzen - - System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot. - Zurücksetzen auf Werkseinstellungen wurde ausgewählt. Drücke Annehmen, um alle Inhalte und Einstellungen zu löschen. Drücke Abbrechen, um mit dem Starten des Gerätes fortzufahren. - Cancel Abbrechen @@ -601,8 +593,17 @@ location set Bestätigen - Unable to mount data partition. Press confirm to reset your device. - Datenpartition kann nicht geöffnet werden. Drücke Annehmen, um dein Gerät auf Werkseinstellungen zurückzusetzen. + Unable to mount data partition. Partition may be corrupted. Press confirm to erase and reset your device. + + + + Press confirm to erase all content and settings. Press cancel to resume boot. + + + + Resetting device... +This may take up to a minute. +
diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index ed08a52df5..856beee7bc 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -574,18 +574,10 @@ location set Are you sure you want to reset your device? 初期化してもよろしいですか? - - Resetting device... - デバイスが初期化されます... - System Reset システムを初期化 - - System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot. - システムの初期化をリクエストしました。「確認」ボタンを押すとデバイスが初期化されます。「キャンセル」ボタンを押すと起動を続行します。 - Cancel キャンセル @@ -599,8 +591,17 @@ location set 確認 - Unable to mount data partition. Press confirm to reset your device. - 「data」パーティションをマウントできません。「確認」ボタンを押すとデバイスが初期化されます。 + Unable to mount data partition. Partition may be corrupted. Press confirm to erase and reset your device. + + + + Press confirm to erase all content and settings. Press cancel to resume boot. + + + + Resetting device... +This may take up to a minute. + diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index 7b742a5048..ee5bb4fd36 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -574,18 +574,10 @@ location set Are you sure you want to reset your device? 장치를 초기화 하시겠습니까? - - Resetting device... - 장치 초기화중... - System Reset 장치 초기화 - - System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot. - 장치를 초기화 합니다. 확인버튼을 누르면 모든 내용과 설정이 초기화됩니다. 부팅을 재개하려면 취소를 누르세요. - Cancel 취소 @@ -599,8 +591,17 @@ location set 확인 - Unable to mount data partition. Press confirm to reset your device. - 데이터 파티션을 마운트할 수 없습니다. 확인 버튼을 눌러 장치를 리셋합니다. + Unable to mount data partition. Partition may be corrupted. Press confirm to erase and reset your device. + + + + Press confirm to erase all content and settings. Press cancel to resume boot. + + + + Resetting device... +This may take up to a minute. + diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index a25c51de3c..156b6f16c1 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -578,18 +578,10 @@ trabalho definido Are you sure you want to reset your device? Tem certeza que quer resetar seu dispositivo? - - Resetting device... - Resetando dispositivo... - System Reset Resetar Sistema - - System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot. - Solicitado reset do sistema. Confirme para apagar todo conteúdo e configurações. Aperte cancelar para continuar boot. - Cancel Cancelar @@ -603,8 +595,17 @@ trabalho definido Confirmar - Unable to mount data partition. Press confirm to reset your device. - Não foi possível montar a partição de dados. Pressione confirmar para resetar seu dispositivo. + Unable to mount data partition. Partition may be corrupted. Press confirm to erase and reset your device. + + + + Press confirm to erase all content and settings. Press cancel to resume boot. + + + + Resetting device... +This may take up to a minute. + diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index 68c1bb766f..f295a72938 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -572,18 +572,10 @@ location set Are you sure you want to reset your device? 您确定要重置您的设备吗? - - Resetting device... - 正在重置设备…… - System Reset 恢复出厂设置 - - System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot. - 已触发系统重置:确认以删除所有内容和设置。取消以正常启动设备。 - Cancel 取消 @@ -597,8 +589,17 @@ location set 确认 - Unable to mount data partition. Press confirm to reset your device. - 无法挂载数据分区。 确认以重置您的设备。 + Unable to mount data partition. Partition may be corrupted. Press confirm to erase and reset your device. + + + + Press confirm to erase all content and settings. Press cancel to resume boot. + + + + Resetting device... +This may take up to a minute. + diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index 71315e118f..b7636c18c5 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -574,18 +574,10 @@ location set Are you sure you want to reset your device? 您確定要重置你的設備嗎? - - Resetting device... - 重置設備中… - System Reset 系統重置 - - System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot. - 系統重置已觸發。請按確認刪除所有內容和設置。按取消恢復啟動。 - Cancel 取消 @@ -599,8 +591,17 @@ location set 確認 - Unable to mount data partition. Press confirm to reset your device. - 無法掛載數據分區。請按確認重置您的設備。 + Unable to mount data partition. Partition may be corrupted. Press confirm to erase and reset your device. + + + + Press confirm to erase all content and settings. Press cancel to resume boot. + + + + Resetting device... +This may take up to a minute. + From 58cd96536b852949f65309fea37684a6fa16668e Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 23 Feb 2023 03:44:53 +0800 Subject: [PATCH 457/484] cabana: small improvements to SignalView (#27419) --- tools/cabana/signaledit.cc | 34 ++++++++++++++++++++++++---------- tools/cabana/signaledit.h | 2 +- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/tools/cabana/signaledit.cc b/tools/cabana/signaledit.cc index e862439b68..c614ecb247 100644 --- a/tools/cabana/signaledit.cc +++ b/tools/cabana/signaledit.cc @@ -61,16 +61,26 @@ void SignalModel::updateState(const QHash *msgs) { auto &dat = can->lastMessage(msg_id).dat; int row = 0; for (auto item : root->children) { - double value = get_raw_value((uint8_t *)dat.begin(), dat.size(), *item->sig); - item->sig_val = QString::number(value); - emit dataChanged(index(row, 1), index(row, 1), {Qt::DisplayRole}); + QString value = QString::number(get_raw_value((uint8_t *)dat.begin(), dat.size(), *item->sig)); + if (value != item->sig_val) { + item->sig_val = value; + emit dataChanged(index(row, 1), index(row, 1), {Qt::DisplayRole}); + } ++row; } } } +SignalModel::Item *SignalModel::getItem(const QModelIndex &index) const { + SignalModel::Item *item = nullptr; + if (index.isValid()) { + item = (SignalModel::Item *)index.internalPointer(); + } + return item ? item : root.get(); +} + int SignalModel::rowCount(const QModelIndex &parent) const { - if (parent.column() > 0) return 0; + if (parent.isValid() && parent.column() > 0) return 0; auto parent_item = getItem(parent); int row_count = parent_item->children.size(); @@ -99,14 +109,19 @@ int SignalModel::signalRow(const Signal *sig) const { } QModelIndex SignalModel::index(int row, int column, const QModelIndex &parent) const { - if (!hasIndex(row, column, parent)) return {}; - return createIndex(row, column, getItem(parent)->children[row]); + if (parent.isValid() && parent.column() != 0) return {}; + + auto parent_item = getItem(parent); + if (parent_item && row < parent_item->children.size()) { + return createIndex(row, column, parent_item->children[row]); + } + return {}; } QModelIndex SignalModel::parent(const QModelIndex &index) const { if (!index.isValid()) return {}; Item *parent_item = getItem(index)->parent; - return parent_item == root.get() ? QModelIndex() : createIndex(parent_item->row(), 0, parent_item); + return !parent_item || parent_item == root.get() ? QModelIndex() : createIndex(parent_item->row(), 0, parent_item); } QVariant SignalModel::data(const QModelIndex &index, int role) const { @@ -287,8 +302,8 @@ SignalItemDelegate::SignalItemDelegate(QObject *parent) : QStyledItemDelegate(pa QSize SignalItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { QSize size = QStyledItemDelegate::sizeHint(option, index); - if (!index.parent().isValid() && index.column() == 0) { - size.rwidth() = std::min(((QWidget*)parent())->size().width() / 2, size.width() + color_label_width + 8); + if (index.column() == 0 && !index.parent().isValid()) { + size.rwidth() = std::min(option.widget->size().width() / 2, size.width() + color_label_width + 8); } return size; } @@ -355,7 +370,6 @@ SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts), setFrameStyle(QFrame::StyledPanel | QFrame::Plain); // title bar QWidget *title_bar = new QWidget(this); - title_bar->setAutoFillBackground(true); QHBoxLayout *hl = new QHBoxLayout(title_bar); hl->addWidget(signal_count_lb = new QLabel()); filter_edit = new QLineEdit(this); diff --git a/tools/cabana/signaledit.h b/tools/cabana/signaledit.h index 3cb193b62f..73a107f5c9 100644 --- a/tools/cabana/signaledit.h +++ b/tools/cabana/signaledit.h @@ -42,7 +42,7 @@ public: bool saveSignal(const Signal *origin_s, Signal &s); void resizeSignal(const Signal *sig, int start_bit, int size); void removeSignal(const Signal *sig); - inline Item *getItem(const QModelIndex &index) const { return index.isValid() ? (Item *)index.internalPointer() : root.get(); } + Item *getItem(const QModelIndex &index) const; int signalRow(const Signal *sig) const; void showExtraInfo(const QModelIndex &index); From e0a6074d00df9fafaa2b6d5e2d0fa01b154cce2f Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 23 Feb 2023 03:45:05 +0800 Subject: [PATCH 458/484] cabana: remove setUpdatesEnabled and setLabelFormat after updateAxisY (#27421) --- tools/cabana/chartswidget.cc | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index e315f82398..8a3a59d989 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -154,12 +154,10 @@ void ChartsWidget::updateState() { can->seekTo(zoomed_range.first); } - charts_layout->parentWidget()->setUpdatesEnabled(false); const auto &range = is_zoomed ? zoomed_range : display_range; for (auto c : charts) { c->updatePlot(cur_sec, range.first, range.second); } - charts_layout->parentWidget()->setUpdatesEnabled(true); } void ChartsWidget::setMaxChartRange(int value) { @@ -213,7 +211,6 @@ ChartView *ChartsWidget::createChart() { } void ChartsWidget::showChart(const MessageId &id, const Signal *sig, bool show, bool merge) { - setUpdatesEnabled(false); ChartView *chart = findChart(id, sig); if (show && !chart) { chart = merge && charts.size() > 0 ? charts.back() : createChart(); @@ -223,7 +220,6 @@ void ChartsWidget::showChart(const MessageId &id, const Signal *sig, bool show, chart->removeIf([&](auto &s) { return s.msg_id == id && s.sig == sig; }); } updateToolBar(); - setUpdatesEnabled(true); } void ChartsWidget::setColumnCount(int n) { @@ -568,6 +564,7 @@ void ChartView::updateAxisY() { QFontMetrics fm(axis_y->labelsFont()); int n = qMax(int(-qFloor(std::log10((max_y - min_y) / (tick_count - 1)))), 0) + 1; y_label_width = qMax(fm.width(QString::number(min_y, 'f', n)), fm.width(QString::number(max_y, 'f', n))) + 15; // left margin 15 + axis_y->setLabelFormat(QString("%.%1f").arg(n)); emit axisYLabelWidthChanged(y_label_width); } } From f2982340f8aed49559713f573b751e6095e51504 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 22 Feb 2023 14:09:16 -0800 Subject: [PATCH 459/484] Ford: adjust curvature rate limits (#27423) * Update values.py * Update values.py * make consistent * Update ref_commit --- selfdrive/car/ford/values.py | 5 +++-- selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index 6be93269c4..1c8917446a 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -28,8 +28,9 @@ class CarControllerParams: # Curvature rate limits # TODO: unify field names used by curvature and angle control cars - ANGLE_RATE_LIMIT_UP = AngleRateLimit(speed_bp=[5, 15, 25], angle_v=[0.005, 0.00056, 0.0002]) - ANGLE_RATE_LIMIT_DOWN = AngleRateLimit(speed_bp=[5, 15, 25], angle_v=[0.008, 0.00089, 0.00032]) + # ~2 m/s^3 up, -3 m/s^3 down + ANGLE_RATE_LIMIT_UP = AngleRateLimit(speed_bp=[5, 15, 25], angle_v=[0.004, 0.00044, 0.0002]) + ANGLE_RATE_LIMIT_DOWN = AngleRateLimit(speed_bp=[5, 15, 25], angle_v=[0.006, 0.00066, 0.00024]) ACCEL_MAX = 2.0 # m/s^s max acceleration ACCEL_MIN = -3.5 # m/s^s max deceleration diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 07fe22b022..1d807d75cd 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -4ad02871365f4cbd83c6b4c4f8ad6bb73015a626 \ No newline at end of file +5f45771d28c8e7112d474edcdd991689c6440acf From 6b4ac801e7ecb1addf74c9018958469741b609a3 Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Wed, 22 Feb 2023 14:11:04 -0800 Subject: [PATCH 460/484] laikad: use new cache param (#27404) * add backward compatibility * rename laikad cache file * rm and update --------- Co-authored-by: Kurt Nistelberger Co-authored-by: Adeeb Shihadeh --- common/params.cc | 2 +- selfdrive/locationd/laikad.py | 2 +- selfdrive/loggerd/tests/test_loggerd.py | 8 ++++---- tools/gpstest/rpc_server.py | 2 +- tools/gpstest/test_laikad.py | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/common/params.cc b/common/params.cc index 5e3361a70f..2ba4f7e8ab 100644 --- a/common/params.cc +++ b/common/params.cc @@ -138,7 +138,7 @@ std::unordered_map keys = { {"IsReleaseBranch", CLEAR_ON_MANAGER_START}, {"IsUpdateAvailable", CLEAR_ON_MANAGER_START}, {"JoystickDebugMode", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF}, - {"LaikadEphemeris", PERSISTENT | DONT_LOG}, + {"LaikadEphemerisV2", PERSISTENT | DONT_LOG}, {"LanguageSetting", PERSISTENT}, {"LastAthenaPingTime", CLEAR_ON_MANAGER_START}, {"LastGPSPosition", PERSISTENT}, diff --git a/selfdrive/locationd/laikad.py b/selfdrive/locationd/laikad.py index e85cd0ad42..2eb1e94526 100755 --- a/selfdrive/locationd/laikad.py +++ b/selfdrive/locationd/laikad.py @@ -28,7 +28,7 @@ from selfdrive.locationd.models.gnss_kf import States as GStates from system.swaglog import cloudlog MAX_TIME_GAP = 10 -EPHEMERIS_CACHE = 'LaikadEphemeris' +EPHEMERIS_CACHE = 'LaikadEphemerisV2' DOWNLOADS_CACHE_FOLDER = "/tmp/comma_download_cache/" CACHE_VERSION = 0.2 POS_FIX_RESIDUAL_THRESHOLD = 100.0 diff --git a/selfdrive/loggerd/tests/test_loggerd.py b/selfdrive/loggerd/tests/test_loggerd.py index 9c3565d130..857dc5c4e9 100755 --- a/selfdrive/loggerd/tests/test_loggerd.py +++ b/selfdrive/loggerd/tests/test_loggerd.py @@ -86,7 +86,7 @@ class TestLoggerd(unittest.TestCase): params.clear_all() for k, _, v in fake_params: params.put(k, v) - params.put("LaikadEphemeris", "abc") + params.put("LaikadEphemerisV2", "abc") lr = list(LogReader(str(self._gen_bootlog()))) initData = lr[0].initData @@ -103,14 +103,14 @@ class TestLoggerd(unittest.TestCase): # check params logged_params = {entry.key: entry.value for entry in initData.params.entries} - expected_params = set(k for k, _, __ in fake_params) | {'LaikadEphemeris'} + expected_params = set(k for k, _, __ in fake_params) | {'LaikadEphemerisV2'} assert set(logged_params.keys()) == expected_params, set(logged_params.keys()) ^ expected_params - assert logged_params['LaikadEphemeris'] == b'', f"DONT_LOG param value was logged: {repr(logged_params['LaikadEphemeris'])}" + assert logged_params['LaikadEphemerisV2'] == b'', f"DONT_LOG param value was logged: {repr(logged_params['LaikadEphemerisV2'])}" for param_key, initData_key, v in fake_params: self.assertEqual(getattr(initData, initData_key), v) self.assertEqual(logged_params[param_key].decode(), v) - params.put("LaikadEphemeris", "") + params.put("LaikadEphemerisV2", "") def test_rotation(self): os.environ["LOGGERD_TEST"] = "1" diff --git a/tools/gpstest/rpc_server.py b/tools/gpstest/rpc_server.py index b35c66d02d..a368566982 100644 --- a/tools/gpstest/rpc_server.py +++ b/tools/gpstest/rpc_server.py @@ -17,7 +17,7 @@ ALT_DELTA = 30 MATCH_NUM = 10 REPORT_STATS = 10 -EPHEM_CACHE = "/data/params/d/LaikadEphemeris" +EPHEM_CACHE = "/data/params/d/LaikadEphemerisV2" DOWNLOAD_CACHE = "/tmp/comma_download_cache" SERVER_LOG_FILE = "/tmp/fuzzy_server.log" diff --git a/tools/gpstest/test_laikad.py b/tools/gpstest/test_laikad.py index 613ac991b1..410a9c70d3 100644 --- a/tools/gpstest/test_laikad.py +++ b/tools/gpstest/test_laikad.py @@ -39,7 +39,7 @@ class TestLaikad(unittest.TestCase): def setUp(self): # ensure laikad cold start - Params().remove("LaikadEphemeris") + Params().remove("LaikadEphemerisV2") os.environ["LAIKAD_NO_INTERNET"] = "1" managed_processes['laikad'].start() From 9d64fb2d1abeb35d0b716b9e54169d473222ab21 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 22 Feb 2023 14:11:29 -0800 Subject: [PATCH 461/484] improved fan malfunction detection (#27405) --- selfdrive/controls/controlsd.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 2c359ec32a..f49b58212a 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -280,8 +280,9 @@ class Controls: # Alert if fan isn't spinning for 5 seconds if self.sm['peripheralState'].pandaType != log.PandaState.PandaType.unknown: - if self.sm['peripheralState'].fanSpeedRpm == 0 and self.sm['deviceState'].fanSpeedPercentDesired > 50: - if (self.sm.frame - self.last_functional_fan_frame) * DT_CTRL > 5.0: + if self.sm['peripheralState'].fanSpeedRpm < 500 and self.sm['deviceState'].fanSpeedPercentDesired > 50: + # allow enough time for the fan controller in the panda to recover from stalls + if (self.sm.frame - self.last_functional_fan_frame) * DT_CTRL > 15.0: self.events.add(EventName.fanMalfunction) else: self.last_functional_fan_frame = self.sm.frame From b00eb57bb735a8c533a28a22ee3aed534afd4a7c Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 22 Feb 2023 15:59:07 -0800 Subject: [PATCH 462/484] Ford: adjust curvature rate limits (#27427) * fix up * Update ref_commit --- selfdrive/car/ford/values.py | 4 ++-- selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index 1c8917446a..3bc56eb9dd 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -28,8 +28,8 @@ class CarControllerParams: # Curvature rate limits # TODO: unify field names used by curvature and angle control cars - # ~2 m/s^3 up, -3 m/s^3 down - ANGLE_RATE_LIMIT_UP = AngleRateLimit(speed_bp=[5, 15, 25], angle_v=[0.004, 0.00044, 0.0002]) + # ~2 m/s^3 up, ~-3 m/s^3 down + ANGLE_RATE_LIMIT_UP = AngleRateLimit(speed_bp=[5, 15, 25], angle_v=[0.004, 0.00044, 0.00016]) ANGLE_RATE_LIMIT_DOWN = AngleRateLimit(speed_bp=[5, 15, 25], angle_v=[0.006, 0.00066, 0.00024]) ACCEL_MAX = 2.0 # m/s^s max acceleration diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 1d807d75cd..5e7a5c8c0a 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -5f45771d28c8e7112d474edcdd991689c6440acf +a38b267bd2d44945dcd664b2a84e6cd0a6163f91 From 90db46e7756e2eb6d61b1ff23f7cb4a7b356abb9 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Wed, 22 Feb 2023 17:26:37 -0800 Subject: [PATCH 463/484] setup: write installer URL to tmpfile (#27426) * setup: write installer URL to tmpfile * missed ptr type --- selfdrive/ui/qt/setup/setup.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/selfdrive/ui/qt/setup/setup.cc b/selfdrive/ui/qt/setup/setup.cc index 392be68a12..409d79796c 100644 --- a/selfdrive/ui/qt/setup/setup.cc +++ b/selfdrive/ui/qt/setup/setup.cc @@ -59,6 +59,11 @@ void Setup::download(QString url) { curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &res_status); if (ret == CURLE_OK && res_status == 200 && is_elf(tmpfile)) { rename(tmpfile, "/tmp/installer"); + + FILE *fp_url = fopen("/tmp/installer_url", "w"); + fprintf(fp_url, "%s", url.toStdString().c_str()); + fclose(fp_url); + emit finished(true); } else { emit finished(false); From c1be6d945bb2efd6564f80f153c0ff71c59a3505 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 23 Feb 2023 12:44:33 +0800 Subject: [PATCH 464/484] cabana: dispaly current FPS & cached minitues on statusbar (#27430) dispaly current FPS & cached minitues on statusbar --- tools/cabana/mainwin.cc | 17 +++++++++++++++++ tools/cabana/mainwin.h | 2 ++ 2 files changed, 19 insertions(+) diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index bc090b2cc0..25e40e15aa 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -63,9 +63,11 @@ MainWindow::MainWindow() : QMainWindow() { QObject::connect(messages_widget, &MessagesWidget::msgSelectionChanged, center_widget, &CenterWidget::setMessage); QObject::connect(charts_widget, &ChartsWidget::dock, this, &MainWindow::dockCharts); QObject::connect(can, &AbstractStream::streamStarted, this, &MainWindow::loadDBCFromFingerprint); + QObject::connect(can, &AbstractStream::eventsMerged, this, &MainWindow::updateStatus); QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &MainWindow::DBCFileChanged); QObject::connect(UndoStack::instance(), &QUndoStack::cleanChanged, this, &MainWindow::undoStackCleanChanged); QObject::connect(UndoStack::instance(), &QUndoStack::indexChanged, this, &MainWindow::undoStackIndexChanged); + QObject::connect(&settings, &Settings::changed, this, &MainWindow::updateStatus); } void MainWindow::createActions() { @@ -179,6 +181,9 @@ void MainWindow::createStatusBar() { progress_bar->setVisible(false); statusBar()->addWidget(new QLabel(tr("For Help, Press F1"))); statusBar()->addPermanentWidget(progress_bar); + + statusBar()->addPermanentWidget(status_label = new QLabel(this)); + updateStatus(); } void MainWindow::createShortcuts() { @@ -403,6 +408,18 @@ void MainWindow::updateDownloadProgress(uint64_t cur, uint64_t total, bool succe } } +void MainWindow::updateStatus() { + float cached_minutes = 0; + if (!can->liveStreaming()) { + if (auto events = can->events(); !events->empty()) { + cached_minutes = (events->back()->mono_time - events->front()->mono_time) / (1e9 * 60); + } + } else { + settings.max_cached_minutes = settings.max_cached_minutes; + } + status_label->setText(tr("Cached Minutes:%1 FPS:%2").arg(cached_minutes, 0, 'f', 1).arg(settings.fps)); +} + void MainWindow::dockCharts(bool dock) { if (dock && floating_window) { floating_window->removeEventFilter(charts_widget); diff --git a/tools/cabana/mainwin.h b/tools/cabana/mainwin.h index dd53b1f213..2aeb20aea8 100644 --- a/tools/cabana/mainwin.h +++ b/tools/cabana/mainwin.h @@ -56,6 +56,7 @@ protected: void undoStackCleanChanged(bool clean); void undoStackIndexChanged(int index); void onlineHelp(); + void updateStatus(); VideoWidget *video_widget = nullptr; QDockWidget *video_dock; @@ -65,6 +66,7 @@ protected: QWidget *floating_window = nullptr; QVBoxLayout *charts_layout; QProgressBar *progress_bar; + QLabel *status_label; QJsonDocument fingerprint_to_dbc; QSplitter *video_splitter;; QString current_file = ""; From 7b43a69089fa862c492eb4d4d1706b19662ff8b4 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 22 Feb 2023 21:04:40 -0800 Subject: [PATCH 465/484] Update RELEASES.md --- RELEASES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index 0ea4241a35..f71810784a 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -2,7 +2,7 @@ Version 0.9.2 (2023-03-XX) ======================== * Draw MPC path instead of model predicted path, this is a more accurate representation of what the car will do. -Version 0.9.1 (2023-02-23) +Version 0.9.1 (2023-02-28) ======================== * New driving model * 30% improved height estimation resulting in better driving performance for tall cars From 4e0a3aa0d51c3abd96774bb12a6e28943082fa81 Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Thu, 23 Feb 2023 19:44:53 +0100 Subject: [PATCH 466/484] cabana: show bus in own column (#27434) --- tools/cabana/messageswidget.cc | 33 +++++++++++++++++++++++---------- tools/cabana/messageswidget.h | 2 +- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index c880c61058..cda3069ad9 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -20,11 +20,15 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { table_widget = new QTableView(this); model = new MessageListModel(this); table_widget->setModel(model); - table_widget->setItemDelegateForColumn(4, new MessageBytesDelegate(table_widget)); + table_widget->setItemDelegateForColumn(5, new MessageBytesDelegate(table_widget)); table_widget->setSelectionBehavior(QAbstractItemView::SelectRows); table_widget->setSelectionMode(QAbstractItemView::SingleSelection); table_widget->setSortingEnabled(true); table_widget->sortByColumn(0, Qt::AscendingOrder); + table_widget->setColumnWidth(0, 150); + table_widget->setColumnWidth(1, 50); + table_widget->setColumnWidth(2, 50); + table_widget->setColumnWidth(3, 50); table_widget->horizontalHeader()->setStretchLastSection(true); table_widget->verticalHeader()->hide(); main_layout->addWidget(table_widget); @@ -108,7 +112,7 @@ void MessagesWidget::reset() { QVariant MessageListModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) - return (QString[]){"Name", "ID", "Freq", "Count", "Bytes"}[section]; + return (QString[]){"Name", "Bus", "ID", "Freq", "Count", "Bytes"}[section]; return {}; } @@ -119,12 +123,13 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const { if (role == Qt::DisplayRole) { switch (index.column()) { case 0: return msgName(id); - case 1: return id.toString(); // TODO: put source and address in separate columns - case 2: return can_data.freq; - case 3: return can_data.count; - case 4: return toHex(can_data.dat); + case 1: return id.source; + case 2: return QString::number(id.address, 16);; + case 3: return can_data.freq; + case 4: return can_data.count; + case 5: return toHex(can_data.dat); } - } else if (role == Qt::UserRole && index.column() == 4) { + } else if (role == Qt::UserRole && index.column() == 5) { QVector colors = can_data.colors; if (!suppressed_bytes.empty()) { for (int i = 0; i < colors.size(); i++) { @@ -171,15 +176,23 @@ void MessageListModel::sortMessages() { }); } else if (sort_column == 1) { std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) { - return sort_order == Qt::AscendingOrder ? l < r : l > r; + auto ll = std::pair{l.source, l}; + auto rr = std::pair{r.source, r}; + return sort_order == Qt::AscendingOrder ? ll < rr : ll > rr; }); } else if (sort_column == 2) { + std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) { + auto ll = std::pair{l.address, l}; + auto rr = std::pair{r.address, r}; + return sort_order == Qt::AscendingOrder ? ll < rr : ll > rr; + }); + } else if (sort_column == 3) { std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) { auto ll = std::pair{can->lastMessage(l).freq, l}; auto rr = std::pair{can->lastMessage(r).freq, r}; return sort_order == Qt::AscendingOrder ? ll < rr : ll > rr; }); - } else if (sort_column == 3) { + } else if (sort_column == 4) { std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) { auto ll = std::pair{can->lastMessage(l).count, l}; auto rr = std::pair{can->lastMessage(r).count, r}; @@ -200,7 +213,7 @@ void MessageListModel::msgsReceived(const QHash *new_msgs) { } for (int i = 0; i < msgs.size(); ++i) { if (new_msgs->contains(msgs[i])) { - for (int col = 2; col < columnCount(); ++col) + for (int col = 3; col < columnCount(); ++col) emit dataChanged(index(i, col), index(i, col), {Qt::DisplayRole}); } } diff --git a/tools/cabana/messageswidget.h b/tools/cabana/messageswidget.h index 926d131c8d..ed9e241b0c 100644 --- a/tools/cabana/messageswidget.h +++ b/tools/cabana/messageswidget.h @@ -16,7 +16,7 @@ Q_OBJECT public: MessageListModel(QObject *parent) : QAbstractTableModel(parent) {} QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; - int columnCount(const QModelIndex &parent = QModelIndex()) const override { return 5; } + int columnCount(const QModelIndex &parent = QModelIndex()) const override { return 6; } QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; int rowCount(const QModelIndex &parent = QModelIndex()) const override { return msgs.size(); } void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; From a5644faa3f1095c2aa36b12e534fbba1b2ac921d Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Thu, 23 Feb 2023 19:45:13 +0100 Subject: [PATCH 467/484] cabana: show units (#27433) * show units on chart y axis * show in signal list * show in historyview * use clear() --- tools/cabana/chartswidget.cc | 12 +++++++++++- tools/cabana/historylog.cc | 10 +++++++++- tools/cabana/signaledit.cc | 3 +++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 8a3a59d989..7f07490abd 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -542,9 +542,16 @@ void ChartView::updateAxisY() { double min = std::numeric_limits::max(); double max = std::numeric_limits::lowest(); + QString unit = sigs[0].sig->unit; + for (auto &s : sigs) { if (!s.series->isVisible()) continue; + // Only show unit when all signals have the same unit + if (unit != s.sig->unit) { + unit.clear(); + } + auto first = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->min(), [](auto &p, double x) { return p.x() < x; }); auto last = std::lower_bound(first, s.vals.end(), axis_x->max(), [](auto &p, double x) { return p.x() < x; }); for (auto it = first; it != last; ++it) { @@ -552,6 +559,8 @@ void ChartView::updateAxisY() { if (it->y() > max) max = it->y(); } } + axis_y->setTitleText(unit); + if (min == std::numeric_limits::max()) min = 0; if (max == std::numeric_limits::lowest()) max = 0; @@ -563,7 +572,8 @@ void ChartView::updateAxisY() { QFontMetrics fm(axis_y->labelsFont()); int n = qMax(int(-qFloor(std::log10((max_y - min_y) / (tick_count - 1)))), 0) + 1; - y_label_width = qMax(fm.width(QString::number(min_y, 'f', n)), fm.width(QString::number(max_y, 'f', n))) + 15; // left margin 15 + int title_spacing = axis_y->titleText().isEmpty() ? 0 : 20; + y_label_width = title_spacing + qMax(fm.width(QString::number(min_y, 'f', n)), fm.width(QString::number(max_y, 'f', n))) + 15; // left margin 15 axis_y->setLabelFormat(QString("%.%1f").arg(n)); emit axisYLabelWidthChanged(y_label_width); } diff --git a/tools/cabana/historylog.cc b/tools/cabana/historylog.cc index abf0b53af8..deee2f1724 100644 --- a/tools/cabana/historylog.cc +++ b/tools/cabana/historylog.cc @@ -48,7 +48,15 @@ QVariant HistoryLogModel::headerData(int section, Qt::Orientation orientation, i if (section == 0) { return "Time"; } - return show_signals ? sigs[section - 1]->name : "Data"; + if (show_signals) { + QString name = sigs[section - 1]->name; + if (!sigs[section - 1]->unit.isEmpty()) { + name += QString(" (%1)").arg(sigs[section - 1]->unit); + } + return name; + } else { + return "Data"; + } } else if (role == Qt::BackgroundRole && section > 0 && show_signals) { return QBrush(getColor(sigs[section - 1])); } diff --git a/tools/cabana/signaledit.cc b/tools/cabana/signaledit.cc index c614ecb247..5fcfc116c1 100644 --- a/tools/cabana/signaledit.cc +++ b/tools/cabana/signaledit.cc @@ -62,6 +62,9 @@ void SignalModel::updateState(const QHash *msgs) { int row = 0; for (auto item : root->children) { QString value = QString::number(get_raw_value((uint8_t *)dat.begin(), dat.size(), *item->sig)); + if (!item->sig->unit.isEmpty()){ + value += " " + item->sig->unit; + } if (value != item->sig_val) { item->sig_val = value; emit dataChanged(index(row, 1), index(row, 1), {Qt::DisplayRole}); From dc4ebeb29c569c2b3bc9c687778b6e4ed754d329 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 24 Feb 2023 02:45:59 +0800 Subject: [PATCH 468/484] cabana: added a new series type to chart: step line (#27422) * add step line series * create buttons in createToolButtons * add inline function clearTrackPoints * do not show tooltip if series is invisible * use QActionGroup --- tools/cabana/chartswidget.cc | 145 +++++++++++++++++++++-------------- tools/cabana/chartswidget.h | 19 +++-- tools/cabana/settings.cc | 2 +- 3 files changed, 103 insertions(+), 63 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 7f07490abd..d895191d86 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -1,5 +1,6 @@ #include "tools/cabana/chartswidget.h" +#include #include #include #include @@ -183,7 +184,7 @@ void ChartsWidget::settingChanged() { range_slider->setRange(1, settings.max_cached_minutes * 60); for (auto c : charts) { c->setFixedHeight(settings.chart_height); - c->setSeriesType(settings.chart_series_type == 0 ? QAbstractSeries::SeriesTypeLine : QAbstractSeries::SeriesTypeScatter); + c->setSeriesType((SeriesType)settings.chart_series_type); } } @@ -309,7 +310,7 @@ bool ChartsWidget::eventFilter(QObject *obj, QEvent *event) { // ChartView ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { - series_type = settings.chart_series_type == 0 ? QAbstractSeries::SeriesTypeLine : QAbstractSeries::SeriesTypeScatter; + series_type = (SeriesType)settings.chart_series_type; QChart *chart = new QChart(); chart->setBackgroundVisible(false); axis_x = new QValueAxis(this); @@ -325,40 +326,54 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { background->setPen(Qt::NoPen); background->setZValue(chart->zValue() - 1); - move_icon = new QGraphicsPixmapItem(utils::icon("grip-horizontal"), chart); + setChart(chart); + + createToolButtons(); + setRenderHint(QPainter::Antialiasing); + // TODO: enable zoomIn/seekTo in live streaming mode. + setRubberBand(can->liveStreaming() ? QChartView::NoRubberBand : QChartView::HorizontalRubberBand); + + QObject::connect(dbc(), &DBCManager::signalRemoved, this, &ChartView::signalRemoved); + QObject::connect(dbc(), &DBCManager::signalUpdated, this, &ChartView::signalUpdated); + QObject::connect(dbc(), &DBCManager::msgRemoved, this, &ChartView::msgRemoved); + QObject::connect(dbc(), &DBCManager::msgUpdated, this, &ChartView::msgUpdated); +} + +void ChartView::createToolButtons() { + move_icon = new QGraphicsPixmapItem(utils::icon("grip-horizontal"), chart()); move_icon->setToolTip(tr("Drag and drop to combine charts")); QToolButton *remove_btn = toolButton("x", tr("Remove Chart")); - close_btn_proxy = new QGraphicsProxyWidget(chart); + close_btn_proxy = new QGraphicsProxyWidget(chart()); close_btn_proxy->setWidget(remove_btn); - close_btn_proxy->setZValue(chart->zValue() + 11); + close_btn_proxy->setZValue(chart()->zValue() + 11); - QToolButton *manage_btn = toolButton("list", ""); + // series types QMenu *menu = new QMenu(this); - line_series_action = menu->addAction(tr("Line"), [this]() { setSeriesType(QAbstractSeries::SeriesTypeLine); }); - line_series_action->setCheckable(true); - line_series_action->setChecked(series_type == QAbstractSeries::SeriesTypeLine); - scatter_series_action = menu->addAction(tr("Scatter"), [this]() { setSeriesType(QAbstractSeries::SeriesTypeScatter); }); - scatter_series_action->setCheckable(true); - scatter_series_action->setChecked(series_type == QAbstractSeries::SeriesTypeScatter); + auto change_series_group = new QActionGroup(menu); + change_series_group->setExclusive(true); + QStringList types{tr("line"), tr("Step Line"), tr("Scatter")}; + for (int i = 0; i < types.size(); ++i) { + QAction *act = new QAction(types[i], change_series_group); + act->setData(i); + act->setCheckable(true); + act->setChecked(i == (int)series_type); + menu->addAction(act); + } menu->addSeparator(); menu->addAction(tr("Manage series"), this, &ChartView::manageSeries); + + QToolButton *manage_btn = toolButton("list", ""); manage_btn->setMenu(menu); manage_btn->setPopupMode(QToolButton::InstantPopup); - manage_btn_proxy = new QGraphicsProxyWidget(chart); + manage_btn_proxy = new QGraphicsProxyWidget(chart()); manage_btn_proxy->setWidget(manage_btn); - manage_btn_proxy->setZValue(chart->zValue() + 11); + manage_btn_proxy->setZValue(chart()->zValue() + 11); - setChart(chart); - setRenderHint(QPainter::Antialiasing); - // TODO: enable zoomIn/seekTo in live streaming mode. - setRubberBand(can->liveStreaming() ? QChartView::NoRubberBand : QChartView::HorizontalRubberBand); - - QObject::connect(dbc(), &DBCManager::signalRemoved, this, &ChartView::signalRemoved); - QObject::connect(dbc(), &DBCManager::signalUpdated, this, &ChartView::signalUpdated); - QObject::connect(dbc(), &DBCManager::msgRemoved, this, &ChartView::msgRemoved); - QObject::connect(dbc(), &DBCManager::msgUpdated, this, &ChartView::msgUpdated); QObject::connect(remove_btn, &QToolButton::clicked, this, &ChartView::remove); + QObject::connect(change_series_group, &QActionGroup::triggered, [this](QAction *action) { + setSeriesType((SeriesType)action->data().toInt()); + }); } void ChartView::addSeries(const MessageId &msg_id, const Signal *sig) { @@ -476,7 +491,7 @@ void ChartView::updateSeriesPoints() { int num_points = std::max(end - begin, 1); int pixels_per_point = width() / num_points; - if (series_type == QAbstractSeries::SeriesTypeScatter) { + if (series_type == SeriesType::Scatter) { ((QScatterSeries *)s.series)->setMarkerSize(std::clamp(pixels_per_point / 3, 2, 8)); } else { s.series->setPointsVisible(pixels_per_point > 20); @@ -490,7 +505,9 @@ void ChartView::updateSeries(const Signal *sig, const std::vector *even if (!sig || s.sig == sig) { if (clear) { s.vals.clear(); + s.step_vals.clear(); s.vals.reserve(settings.max_cached_minutes * 60 * 100); // [n]seconds * 100hz + s.step_vals.reserve(settings.max_cached_minutes * 60 * 100 * 2); s.last_value_mono_time = 0; } s.series->setColor(getColor(s.sig)); @@ -498,6 +515,7 @@ void ChartView::updateSeries(const Signal *sig, const std::vector *even struct Chunk { std::vector::const_iterator first, second; QVector vals; + QVector step_vals; }; // split into one minitue chunks QVector chunks; @@ -510,6 +528,7 @@ void ChartView::updateSeries(const Signal *sig, const std::vector *even QtConcurrent::blockingMap(chunks, [&](Chunk &chunk) { chunk.vals.reserve(60 * 100); // 100 hz + chunk.step_vals.reserve(60 * 100 * 2); // 100 hz double route_start_time = can->routeStartTime(); for (auto it = chunk.first; it != chunk.second; ++it) { if ((*it)->which == cereal::Event::Which::CAN) { @@ -519,6 +538,10 @@ void ChartView::updateSeries(const Signal *sig, const std::vector *even double value = get_raw_value((uint8_t *)dat.begin(), dat.size(), *s.sig); double ts = ((*it)->mono_time / (double)1e9) - route_start_time; // seconds chunk.vals.push_back({ts, value}); + if (!chunk.step_vals.empty()) { + chunk.step_vals.push_back({ts, chunk.step_vals.back().y()}); + } + chunk.step_vals.push_back({ts,value}); } } } @@ -526,11 +549,12 @@ void ChartView::updateSeries(const Signal *sig, const std::vector *even }); for (auto &c : chunks) { s.vals.append(c.vals); + s.step_vals.append(c.step_vals); } if (events->size()) { s.last_value_mono_time = events->back()->mono_time; } - s.series->replace(s.vals); + s.series->replace(series_type == SeriesType::StepLine ? s.step_vals : s.vals); } } updateAxisY(); @@ -607,7 +631,7 @@ qreal ChartView::niceNumber(qreal x, bool ceiling) { } void ChartView::leaveEvent(QEvent *event) { - track_pts.clear(); + clearTrackPoints(); scene()->update(); QChartView::leaveEvent(event); } @@ -663,26 +687,31 @@ void ChartView::mouseMoveEvent(QMouseEvent *ev) { auto rubber = findChild(); bool is_zooming = rubber && rubber->isVisible(); const auto plot_area = chart()->plotArea(); - track_pts.clear(); + clearTrackPoints(); + if (!is_zooming && plot_area.contains(ev->pos())) { - track_pts.resize(sigs.size()); QStringList text_list; const double sec = chart()->mapToValue(ev->pos()).x(); - for (int i = 0; i < sigs.size(); ++i) { - QString value = "--"; + qreal x = -1; + for (auto &s : sigs) { + if (!s.series->isVisible()) continue; + // use reverse iterator to find last item <= sec. - auto it = std::lower_bound(sigs[i].vals.rbegin(), sigs[i].vals.rend(), sec, [](auto &p, double x) { return p.x() > x; }); - if (it != sigs[i].vals.rend() && it->x() >= axis_x->min()) { - value = QString::number(it->y()); - track_pts[i] = chart()->mapToPosition(*it); + auto it = std::lower_bound(s.vals.rbegin(), s.vals.rend(), sec, [](auto &p, double x) { return p.x() > x; }); + if (it != s.vals.rend() && it->x() >= axis_x->min()) { + s.track_pt = chart()->mapToPosition(*it); + x = std::max(x, s.track_pt.x()); } - text_list.push_back(QString("%2: %3").arg(sigs[i].series->color().name(), sigs[i].sig->name, value)); + text_list.push_back(QString("%2: %3") + .arg(s.series->color().name(), s.sig->name, + s.track_pt.isNull() ? "--" : QString::number(s.track_pt.y()))); + } + if (x < 0) { + x = ev->pos().x(); } - auto max = std::max_element(track_pts.begin(), track_pts.end(), [](auto &a, auto &b) { return a.x() < b.x(); }); - auto pt = (max == track_pts.end()) ? ev->pos() : *max; - text_list.push_front(QString::number(chart()->mapToValue(pt).x(), 'f', 3)); - QPointF tooltip_pt(pt.x() + 12, plot_area.top() - 20); - QToolTip::showText(mapToGlobal(tooltip_pt.toPoint()), pt.isNull() ? "" : text_list.join("
"), this, plot_area.toRect()); + text_list.push_front(QString::number(chart()->mapToValue({x, 0}).x(), 'f', 3)); + QPointF tooltip_pt(x + 12, plot_area.top() - 20); + QToolTip::showText(mapToGlobal(tooltip_pt.toPoint()), text_list.join("
"), this, plot_area.toRect()); scene()->invalidate({}, QGraphicsScene::ForegroundLayer); } else { QToolTip::hideText(); @@ -727,6 +756,7 @@ void ChartView::dropEvent(QDropEvent *event) { } void ChartView::drawForeground(QPainter *painter, const QRectF &rect) { + // draw time line qreal x = chart()->mapToPosition(QPointF{cur_sec, 0}).x(); x = std::clamp(x, chart()->plotArea().left(), chart()->plotArea().right()); qreal y1 = chart()->plotArea().top() - 2; @@ -734,18 +764,20 @@ void ChartView::drawForeground(QPainter *painter, const QRectF &rect) { painter->setPen(QPen(chart()->titleBrush().color(), 2)); painter->drawLine(QPointF{x, y1}, QPointF{x, y2}); - auto max = std::max_element(track_pts.begin(), track_pts.end(), [](auto &a, auto &b) { return a.x() < b.x(); }); - if (max != track_pts.end() && !max->isNull()) { - painter->setPen(QPen(Qt::darkGray, 1, Qt::DashLine)); - painter->drawLine(QPointF{max->x(), y1}, QPointF{max->x(), y2}); - painter->setPen(Qt::NoPen); - for (int i = 0; i < track_pts.size(); ++i) { - if (!track_pts[i].isNull() && i < sigs.size()) { - painter->setBrush(sigs[i].series->color().darker(125)); - painter->drawEllipse(track_pts[i], 5.5, 5.5); - } + // draw track points + painter->setPen(Qt::NoPen); + qreal track_line_x = -1; + for (auto &s : sigs) { + if (!s.track_pt.isNull() && s.series->isVisible()) { + painter->setBrush(s.series->color().darker(125)); + painter->drawEllipse(s.track_pt, 5.5, 5.5); + track_line_x = std::max(track_line_x, s.track_pt.x()); } } + if (track_line_x > 0) { + painter->setPen(QPen(Qt::darkGray, 1, Qt::DashLine)); + painter->drawLine(QPointF{track_line_x, y1}, QPointF{track_line_x, y2}); + } // paint points. OpenGL mode lacks certain features (such as showing points) painter->setPen(Qt::NoPen); @@ -761,11 +793,14 @@ void ChartView::drawForeground(QPainter *painter, const QRectF &rect) { } } -QXYSeries *ChartView::createSeries(QAbstractSeries::SeriesType type, QColor color) { +QXYSeries *ChartView::createSeries(SeriesType type, QColor color) { QXYSeries *series = nullptr; - if (type == QAbstractSeries::SeriesTypeLine) { + if (type == SeriesType::Line) { series = new QLineSeries(this); chart()->legend()->setMarkerShape(QLegend::MarkerShapeRectangle); + } else if (type == SeriesType::StepLine) { + series = new QLineSeries(this); + chart()->legend()->setMarkerShape(QLegend::MarkerShapeFromSeries); } else { series = new QScatterSeries(this); chart()->legend()->setMarkerShape(QLegend::MarkerShapeCircle); @@ -786,9 +821,7 @@ QXYSeries *ChartView::createSeries(QAbstractSeries::SeriesType type, QColor colo return series; } -void ChartView::setSeriesType(QAbstractSeries::SeriesType type) { - line_series_action->setChecked(type == QAbstractSeries::SeriesTypeLine); - scatter_series_action->setChecked(type == QAbstractSeries::SeriesTypeScatter); +void ChartView::setSeriesType(SeriesType type) { if (type != series_type) { series_type = type; for (auto &s : sigs) { @@ -797,7 +830,7 @@ void ChartView::setSeriesType(QAbstractSeries::SeriesType type) { } for (auto &s : sigs) { auto series = createSeries(series_type, getColor(s.sig)); - series->replace(s.vals); + series->replace(series_type == SeriesType::StepLine ? s.step_vals : s.vals); s.series = series; } updateSeriesPoints(); diff --git a/tools/cabana/chartswidget.h b/tools/cabana/chartswidget.h index a1dcf32513..76943c1fe9 100644 --- a/tools/cabana/chartswidget.h +++ b/tools/cabana/chartswidget.h @@ -20,6 +20,12 @@ using namespace QtCharts; const int CHART_MIN_WIDTH = 300; +enum class SeriesType { + Line = 0, + StepLine, + Scatter +}; + class ChartView : public QChartView { Q_OBJECT @@ -29,7 +35,7 @@ public: bool hasSeries(const MessageId &msg_id, const Signal *sig) const; void updateSeries(const Signal *sig = nullptr, const std::vector *events = nullptr, bool clear = true); void updatePlot(double cur, double min, double max); - void setSeriesType(QAbstractSeries::SeriesType type); + void setSeriesType(SeriesType type); void updatePlotArea(int left); struct SigItem { @@ -37,7 +43,9 @@ public: const Signal *sig = nullptr; QXYSeries *series = nullptr; QVector vals; + QVector step_vals; uint64_t last_value_mono_time = 0; + QPointF track_pt{}; }; signals: @@ -57,6 +65,7 @@ private slots: void signalRemoved(const Signal *sig) { removeIf([=](auto &s) { return s.sig == sig; }); } private: + void createToolButtons(); void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *ev) override; @@ -70,15 +79,15 @@ private: void drawForeground(QPainter *painter, const QRectF &rect) override; std::tuple getNiceAxisNumbers(qreal min, qreal max, int tick_count); qreal niceNumber(qreal x, bool ceiling); - QXYSeries *createSeries(QAbstractSeries::SeriesType type, QColor color); + QXYSeries *createSeries(SeriesType type, QColor color); void updateSeriesPoints(); void removeIf(std::function predicate); + inline void clearTrackPoints() { for (auto &s : sigs) s.track_pt = {}; } int y_label_width = 0; int align_to = 0; QValueAxis *axis_x; QValueAxis *axis_y; - QVector track_pts; QGraphicsPixmapItem *move_icon; QGraphicsProxyWidget *close_btn_proxy; QGraphicsProxyWidget *manage_btn_proxy; @@ -86,9 +95,7 @@ private: QList sigs; double cur_sec = 0; const QString mime_type = "application/x-cabanachartview"; - QAbstractSeries::SeriesType series_type = QAbstractSeries::SeriesTypeLine; - QAction *line_series_action; - QAction *scatter_series_action; + SeriesType series_type = SeriesType::Line; friend class ChartsWidget; }; diff --git a/tools/cabana/settings.cc b/tools/cabana/settings.cc index 6cbd16cabf..23f001efff 100644 --- a/tools/cabana/settings.cc +++ b/tools/cabana/settings.cc @@ -65,7 +65,7 @@ SettingsDlg::SettingsDlg(QWidget *parent) : QDialog(parent) { form_layout->addRow(tr("Max Cached Minutes"), cached_minutes); chart_series_type = new QComboBox(this); - chart_series_type->addItems({tr("Line"), tr("Scatter")}); + chart_series_type->addItems({tr("Line"), tr("Step Line"), tr("Scatter")}); chart_series_type->setCurrentIndex(settings.chart_series_type); form_layout->addRow(tr("Chart Default Series Type"), chart_series_type); From 7a3b3e8b567ea476088e6f3d7344c7220e35e398 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 23 Feb 2023 15:01:10 -0800 Subject: [PATCH 469/484] better main off alert (#27442) --- selfdrive/controls/lib/events.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/controls/lib/events.py b/selfdrive/controls/lib/events.py index 4e664d8cf0..e4ddfb5326 100644 --- a/selfdrive/controls/lib/events.py +++ b/selfdrive/controls/lib/events.py @@ -317,9 +317,9 @@ def modeld_lagging_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubM def wrong_car_mode_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: - text = "Cruise Mode Disabled" + text = "Enable Adaptive Cruise to Engage" if CP.carName == "honda": - text = "Main Switch Off" + text = "Enable Main Switch to Engage" return NoEntryAlert(text) From d5688ae49fd8652bf500ec145f2f3aa711c6bbf3 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 23 Feb 2023 16:18:32 -0800 Subject: [PATCH 470/484] Angle control: fix limiting bug (#27428) * fix steer up bug * Update ref_commit --- selfdrive/car/__init__.py | 2 +- selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/car/__init__.py b/selfdrive/car/__init__.py index c056f7ca3d..a90f023ed1 100644 --- a/selfdrive/car/__init__.py +++ b/selfdrive/car/__init__.py @@ -115,7 +115,7 @@ def apply_toyota_steer_torque_limits(apply_torque, apply_torque_last, motor_torq def apply_std_steer_angle_limits(apply_angle, apply_angle_last, v_ego, LIMITS): # pick angle rate limits based on wind up/down - steer_up = apply_angle_last * apply_angle > 0. and abs(apply_angle) > abs(apply_angle_last) + steer_up = apply_angle_last * apply_angle >= 0. and abs(apply_angle) > abs(apply_angle_last) rate_limits = LIMITS.ANGLE_RATE_LIMIT_UP if steer_up else LIMITS.ANGLE_RATE_LIMIT_DOWN angle_rate_lim = interp(v_ego, rate_limits.speed_bp, rate_limits.angle_v) diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 5e7a5c8c0a..9991154b9a 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -a38b267bd2d44945dcd664b2a84e6cd0a6163f91 +772f30de36fc7f8421dabb779cc02f45eb83d7bb From 79e6910106de6ae25127613728332e8b743d962a Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Thu, 23 Feb 2023 19:45:48 -0800 Subject: [PATCH 471/484] setup: detailed error messages (#27429) * setup: specific error state for non-executable file * Result -> DownloadResult * complete -> finished * rename widgets to be more consistent * fix typos * fix setCurrentIndex This appears to have different behaviour on device than on PC (off by one) * load fonts * copy Co-authored-by: Adeeb Shihadeh * Revert "load fonts" This reverts commit e8756598ed99aea39ef5721453e97920a494051c. * font family * undo * less widgets more better * font size --------- Co-authored-by: Adeeb Shihadeh --- selfdrive/ui/qt/setup/setup.cc | 41 ++++++++++++++++-------- selfdrive/ui/qt/setup/setup.h | 5 +-- selfdrive/ui/translations/main_de.ts | 8 +++++ selfdrive/ui/translations/main_ja.ts | 8 +++++ selfdrive/ui/translations/main_ko.ts | 8 +++++ selfdrive/ui/translations/main_pt-BR.ts | 8 +++++ selfdrive/ui/translations/main_zh-CHS.ts | 8 +++++ selfdrive/ui/translations/main_zh-CHT.ts | 8 +++++ 8 files changed, 78 insertions(+), 16 deletions(-) diff --git a/selfdrive/ui/qt/setup/setup.cc b/selfdrive/ui/qt/setup/setup.cc index 409d79796c..3de6255521 100644 --- a/selfdrive/ui/qt/setup/setup.cc +++ b/selfdrive/ui/qt/setup/setup.cc @@ -34,7 +34,7 @@ bool is_elf(char *fname) { void Setup::download(QString url) { CURL *curl = curl_easy_init(); if (!curl) { - emit finished(false); + emit finished(url, tr("Something went wrong. Reboot the device.")); return; } @@ -57,16 +57,19 @@ void Setup::download(QString url) { int ret = curl_easy_perform(curl); long res_status = 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &res_status); - if (ret == CURLE_OK && res_status == 200 && is_elf(tmpfile)) { + + if (ret != CURLE_OK || res_status != 200) { + emit finished(url, tr("Ensure the entered URL is valid, and the device’s internet connection is good.")); + } else if (!is_elf(tmpfile)) { + emit finished(url, tr("No custom software found at this URL.")); + } else { rename(tmpfile, "/tmp/installer"); FILE *fp_url = fopen("/tmp/installer_url", "w"); fprintf(fp_url, "%s", url.toStdString().c_str()); fclose(fp_url); - emit finished(true); - } else { - emit finished(false); + emit finished(url); } curl_slist_free_all(list); @@ -239,10 +242,10 @@ QWidget * Setup::downloading() { return widget; } -QWidget * Setup::download_failed() { +QWidget * Setup::download_failed(QLabel *url, QLabel *body) { QWidget *widget = new QWidget(); QVBoxLayout *main_layout = new QVBoxLayout(widget); - main_layout->setContentsMargins(55, 225, 55, 55); + main_layout->setContentsMargins(55, 185, 55, 55); main_layout->setSpacing(0); QLabel *title = new QLabel(tr("Download Failed")); @@ -251,7 +254,13 @@ QWidget * Setup::download_failed() { main_layout->addSpacing(67); - QLabel *body = new QLabel(tr("Ensure the entered URL is valid, and the device’s internet connection is good.")); + url->setWordWrap(true); + url->setAlignment(Qt::AlignTop | Qt::AlignLeft); + url->setStyleSheet("font-family: \"JetBrains Mono\"; font-size: 64px; font-weight: 400; margin-right: 100px;"); + main_layout->addWidget(url); + + main_layout->addSpacing(48); + body->setWordWrap(true); body->setAlignment(Qt::AlignTop | Qt::AlignLeft); body->setStyleSheet("font-size: 80px; font-weight: 300; margin-right: 100px;"); @@ -276,7 +285,7 @@ QWidget * Setup::download_failed() { restart->setProperty("primary", true); blayout->addWidget(restart); QObject::connect(restart, &QPushButton::clicked, this, [=]() { - setCurrentIndex(2); + setCurrentIndex(1); }); widget->setStyleSheet(R"( @@ -309,15 +318,19 @@ Setup::Setup(QWidget *parent) : QStackedWidget(parent) { downloading_widget = downloading(); addWidget(downloading_widget); - failed_widget = download_failed(); + QLabel *url_label = new QLabel(); + QLabel *body_label = new QLabel(); + failed_widget = download_failed(url_label, body_label); addWidget(failed_widget); - QObject::connect(this, &Setup::finished, [=](bool success) { - // hide setup on success - qDebug() << "finished" << success; - if (success) { + QObject::connect(this, &Setup::finished, [=](const QString &url, const QString &error) { + qDebug() << "finished" << url << error; + if (error.isEmpty()) { + // hide setup on success QTimer::singleShot(3000, this, &QWidget::hide); } else { + url_label->setText(url); + body_label->setText(error); setCurrentWidget(failed_widget); } }); diff --git a/selfdrive/ui/qt/setup/setup.h b/selfdrive/ui/qt/setup/setup.h index f990b5a6cb..bf5d97070d 100644 --- a/selfdrive/ui/qt/setup/setup.h +++ b/selfdrive/ui/qt/setup/setup.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -15,13 +16,13 @@ private: QWidget *getting_started(); QWidget *network_setup(); QWidget *downloading(); - QWidget *download_failed(); + QWidget *download_failed(QLabel *url, QLabel *body); QWidget *failed_widget; QWidget *downloading_widget; signals: - void finished(bool success); + void finished(const QString &url, const QString &error = ""); public slots: void nextPage(); diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index 77670ede3b..5f3511d3cc 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -703,6 +703,14 @@ This may take up to a minute. Start over Von neuem beginnen + + No custom software found at this URL. + + + + Something went wrong. Reboot the device. + +
SetupWidget diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index 856beee7bc..1e0223606b 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -701,6 +701,14 @@ This may take up to a minute. Start over 最初からやり直す + + No custom software found at this URL. + + + + Something went wrong. Reboot the device. + + SetupWidget diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index ee5bb4fd36..5470e57ce5 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -701,6 +701,14 @@ This may take up to a minute. Start over 다시 시작 + + No custom software found at this URL. + + + + Something went wrong. Reboot the device. + + SetupWidget diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index 156b6f16c1..5c4eab3327 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -705,6 +705,14 @@ This may take up to a minute. Start over Inicializar + + No custom software found at this URL. + + + + Something went wrong. Reboot the device. + + SetupWidget diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index f295a72938..cfc055ac98 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -699,6 +699,14 @@ This may take up to a minute. Start over 重来 + + No custom software found at this URL. + + + + Something went wrong. Reboot the device. + + SetupWidget diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index b7636c18c5..e4058ee0db 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -701,6 +701,14 @@ This may take up to a minute. Start over 重新開始 + + No custom software found at this URL. + + + + Something went wrong. Reboot the device. + + SetupWidget From f64fa07b0a42bb04d036f1280953dbbd27de41a0 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Thu, 23 Feb 2023 19:46:06 -0800 Subject: [PATCH 472/484] setup: set download timeout (#27441) --- selfdrive/ui/qt/setup/setup.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/selfdrive/ui/qt/setup/setup.cc b/selfdrive/ui/qt/setup/setup.cc index 3de6255521..de5021c8bc 100644 --- a/selfdrive/ui/qt/setup/setup.cc +++ b/selfdrive/ui/qt/setup/setup.cc @@ -53,6 +53,7 @@ void Setup::download(QString url) { curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_USERAGENT, (USER_AGENT + version).c_str()); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L); int ret = curl_easy_perform(curl); long res_status = 0; From 608ac253df5295187c6c7289b5b41a983482123e Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 24 Feb 2023 12:02:46 +0800 Subject: [PATCH 473/484] cabana: right align signal values & fixed text overflow (#27444) right align the signal values --- tools/cabana/signaledit.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/cabana/signaledit.cc b/tools/cabana/signaledit.cc index 5fcfc116c1..d5cd4ae825 100644 --- a/tools/cabana/signaledit.cc +++ b/tools/cabana/signaledit.cc @@ -338,6 +338,15 @@ void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op text = painter->fontMetrics().elidedText(text, Qt::ElideRight, text_rect.width()); painter->drawText(text_rect, option.displayAlignment, text); painter->restore(); + } else if (index.column() == 1 && item && item->type == SignalModel::Item::Sig) { + // draw signal value + if (option.state & QStyle::State_Selected) { + painter->fillRect(option.rect, option.palette.highlight()); + } + painter->setPen((option.state & QStyle::State_Selected ? option.palette.highlightedText() : option.palette.text()).color()); + QRect rc = option.rect.adjusted(0, 0, -70, 0); + auto text = painter->fontMetrics().elidedText(index.data(Qt::DisplayRole).toString(), Qt::ElideRight, rc.width()); + painter->drawText(rc, Qt::AlignRight | Qt::AlignVCenter, text); } else { QStyledItemDelegate::paint(painter, option, index); } From c80bc583817991a47bdcb59cc9d88713b1322b88 Mon Sep 17 00:00:00 2001 From: Robbe Derks Date: Fri, 24 Feb 2023 17:01:12 +0100 Subject: [PATCH 474/484] bump panda --- panda | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda b/panda index 9fe24bd368..1923b14189 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 9fe24bd3683377146b04c68ab94d0935f0bd9d78 +Subproject commit 1923b1418933e464ff7460a3e0a05d63aad0d53b From 78116029d33ab7724daa087201f1743fd34d6a19 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sun, 26 Feb 2023 06:27:18 +0800 Subject: [PATCH 475/484] cabana: replace hardcoded margins, colors, spacing with values from style. (#27449) replace hardcoded margins, colors, spacing improve performance of MessageBytesDelegate --- tools/cabana/binaryview.cc | 7 ++----- tools/cabana/chartswidget.cc | 21 +++++++++++--------- tools/cabana/historylog.cc | 4 +++- tools/cabana/mainwin.cc | 5 +++++ tools/cabana/messageswidget.cc | 4 +++- tools/cabana/signaledit.cc | 20 ++++++++++++------- tools/cabana/util.cc | 36 ++++++++++++++++++++++------------ tools/cabana/util.h | 9 +++++++-- 8 files changed, 68 insertions(+), 38 deletions(-) diff --git a/tools/cabana/binaryview.cc b/tools/cabana/binaryview.cc index d9b00f6882..120a85a330 100644 --- a/tools/cabana/binaryview.cc +++ b/tools/cabana/binaryview.cc @@ -293,7 +293,6 @@ void BinaryViewModel::updateState() { double max_f = 255.0; double factor = 0.25; double scaler = max_f / log2(1.0 + factor); - char hex[3] = {'\0'}; for (int i = 0; i < binary.size(); ++i) { for (int j = 0; j < 8; ++j) { auto &item = items[i * column_count + j]; @@ -305,9 +304,7 @@ void BinaryViewModel::updateState() { double alpha = std::clamp(offset + log2(1.0 + factor * (double)n / (double)last_msg.count) * scaler, min_f, max_f); item.bg_color.setAlpha(alpha); } - hex[0] = toHex(binary[i] >> 4); - hex[1] = toHex(binary[i] & 0xf); - items[i * column_count + 8].val = hex; + items[i * column_count + 8].val = toHex(binary[i]); items[i * column_count + 8].bg_color = last_msg.colors[i]; } for (int i = binary.size() * column_count; i < items.size(); ++i) { @@ -375,7 +372,7 @@ void BinaryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op bg.setAlpha(std::max(50, bg.alpha())); } painter->fillRect(option.rect, bg); - painter->setPen(Qt::black); + painter->setPen(option.palette.color(QPalette::Text)); } } diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index d895191d86..7982a5899c 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -25,11 +25,12 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QFrame(parent) { // toolbar QToolBar *toolbar = new QToolBar(tr("Charts"), this); - toolbar->setIconSize({16, 16}); + int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize); + toolbar->setIconSize({icon_size, icon_size}); QAction *new_plot_btn = toolbar->addAction(utils::icon("file-plus"), tr("New Plot")); toolbar->addWidget(title_label = new QLabel()); - title_label->setContentsMargins(0, 0, 12, 0); + title_label->setContentsMargins(0, 0, style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing), 0); QMenu *menu = new QMenu(this); for (int i = 0; i < MAX_COLUMN_COUNT; ++i) { @@ -77,8 +78,8 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QFrame(parent) { main_layout->addWidget(charts_scroll); // init settings - use_dark_theme = QApplication::style()->standardPalette().color(QPalette::WindowText).value() > - QApplication::style()->standardPalette().color(QPalette::Background).value(); + use_dark_theme = QApplication::palette().color(QPalette::WindowText).value() > + QApplication::palette().color(QPalette::Background).value(); column_count = std::clamp(settings.chart_column_count, 1, MAX_COLUMN_COUNT); max_chart_range = std::clamp(settings.chart_range, 1, settings.max_cached_minutes * 60); display_range = {0, max_chart_range}; @@ -322,7 +323,7 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) { chart->setMargins({0, 0, 0, 0}); background = new QGraphicsRectItem(chart); - background->setBrush(Qt::white); + background->setBrush(QApplication::palette().color(QPalette::Base)); background->setPen(Qt::NoPen); background->setZValue(chart->zValue() - 1); @@ -443,10 +444,12 @@ void ChartView::manageSeries() { void ChartView::resizeEvent(QResizeEvent *event) { updatePlotArea(align_to); - int x = event->size().width() - close_btn_proxy->size().width() - 11; - close_btn_proxy->setPos(x, 8); - manage_btn_proxy->setPos(x - manage_btn_proxy->size().width() - 5, 8); - move_icon->setPos(11, 8); + int top_margin = style()->pixelMetric(QStyle::PM_LayoutTopMargin); + int spacing = style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing); + int x = event->size().width() - close_btn_proxy->size().width() - style()->pixelMetric(QStyle::PM_LayoutRightMargin); + close_btn_proxy->setPos(x, top_margin); + manage_btn_proxy->setPos(x - manage_btn_proxy->size().width() - spacing, top_margin); + move_icon->setPos(style()->pixelMetric(QStyle::PM_LayoutLeftMargin), top_margin); QChartView::resizeEvent(event); } diff --git a/tools/cabana/historylog.cc b/tools/cabana/historylog.cc index deee2f1724..57f65b97d8 100644 --- a/tools/cabana/historylog.cc +++ b/tools/cabana/historylog.cc @@ -15,8 +15,10 @@ QVariant HistoryLogModel::data(const QModelIndex &index, int role) const { return QString::number((m.mono_time / (double)1e9) - can->routeStartTime(), 'f', 2); } return show_signals ? QString::number(m.sig_values[index.column() - 1]) : toHex(m.data); - } else if (role == Qt::UserRole && index.column() == 1 && !show_signals) { + } else if (role == ColorsRole) { return QVariant::fromValue(m.colors); + } else if (role == BytesRole) { + return m.data; } return {}; } diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index 25e40e15aa..282a541244 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -58,6 +58,11 @@ MainWindow::MainWindow() : QMainWindow() { fingerprint_to_dbc = QJsonDocument::fromJson(json_file.readAll()); } + setStyleSheet(QString(R"(QMainWindow::separator { + width: %1px; /* when vertical */ + height: %1px; /* when horizontal */ + })").arg(style()->pixelMetric(QStyle::PM_SplitterWidth))); + QObject::connect(this, &MainWindow::showMessage, statusBar(), &QStatusBar::showMessage); QObject::connect(this, &MainWindow::updateProgressBar, this, &MainWindow::updateDownloadProgress); QObject::connect(messages_widget, &MessagesWidget::msgSelectionChanged, center_widget, &CenterWidget::setMessage); diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index cda3069ad9..b4a7ed7b34 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -129,7 +129,7 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const { case 4: return can_data.count; case 5: return toHex(can_data.dat); } - } else if (role == Qt::UserRole && index.column() == 5) { + } else if (role == ColorsRole) { QVector colors = can_data.colors; if (!suppressed_bytes.empty()) { for (int i = 0; i < colors.size(); i++) { @@ -139,6 +139,8 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const { } } return QVariant::fromValue(colors); + } else if (role == BytesRole) { + return can_data.dat; } return {}; } diff --git a/tools/cabana/signaledit.cc b/tools/cabana/signaledit.cc index d5cd4ae825..e425a13f82 100644 --- a/tools/cabana/signaledit.cc +++ b/tools/cabana/signaledit.cc @@ -322,19 +322,22 @@ void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op // color label auto bg_color = getColor(item->sig); - QRect rc{option.rect.left(), option.rect.top(), color_label_width, option.rect.height()}; + int h_margin = option.widget->style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1; + int v_margin = option.widget->style()->pixelMetric(QStyle::PM_FocusFrameVMargin); + QRect rc{option.rect.left() + h_margin, option.rect.top(), color_label_width, option.rect.height()}; painter->setPen(Qt::NoPen); painter->setBrush(item->highlight ? bg_color.darker(125) : bg_color); - painter->drawRoundedRect(rc.adjusted(0, 2, 0, -2), 3, 3); + painter->drawRoundedRect(rc.adjusted(0, v_margin, 0, -v_margin), 3, 3); painter->setPen(item->highlight ? Qt::white : Qt::black); painter->setFont(small_font); painter->drawText(rc, Qt::AlignCenter, QString::number(item->row() + 1)); // signal name painter->setFont(option.font); - painter->setPen((option.state & QStyle::State_Selected ? option.palette.highlightedText() : option.palette.text()).color()); + painter->setPen(option.palette.color(option.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text)); QString text = index.data(Qt::DisplayRole).toString(); - QRect text_rect = option.rect.adjusted(rc.width() + 6, 0, 0, 0); + QRect text_rect = option.rect; + text_rect.setLeft(rc.right() + h_margin * 2); text = painter->fontMetrics().elidedText(text, Qt::ElideRight, text_rect.width()); painter->drawText(text_rect, option.displayAlignment, text); painter->restore(); @@ -343,7 +346,7 @@ void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op if (option.state & QStyle::State_Selected) { painter->fillRect(option.rect, option.palette.highlight()); } - painter->setPen((option.state & QStyle::State_Selected ? option.palette.highlightedText() : option.palette.text()).color()); + painter->setPen(option.palette.color(option.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text)); QRect rc = option.rect.adjusted(0, 0, -70, 0); auto text = painter->fontMetrics().elidedText(index.data(Qt::DisplayRole).toString(), Qt::ElideRight, rc.width()); painter->drawText(rc, Qt::AlignRight | Qt::AlignVCenter, text); @@ -442,8 +445,11 @@ void SignalView::rowsChanged() { if (!tree->indexWidget(index)) { QWidget *w = new QWidget(this); QHBoxLayout *h = new QHBoxLayout(w); - h->setContentsMargins(0, 2, 0, 2); - h->addStretch(1); + int v_margin = style()->pixelMetric(QStyle::PM_FocusFrameVMargin); + int h_margin = style()->pixelMetric(QStyle::PM_FocusFrameHMargin); + h->setContentsMargins(0, v_margin, -h_margin, v_margin); + h->setSpacing(style()->pixelMetric(QStyle::PM_ToolBarItemSpacing)); + h->addStretch(0); auto remove_btn = toolButton("x", tr("Remove signal")); auto plot_btn = toolButton("graph-up", ""); diff --git a/tools/cabana/util.cc b/tools/cabana/util.cc index 4f79f9a3ac..5e4f505d29 100644 --- a/tools/cabana/util.cc +++ b/tools/cabana/util.cc @@ -71,26 +71,26 @@ void ChangeTracker::clear() { MessageBytesDelegate::MessageBytesDelegate(QObject *parent) : QStyledItemDelegate(parent) { fixed_font = QFontDatabase::systemFont(QFontDatabase::FixedFont); + byte_width = QFontMetrics(fixed_font).width("00 "); } void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { + auto colors = index.data(ColorsRole).value>(); + auto byte_list = index.data(BytesRole).toByteArray(); + + int v_margin = option.widget->style()->pixelMetric(QStyle::PM_FocusFrameVMargin); + int h_margin = option.widget->style()->pixelMetric(QStyle::PM_FocusFrameHMargin); + QRect rc{option.rect.left() + h_margin, option.rect.top() + v_margin, byte_width, option.rect.height() - 2 * v_margin}; + auto color_role = option.state & QStyle::State_Selected ? QPalette::HighlightedText: QPalette::Text; painter->setPen(option.palette.color(color_role)); painter->setFont(fixed_font); - int space = painter->boundingRect(option.rect, option.displayAlignment, " ").width(); - QRect pos = painter->boundingRect(option.rect, option.displayAlignment, "00").adjusted(0, 0, 2, 0); - pos.moveLeft(pos.x() + space); - int m = space / 2; - const QMargins margins(m, m, m, m); - - auto colors = index.data(Qt::UserRole).value>(); - auto byte_list = index.data(Qt::DisplayRole).toString().split(" "); for (int i = 0; i < byte_list.size(); ++i) { if (i < colors.size() && colors[i].alpha() > 0) { - painter->fillRect(pos.marginsAdded(margins), colors[i]); + painter->fillRect(rc, colors[i]); } - painter->drawText(pos, Qt::AlignCenter, byte_list[i]); - pos.moveLeft(pos.right() + space); + painter->drawText(rc, Qt::AlignCenter, toHex(byte_list[i])); + rc.moveLeft(rc.right() + 1); } } @@ -114,8 +114,8 @@ QValidator::State NameValidator::validate(QString &input, int &pos) const { namespace utils { QPixmap icon(const QString &id) { - static bool dark_theme = QApplication::style()->standardPalette().color(QPalette::WindowText).value() > - QApplication::style()->standardPalette().color(QPalette::Background).value(); + static bool dark_theme = QApplication::palette().color(QPalette::WindowText).value() > + QApplication::palette().color(QPalette::Background).value(); QPixmap pm; QString key = "bootstrap_" % id % (dark_theme ? "1" : "0"); if (!QPixmapCache::find(key, &pm)) { @@ -138,3 +138,13 @@ QToolButton *toolButton(const QString &icon, const QString &tooltip) { btn->setAutoRaise(true); return btn; }; + + +QString toHex(uint8_t byte) { + static std::array hex = []() { + std::array ret; + for (int i = 0; i < 256; ++i) ret[i] = QStringLiteral("%1").arg(i, 2, 16, QLatin1Char('0')).toUpper(); + return ret; + }(); + return hex[byte]; +} diff --git a/tools/cabana/util.h b/tools/cabana/util.h index b72ddfcaa0..5eb5c3c5aa 100644 --- a/tools/cabana/util.h +++ b/tools/cabana/util.h @@ -14,7 +14,6 @@ #include "tools/cabana/dbcmanager.h" using namespace dbcmanager; - class ChangeTracker { public: void compute(const QByteArray &dat, double ts, uint32_t freq); @@ -31,16 +30,22 @@ private: QByteArray prev_dat; }; +enum { + ColorsRole = Qt::UserRole + 1, + BytesRole = Qt::UserRole + 2 +}; + class MessageBytesDelegate : public QStyledItemDelegate { Q_OBJECT public: MessageBytesDelegate(QObject *parent); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QFont fixed_font; + int byte_width; }; inline QString toHex(const QByteArray &dat) { return dat.toHex(' ').toUpper(); } -inline char toHex(uint value) { return "0123456789ABCDEF"[value & 0xF]; } +QString toHex(uint8_t byte); QColor getColor(const dbcmanager::Signal *sig); class NameValidator : public QRegExpValidator { From 581fd62d2642629b23ac111112245a963202143f Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sun, 26 Feb 2023 17:32:52 -0800 Subject: [PATCH 476/484] steer limits: rename common dist to meas function (#27453) * rename function * make a wrapper function (ford uses dynamic up/down limits * make two functions consistent * make torque function convert to int --- selfdrive/car/__init__.py | 38 ++++++++++++++--------- selfdrive/car/chrysler/carcontroller.py | 4 +-- selfdrive/car/gm/carcontroller.py | 4 +-- selfdrive/car/hyundai/carcontroller.py | 4 +-- selfdrive/car/mazda/carcontroller.py | 6 ++-- selfdrive/car/subaru/carcontroller.py | 4 +-- selfdrive/car/toyota/carcontroller.py | 4 +-- selfdrive/car/volkswagen/carcontroller.py | 4 +-- 8 files changed, 38 insertions(+), 30 deletions(-) diff --git a/selfdrive/car/__init__.py b/selfdrive/car/__init__.py index a90f023ed1..58cde85817 100644 --- a/selfdrive/car/__init__.py +++ b/selfdrive/car/__init__.py @@ -73,7 +73,7 @@ def dbc_dict(pt_dbc, radar_dbc, chassis_dbc=None, body_dbc=None) -> Dict[str, st return {'pt': pt_dbc, 'radar': radar_dbc, 'chassis': chassis_dbc, 'body': body_dbc} -def apply_std_steer_torque_limits(apply_torque, apply_torque_last, driver_torque, LIMITS): +def apply_driver_steer_torque_limits(apply_torque, apply_torque_last, driver_torque, LIMITS): # limits due to driver torque driver_max_torque = LIMITS.STEER_MAX + (LIMITS.STEER_DRIVER_ALLOWANCE + driver_torque * LIMITS.STEER_DRIVER_FACTOR) * LIMITS.STEER_DRIVER_MULTIPLIER @@ -93,24 +93,32 @@ def apply_std_steer_torque_limits(apply_torque, apply_torque_last, driver_torque return int(round(float(apply_torque))) -def apply_toyota_steer_torque_limits(apply_torque, apply_torque_last, motor_torque, LIMITS): - # limits due to comparison of commanded torque VS motor reported torque - max_lim = min(max(motor_torque + LIMITS.STEER_ERROR_MAX, LIMITS.STEER_ERROR_MAX), LIMITS.STEER_MAX) - min_lim = max(min(motor_torque - LIMITS.STEER_ERROR_MAX, -LIMITS.STEER_ERROR_MAX), -LIMITS.STEER_MAX) +def apply_dist_to_meas_limits(val, val_last, val_meas, + STEER_DELTA_UP, STEER_DELTA_DOWN, + STEER_ERROR_MAX, STEER_MAX): + # limits due to comparison of commanded val VS measured val (torque/angle/curvature) + max_lim = min(max(val_meas + STEER_ERROR_MAX, STEER_ERROR_MAX), STEER_MAX) + min_lim = max(min(val_meas - STEER_ERROR_MAX, -STEER_ERROR_MAX), -STEER_MAX) - apply_torque = clip(apply_torque, min_lim, max_lim) + val = clip(val, min_lim, max_lim) - # slow rate if steer torque increases in magnitude - if apply_torque_last > 0: - apply_torque = clip(apply_torque, - max(apply_torque_last - LIMITS.STEER_DELTA_DOWN, -LIMITS.STEER_DELTA_UP), - apply_torque_last + LIMITS.STEER_DELTA_UP) + # slow rate if val increases in magnitude + if val_last > 0: + val = clip(val, + max(val_last - STEER_DELTA_DOWN, -STEER_DELTA_UP), + val_last + STEER_DELTA_UP) else: - apply_torque = clip(apply_torque, - apply_torque_last - LIMITS.STEER_DELTA_UP, - min(apply_torque_last + LIMITS.STEER_DELTA_DOWN, LIMITS.STEER_DELTA_UP)) + val = clip(val, + val_last - STEER_DELTA_UP, + min(val_last + STEER_DELTA_DOWN, STEER_DELTA_UP)) - return int(round(float(apply_torque))) + return float(val) + + +def apply_meas_steer_torque_limits(apply_torque, apply_torque_last, motor_torque, LIMITS): + return int(round(apply_dist_to_meas_limits(apply_torque, apply_torque_last, motor_torque, + LIMITS.STEER_DELTA_UP, LIMITS.STEER_DELTA_DOWN, + LIMITS.STEER_ERROR_MAX, LIMITS.STEER_MAX))) def apply_std_steer_angle_limits(apply_angle, apply_angle_last, v_ego, LIMITS): diff --git a/selfdrive/car/chrysler/carcontroller.py b/selfdrive/car/chrysler/carcontroller.py index 20a44bce21..b418179e0e 100644 --- a/selfdrive/car/chrysler/carcontroller.py +++ b/selfdrive/car/chrysler/carcontroller.py @@ -1,6 +1,6 @@ from opendbc.can.packer import CANPacker from common.realtime import DT_CTRL -from selfdrive.car import apply_toyota_steer_torque_limits +from selfdrive.car import apply_meas_steer_torque_limits from selfdrive.car.chrysler.chryslercan import create_lkas_hud, create_lkas_command, create_cruise_buttons from selfdrive.car.chrysler.values import RAM_CARS, CarControllerParams, ChryslerFlags @@ -67,7 +67,7 @@ class CarController: # steer torque new_steer = int(round(CC.actuators.steer * self.params.STEER_MAX)) - apply_steer = apply_toyota_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorqueEps, self.params) + apply_steer = apply_meas_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorqueEps, self.params) if not lkas_active or not lkas_control_bit: apply_steer = 0 self.apply_steer_last = apply_steer diff --git a/selfdrive/car/gm/carcontroller.py b/selfdrive/car/gm/carcontroller.py index 42eaf7e88a..2a996c0ff6 100644 --- a/selfdrive/car/gm/carcontroller.py +++ b/selfdrive/car/gm/carcontroller.py @@ -3,7 +3,7 @@ from common.conversions import Conversions as CV from common.numpy_fast import interp from common.realtime import DT_CTRL from opendbc.can.packer import CANPacker -from selfdrive.car import apply_std_steer_torque_limits +from selfdrive.car import apply_driver_steer_torque_limits from selfdrive.car.gm import gmcan from selfdrive.car.gm.values import DBC, CanBus, CarControllerParams, CruiseButtons @@ -73,7 +73,7 @@ class CarController: if CC.latActive: new_steer = int(round(actuators.steer * self.params.STEER_MAX)) - apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.params) + apply_steer = apply_driver_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.params) else: apply_steer = 0 diff --git a/selfdrive/car/hyundai/carcontroller.py b/selfdrive/car/hyundai/carcontroller.py index 1e6f78af20..2572038492 100644 --- a/selfdrive/car/hyundai/carcontroller.py +++ b/selfdrive/car/hyundai/carcontroller.py @@ -3,7 +3,7 @@ from common.conversions import Conversions as CV from common.numpy_fast import clip from common.realtime import DT_CTRL from opendbc.can.packer import CANPacker -from selfdrive.car import apply_std_steer_torque_limits +from selfdrive.car import apply_driver_steer_torque_limits from selfdrive.car.hyundai import hyundaicanfd, hyundaican from selfdrive.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CANFD_CAR, CAR @@ -60,7 +60,7 @@ class CarController: # steering torque new_steer = int(round(actuators.steer * self.params.STEER_MAX)) - apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.params) + apply_steer = apply_driver_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.params) if not CC.latActive: apply_steer = 0 diff --git a/selfdrive/car/mazda/carcontroller.py b/selfdrive/car/mazda/carcontroller.py index 524a02a370..cb401a8abd 100644 --- a/selfdrive/car/mazda/carcontroller.py +++ b/selfdrive/car/mazda/carcontroller.py @@ -1,6 +1,6 @@ from cereal import car from opendbc.can.packer import CANPacker -from selfdrive.car import apply_std_steer_torque_limits +from selfdrive.car import apply_driver_steer_torque_limits from selfdrive.car.mazda import mazdacan from selfdrive.car.mazda.values import CarControllerParams, Buttons @@ -23,8 +23,8 @@ class CarController: if CC.latActive: # calculate steer and also set limits due to driver torque new_steer = int(round(CC.actuators.steer * CarControllerParams.STEER_MAX)) - apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, - CS.out.steeringTorque, CarControllerParams) + apply_steer = apply_driver_steer_torque_limits(new_steer, self.apply_steer_last, + CS.out.steeringTorque, CarControllerParams) if CC.cruiseControl.cancel: # If brake is pressed, let us wait >70ms before trying to disable crz to avoid diff --git a/selfdrive/car/subaru/carcontroller.py b/selfdrive/car/subaru/carcontroller.py index 24d85877d7..a6dbf4a39e 100644 --- a/selfdrive/car/subaru/carcontroller.py +++ b/selfdrive/car/subaru/carcontroller.py @@ -1,5 +1,5 @@ from opendbc.can.packer import CANPacker -from selfdrive.car import apply_std_steer_torque_limits +from selfdrive.car import apply_driver_steer_torque_limits from selfdrive.car.subaru import subarucan from selfdrive.car.subaru.values import DBC, GLOBAL_GEN2, PREGLOBAL_CARS, CarControllerParams @@ -34,7 +34,7 @@ class CarController: # limits due to driver torque new_steer = int(round(apply_steer)) - apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.p) + apply_steer = apply_driver_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.p) if not CC.latActive: apply_steer = 0 diff --git a/selfdrive/car/toyota/carcontroller.py b/selfdrive/car/toyota/carcontroller.py index 2e0d7009c8..8004ea9dca 100644 --- a/selfdrive/car/toyota/carcontroller.py +++ b/selfdrive/car/toyota/carcontroller.py @@ -1,6 +1,6 @@ from cereal import car from common.numpy_fast import clip, interp -from selfdrive.car import apply_toyota_steer_torque_limits, create_gas_interceptor_command, make_can_msg +from selfdrive.car import apply_meas_steer_torque_limits, create_gas_interceptor_command, make_can_msg from selfdrive.car.toyota.toyotacan import create_steer_command, create_ui_command, \ create_accel_command, create_acc_cancel_command, \ create_fcw_command, create_lta_steer_command @@ -60,7 +60,7 @@ class CarController: # steer torque new_steer = int(round(actuators.steer * CarControllerParams.STEER_MAX)) - apply_steer = apply_toyota_steer_torque_limits(new_steer, self.last_steer, CS.out.steeringTorqueEps, self.torque_rate_limits) + apply_steer = apply_meas_steer_torque_limits(new_steer, self.last_steer, CS.out.steeringTorqueEps, self.torque_rate_limits) # Count up to MAX_STEER_RATE_FRAMES, at which point we need to cut torque to avoid a steering fault if lat_active and abs(CS.out.steeringRateDeg) >= MAX_STEER_RATE: diff --git a/selfdrive/car/volkswagen/carcontroller.py b/selfdrive/car/volkswagen/carcontroller.py index 5d00b5a52f..c17b632450 100644 --- a/selfdrive/car/volkswagen/carcontroller.py +++ b/selfdrive/car/volkswagen/carcontroller.py @@ -3,7 +3,7 @@ from opendbc.can.packer import CANPacker from common.numpy_fast import clip from common.conversions import Conversions as CV from common.realtime import DT_CTRL -from selfdrive.car import apply_std_steer_torque_limits +from selfdrive.car import apply_driver_steer_torque_limits from selfdrive.car.volkswagen import mqbcan, pqcan from selfdrive.car.volkswagen.values import CANBUS, PQ_CARS, CarControllerParams @@ -44,7 +44,7 @@ class CarController: if CC.latActive: new_steer = int(round(actuators.steer * self.CCP.STEER_MAX)) - apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.CCP) + apply_steer = apply_driver_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.CCP) if apply_steer == 0: hcaEnabled = False self.hcaEnabledFrameCount = 0 From c995acda7c502bc14fff431ba4832faa9dbaec18 Mon Sep 17 00:00:00 2001 From: ZwX1616 Date: Tue, 28 Feb 2023 03:01:55 +0800 Subject: [PATCH 477/484] driverview: use static icon (#27448) * fix alpha * prerender --- selfdrive/assets/img_driver_face.png | Bin 19875 -> 3769 bytes selfdrive/assets/img_driver_face_static.png | Bin 0 -> 4908 bytes selfdrive/ui/qt/offroad/driverview.cc | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 selfdrive/assets/img_driver_face_static.png diff --git a/selfdrive/assets/img_driver_face.png b/selfdrive/assets/img_driver_face.png index 03765a0376dc33e72f88fb68d0f3879bffff90ff..e2d943e537fb1cc489dbcfbb3dcfeb57258fb4b0 100644 GIT binary patch literal 3769 zcmbtXYgAKL7S7EjiRN(uK?oW(NWhy>G=P=FX9B1M7(whPj#kqsXltwqxX=+Fxrt(E zsUl_s9aPoJNsPzOs9Wlt@*(r=iIZu z{q4QKbMC%H8A}#RgyV!94o9L-PRfM;x6t1xK8)ipz50N|A*}i&-MibdKDyw`um12} z(?13`Toqj{U*i0|v+}R+ZqzL;dC%j^q$F<_8{((M)U}r5h20zf5OpW!kY>u6nAm@E zgO(28<=v2d89aIhd8Dx1G5JgKWW=$D&tCOhso?e14Za<2dwI6u^RJJ;?(KgbnWFr{ zk=aaER%isclH8Eu)N9(~c-gU)y~XyUnHl*B&aX6TgRpsHrTB^AM6gqr)RSu_l;ozO zT$??267TVED(^MvdNMNto>tiBWvVbiK_w##dg`!Dcprz&(N8VMMP^wi#ej2R_ zZy7C^<1)N2>vh=n1&N+A&ftn5B40}<6z$733xzEj!D*L)@;0j5OKr7B?9s}sfEqdB zcDck|hE!pQcAC{5O_Hnfs(Cy?noKQ-P?B@H4GNwhMMk2@VT;OU!xcn{JuukWz)mI; z4m62-Xz=3+5@e%Lq;(8T;-(N*I~DBgu&7FGVL@?E402v}n@n6~IOR>#NRxFMDZ$TB zB^nE5VwX#%5a48aSWFzOWe~@}@aA3!5huw{9vV2jIC)MZ%@HJ1It-2_^`mCP#H4EYo$}e%okAk@{IiMzd1$ETejzk<0GF0kJyK*aY}w z_I~6gyk;FSF4!51fqi~bZwc~}<^}r+0du-si8^Ia?wu0`f%kxVBY;f=NOOu&z}lzF zW|HbiOA3p;7{&^lI#M7Jrf6`OnDiAk1Xd9UOK!yyT4%RqLVgI|s(@91>2k(Bap~v~ z?-76@v-toi)thMWoSD-Idk=X-*awTy16}bW#3im9EJhg>$jQ-gV9XyA!3LS8*_fd! zk+ME=ZUP48)goH4c@Ugf&dBbpIG2h4myA(|TJTS@mX7)iBz@i6;yBzGcIwUB< z#~uj!PWY^mO^;kU-TrnD3Ih~K@FbO25Lv=$=d-`jdigJY6TD3hKI_d4-p+=PV*|oq z%u1Ze=97!^S-g>Tun$H?T2qLUHf=8eH96j44RkQ77oB|w{*@xA2_0yZAgU{+FdhhK zVl&VPc;>z8tB6v_RUjGRjX{O2)xszXKVU(D@=z@og47TaWR(6`-N+g=gTYwKUe+~= zXKL3d#Cs(P9+rCpQ7UUQPz;U;mJ$u=83p_KK(!pK!Vj<^@IaSx*pB=fz-Vul1r>T^ z`C}HH-Mt)TH=6~iVMQi-0_Am#Wqqn(OgPI1WdrR77{6sHqN)Z=Wtsec(6W(4vL64Z zX#O*J#QOv)VBlhwVFC!RdNM>0VeSK!{a1ETAu>a*0rjz#Vm4byIa9k(9*M0%8kw~gc%s`x93AWGq^M1HL)9uQ-scR~0%rJx0{-$~k!Xu#zv5w-

rm_t!z9LIw?96gA^h_s^Hg9tbrV=GS16K`Oh_gw`T!3t6&NVD^WaR-~|JT zm&0z7!=`qv7@ZoWzyleO{NhM1Vgm{T!5BC(HsstAN6`hgXbiep45v&Bbgc`PxpRi@mdPx7+f2H@nXp~tOHQE0=!)c7guuHZM(}(W|IHL8U*BITHVzee)?(jV zifv&KrXjVZeCO$o+*R1RewF8?bp_@RZ;1`)+Ket*hA0?`B~KSC8a``X zKg9E-QqS9Mwt0|RCfG|ro~zdp`ANE-+F>3uJ3om|f{@j`8gy&+TJ57r@;xG|(y$n< zM%F~Ys)?q(!OrQO+pu(_QOI7Ua6PkWA@ZL;pk{D?#&?!Zz3;zsP3tCde8=U9_4syv z&@vSYdX79F36Kz6e7)B5fYZJPjO;C@wSH@w{esUvg!jsv$x)2q8?oa|*c-imOJ(n;eVvpjEeR}ucS$)gS7y~j zGoN_|^IBh03!JZh*Zjz|U{t+$?*7;JN3AY-wNg;vGBkOUWW6Oee&>1K=eP8GgmYK4 zhLVSChqcTi=7P1!(z@9C!0)HDQcWZ67o4U|yGARO?Vf>jYzt?i_iyw0_sE`Mr@mwN z81v}uGJ|$?-*SCuhlf5umRrlq?T3Pk4MX9c-+C6F%V8!mfu%1_rClBPj!N}>k<}lf zzYtSmUzVW?jERAMLVA55k-A-T{~&kEAJ;c4ObbLnwfyG|+SQg%sye0D>11J)V!%V2 zk8G7hbnKS;a+m^jPi57r%~R7lG@bESnYyRWbE|aCjSyL}%V0F7qxM44`8`%<-H_KR zpvv@rIb56PUdvtWckXeOKOU-RD2i^s99mz91@}_z5vVb+^{vHTFjWQAK`cJ`?b_y+ zb|{|T+{3T$iy&jO+zj+B1@eKLG+*dmKQxQUX&cJXA9qz8+q|#)kSCkzIXk$ViPHD4 z6QRyxN|#vOuBAgv3xXgP=b5nWk-pjbNtG4H`ljGz)~4F~3;Dq4lQnK(JT;j!T2^+VmB3+C2&o*-&l>f6}tnVp09bnN!J zh61ao^o2nLZHRf1H!KgDra;pJ?vGsG=89JzFyBWGYhbUxU^;%=&~Tdz2|E-x(q@IB z28%N7ndeLO+*sLvP%#{vt?CpuZR3<#Cv9jr$O%;pN8_52{tWeO_tw)Hcqd#v?;AJp&gKY3#Sr$V{$xWBQXlL}3a9j}%q z)5VUXxzKv@I|l`8By&Sm@^ks|z7%FqvFlLoSRfu@P4&dpAHyu)eiGSGAwkl~@fJg8 zN{@5%c1{JtIc)|0zPi%+Ng8^f+Zu|c}p%%5I9Ch{^t9Q-wEoC-g zmak`U_HceYP<2tcqGtGvCz8Rem}MV^{mcg`9h}LsD#B5B+|+(~l8?(w_G#OO7V6KR zzSrcoHsPVvl)C#Dsa4cP#Sd4;5&TS5UY8YF#kePlFNM9)wH1u_8TDJ9%Idx{&lQ5g zK6S6qzm+t)8_%kxIvNWQT%|_|mzy3;db1fRb&QW-4g%Wnl&9r=r@7}j6H_}E>M!K( zg`Qm&V6n=ItEzWJ`+}K%^>u4~*GtbPMS~-+c|b(1Fz>@Gtt%)|>F(oW1relL)n(O4 z`yYBvcl3S+H}BCr??61Kj5_{)-EAyhzvBxy`7*hMkri*7VUFIlmiv)ljbP{M))gyR zk#NhX)72e&IPt1d8y*$XNY7j&_;I=q(+}3F+zXR)=VIH#e7NVGG%Za9iMkDg)A94p zSIQ8tyKyfNpMhT2NOA>EoU&Y~dajYXkNfS6@O|e=S7AI+R117WWtcg#v}9T% zZwZHqDv^k)Vq}3&%k4$V_BcV%GV_umn?2TH%Uj|+qb8@2$K*St3uu^Ct5Jx z$yDcE*))eOwwyfugt5Z+Q&9F8B1fz1P%a&4hC9^R5OC7DG)bB5{24DB8jO$Z+`b<^ PNjdt3OOnnd7;OIrwou~J literal 19875 zcmV)BK*PU@P) zaB^>EX>4U6ba`-PAZ2)IW&i+q+O559(&RdhrTJg0=p~4x__Z90q|9aqv;27;dPG)c zRaRHG*<|Fs@m>#ixkLg7=YRm>+W+tW^IiYzfBmoU+`U{+uC4S^ZvOZ5r^mq$?f(6* zpMQt@IsZQYo9oY?g@1kh`o|wcUP}C)e*am=pZ5=5&;Rj(zuytwe|`OGfBq!4KNtG* z2Y)Y^{N~6H>z{9u*W>T?@cg+@-@hM9Z}p#V^^JcX<t^D~X)pO~8T*Uvr{`rNc zvPvuSt`y@=A>HTS?RN*se^MIfcmG{|-}#w8yZ9}nzjwld{LAD0{r$fGd4c|Ak^la5 z{pZ#H$4~!h@#FaOdiqamS^lgMKm6N22>IVH{#oLG`r`DDJBt7M2dk(2UmoYrKi{|7 z_wC=)ji<1p<&Q<(*WcCgGraL-$ilZ=xF?&l?s>obnxTitu6$Q` z!U`jNVV~b;nB3wMcVtguj5DV5T4RrToa_}_xiw%8J{Kvh7^S%9i z-wcg!UV*p9z{SGZ?EmK1^}qYW|C?Xe_f3~5-0dH0#eGFBm)mgX{5S7nL&EoG-||`D zzrX%$|NT$)gQ>EE`7Cqe0muFRF7f8@AGVdwJ}2In`Tge=;i~J;1y~}!J99H3u>*Gr zDTfm78e!+%oa5pq~L+6Vm~|Yvg>ZU@9|-4U;3Uef90!R`}#Njv1@;J^}qc3zu2|#XV>D{Dc@KB zv1`0~)%y1mK{(0o89NrAz>ZgU0E3S1+18+A_|~vtvImbWQJs_wnbujVm_Z zvH2Ot1kcKz-^_KV`+Zj2-~LAA=1QG!>b1ms_6A1gucnXqb$=;uKr^lDV?jsaE_Qg^ z<{Q>x%~s<%OKJBLS=vcR0-GKd_};m-{e(0(Pkld&Z+N-!aK$z9kiyhPH+0c;Qj-9^v%V)#`iodHo#V5s2dmz^BwmFoo2ri+cpazfHB9u z!2G;+uayUB>wE6+&I{arvcYBwDKDN!`>sW)$aGLO`OD>Hv5*# zlUaicOoVG?q7k30YsdI;VSF{j_YL)X6L(&?Mg|r!|HR90XlwB(8cyD0=Dymsb{Q#s zJ>Vg4T63-z*FN`IG2?;F)D_t^o|WuXZI9t9k zpT(zgdibJu&lEG}GxJ;f1TZkrG6~tEE?{M0ChU5u`@LD-c&~!BtPka=OQIwS{o#Xb3RRz2p5T`ka&D?$&(Ojnp6 z_=XdX);`Z32ztaIdbj0%bs zHrCyFuvW^uu+rVqcP{wV4XPLZeZA;#n6wpXbMQGQky- zeoM!V14s$p%z;MvD(i%+-de8K#}yLZ41j2SA7nBBBbD1WG_Kw;JE9Pn!uo!&AY(oF z11Ro-k>S{26ON4yV$(P_IPv@(d*usAe&^mFpb7r67sRJo_-Nd0UGKybya~V>UnWlB zfcLlOHtKL=X|fw@ueq=OrqnZ56ieD=reM6NY;WQY@4oXp+znJ1fBdkAFL7O;hPC9c zJzRt}+Up|F?^SPbDCHp$p3fjIY1=IW2-wBKRURD`I4?Nb_*!6{f$)J5mM?-$&sBIM zW>L7{l&^oi9yrfHQQwfUy}lg*-BRD0&rm*?3HVk%7CRcEp0x_d z)}95@c)zuWjF|04TWK)138soNmy!kJCI)Q)0~=m=2zJ)q`Vh`5Yt8evcVBCeORQnP^@51tNWeM! z&+Qxapnq>1Z6B!z!GutCmU}|U+R&gzUu!DoB+-d4{KVlq-jET zvgEQTR58Z4v1NH0QwZLnfJY4|%o8z)9j*gy&3k_#iNFN$^JN>6HT7nnLM-qEuJnfr zVNJUcTP5sl7(mm0?({%K6L0%Ky6igC1pvKRkhGyXhK^zas0#ofV2l(LZN!|fM zB<}uB^ZK|52o5gjH98@vzB(&JgorTM)^d$}>kA4ekQO84vB>T zSfH`$2Ju0p$+*SFm5J70#2Ma-Q-S>tD-OFtsK9veZ0<3Gwa|w5+E43`-0H=Mp~~&v zs}XfpZK1Xf96_wX0wH^&UEi;dazl>bJ8(8A1Bi@_fM{u;o8@x1->)_-8T%Imv2LOc z23PWl38}JQ2@=2az`2Q@gjrM#&Yv#!i|v+Cf9w)o4T(vm^`Qt6wuzYoe#34AYF%n;hM<~s{FC30t-tvwDVZe7foJYOx)d&B)ISCAK$S3^W zkh|dQy|^J8{z62$2F6T`S_traH$*xZ(dX^voM>-&VWH862nx0&FD!@~#^N^axC^Rv z&Zl0n!nb$|JV~a-<|7IxP=i%1&>k7`p_ATTpcRz>w}=xERTMrHp+k&$Ok5RTVG3Xj>}?okHwYl_6H5Ou)qz~ck-iG|hrv!aHQgP{Hfultf@TG* z^5&+H!$u|86%h9wlfWC})?oU3W#spZawzM0N5dH(^H*>AO(<36eGc=m~xB{8jbb3 zfsOl8(X)p2+%!IDVu1KapqU5j3R(jw=29yBhj%}iCUCS-oaR1J4pDlH`=BsCF9r4d zMPz`WiGt8*?$xliLD*L)W@d5Ba{EZ21PTJ6MkubdF8BvGA^CtoK%M2hfC<60ePy8kV~#5#0I`8) z5(Afp510i7l|e<^6PLmu2M*7TjPZP$aV=n;d4j<6H=rMFhGY!W$o{w$3%2vZL2q%Q zJ~u>yaCspV6VKLaeWZ^G&QFBFDE33R!2_To@cxbLf)ebw%qA4T-~mUc6{jv zYV0+F=zFFL9rv!HM%_aK4u<<4q7gdZ6A4qSTtl`G6bd~jNAhD2Y!6gktA%G)^Iq6Y z$2=hl1AYU=#R5V`y3GAtnlxNw{)90BG5v-3*E?i~CsZm)BHBpvNr4B%2J`VNa06_@ z@b<-4-``LogI(Yu7|M!2Y8ME}oZu^wkd5fgFbvms0SNcsyV*wJ0z9wnVkrpyr9ivP z`pFXETgd2082XSqSPn|KB5R3WbA#ZRR7^PRV0XZHu0;St)ZkGsdh?EWIwW&6w1;+P zByh>Nxp#XBf~7HGoXjFDB_m^?7-&US^^#R0rn(HDzyN~a#0gt~941tZ1e6G}Dtdwo zgaU}&(4z=r<+q&FEC94b3ptT6*1mpG6xzpkJ|Ou9p^WWr&|tCN!d}8D4u%m0@8;8* z_3dmwK^8zoiQw~zr{YnF6Zi!|MjZ*iGq|xO&d-Kus39goefjma(&xH^c|S zB5J%c(H9@!o{2;()!t_Jqe45t)-E&;(W$a_v;KAq&Q5d;<8|dSi-Qmxh>DY0ReuB5 zL|)eISQ0pBdh#O3gHb#&Ot5T}d&Hll8w3TI9R64FFSty>kLv?h1>w2d=@uag0X?~3 zAoiF2@N)%6Baw1J$7Rqj%oI5UQ3HJ+Q|apbjDGOc3tGl1o0~L;841PLi!6BMGXs=v z01LQ-`aJAupA^jw2va<+)CV-sOIB_;FS=)Rh>xc+DO})*nWLwnxeSyzwn->WrbGGx zXN>iU3kZh_o4#b5J5Io#e{ou)6k$>0$J4K4K{yk^V>z-a0W%AL*DOlUx8k_fi|0)``Ca0UwHrl~eU z?yAp)ikp9M@J14qB%fmU(5Ef;4G-ow6T)VPHO-B3U)uMAv=PszDt|p!5x@)frpV*A z5=;2JMC#gWG=@HAH9V23E~Rl4M#K6geD5hlcIDxymvn95{fkHsyY$PdNxJ}aJRp6x zDj;RBmc449DKE5ZOv9V!bNwZH-eyL*fTUlG#Uv3+DA0$<2X1W-hbnKtX9;3#G*84m z;3uz%laB1%0|4Od7qJF*5bDXYa5*+LAASP(a|>n!u;$q0`Hzf%I7ejqB8K7l2X_SM z?05|n8BnL7-^z3J{5_(f5qg0&-W~uz`)cJIE7296zytomI->W(YI)S}V$XTp4kUVl z6pp=mko5)_Hcmo}t}OXtDF`Gy4$twJ^6ZEI&Vk^nM2(9JRD~xV`w=ET$V;pb?0s;j zAo7;vMU|}#0~Bn~h?@`u7LC{RY|V*XxO|`jeh~|Z*vqV5E4O456GuD>gYEcR?0o$7 zEBR*0GjSA&45>wUV@H4**bQ(Pe+U}cTb`dO2@~3K)B;Mu4%0knKUtiB`8+pw+~m0^ z)#u{#xqX@{JBKDB9d+*wjF1509dg4p!SIF?N|Y&_9m zjNC*J!)6g5A0b-Z{{0eUY<50k4ATKW@Q_0i30zrq#J3V=H{p_CA5F=yOX0&=z$-IU z>zJYcVFJ)YD@$L9SV&(5COVfW(#)6Fj5vBQDNn#G!`K5gkK&J_0+#mPo}5j^zzj%_ z{yQ|oT)b~l>e?ONzEP^s7R2Vgvtf7=$BxJ|DUWZBb5h5bXLIGB`W6!3jyS=8OI=sr zC7rD0(NuY$KyjZ0q@!KD26y5)v96=a(1t|4} zs2@kn>REhst{=;;I0+ACbz8hd2saIpO@y_3%L4M1*)xx24B!u|{VgOU$m$_q>XisL zJQVNzDb$%e2g3s^D3C@M!hSKno0eRl+<$%KxWIW3wtT&uk!JR!&6C0o! zEcVwXMuve5$cP6qFuDX+!myx`t~+rU{VX??m%J`0q;=+ac;E|7gg{l)QJ>=^y>Cy+ zO&c^5!pB>14|GV9YT;3y6$KE5n2($FY)*tPAXww?#o_;f=SdqZr-Dc-*Uvl=kN0Bc5hkh!V97= zS(Cgy2Eys^bGE>wFl33vW$|L?Q#a%rtZk=UBY%^J3D-8}4CLi08eof204!lo0w6jU z=3OVT3aJ8Pipf+5RQV2oCt^wzXn_5?wlxDR*xLsrx@@GYnhqNPWGDCe5e#+1bN1tb znxYQLd{+Ntv(uMwkynp|5_-%E*{cx3VMa_z`RCO87X*pco?9YtYa>*$)6ZvKp_n9@ z$ z!{Pu{Gb{Hw$M*!EHG9v)sNd>>>`2E!k*;&?<+1wh_VW|*qj=o>D?%_U4X(h6zN73| zIac|DlV)PULU$t*K*487BC+~6rYqj(XqZ6o5gAt6?Zr@Eg3sWCPxm0_Bl30ZUU+;# z+MLZcTh2+;9|RX7C*rtE7cjeLb?e|v%@Kc=n}*hVq~tAd$#c}>*cD^PJjwZjEK5(K$Ht{TO<~dCb zdmt=kJk($z#R@lZR^aJKPp_22TJ@aP@ZKiQR00 zc@;bwLJOhl%6)iRBINKyEEJ$IJeZ7Tlo5p|p&W0bd^0hz(IOR1fo(%=*VuR(TGwcK zj^y8#%kv=iLI)gII!A63f63;gkU0dEWmW~oMv%P+YiU-wkBZlsHOv4sg3^dK_kwqI zOHvN8wUnZ88G9(d0R*AzFT;JNU%q!dNP%+4N#MWX|$MuZ02ZFnbkpK-Sp(L(ZI(}$b8zBf32+0%% zWX>egQ{w}5YDqecDImyRkBU)U-+!V>*AHL_Y&UVRwAVwFYOY4o;PIJG0@0gRSfS>D zk6Eo(v@oT9SnA2E;(qec5GVgZC=MT#Z}o#DE&fkPa{Z?l03wiNz8@hXW~)CuC+0QcJhpp| z)bRF@EJOLuiO_NO?D-ld_h}yh;`6+}yzmV-`8W{6U2I7=`VlZCrM!gk<#_d5~}Z!yDT!Rd)0;7sOj)X?EFU?<34lyCCMS7KiQ@!%=n zsgRHdo4~k>>3$;nXb>F23{(kf^u<$uR3$$i9^onfbTs_0=FaN=_aXe267-AhEe$qb zd?c;T`o=OQw}eHrnp#|>tF-AX(Qq-J7E65-s}31H%NzR%`AMZc{X!uie~jV zA$<@fbH)fCZPk*8pxVkEc=U*<+}K#VFJxePzmq$KTmEa5{GWagMDV{}di`5B64e>7JBZBVtF*W6JbbqgxIOM54fYD9zJSKCk8Erfkau`2m9mdH* zycTz_YH_EBZ}Mf2@l5Q1*%<1cn@Er5wwWTflHf|^)Z9Rl65Q#DFp0B&9XS3^TiY#} zj2HI%1^p{LfNnXG^|ejFbo==%2;dfZ6p0rEq&|@rQU_0lZg-#nW0FM7VJnlRfE!h3 z+8djH>a0**bB6dE;6(@+B}EqYw&Xe7fHt8te!K!*nO>-vxW{dmxyV!q5w9T`8^)rL zgAnIZiIPD`I7bwd#XuQI5ItGeyRU|wj)(hgvd4%U1y2=bHlNhb4=P*$bA$)hQsFtvPH zO1W&WdHy`@9RD5T_{OPnD?<695k(sD{R;W6h3?41)0P`^^jsYVY0(gI*TSU;qMhKtUM<6ohvSI?3XDz7J8@`HtJJ8C9cyA15exbiS_lXP#pDl zM^J8*+OgJnZe8~BlQ{YJ6{Z*kc1H|)%yu$WMCF%_-^AEsJ^B4>9M^xg5=-&#M>x;rCxXGobH@;_Nen@vd=*w| z4$Q*XQ|=V=Ws2hzF&QKMSbHtw^qADt|KVhJ`Jc7u^{*TJ5{M(J`ZHgCa@P|U8~C;m za723>mesmb>p}|?{6r2Y`PGDu@W=Qv@#I+bXC2*V*9RWD?NNZX=@;c>&`m#?r}C}| zBS?~XJyYWUL{?;57&AQ}2)encwa@TzRL1+?Bt;=o2FY^$ip~3p66;r&?y&|@A}OKe z4Q;Xb!gVl!g6RD_VjOc~(X)~A804?Ee}J@=xuO)iEG)0&4KmK<*xNAC5) zNN?;GOAWbXo1u83bAoRJK>5`W0r2=A&uI^x>~%i`)_bEmS^V#XrC!x;p!|Hq`_~id zVYc->7zt^BD)9DE*~*T^LmdXj{&G(eG!|;z?kXhjl2Yz;Ds?2s5k-ps!Xc%H9{zl< zA>^bso0TMluBpfz#aPKMyXrywmq~rk|LFnrrY{cEDUZpOsB9t+uZIs1iCW(iO6+o zCsCRqqKG}asCPo^Kh;{nei~bna)12o67XQ$;=TfJc#f}~4wkzUz%1ko8`KCN&{&5B ze+a9Uw1odA)&H-W)*rH{9;e5qf)2rDXBMX8J{UdgzGH?84c6_pNvV7IfRp~p#LTdp zKC8r(9`?Ofu$&LO$hI|@oWYi(g_m>i+%`@q&<}Zfg7g~&zb#yV`Bxi_5)e;Z>95@5 zHOWmtwP16cdWE4?Ta9m36@(x1w(|q>5R3@tvn`<3HZMHG!%d4u&8oJ~ZU8GMA9w&p z{PG3+j@t1!N6@xtoHFw7542wY5|P=Odv-f)>is+j(TpUCSnJ*vNuC51>J~8|?D?-y z*Wf8rxV-R(CcuLft?1HDtT@YoL>yKNU}*8^4VZAT|72irw;>0OYJVdBw=Sr3A%L5d zVFgX0cl(2u{SJU6YRyz%!)5K2c(YZjv6<9|Mz{XhKT&FcM7#mZwTcQnYPb?wbhK)z zK6ltz`$Ie?2pV{_#{-NyCj=wSK3Qc$lWUabSL|`fCtro(LmmpY%u7h5usx=K`_rOA z3`jyVSwS!}7;#GF?*+CD|Z3w^93z}YW+G2lMePi-ii%2;nupf-9cc+1|tVRU#kTwjsBeKrG#^L+dN8AH{9CaYS28 zO2l6i!QmoYZfN8UEpwZLV9zXlvZF(v1?Ru`KqBnA^^W2Hga3a6UTTi>-vVKl%-wcv zlvBE}J^z9=d>4`*g=HMP&CN;KAa63}vpse(d?J2ejb3bbty+mHx-!9u0HPkVgQcH* z65%$bc_aq$aWqIZ-bz?x4~6ZeZb^#}86 zWtxe7tTThLcN>k;{g+_tekYs%&hoA8OZN9S z+kV(MnAgW`{mw~`?03>d=Qab&Q7`q4W# z;3-wyr+dQMPj*hkTBkDN3d*K0?uLui{94dx*(`xDbem($Cn4CTZE~epTnGsogNR$g zsdLTG$%S5^N6WAumkH9J<{Ry}B^nkBF$sq7l%DLP7#5Y<@6tVdt1L?X9ib58En+SU zq&9;Mj=vBWds%VRWqQd<&GFQ6;->}Li#UC3)g&9%_u;*-M;BMN0slUS#g8y==`)yO z4QB8}nQ-HYZ2Nk$72P=*C^vQjfz)5S!L9CuXq`+w%)A3r#DNZzXH9rU>z;~-P4Lbt zrCbEHSZrP98H~kTkq0R-e{;3J#LKsC*zC!V#eY6U!jHGccv|eBu+!?_`mkY*XifOD zOu=GLMNKxjVUeHZll)0xrxas7jefioA$RIxZ5BUuvCv5yi^?PNfM#|OUIHZs*c$@5 zHn?mK){V1MaF{YoDx)5%f$IK>SdPzN(7z5gi`UR)Y}nQI2W5eaJTFFP^P08}0$Fba zv0hHJNxEjVu)BP>wM^V~#ZS6rZ}uilFxyiq5Nh}#@B|BLZdiFt{MwFW^WA3{dQQO< zAPZ~5ax2eEgpMeiSi*Jo+lovOdKSLN446_#|EvZM7IIss;`^}xP8K)VawNKn8&I7UUIoKz~qxEQ!28g{a7~U2&oetbn zik>ZNe!`A7KpM;f!Ds5x+UO1F#b<6GNnz0-Bm=K1GAH)Ql({FzES7b~Mgu$oupp8) zs}&H%T(R3=cth`Fx0&F+RLUd#8vH`B8|ha7JIaQaL2KcWl;xa6xSgNYaz{^W2rKn>mG5ZUzgKa5&RGBG z1F!5kc&$RMr@{=THRK6RemhN^ z_k(rvO+9BsjhR8HHzEdP4tC3@f-5CEqa@#VyiW{42zXQieCTzsgwMX6+f&EHdR<3? zCbL>*4b15jh}^>bdk7vRi}~E`cS3d&|FDr4MD1)#v?Z+*-qy7(o2`=i1ebx~Rx84K zv`tF|iX-LcRf7P=fsZarvMhSR{vnVB0)Ujv7sg2JB9L^MIa?fmg`oicgKeJEq|eh8 z%yB@-?q*RG!QlCo78wRX%6MUah@6U>1AqzJM4*D6_F`eeD9T*B!qNPa^?rnu7v{2& zd`^3SKs*yn0*}zMrwS_Ys0J+T1|Odg40DOB&mhLC0J7Z_!M0j^U_w%GzzZ#y1AZ^N zzzY9qBa^T#_rN~F_>Xj1)a7i7Er2SJT&zzgoTIREN;gW<=^h7Sn&UW3reIX#oEAWQ zABSs*%NC~+qPS^Tl8IBM|sj;+xWOe)rP-lTZIxq5i#4A2=Wm4 zV18yNT2hwUx59NB4xnV-cQS-&45&o3@SYcBb)BsOpkDMpC>0RHaQrVLec5B;v`MC5 zC0uf~_qKV?X*iNdHckP3J)-kmAP_u-csf}79pHKn)p!hkj-6luL06M5tytm!OOSF{ z#m5q&;@V}u6f~k#dlZr-k3B_T`*aze{%o_7k?VvrL8mE>u=adPD(AM7(+0!KKIdB>59tXVX_@I#2SDy654Eqxh;(*00I)WIG6p4EG2j@79K3UO*X2Q zY%wJ?u3_-0{;F*)C74dF0t#NcRqfQ{h8{@(B*EG@WQpSevQ-hc=`?G(cSIdIX#=(S z$mmZ*#L~?3(~g$R_HV z2G?3FUW02r{!~+Z%kW7UMO67~TXH^4(J5?7`~; zH1aOnf=%EN$y|3l(&w?XJap@zhV^z6wLi**i~YtNK?$=}IQ@(VV$1ol5nDnH2up(N zPoo6f!yYtO={x<}xWwpS_UQ$$%Fh`4Ik0LDtGKJJh5)do8Slxz13{J?dm8)Mr27%& z#&0lAD}hcS8;$Cc-eD--?m?_MXk3UWY zu;R!{sTq~3M-)1-Z;V5Ab4kK&2I zbBqvox8u~2XMYYZCn0LRUpnPQ(OPYu32bJ~Ev~TL0Bf{6xY(Hl19v>xT$WQg5-XQsp-y+;&22Se zI0R<-9q;K*YrfRjZylcKGi`XfNl!iIYK??3yZ0Co`nMHC=c zW5gnfG?qW!@HuCSbWFJY#v_=k9|@;D=YL0f;5?31$iuNNtgu}YENJbHIx!7VZEUdp zHT*#3v~e`snSB!Y?lPxW0M2$qj9?W0; zQLtWPTN1nPwuMegS@;eO9nt+a?@iV*$G9q+i|(UK@_c&+JIGqYyOm zR0F~W3xM$kFmhc3hf9zMk4GH0*qzG4wJ@P%;~xaY%2oub!v`+HoBV^je%iIZ+J4&^cCD&`aM zLUy`$?GdvfIBkz5^RKEkIS_#RfsLedy)9pU)tT82XT*Ob0ut2iJF?vZ7q(2*U97ft zd0r2QJD)|L*sPJqseo|zIG{S#r2h((5dc#-m4ZhL>!MvFXs>)23`g)F4ct(k_B1sjg;sJ%_3UO=}iU8we}gG#TjkIq&J@f;>C==V)!e(GP)%kr~U$>r|4NbUwP08d6_2b#RsE1f4$C_~_- z1&GB_Af@d)qkoa?<{^JoYPtjB$P|jr0T51L7cuv|5O(J-C?I14pTms9TYuziS$5J+ zS%l{yR{0W(i#236mE4Dro0)hKF&AXE5jSoJxAY8YiO|c_UuGvfss?doQG<sTY2f}9v$n-|o^FAm*KHMobWSNag0Fys1<>1%dI92pq;@As2|^(@_#);aL~ z-;@So5Y{a%#f!@Cx6<)(Rw|c(^`n*G_cpd(0CPCiB>C4j6DncUsP0iE7H&YvF#oq^ z`<_rZ@g}}?`+xE6=72X(!dUElDuDp*d3d%|aozgf=YUh^Ld_A8Me#k;>?+Vb9ON?J zzjMYMGj;*Fz4kL_Y>eP2m1^e$T89VIP3V+J@@Yt0{d0%t2p9+DG zX6N+4UtSo8Q@2z(iIj;A~w z{@+Ohk6)*ZlZvcE>($943q{Yn?MaQMfuJ=`jkNOob!f?w3ph^jY4Oak>wv_@%AV~| z8qdj3Kag+f&T>v)ZrC`!rg}q5C$lt;*)+Jhw^^4m`#CG5YyTOq=s+bKh=HgP&cF~G zatT=%JanP~h_jDJdQgp1{_2zo8$2z!16!QxI$N>y>M@{$7Y??Bh53G9YFu~~H1H%= zoqKcIqm?%fs=}&xflmXU&R|u&wO(L%_rQ_3F9#o-)1Y*niVc{tQ8+so6kQCTmL5pZ zG4N%2<8%JVY4WN*)wHAfakx%Ka>Mn25rF7{3~WA)%@R%_zA(Pu8Q4691cfIw1G`T@ zIo#Kdwfo)D;fN!s7Op4>2F{CWNJBaGg8_RyYV_BpZzzF{Us2r zem#aS6s`T+)=oIn1%M#fydJ~YIBAa0*?Q?TyS&h{X#@U~F(fmf!7>x@Xhu^Er*d1Y z|9CWl<*$49w8JPP5$>vw;@Q*ZEW2fi4Y=^JIrgkw7CrJL6@$lyNB*YvuO{))z;)nZ!L z%)dj22|Z_u8>^VOSIplwd=$eI9uvVlI~UC6LLs9@*uNuvTXLi??)LsPK5-^+JH=yb z%q1Qnb0UEGEoUDccryKFV=k;7uD`5>pLc@&*oI(~d-Cbalv0Cm+2VHNuL)Rh`wZ&k z+QDWiz`ucw3cd2{{Q^jWV?460aj%i*%rUZ23ZY>>a&{9YgvyAmZ=;!$kFH!!6U=Ru zb0#i00)lYq#2lEn&Hac5=gkJETZJHUy!>^Dk;Tl?wtCrTVowM}uRF^!%zs~nWtDq(8Yjr}aMIbwKC^$EQB(EL(6l21H6#Nxif6)OK=V}q zguu)#AbHabyS9+-!7WP$n{6h3{$_@=;~Fkg_sfF5=2+w03ZJ&yTowp02WKz`v*g2m zvSwuy(KdPY`$R?x}6)+RR8L3Q#f2#LM);K)a(Mn!8Vlharn zEnTs2u(w%DVJDt#w2;+y#-LegS|GisSZHI#X#1Apk(4L<*-6-lV|o43u$(?m<0zvBl65ZdQzavuNiZqj~qQ zjMTViPfcZ&A<#XkH%(~c@z$WA8>aP}r{TDs$)REc7keF@1@kqfZ-Fe7_j;V3I-WUs z`xhm}mh_|noq^2=-pLtkmhqkg#kCIn1A{M&pm9_7dyRGq2LQcw^#!*g_O3uiu3F>!l- z=(i_eka)PsgW-;ApMo{$Z?|GNf5yHChWj`6IcK;a8;%IgPd%}Z$U^|P3B3I6(?PGv zm}l(fDp-Ah`lR(?)J_+5tgu5YkXXNBuw=Xno>&`rcK9f`PGOBePb2`9aI8X!P3hm?~(a{6>HKn>7o(lM3@K%O7 zGWAT)n(1=H&1!zsw!dzDDtheiJelJFP6Dka7%))B0Vrw9p56YU@s(&#-Ddlp>V}A! z5y^N)zuW9|JA+lNxxmW{BLv_r7oO)F^5!X?0n*-G2N1RQ zmt!!wz?KqA7I1!#*Fm4HL*cW-Lv5X^d}=$n;=NCz;}qIBAhq5}hI$dX8pdY+2T9;tn008KD*{0Be0l|tjaW1{XG^S&`2EgF#4lA8J z)ELzcpnMIC%jtCQJcXT3xH9|tJ9=b)ECKvju7W~Xzp>r#gfmcI_ELp z!@psKn|8FOb1^odXCdqijOJV{yFh?hSdYU2FR z?R&Zt9I|aC5eme^pYH<(PqA0?XacqV)-$!x9PMd&4c~84bsolK8vr(vZoZF%)-#gn zSIGy|Jgr{A%9YT@FXwnWruVjAB|N{@s@weCnTlw7<1nIseCvJx{a#zD!k-Z-ztW9j z=GM{8koV8WJRgk?);dZ6&awTgeTL;hzse5BskD4Oq4wWslW;qx&5_{fQM^~MLk{Nm z9l<*J?j)7Z!xSizm6+YhTgg(ll-AYk1+$Lzh)oBki$28}ptt8>w-u2mHHrCd+eI8A zVQjY0fY|$(*f=#2S;CjY8K@GR;N%dx?>-OD#@f}lZR+vwXM!IhOFN7jDK#Q5KnbGb z0xw5N!EUip+h3-$fgN!O(E3+W6ohYo%xJ&PM?*R}Ul#F%6g$BwryAh-U%5QRFcBKt znU+wF?6PLRpXNyC+q3$R0R9{&TFwx6=EUZQ@Znz5lEtwvl_U#`bbV2?xWqZmgLRs5 zdaOTl5Ch!Wm?Fr$COICDrnqW&Z5ao_iEB=J;_Z3;CUCYcS9GxRv_fi$j=_eNGOP*B z{-qYtaj7PCyF*GIw=`dP(BG_4_OE$}kJ^4Y)OJ3>iJQ)>N2Op64v({h(-Bz?we(2C z@+XgoFHiT)7t~WMkR8rwiemZ~L4?my;xq5*K+b-|XLj6=$9T5*nG5tJqD2>%Qhp*mR*ztByqP=pGhR%q41dwn9(V5mp;2L~85jjr%`#GnxR}YV ziosV12qAzOOv=pE=Oif!&+&B+A7AfcJj?sspQ9(2HyPj)iD#K^Si~E|Q=68~d7n7K zN^*|)oOr~b3lcwaU2*x1bJ1aeXGV;4YMwYkEEYRh>0nkeRN@KZXin8AU&y$e z+3|z_!S8O(!uXh*6pjO3FSh+L1O#`1X5F^Gk8Qho0{EYSE3NIXHh}3*((7$4dIa=t z0~gnAP1yr3cYyvUT{dJ#^3xOw1>pURz9|dz-2&ZfZf~u9oIU_)>MD5y92^3}Mao|H zcz39?w|~#H`uhQ=0&=`#dS>JR000JJOGiWi000000Qp0^e*gdg32;bRa{vGf6951U z69E94oEQKA00(qQO+^Ri0tf~a55j#+V*mgR2;>2$M`Y&Wt&j^LBCevBPoiefG!tc-LCb1BV&@=bXLQ$NO9Fv)0~gAAw=1YUg_* z0xDnS+_x@7WAru17!i#6sPduvHbgvn zg{G_DLx7W0+^Fwit{|{N_G`#^l#m6i&1R(lq{a-DdcGtv3R)^SmLaJXq!@81xfPg@ z5TL2?mnAegf>Samr9l*H(3V_jCn$s`mCKP76|e{nLCF`O*wX?jr6Re&5-RXw*25A! zDF$dMX#}tmWY$LHQtO(^(MZaQrD$*80hH;4SxbN?)u1dXD;Kk!eFaLP*ETdacv89; zNi{0Oiaj3zQQ6h>YzCK9gR!vl2}-VQNN5anG(pL;U6)0R)qT=dfLvo|YR3{54oYJf zkgjxB!|nHkh3!6N?k%bPVTz6;EbN1!bEHmaa=bg4u;dR^Lfv^ysP{6zh8mr)x49wIO+>$r*rMf6Ow0$MSK?#dL zI7))aLVx;*Z3%hsT7j{&pDpK=Pv8COE2d@6NkVl~K5L?`3~ zOwK!`EdUhTP56psnRBuL&)qc-Oo@*Y$mLQ(C~(FeR;He?HehOa?nVyV&*%2Pk9}Px zOzw~dBP`{^lZ!PM0uvXOfQu(Zz>@ju04sL1z5IBEiWSnv#IY z@iwWrr3!YH$q_Qh9####0oer z#&rk+laEAXaGkAP!wUTn_~kD}ON=MbSW2x!JDdC1Yz{0b2H+RnMG*RNu>__LcuEDQ z?Nlyg$|W%6STB0A#p@|tZ)^=MG7=kmj&MrjI zNBQ3>F< zSimsLFx!+OXJHgzr!(8+lx>`+2I7#;Y?H@-gxl>PTMvE5Prn%JA zQdDjOw#%{56ti|po90rSl=B%X=WOG0DlA1=$TlwK$tbFXY~yk$B$f4OUR<_uF-}5J zC1o3Dxp09RkWoamL8ZH7*6c++0%IjhPp`mD#=VY2%76G`{LCu+ZFw;;J=QL$o z1B%d4w7KO%!{uhG@&Q7t&oo>NpgpENqbNed(T*|>4R6$>Xm6QmD2mWfw8eym7K0)* z6h&w#iXt==MQA9B&``Ej`vMA~p(sK_QG|w~2n|IQ0Sz~qQA1UGs4&w|6rrIgLPJq` zLc>jJRORzHVWy#isr2!c&`=bip$u4=dZ6L88meMTsgt0gD2mWf6rrKNHeqFIo1@8O zXn3uL;vm%mO>K(-90w@cGg>ri{J^P%hGJ$8G@qFSO|J8rzHCE_<_DNsGCDI2!J&LW zQ|e;CrfLzK`m)Rv=fq4yaA?b7K$G+JnjsxbaM%`oj?0bxLJc;J;0$kr8kh5{wPyr} zz)W>aF7^p&Pwdy18K&Q9xv5{QCEL^l&d_@Dxo4G&;M57s9H-<`o!A;UHP3OFVa7Qu zH}?Qs93U{XXH+%d)Cx?WLmHAKT*v|t9G1V@Ov9(tlofcDUw_gl&@kJS6M)Px^OJ+~ zeZU$4U?A3$Ww;Pf*$7U!{UilvjpCdG%5oFTDH~y%0Sy-fm<4OMX5Q^tuGz+Cv8^?M zukw@(-q>WYHRS{)t(HyMmAL6Us>&l<3d0sX}SUWCjf2)aMlbTY!;lko5%*#?Erw^uC!-0 zS;kuscF7VbEjp^IKUUStmb(>)?pjS1tRZm#$@1gM-eNHo!=2UZ= z5sh*Rz)=8~1Nhezznz>BzV1!C?mf5QUI?7uz%_V zx8zl_was~-1n~Kxz^wO+v3kwwG_g)~3?O}!FNcgXr<$sI0>H~GwRx&|%nF)NBbLYV z6$wme*WeSN7_sPLRC-Q>Tb^j)sgl+OjQMYRhMV`YNreI;PM88t~5mP6GH3 zfXAD^{RzNv-vMfgZ;v2f17Ht;Zv*&1Xlpekz+(-p5~9K>GE#?*0r)w9`vLq4z&~PT zh}7Wtu2V~WZUS%xfGeBDl%&}q=Db>#4dUyVQwkpdmgfLG0^p+}a%58whqP5G;E6p5 zte#I*KMUY9F##G9KXaj3Wn^o4K40Jrv1uVjNudxzMjkT+N^j~f~uL-VO~ zLRz;e!S4f@)qkBOOEIPqmTKzoKt~qcr>a{<$~^d?gei|XGq`qPaqX*1KLBvvoFE>1 zb>9~-t8$@&Ixr}!e^&qxcYN(~0J{uA@8xAls=5=vwEzxveB?0z_W*dZuRpEFVK8WX z7I!u-Y+Z){yarT^`OI4Y+zQ~5#xAexha&Q$W{?{+AX=3R>5Hc}!h}XV4B(3` zf4>mG{i?d#nC~x9XE!gPeO30f$3ZgYpxq9Q#dJTiV#fh|qhnsP6Tqdax@ozaVLwlM z0KB|qfhMHGh+@mu`Ea3OnNWeL4ZRKEj~(y5ty$D#stKoaRx-`%p!IJ6H;KsYkp|F^ zb0mmJoK0vWD>Ajx&j5I^x zc&~^&R0=T3QeR+jjsW;u$H;gyfNwJ1=UcO;3tx$qTa{XLs(DSO(i$)({h|kO9szI} znmzw5I!KG=O}hb{+w=Z$5qYj8U`#+UL=%tw&yK^LR@Ez2b!X})$K;S{I$m3<`mLTW zJFTi83mF&HECfs)DJC}3cK|%v^7m~3cLR72v6yDf0GtQl{GRtuHm^yrSkExY?O8x@ zz6;>{9q(NL;A&d5@!i$@yQi^cU3#*4dAH({*)ws2S{R>JZF%KiRrjgtUt11yruq97 zsw%W-qpaC=s`^+@7u}<(7hAFEA>!B#JyGbuVkAuEr)p%7X$|N`A${6RaKwq>5{Jw zy}wUK$XYDB86%E~dCrE%;wlywHGk7{n0olPmB?PD}*vdu-RdV9~;A5hf;%r>3DY7 z+4!n@wyOT2=WD-f?iQ#Hnig1~su!y2Pd7C6mshQ@@4+rLJiYeJiY4=D*IGK_SpdK9 z`P!QS+y>wVtX%P~aBj1#@Ie3t3n|ew&Bq%IBLtt#IY+e6XW!R`tRLvnlq<$}KTtA6oDFz_5Os&%{PQ3gGzx zpF0TPb67mwd#eJQwrtDu(SI}Ms-6IF=alSX?H;np3`7)&`xl^-PGj-B=)2Htm(F?9;9B!+1Nbn2TLA1cVxi5(R&Z#BtjYyGe~4k{J^&8^ z__c`KUBJZuX8^7=Vh33%vTQ|T#h=s;3;`-` z-WEI#w+KvkaMTWv@TcR_WSUaiy%vB<044NA&!EthI#87cP>C5OWIP622=Lhtp^jTC zL{=bKZ?+J{TAst~>RE0hMhzh4w%V0L-)0F-&epH?yAn$1S7F0aB~I2uia*>+rLv9L ami>RG56lgMCO}*O0000vK&AGLy2(^ zgsEan1(Bg_Wtp~;9;jqusM4V@bEf^|RF;q~o_<9L_^D6=eD}UAozwaAJLmg@@V@VJ z@9+NZ^4{k@?}eBsp_RpNEGQI;Rb+%94*q|Me@s*ObsGGi*C~`;k0S-4;sWZUznyDa zUkkmz`0&ho);|9VZx4U&DB^t14_q29NztsTre-*-4+iuFgY*^-PG z+;6rw37$k(oU*d;46_@4*UhVz$$R-n!O5Wh)T5s+(BEHJNnIF!@@e-UQX0n}Hasrm zYqI-~L{2=)^>$S+g|oIjOKf&vUSsG1#mN1saW=MCM62#lcUss6fi| z^VXNOt9lh&4xeB<+AiJ`Cr4=A9bG_cWs7R@@KFI@#(rkI$gB{y>P5uB zdES`)qCzfg(OVJ&NqEDQ3!CufBT0jsp31UG9=^Zs<|-#rUL7{)xHK6k zk_K04f4$O9z;~Ucwg-i3v)OE}qQF%{kM)n&X4~}SPTj8>XNo9 zs+dl3{x4}?=DFNZydwrtYPvh`v?4Kd*v_`E97P=tHR%}Au$>eYDOBaTo@_e~fB>p51QYPXv=tRTZS}T5-yG5_7Hyhx zwD;RIbxASt{$Buz0V}Ge+;ldbBq+^4fPoJWpY|gKF>nb@! zRii9ZQ~=aOig;K~Ua3CFr2;93IDa>3ek@*z{cUB@v)9r8HmR#LpN%%bgvLjf%>K?1;dHD)^|CNJs4K)Bda;M*m2 z)mzSr5WN$$_cwz|ItWJDkHvr>x(b32kVJsJpxFb!Cu00<`%VgwI@%5miiJzI*cl#* zLC69`qHLqU0jRt(#Z>dAikq}Gw;V|Nw4s>}Fyjf>0Tp(@qN@dN``bYJ9WYhzHi{f^ zc_17tBt`e%vbMiWI6$zWqDv%9^>_g0r5@>qK-I44G^e-Hp0&^m!5E(+8r2X%bMnsC;Ogl8Z z^Xwgvvy_{IIVUnUuY&b20LfG%2MsIq_`_BJI0b>VX42vJP}4RZt_HbUf{jc8l73+e zMHaFf^%MXw^#)*pMwoVQI_e?Gyw3Km)gu}#okN&7l3*36sob5Bh#Q9R>uu=0v!%!e z^+-1E;KSrDYqWwDxoHJ5p}&$V1m}MddCj#)DX0Jd3oKWv11`J^)nf+*`euoag=#Q)Z0cTS!*XVY6EY!-5bL7~{JoKT6BwIfv66A|2MPOZ^z2vUsxSh-A{Ps_ z|0{BVVEX@`-2WkRS0E6-jvV&3-~C(A%eE^(EF;U_Y}3W6+*h{4zQ21bPucR5I}yN_ z8bh{KQ)DiNA<~(U3qZWIZJAWYoJ}(GH*+gjOi2;u7r`n_?tV-z27If;$rMSkh9Rf$ zE`&f)kg0Uo&P=ws*$(2@XgUT=pM=+tWCk7lt!<9NAbe{{?e31Pt4YFor-(X{0QS8j znjJGBh`#*C(+>CJibA9V8IJoFli7t z`r>K8QEa9#Xh++>|D z6!2qV6xfum2*6m@D-+f%4Tp4}i4+CRywdn*)TZj%U>IM~6_uT< z`|fT~GelB|WcOwbnJllV_fd{j`CNcnfbmKWL#M4wIDBgDdBb_NBX{L&lvpOvy2rqe zC@alm-88{ghU1;%uI?sj5<~Nmh4G&rGh?{H^K&7z4NZ^kQxD{N61WY)48~w5*voEa zl`c@v+KjJ0uiNLba*&I3sc5MD);{{6WwCi>3Cj>x+xTH(2u^tIswPF*tr=#q|M==5 z-79rQS_Y1pRGd*;Z}pf>TN>Zey~pZ@{qV&OWF`WI(UM6@DY+4*(@aO+@ zw=|Gj7@R@cg3#zqvVxro%nR&dT=_qxM5cbHP_iz7)+rczgF+uv)2A&4?|o!Z%v^5q z*ryYLD|`w}o#2ln^u9;WtRO3xx@@h>UmA3*8|=&yk70OCVI-He z-pXO5>I;KegGWaK-+UX+B*{sM5ilro-&pM|y;^$Uhr(c!Vo7bz_t$ZzE``&=*jG>A zS6@`0DV(mR9Pn7l8~evocl8;VqN(Yj$oK-G|ef9J$5KsG|*M@q)qtFHmOX>+{EHcwaN`vV}hwIP&rA~`nivF}$ zX{A)v+-hV(KHVzUe4#{hW=%k}%R3WKKmnhueWGrp-+uSbdU|c}I9t-?tuO!P_}7p} z+WNuDHE&QjH!MO0Ul{D*fd|0@7;SV3^x32#im^nOtgUrF}ch)m&bKn4ci+YS+KrB03ucqo! z)T_0{3Oet!azbS&6_473d^Lo2xy1QtiaNn-rJA3fb@RXDORtuVXUyerS329W#M$Bo zS3Q+!BZAX^_Qz}9x*S^0!(MoSt&HxvlE`&#bmD_?`fbzYKYpGnTJp=r4+8XEE=@|i z@T@dev7e!*FcseFMzh;)BMAny3f?r+?9jXi4WR-&Qkwf z{qW7nb7s@`E^u6W4H4qxyG3O~bmMEa)rNZ?TfH1t;$@Ji7Z|B`*wAukFSn}(ua@<^ zx)jgnWZi7O)o51CHXJEk>d18?ss#mdp5ISbOE+l6XKH&M60fiRQ{70LHZSHmIxOCk z3%m47>j4Hj#M1Z&7uBr|T0uYpep%r*>0dln^PLsn6`JPq6ZJl`btWt*D!46r3yw=E zZf`V=omj?z84o@}L`M{8;426mLlYbp1r>YQ14*z2_ZA%=o>?hV2~silSgr zSTqsLc~wk}@LVn{rZUs>bJ#W!+IhHOvz=`F2HJYzaLR_mBat^S6^UP;MmFe&++QrO Us`>2_d Date: Mon, 27 Feb 2023 13:49:06 -0800 Subject: [PATCH 478/484] Update check-submodules.sh --- release/check-submodules.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/check-submodules.sh b/release/check-submodules.sh index 2585db6bd7..5f4e307e49 100755 --- a/release/check-submodules.sh +++ b/release/check-submodules.sh @@ -1,7 +1,7 @@ #!/bin/bash while read hash submodule ref; do - git -C $submodule fetch --depth 300 origin master + git -C $submodule fetch --depth 1000 origin master git -C $submodule branch -r --contains $hash | grep "origin/master" if [ "$?" -eq 0 ]; then echo "$submodule ok" From dc39a87543d61eb5d24ee5f27b661474f7f16598 Mon Sep 17 00:00:00 2001 From: Lee Jong Mun <43285072+crwusiz@users.noreply.github.com> Date: Tue, 28 Feb 2023 08:50:32 +0900 Subject: [PATCH 479/484] Multilang: kor translation update (#27445) --- selfdrive/ui/translations/main_ko.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index 5470e57ce5..be11f2efde 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -403,7 +403,7 @@ location set Waiting for GPS - GPS를 기다리는 중 + GPS 수신중 입니다 @@ -592,16 +592,17 @@ location set Unable to mount data partition. Partition may be corrupted. Press confirm to erase and reset your device. - + 데이터 파티션을 마운트할 수 없습니다. 파티션이 손상되었을 수 있습니다. 장치를 초기화하려면 확인을 누르세요. Press confirm to erase all content and settings. Press cancel to resume boot. - + 모든 콘텐츠와 설정을 지우려면 확인을 누르세요. 부팅을 재개하려면 취소를 누르세요. Resetting device... This may take up to a minute. - + 장치 초기화 중... +최대 1분이 소요될 수 있습니다. @@ -702,12 +703,12 @@ This may take up to a minute. 다시 시작 - No custom software found at this URL. - + Something went wrong. Reboot the device. + 문제가 발생했습니다. 장치를 재부팅하세요. - Something went wrong. Reboot the device. - + No custom software found at this URL. + 이 URL에서 커스텀 소프트웨어를 찾을 수 없습니다. From ce3e9d5649d930ea0daae2d2a1587cc6e69c158b Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 28 Feb 2023 07:51:23 +0800 Subject: [PATCH 480/484] cabana: fix gaps in step line between chunks (#27447) --- tools/cabana/chartswidget.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 7982a5899c..42f8e4e06a 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -552,7 +552,12 @@ void ChartView::updateSeries(const Signal *sig, const std::vector *even }); for (auto &c : chunks) { s.vals.append(c.vals); - s.step_vals.append(c.step_vals); + if (!c.step_vals.empty()) { + if (!s.step_vals.empty()) { + s.step_vals.append({c.step_vals.first().x(), s.step_vals.back().y()}); + } + s.step_vals.append(c.step_vals); + } } if (events->size()) { s.last_value_mono_time = events->back()->mono_time; From 8d317ef51ce3518e6bd3bb1a97567f8686ca8c8d Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 1 Mar 2023 07:50:30 +0800 Subject: [PATCH 481/484] cabana: press F11 to toggle fullscreen (#27455) --- tools/cabana/mainwin.cc | 15 +++++++++++++++ tools/cabana/mainwin.h | 1 + 2 files changed, 16 insertions(+) diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index 282a541244..abcb4948a9 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -194,6 +194,8 @@ void MainWindow::createStatusBar() { void MainWindow::createShortcuts() { auto shortcut = new QShortcut(QKeySequence(Qt::Key_Space), this, nullptr, nullptr, Qt::ApplicationShortcut); QObject::connect(shortcut, &QShortcut::activated, []() { can->pause(!can->isPaused()); }); + shortcut = new QShortcut(QKeySequence(QKeySequence::FullScreen), this, nullptr, nullptr, Qt::ApplicationShortcut); + QObject::connect(shortcut, &QShortcut::activated, this, &MainWindow::toggleFullScreen); // TODO: add more shortcuts here. } @@ -482,6 +484,19 @@ void MainWindow::onlineHelp() { } } +void MainWindow::toggleFullScreen() { + if (isFullScreen()) { + menuBar()->show(); + statusBar()->show(); + showNormal(); + showMaximized(); + } else { + menuBar()->hide(); + statusBar()->hide(); + showFullScreen(); + } +} + // HelpOverlay HelpOverlay::HelpOverlay(MainWindow *parent) : QWidget(parent) { setAttribute(Qt::WA_NoSystemBackground, true); diff --git a/tools/cabana/mainwin.h b/tools/cabana/mainwin.h index 2aeb20aea8..1c21d69370 100644 --- a/tools/cabana/mainwin.h +++ b/tools/cabana/mainwin.h @@ -56,6 +56,7 @@ protected: void undoStackCleanChanged(bool clean); void undoStackIndexChanged(int index); void onlineHelp(); + void toggleFullScreen(); void updateStatus(); VideoWidget *video_widget = nullptr; From b6881220426ac231215bbf4476f6fedd11e3e540 Mon Sep 17 00:00:00 2001 From: Kurt Nistelberger Date: Tue, 28 Feb 2023 21:40:54 -0800 Subject: [PATCH 482/484] laikad: reduce min satellite number (#27440) * bump laika * update laikad tests * update refs * bump laika * update refs --------- Co-authored-by: Kurt Nistelberger --- laika_repo | 2 +- selfdrive/locationd/laikad.py | 20 +++++++++++++------- selfdrive/locationd/test/test_laikad.py | 10 +++++----- selfdrive/test/process_replay/ref_commit | 2 +- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/laika_repo b/laika_repo index 278b44ba8c..b740b71c82 160000 --- a/laika_repo +++ b/laika_repo @@ -1 +1 @@ -Subproject commit 278b44ba8c2dec28adc5b30cce13dabc195f15fc +Subproject commit b740b71c82a748e3520b1599487d9a7aaf728670 diff --git a/selfdrive/locationd/laikad.py b/selfdrive/locationd/laikad.py index 2eb1e94526..10c5e1b7e5 100755 --- a/selfdrive/locationd/laikad.py +++ b/selfdrive/locationd/laikad.py @@ -97,19 +97,25 @@ class Laikad: def get_lsq_fix(self, t, measurements): if self.last_fix_t is None or abs(self.last_fix_t - t) > 0: - min_measurements = 7 if any(p.constellation_id == ConstellationId.GLONASS for p in measurements) else 6 - position_solution, pr_residuals = calc_pos_fix(measurements, self.posfix_functions, min_measurements=min_measurements) + min_measurements = 5 if any(p.constellation_id == ConstellationId.GLONASS for p in measurements) else 4 + position_solution, pr_residuals, pos_std = calc_pos_fix(measurements, self.posfix_functions, min_measurements=min_measurements) if len(position_solution) < 3: return None position_estimate = position_solution[:3] - #TODO median abs residual is decent estimate of std, can be improved with measurements stds and/or DOP - position_std = np.median(np.abs(pr_residuals)) * np.ones(3) - velocity_solution, prr_residuals = calc_vel_fix(measurements, position_estimate, self.velfix_function, min_measurements=min_measurements) + + position_std_residual = np.median(np.abs(pr_residuals)) + position_std = np.median(np.abs(pos_std))/10 + position_std = max(position_std_residual, position_std) * np.ones(3) + + velocity_solution, prr_residuals, vel_std = calc_vel_fix(measurements, position_estimate, self.velfix_function, min_measurements=min_measurements) if len(velocity_solution) < 3: return None - velocity_estimate = velocity_solution[:3] - velocity_std = np.median(np.abs(prr_residuals)) * np.ones(3) + + velocity_std_residual = np.median(np.abs(prr_residuals)) + velocity_std = np.median(np.abs(vel_std))/10 + velocity_std = max(velocity_std, velocity_std_residual) * np.ones(3) + return position_estimate, position_std, velocity_estimate, velocity_std def is_good_report(self, gnss_msg): diff --git a/selfdrive/locationd/test/test_laikad.py b/selfdrive/locationd/test/test_laikad.py index d89f521228..10ac7790b6 100755 --- a/selfdrive/locationd/test/test_laikad.py +++ b/selfdrive/locationd/test/test_laikad.py @@ -160,7 +160,7 @@ class TestLaikad(unittest.TestCase): laikad = Laikad(auto_update=True, valid_ephem_types=EphemerisType.ULTRA_RAPID_ORBIT) correct_msgs = verify_messages(self.logs, laikad) - correct_msgs_expected = 554 + correct_msgs_expected = 559 self.assertEqual(correct_msgs_expected, len(correct_msgs)) self.assertEqual(correct_msgs_expected, len([m for m in correct_msgs if m.gnssMeasurements.positionECEF.valid])) @@ -180,7 +180,7 @@ class TestLaikad(unittest.TestCase): laikad = Laikad(auto_update=True, valid_ephem_types=EphemerisType.NAV) # Disable fetch_orbits to test NAV only correct_msgs = verify_messages(self.logs, laikad) - correct_msgs_expected = 554 + correct_msgs_expected = 559 self.assertEqual(correct_msgs_expected, len(correct_msgs)) self.assertEqual(correct_msgs_expected, len([m for m in correct_msgs if m.gnssMeasurements.positionECEF.valid])) @@ -195,8 +195,8 @@ class TestLaikad(unittest.TestCase): downloader_mock.side_effect = DownloadFailed laikad = Laikad(auto_update=False) correct_msgs = verify_messages(self.logs, laikad) - self.assertEqual(0, len(correct_msgs)) - self.assertEqual(0, len([m for m in correct_msgs if m.gnssMeasurements.positionECEF.valid])) + self.assertEqual(255, len(correct_msgs)) + self.assertEqual(255, len([m for m in correct_msgs if m.gnssMeasurements.positionECEF.valid])) def test_laika_get_orbits(self): laikad = Laikad(auto_update=False) @@ -282,7 +282,7 @@ class TestLaikad(unittest.TestCase): gm = msg.gnssMeasurements if len(gm.correctedMeasurements) != 0 and gm.positionECEF.valid: cnt += 1 - self.assertEqual(cnt, 554) + self.assertEqual(cnt, 559) def dict_has_values(self, dct): self.assertGreater(len(dct), 0) diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 9991154b9a..b44c9c2f46 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -772f30de36fc7f8421dabb779cc02f45eb83d7bb +dfa8e947c4ef76a9d89974a434e94a078e1ccc6a \ No newline at end of file From 85a37d276c105494b97eee03aa6bb31a212f2f92 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 1 Mar 2023 15:41:28 +0800 Subject: [PATCH 483/484] cabana: recalc y-axis label width after unit change (#27466) recalc width --- tools/cabana/chartswidget.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc index 42f8e4e06a..2c27fbd8b9 100644 --- a/tools/cabana/chartswidget.cc +++ b/tools/cabana/chartswidget.cc @@ -591,11 +591,14 @@ void ChartView::updateAxisY() { if (it->y() > max) max = it->y(); } } - axis_y->setTitleText(unit); - if (min == std::numeric_limits::max()) min = 0; if (max == std::numeric_limits::lowest()) max = 0; + if (axis_y->titleText() != unit) { + axis_y->setTitleText(unit); + y_label_width = 0;// recalc width + } + double delta = std::abs(max - min) < 1e-3 ? 1 : (max - min) * 0.05; auto [min_y, max_y, tick_count] = getNiceAxisNumbers(min - delta, max + delta, axis_y->tickCount()); if (min_y != axis_y->min() || max_y != axis_y->max() || y_label_width == 0) { From d6abf1d5725e1de58d6f3dc23a238aa4bf65b642 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 1 Mar 2023 13:38:49 -0800 Subject: [PATCH 484/484] No disengage on gas by default (#27472) --- selfdrive/manager/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/manager/manager.py b/selfdrive/manager/manager.py index 865966d6c5..963d94066b 100755 --- a/selfdrive/manager/manager.py +++ b/selfdrive/manager/manager.py @@ -36,7 +36,7 @@ def manager_init() -> None: default_params: List[Tuple[str, Union[str, bytes]]] = [ ("CompletedTrainingVersion", "0"), - ("DisengageOnAccelerator", "1"), + ("DisengageOnAccelerator", "0"), ("GsmMetered", "1"), ("HasAcceptedTerms", "0"), ("LanguageSetting", "main_en"),