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|[](##)|[](##)|Toyota|
|Toyota|Corolla Hybrid 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|Toyota|Highlander 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[](##)|[](##)|Toyota|
-|Toyota|Highlander 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
+|Toyota|Highlander 2020-23|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|Toyota|Highlander Hybrid 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[](##)|[](##)|Toyota|
|Toyota|Highlander Hybrid 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|Toyota|Mirai 2021|All|openpilot|0 mph|0 mph|[](##)|[](##)|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|[](##)|[](##)|Toyota|
|Toyota|Highlander 2020-23|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|Toyota|Highlander Hybrid 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[](##)|[](##)|Toyota|
-|Toyota|Highlander Hybrid 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
+|Toyota|Highlander Hybrid 2020-23|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|Toyota|Mirai 2021|All|openpilot|0 mph|0 mph|[](##)|[](##)|Toyota|
|Toyota|Prius 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|0 mph|0 mph|[](##)|[](##)|Toyota|
|Toyota|Prius 2017-20|All|openpilot available[2](#footnotes)|0 mph|0 mph|[](##)|[](##)|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.**
[](https://github.com/commaai/openpilot/actions)
-[](https://lgtm.com/projects/g/commaai/openpilot/alerts/)
-[](https://lgtm.com/projects/g/commaai/openpilot/context:python)
-[](https://lgtm.com/projects/g/commaai/openpilot/context:cpp)
[](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: "*"