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 01/16] 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 02/16] 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 03/16] 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 04/16] 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 05/16] 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 06/16] 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 07/16] 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 08/16] 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 09/16] 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 10/16] 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 11/16] 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 12/16] 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 13/16] 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 14/16] 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 15/16] 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 16/16] 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: "*"