diff --git a/Jenkinsfile b/Jenkinsfile
index 3ededf4506..78a3a0fa63 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -11,6 +11,9 @@ export GIT_BRANCH=${env.GIT_BRANCH}
export GIT_COMMIT=${env.GIT_COMMIT}
source ~/.bash_profile
+if [ -f /TICI ]; then
+ source /etc/profile
+fi
ln -snf ${env.TEST_DIR} /data/pythonpath
@@ -151,7 +154,6 @@ pipeline {
["test boardd loopback", "nosetests -s selfdrive/boardd/tests/test_boardd_loopback.py"],
["test loggerd", "python selfdrive/loggerd/tests/test_loggerd.py"],
["test encoder", "python selfdrive/loggerd/tests/test_encoder.py"],
- ["test camerad", "python selfdrive/camerad/test/test_camerad.py"],
["test logcatd", "python selfdrive/logcatd/tests/test_logcatd_android.py"],
//["test updater", "python installer/updater/test_updater.py"],
])
@@ -167,12 +169,31 @@ pipeline {
["build", "SCONS_CACHE=1 scons -j16"],
["test loggerd", "python selfdrive/loggerd/tests/test_loggerd.py"],
["test encoder", "LD_LIBRARY_PATH=/usr/local/lib python selfdrive/loggerd/tests/test_encoder.py"],
- ["test camerad", "python selfdrive/camerad/test/test_camerad.py"],
//["build release3-staging", "cd release && PUSH=${env.R3_PUSH} ./build_release3.sh"],
])
}
}
+ stage('camerad') {
+ steps {
+ phone_steps("eon-party", [
+ ["build", "SCONS_CACHE=1 scons -j16"],
+ ["test camerad", "python selfdrive/camerad/test/test_camerad.py"],
+ ["test exposure", "python selfdrive/camerad/test/test_exposure.py"],
+ ])
+ }
+ }
+
+ stage('Tici camerad') {
+ steps {
+ phone_steps("tici-party", [
+ ["build", "SCONS_CACHE=1 scons -j16"],
+ ["test camerad", "python selfdrive/camerad/test/test_camerad.py"],
+ ["test exposure", "python selfdrive/camerad/test/test_exposure.py"],
+ ])
+ }
+ }
+
}
}
}
diff --git a/README.md b/README.md
index 590fba0cdd..d8ead59e82 100644
--- a/README.md
+++ b/README.md
@@ -169,16 +169,20 @@ Community Maintained Cars and Features
| Nissan | Leaf 2018-20 | ProPILOT | Stock | 0mph | 0mph |
| Nissan | Rogue 2018-19 | ProPILOT | Stock | 0mph | 0mph |
| Nissan | X-Trail 2017 | ProPILOT | Stock | 0mph | 0mph |
+| SEAT | Ateca 2018 | Driver Assistance | Stock | 0mph | 0mph |
| Škoda | Kodiaq 2018 | Driver Assistance | Stock | 0mph | 0mph |
+| Škoda | Scala 2020 | Driver Assistance | Stock | 0mph | 0mph |
| Subaru | Ascent 2019 | EyeSight | Stock | 0mph | 0mph |
| Subaru | Crosstrek 2018-19 | EyeSight | Stock | 0mph | 0mph |
| Subaru | Forester 2019-20 | EyeSight | Stock | 0mph | 0mph |
| Subaru | Impreza 2017-19 | EyeSight | Stock | 0mph | 0mph |
| Volkswagen| Jetta 2018-21 | Driver Assistance | Stock | 0mph | 0mph |
+| Volkswagen| Passat 2016-172 | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| Golf 2015-19 | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| Tiguan 2020 | Driver Assistance | Stock | 0mph | 0mph |
1Requires an [OBD-II car harness](https://comma.ai/shop/products/comma-car-harness) and [community built ASCM harness](https://github.com/commaai/openpilot/wiki/GM#hardware). ***NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).***
+2Only includes the MQB Passat sold outside of North America. The NMS Passat made in Chattanooga TN is not yet supported.
Although it's not upstream, there's a community of people getting openpilot to run on Tesla's [here](https://tinkla.us/)
diff --git a/RELEASES.md b/RELEASES.md
index fd1058e187..4bdc9d0755 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -10,7 +10,9 @@ Version 0.8.3 (2021-XX-XX)
* Default SSH key only used for setup
* Kia Ceed 2019 support thanks to ZanZaD13!
* Kia Seltos 2021 support thanks to speedking456!
+ * Škoda Kodiaq 2018 support thanks to jyoung8607!
* Volkswagen Tiguan 2020 support thanks to jyoung8607!
+ * Volkswagen Jetta 2018-2021 support thanks to jyoung8607!
Version 0.8.2 (2021-02-26)
========================
diff --git a/common/params_pyx.pyx b/common/params_pyx.pyx
index 2add3a0772..499e6ea6e7 100755
--- a/common/params_pyx.pyx
+++ b/common/params_pyx.pyx
@@ -15,6 +15,9 @@ cdef enum TxType:
keys = {
b"AccessToken": [TxType.CLEAR_ON_MANAGER_START],
+ b"ApiCache_DriveStats": [TxType.PERSISTENT],
+ b"ApiCache_Device": [TxType.PERSISTENT],
+ b"ApiCache_Owner": [TxType.PERSISTENT],
b"AthenadPid": [TxType.PERSISTENT],
b"CalibrationParams": [TxType.PERSISTENT],
b"CarBatteryCapacity": [TxType.PERSISTENT],
diff --git a/installer/updater/update.json b/installer/updater/update.json
index 18d2139572..faff0c52d7 100644
--- a/installer/updater/update.json
+++ b/installer/updater/update.json
@@ -1,7 +1,7 @@
{
- "ota_url": "https://commadist.azureedge.net/neosupdate/ota-signed-c082c7c3365829b9df9cbbc5b20a9aed3cc5c98ebb17351e7e00e6285072c403.zip",
- "ota_hash": "c082c7c3365829b9df9cbbc5b20a9aed3cc5c98ebb17351e7e00e6285072c403",
- "recovery_url": "https://commadist.azureedge.net/neosupdate/recovery-a7c7d5d54b9f3afa6ae3d22ceab44f018b819c02443e4a09578089fbdb2ec4ee.img",
+ "ota_url": "https://commadist.azureedge.net/neosupdate/ota-signed-969e22c42e5c6314e54bc3ccaa5c6a684f3130a53a7a70e0cea9f1453ceb0b06.zip",
+ "ota_hash": "969e22c42e5c6314e54bc3ccaa5c6a684f3130a53a7a70e0cea9f1453ceb0b06",
+ "recovery_url": "https://commadist.azureedge.net/neosupdate/recovery-9c784a24826c25df315d0ace864224478e9c0e86b904f5d1f8e18ea1037e842b.img",
"recovery_len": 15209772,
- "recovery_hash": "a7c7d5d54b9f3afa6ae3d22ceab44f018b819c02443e4a09578089fbdb2ec4ee"
+ "recovery_hash": "9c784a24826c25df315d0ace864224478e9c0e86b904f5d1f8e18ea1037e842b"
}
diff --git a/launch_chffrplus.sh b/launch_chffrplus.sh
index cc6e8f4973..c9605cbab3 100755
--- a/launch_chffrplus.sh
+++ b/launch_chffrplus.sh
@@ -10,9 +10,6 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
function two_init {
- export QT_QPA_EGLFS_PHYSICAL_WIDTH=151
- export QT_QPA_EGLFS_PHYSICAL_HEIGHT=74
-
# Wifi scan
wpa_cli IFNAME=wlan0 SCAN
diff --git a/launch_env.sh b/launch_env.sh
index 6bc84c7068..da81aa4126 100755
--- a/launch_env.sh
+++ b/launch_env.sh
@@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1
export VECLIB_MAXIMUM_THREADS=1
if [ -z "$REQUIRED_NEOS_VERSION" ]; then
- export REQUIRED_NEOS_VERSION="16.1"
+ export REQUIRED_NEOS_VERSION="16.2"
fi
if [ -z "$AGNOS_VERSION" ]; then
diff --git a/panda b/panda
index ad9ecefe65..f146aa367b 160000
--- a/panda
+++ b/panda
@@ -1 +1 @@
-Subproject commit ad9ecefe65ce4ea7bcaf3bfe692ee2a3fc6b0b63
+Subproject commit f146aa367b82f041b4eadcaab0bb2fc79e388b94
diff --git a/release/files_common b/release/files_common
index 4a4c185233..8536da8758 100644
--- a/release/files_common
+++ b/release/files_common
@@ -336,6 +336,7 @@ selfdrive/test/helpers.py
selfdrive/test/setup_device_ci.sh
selfdrive/test/test_fingerprints.py
+selfdrive/ui/.gitignore
selfdrive/ui/SConscript
selfdrive/ui/*.cc
selfdrive/ui/*.hpp
diff --git a/selfdrive/camerad/test/test_camerad.py b/selfdrive/camerad/test/test_camerad.py
index e2333b3839..11e070f328 100755
--- a/selfdrive/camerad/test/test_camerad.py
+++ b/selfdrive/camerad/test/test_camerad.py
@@ -2,11 +2,9 @@
import time
import unittest
-import numpy as np
import cereal.messaging as messaging
from selfdrive.test.helpers import with_processes
-from selfdrive.camerad.snapshot.snapshot import get_snapshots
# only tests for EON and TICI
from selfdrive.hardware import EON, TICI
@@ -31,70 +29,6 @@ class TestCamerad(unittest.TestCase):
if not (EON or TICI):
raise unittest.SkipTest
- # assert "SEND_REAR" in os.environ
- # assert "SEND_FRONT" in os.environ
-
- def _numpy_bgr2gray(self, im):
- ret = np.clip(im[:,:,0] * 0.114 + im[:,:,1] * 0.587 + im[:,:,2] * 0.299, 0, 255).astype(np.uint8)
- return ret
-
- def _numpy_lap(self, im):
- ret = np.zeros(im.shape)
- ret += -4 * im
- ret += np.concatenate([np.zeros((im.shape[0],1)),im[:,:-1]], axis=1)
- ret += np.concatenate([im[:,1:],np.zeros((im.shape[0],1))], axis=1)
- ret += np.concatenate([np.zeros((1,im.shape[1])),im[:-1,:]], axis=0)
- ret += np.concatenate([im[1:,:],np.zeros((1,im.shape[1]))], axis=0)
- ret = np.clip(ret, 0, 255).astype(np.uint8)
- return ret
-
- def _is_really_sharp(self, i, threshold=800, roi_max=np.array([8,6]), roi_xxyy=np.array([1,6,2,3])):
- i = self._numpy_bgr2gray(i)
- x_pitch = i.shape[1] // roi_max[0]
- y_pitch = i.shape[0] // roi_max[1]
- lap = self._numpy_lap(i)
- lap_map = np.zeros((roi_max[1], roi_max[0]))
- for r in range(lap_map.shape[0]):
- for c in range(lap_map.shape[1]):
- selected_lap = lap[r*y_pitch:(r+1)*y_pitch, c*x_pitch:(c+1)*x_pitch]
- lap_map[r][c] = 5*selected_lap.var() + selected_lap.max()
- print(lap_map[roi_xxyy[2]:roi_xxyy[3]+1,roi_xxyy[0]:roi_xxyy[1]+1])
- if (lap_map[roi_xxyy[2]:roi_xxyy[3]+1,roi_xxyy[0]:roi_xxyy[1]+1] > threshold).sum() > \
- (roi_xxyy[1]+1-roi_xxyy[0]) * (roi_xxyy[3]+1-roi_xxyy[2]) * 0.9:
- return True
- else:
- return False
-
- def _is_exposure_okay(self, i, med_ex=np.array([0.2,0.4]), mean_ex=np.array([0.2,0.6])):
- i = self._numpy_bgr2gray(i)
- i_median = np.median(i) / 256
- i_mean = np.mean(i) / 256
- print([i_median, i_mean])
- return med_ex[0] < i_median < med_ex[1] and mean_ex[0] < i_mean < mean_ex[1]
-
- @unittest.skip # skip for now
- @with_processes(['camerad'])
- def test_camera_operation(self):
- print("checking image outputs")
- if EON:
- # run checks similar to prov
- time.sleep(15) # wait for startup and AF
- pic, fpic = get_snapshots()
- self.assertTrue(self._is_really_sharp(pic))
- self.assertTrue(self._is_exposure_okay(pic))
- self.assertTrue(self._is_exposure_okay(fpic))
-
- time.sleep(30)
- # check again for consistency
- pic, fpic = get_snapshots()
- self.assertTrue(self._is_really_sharp(pic))
- self.assertTrue(self._is_exposure_okay(pic))
- self.assertTrue(self._is_exposure_okay(fpic))
- elif TICI:
- raise unittest.SkipTest # TBD
- else:
- raise unittest.SkipTest
-
@with_processes(['camerad'])
def test_frame_packets(self):
print("checking frame pkts continuity")
diff --git a/selfdrive/camerad/test/test_exposure.py b/selfdrive/camerad/test/test_exposure.py
new file mode 100755
index 0000000000..0b5b5ace88
--- /dev/null
+++ b/selfdrive/camerad/test/test_exposure.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+
+import time
+import unittest
+import os
+import numpy as np
+
+from selfdrive.test.helpers import with_processes
+from selfdrive.camerad.snapshot.snapshot import get_snapshots
+
+from selfdrive.hardware import EON, TICI
+
+TEST_TIME = 45
+REPEAT = 5
+
+os.environ["SEND_ROAD"] = "1"
+os.environ["SEND_DRIVER"] = "1"
+if TICI:
+ os.environ["SEND_WIDE_ROAD"] = "1"
+
+class TestCamerad(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ if not (EON or TICI):
+ raise unittest.SkipTest
+
+ def _numpy_rgb2gray(self, im):
+ ret = np.clip(im[:,:,2] * 0.114 + im[:,:,1] * 0.587 + im[:,:,0] * 0.299, 0, 255).astype(np.uint8)
+ return ret
+
+ def _is_exposure_okay(self, i, med_mean=np.array([[0.2,0.4],[0.2,0.6]])):
+ h, w = i.shape[:2]
+ i = i[h//10:9*h//10,w//10:9*w//10]
+ med_ex, mean_ex = med_mean
+ i = self._numpy_rgb2gray(i)
+ i_median = np.median(i) / 255.
+ i_mean = np.mean(i) / 255.
+ print([i_median, i_mean])
+ return med_ex[0] < i_median < med_ex[1] and mean_ex[0] < i_mean < mean_ex[1]
+
+
+ @with_processes(['camerad'])
+ def test_camera_operation(self):
+ print("checking image outputs")
+
+ start = time.time()
+ passed = 0
+ while(time.time() - start < TEST_TIME and passed < REPEAT):
+ rpic, dpic = get_snapshots(frame="roadCameraState", front_frame="driverCameraState")
+
+ res = self._is_exposure_okay(rpic)
+ res = res and self._is_exposure_okay(dpic)
+
+ if TICI:
+ wpic, _ = get_snapshots(frame="wideRoadCameraState")
+ res = res and self._is_exposure_okay(wpic)
+
+ if passed > 0 and not res:
+ passed = -passed # fails test if any failure after first sus
+ break
+
+ passed += int(res)
+ time.sleep(2)
+ print(passed)
+ self.assertTrue(passed >= REPEAT)
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/selfdrive/car/volkswagen/interface.py b/selfdrive/car/volkswagen/interface.py
index dd774c3596..f33eba532b 100644
--- a/selfdrive/car/volkswagen/interface.py
+++ b/selfdrive/car/volkswagen/interface.py
@@ -67,21 +67,36 @@ class CarInterface(CarInterfaceBase):
ret.mass = 1328 + STD_CARGO_KG
ret.wheelbase = 2.71
+ elif candidate == CAR.PASSAT_MK8:
+ # Averages of all 3C Passat variants
+ ret.mass = 1551 + STD_CARGO_KG
+ ret.wheelbase = 2.79
+
elif candidate == CAR.TIGUAN_MK2:
# Average of SWB and LWB variants
ret.mass = 1715 + STD_CARGO_KG
ret.wheelbase = 2.74
- elif candidate == CAR.AUDI_A3_MK3:
+ elif candidate == CAR.AUDI_A3:
# Averages of all 8V A3 variants
ret.mass = 1335 + STD_CARGO_KG
ret.wheelbase = 2.61
+ elif candidate == CAR.SEAT_ATECA_MK1:
+ # Averages of all 5F Ateca variants
+ ret.mass = 1900 + STD_CARGO_KG
+ ret.wheelbase = 2.64
+
elif candidate == CAR.SKODA_KODIAQ_MK1:
# Averages of all 5N Kodiaq variants
ret.mass = 1569 + STD_CARGO_KG
ret.wheelbase = 2.79
+ elif candidate == CAR.SKODA_SCALA_MK1:
+ # Averages of all NW Scala variants
+ ret.mass = 1192 + STD_CARGO_KG
+ ret.wheelbase = 2.65
+
ret.centerToFront = ret.wheelbase * 0.45
ret.enableCamera = True # Stock camera detection doesn't apply to VW
diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py
index 3e59e8324a..f9ffd0143a 100644
--- a/selfdrive/car/volkswagen/values.py
+++ b/selfdrive/car/volkswagen/values.py
@@ -57,9 +57,12 @@ MQB_LDW_MESSAGES = {
class CAR:
GOLF = "VOLKSWAGEN GOLF" # Chassis 5G/AU/BA/BE, Mk7 VW Golf and variants
JETTA_MK7 = "VOLKSWAGEN JETTA 7TH GEN" # Chassis BU, Mk7 Jetta
+ PASSAT_MK8 = "VOLKSWAGEN PASSAT 8TH GEN" # Chassis 3G, Mk8 Passat and variants
TIGUAN_MK2 = "VOLKSWAGEN TIGUAN 2ND GEN" # Chassis AD/BW, Mk2 VW Tiguan and variants
- AUDI_A3_MK3 = "AUDI A3 3RD GEN" # Chassis 8V/FF, Mk3 Audi A3 and variants
+ AUDI_A3 = "AUDI A3" # Chassis 8V/FF, Mk3 Audi A3 and variants
+ SEAT_ATECA_MK1 = "SEAT ATECA 1ST GEN" # Chassis 5F, Mk1 SEAT Ateca and CUPRA Ateca
SKODA_KODIAQ_MK1 = "SKODA KODIAQ 1ST GEN" # Chassis NS, Mk1 Skoda Kodiaq
+ SKODA_SCALA_MK1 = "SKODA SCALA 1ST GEN" # Chassis NW, Mk1 Skoda Scala and Skoda Kamiq
FINGERPRINTS = {
CAR.GOLF: [{
@@ -68,18 +71,27 @@ FINGERPRINTS = {
CAR.JETTA_MK7: [{
64: 8, 134: 8, 159: 8, 173: 8, 178: 8, 253: 8, 257: 8, 260: 8, 262: 8, 264: 8, 278: 8, 279: 8, 283: 8, 286: 8, 288: 8, 289: 8, 290: 8, 294: 8, 299: 8, 302: 8, 346: 8, 376: 8, 418: 8, 427: 8, 679: 8, 681: 8, 695: 8, 779: 8, 780: 8, 783: 8, 792: 8, 795: 8, 804: 8, 806: 8, 807: 8, 808: 8, 809: 8, 828: 8, 870: 8, 879: 8, 884: 8, 888: 8, 891: 8, 901: 8, 913: 8, 919: 8, 949: 8, 958: 8, 960: 4, 981: 8, 987: 8, 988: 8, 991: 8, 997: 8, 1000: 8, 1019: 8, 1122: 8, 1123: 8, 1124: 8, 1153: 8, 1156: 8, 1157: 8, 1158: 8, 1162: 8, 1312: 8, 1343: 8, 1385: 8, 1413: 8, 1440: 5, 1471: 4, 1514: 8, 1515: 8, 1520: 8, 1600: 8, 1601: 8, 1603: 8, 1605: 8, 1624: 8, 1626: 8, 1629: 8, 1631: 8, 1635: 8, 1646: 8, 1648: 8, 1712: 6, 1714: 8, 1716: 8, 1717: 8, 1719: 8, 1720: 8
}],
+ CAR.PASSAT_MK8: [{
+ 64: 8, 134: 8, 159: 8, 173: 8, 178: 8, 253: 8, 257: 8, 260: 8, 262: 8, 264: 8, 278: 8, 279: 8, 283: 8, 286: 8, 288: 8, 289: 8, 290: 8, 294: 8, 295: 8, 299: 8, 302: 8, 346: 8, 385: 8, 391: 8, 427: 8, 668: 8, 679: 8, 681: 8, 695: 8, 779: 8, 780: 8, 783: 8, 787: 8, 788: 8, 789: 8, 791: 8, 792: 8, 799: 8, 802: 8, 804: 8, 806: 8, 807: 8, 808: 8, 809: 8, 828: 8, 838: 8, 839: 8, 840: 8, 841: 8, 842: 8, 843: 8, 844: 8, 845: 8, 870: 8, 896: 8, 897: 8, 898: 8, 901: 8, 917: 8, 919: 8, 927: 8, 949: 8, 958: 8, 960: 4, 981: 8, 987: 8, 988: 8, 991: 8, 997: 8, 1000: 8, 1019: 8, 1120: 8, 1122: 8, 1123: 8, 1124: 8, 1153: 8, 1162: 8, 1175: 8, 1312: 8, 1385: 8, 1413: 8, 1438: 8, 1440: 5, 1461: 8, 1514: 8, 1515: 8, 1520: 8, 1529: 8, 1600: 8, 1601: 8, 1603: 8, 1624: 8, 1629: 8, 1631: 8, 1646: 8, 1648: 8, 1712: 6, 1714: 8, 1716: 8, 1717: 8, 1719: 8, 1720: 8, 1721: 8
+ }],
CAR.TIGUAN_MK2: [{
64: 8, 134: 8, 159: 8, 173: 8, 178: 8, 253: 8, 257: 8, 260: 8, 262: 8, 278: 8, 279: 8, 283: 8, 286: 8, 288: 8, 289: 8, 290: 8, 294: 8, 299: 8, 302: 8, 346: 8, 376: 8, 418: 8, 427: 8, 573: 8, 679: 8, 681: 8, 684: 8, 695: 8, 779: 8, 780: 8, 783: 8, 787: 8, 788: 8, 789: 8, 792: 8, 795: 8, 804: 8, 806: 8, 807: 8, 808: 8, 809: 8, 828: 8, 870: 8, 879: 8, 884: 8, 888: 8, 891: 8, 896: 8, 897: 8, 898: 8, 901: 8, 913: 8, 917: 8, 919: 8, 949: 8, 958: 8, 960: 4, 981: 8, 987: 8, 988: 8, 991: 8, 997: 8, 1000: 8, 1019: 8, 1122: 8, 1123: 8, 1124: 8, 1153: 8, 1156: 8, 1157: 8, 1158: 8, 1162: 8, 1175: 8, 1312: 8, 1343: 8, 1385: 8, 1413: 8, 1440: 5, 1471: 4, 1514: 8, 1515: 8, 1520: 8, 1600: 8, 1601: 8, 1603: 8, 1605: 8, 1624: 8, 1626: 8, 1629: 8, 1631: 8, 1635: 8, 1646: 8, 1648: 8, 1712: 6, 1714: 8, 1716: 8, 1717: 8, 1719: 8, 1720: 8, 1721: 8
}],
- CAR.AUDI_A3_MK3: [{
+ CAR.AUDI_A3: [{
64: 8, 134: 8, 159: 8, 173: 8, 178: 8, 253: 8, 257: 8, 260: 8, 262: 8, 278: 8, 279: 8, 283: 8, 285: 8, 286: 8, 288: 8, 289: 8, 290: 8, 294: 8, 295: 8, 299: 8, 302: 8, 346: 8, 418: 8, 427: 8, 506: 8, 679: 8, 681: 8, 695: 8, 779: 8, 780: 8, 783: 8, 787: 8, 788: 8, 789: 8, 792: 8, 802: 8, 804: 8, 806: 8, 807: 8, 808: 8, 809: 8, 846: 8, 847: 8, 870: 8, 896: 8, 897: 8, 898: 8, 901: 8, 917: 8, 919: 8, 949: 8, 958: 8, 960: 4, 981: 8, 987: 8, 988: 8, 991: 8, 997: 8, 1000: 8, 1019: 8, 1122: 8, 1123: 8, 1124: 8, 1153: 8, 1162: 8, 1175: 8, 1312: 8, 1385: 8, 1413: 8, 1440: 5, 1514: 8, 1515: 8, 1520: 8, 1600: 8, 1601: 8, 1603: 8, 1624: 8, 1629: 8, 1631: 8, 1646: 8, 1648: 8, 1712: 6, 1714: 8, 1716: 8, 1717: 8, 1719: 8, 1720: 8, 1721: 8, 1792: 8, 1872: 8, 1976: 8, 1977: 8, 1982: 8, 1985: 8
}],
+ CAR.SEAT_ATECA_MK1: [{
+ 64: 8, 134: 8, 159: 8, 173: 8, 178: 8, 253: 8, 257: 8, 260: 8, 262: 8, 278: 8, 279: 8, 283: 8, 286: 8, 288: 8, 289: 8, 290: 8, 294: 8, 299: 8, 302: 8, 346: 8, 385: 8, 418: 8, 427: 8, 668: 8, 679: 8, 681: 8, 684: 8, 779: 8, 780: 8, 792: 8, 795: 8, 804: 8, 806: 8, 807: 8, 808: 8, 809: 8, 870: 8, 901: 8, 917: 8, 919: 8, 927: 8, 949: 8, 958: 8, 960: 4, 981: 8, 987: 8, 988: 8, 991: 8, 997: 8, 1000: 8, 1019: 8, 1120: 8, 1122: 8, 1123: 8, 1124: 8, 1153: 8, 1162: 8, 1175: 8, 1312: 8, 1385: 8, 1413: 8, 1440: 5, 1514: 8, 1515: 8, 1520: 8, 1600: 8, 1601: 8, 1603: 8, 1605: 8, 1624: 8, 1626: 8, 1629: 8, 1631: 8, 1646: 8, 1648: 8, 1712: 6, 1714: 8, 1716: 8, 1717: 8, 1719: 8, 1720: 8, 1721: 8
+ }],
CAR.SKODA_KODIAQ_MK1: [{
64: 8, 134: 8, 159: 8, 173: 8, 178: 8, 253: 8, 257: 8, 260: 8, 262: 8, 278: 8, 279: 8, 283: 8, 286: 8, 288: 8, 289: 8, 290: 8, 294: 8, 299: 8, 302: 8, 346: 8, 385: 8, 418: 8, 427: 8, 573: 8, 668: 8, 679: 8, 681: 8, 684: 8, 695: 8, 779: 8, 780: 8, 783: 8, 787: 8, 788: 8, 789: 8, 792: 8, 795: 8, 802: 8, 804: 8, 806: 8, 807: 8, 808: 8, 809: 8, 828: 8, 870: 8, 896: 8, 897: 8, 898: 8, 901: 8, 917: 8, 919: 8, 949: 8, 958: 8, 960: 4, 981: 8, 987: 8, 988: 8, 991: 8, 997: 8, 1000: 8, 1019: 8, 1120: 8, 1153: 8, 1162: 8, 1175: 8, 1312: 8, 1385: 8, 1413: 8, 1440: 5, 1514: 8, 1515: 8, 1520: 8, 1529: 8, 1600: 8, 1601: 8, 1603: 8, 1605: 8, 1624: 8, 1626: 8, 1629: 8, 1631: 8, 1646: 8, 1648: 8, 1712: 6, 1714: 8, 1716: 8, 1717: 8, 1719: 8, 1720: 8, 1721: 8, 1792: 8, 1871: 8, 1872: 8, 1879: 8, 1909: 8, 1976: 8, 1977: 8, 1985: 8
}],
+ CAR.SKODA_SCALA_MK1: [{
+ 64: 8, 134: 8, 159: 8, 173: 8, 178: 8, 253: 8, 257: 8, 262: 8, 278: 8, 279: 8, 283: 8, 286: 8, 288: 8, 289: 8, 290: 8, 294: 8, 299: 8, 302: 8, 346: 8, 418: 8, 427: 8, 506: 8, 568: 8, 569: 8, 572: 8, 573: 8, 679: 8, 681: 8, 684: 8, 695: 8, 779: 8, 780: 8, 783: 8, 787: 8, 788: 8, 789: 8, 792: 8, 795: 8, 804: 8, 806: 8, 807: 8, 808: 8, 809: 8, 826: 8, 827: 8, 828: 8, 870: 8, 879: 8, 884: 8, 888: 8, 891: 8, 901: 8, 913: 8, 917: 8, 919: 8, 949: 8, 958: 8, 960: 4, 981: 8, 987: 8, 988: 8, 991: 8, 997: 8, 1000: 8, 1019: 8, 1122: 8, 1123: 8, 1124: 8, 1153: 8, 1156: 8, 1157: 8, 1158: 8, 1162: 8, 1175: 8, 1312: 8, 1343: 8, 1385: 8, 1413: 8, 1440: 5, 1514: 8, 1515: 8, 1520: 8, 1600: 8, 1601: 8, 1603: 8, 1605: 8, 1624: 8, 1626: 8, 1629: 8, 1631: 8, 1635: 8, 1646: 8, 1648: 8, 1712: 6, 1714: 8, 1716: 8, 1717: 8, 1719: 8, 1720: 8, 1721: 8, 1792: 8, 1872: 8, 1879: 8, 1976: 8, 1977: 8, 1982: 8, 1985: 8
+ }],
}
-IGNORED_FINGERPRINTS = [CAR.JETTA_MK7, CAR.TIGUAN_MK2, CAR.SKODA_KODIAQ_MK1]
+IGNORED_FINGERPRINTS = [CAR.JETTA_MK7, CAR.PASSAT_MK8, CAR.TIGUAN_MK2, CAR.SEAT_ATECA_MK1, CAR.SKODA_KODIAQ_MK1, CAR.SKODA_SCALA_MK1]
FW_VERSIONS = {
CAR.GOLF: {
@@ -127,6 +139,28 @@ FW_VERSIONS = {
b'\xf1\x875Q0907572R \xf1\x890771',
],
},
+ CAR.PASSAT_MK8: {
+ (Ecu.engine, 0x7e0, None): [
+ b'\xf1\x8704E906023AH\xf1\x893379',
+ b'\xf1\x8704L906026GA\xf1\x892013',
+ ],
+ (Ecu.transmission, 0x7e1, None): [
+ b'\xf1\x870DD300045T \xf1\x891601',
+ b'\xf1\x870D9300014L \xf1\x895002',
+ ],
+ (Ecu.srs, 0x715, None): [
+ b'\xf1\x875Q0959655S \xf1\x890870\xf1\x82\02315120011111200631145171716121691132111',
+ b'\xf1\x873Q0959655AN\xf1\x890306\xf1\x82\r58160058140013036914110311',
+ ],
+ (Ecu.eps, 0x712, None): [
+ b'\xf1\x875Q0909143M \xf1\x892041\xf1\x820522B0080803',
+ b'\xf1\x875Q0909144T \xf1\x891072\xf1\x82\00521B00703A1',
+ ],
+ (Ecu.fwdRadar, 0x757, None): [
+ b'\xf1\x875Q0907572R \xf1\x890771',
+ b'\xf1\x873Q0907572C \xf1\x890195',
+ ],
+ },
CAR.TIGUAN_MK2: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8783A907115B \xf1\x890005',
@@ -144,7 +178,7 @@ FW_VERSIONS = {
b'\xf1\x872Q0907572R \xf1\x890372',
],
},
- CAR.AUDI_A3_MK3: {
+ CAR.AUDI_A3: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x878V0906264B \xf1\x890003',
b'\xf1\x875G0906259L \xf1\x890002',
@@ -169,6 +203,23 @@ FW_VERSIONS = {
b'\xf1\x875Q0907572G \xf1\x890571',
],
},
+ CAR.SEAT_ATECA_MK1: {
+ (Ecu.engine, 0x7e0, None): [
+ b'\xf1\x8704E906027KA\xf1\x893749',
+ ],
+ (Ecu.transmission, 0x7e1, None): [
+ b'\xf1\x870D9300014S \xf1\x895202',
+ ],
+ (Ecu.srs, 0x715, None): [
+ b'\xf1\x873Q0959655BH\xf1\x890703\xf1\x82\0161212001211001305121211052900',
+ ],
+ (Ecu.eps, 0x712, None): [
+ b'\xf1\x873Q0909144L \xf1\x895081\xf1\x82\00571N60511A1',
+ ],
+ (Ecu.fwdRadar, 0x757, None): [
+ b'\xf1\x872Q0907572M \xf1\x890233',
+ ],
+ },
CAR.SKODA_KODIAQ_MK1: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8704E906027DD\xf1\x893123',
@@ -189,12 +240,32 @@ FW_VERSIONS = {
b'\xf1\x872Q0907572R \xf1\x890372',
],
},
+ CAR.SKODA_SCALA_MK1: {
+ (Ecu.engine, 0x7e0, None): [
+ b'\xf1\x8704C906025AK\xf1\x897053',
+ ],
+ (Ecu.transmission, 0x7e1, None): [
+ b'\xf1\x870CW300050 \xf1\x891709',
+ ],
+ (Ecu.srs, 0x715, None): [
+ b'\xf1\x872Q0959655AM\xf1\x890351\xf1\x82\022111104111104112104040404111111112H14',
+ ],
+ (Ecu.eps, 0x712, None): [
+ b'\xf1\x872Q1909144M \xf1\x896041',
+ ],
+ (Ecu.fwdRadar, 0x757, None): [
+ b'\xf1\x872Q0907572R \xf1\x890372',
+ ],
+ },
}
DBC = {
CAR.GOLF: dbc_dict('vw_mqb_2010', None),
CAR.JETTA_MK7: dbc_dict('vw_mqb_2010', None),
+ CAR.PASSAT_MK8: dbc_dict('vw_mqb_2010', None),
CAR.TIGUAN_MK2: dbc_dict('vw_mqb_2010', None),
- CAR.AUDI_A3_MK3: dbc_dict('vw_mqb_2010', None),
+ CAR.AUDI_A3: dbc_dict('vw_mqb_2010', None),
+ CAR.SEAT_ATECA_MK1: dbc_dict('vw_mqb_2010', None),
CAR.SKODA_KODIAQ_MK1: dbc_dict('vw_mqb_2010', None),
+ CAR.SKODA_SCALA_MK1: dbc_dict('vw_mqb_2010', None),
}
diff --git a/selfdrive/debug/internal/run_paramsd_on_route.py b/selfdrive/debug/internal/run_paramsd_on_route.py
new file mode 100755
index 0000000000..54519e3777
--- /dev/null
+++ b/selfdrive/debug/internal/run_paramsd_on_route.py
@@ -0,0 +1,150 @@
+#!/usr/bin/env python3
+# pylint: skip-file
+# flake8: noqa
+# type: ignore
+
+import math
+import multiprocessing
+
+import numpy as np
+from tqdm import tqdm
+
+from selfdrive.locationd.paramsd import ParamsLearner, States
+from tools.lib.logreader import LogReader
+from tools.lib.route import Route
+
+ROUTE = "b2f1615665781088|2021-03-14--17-27-47"
+PLOT = True
+
+
+def load_segment(segment_name):
+ print(f"Loading {segment_name}")
+ if segment_name is None:
+ return []
+
+ try:
+ return list(LogReader(segment_name))
+ except ValueError as e:
+ print(f"Error parsing {segment_name}: {e}")
+ return []
+
+
+if __name__ == "__main__":
+ route = Route(ROUTE)
+
+ msgs = []
+ with multiprocessing.Pool(24) as pool:
+ for d in pool.map(load_segment, route.log_paths()):
+ msgs += d
+
+ for m in msgs:
+ if m.which() == 'carParams':
+ CP = m.carParams
+ break
+
+ params = {
+ 'carFingerprint': CP.carFingerprint,
+ 'steerRatio': CP.steerRatio,
+ 'stiffnessFactor': 1.0,
+ 'angleOffsetAverageDeg': 0.0,
+ }
+
+ for m in msgs:
+ if m.which() == 'liveParameters':
+ params['steerRatio'] = m.liveParameters.steerRatio
+ params['angleOffsetAverageDeg'] = m.liveParameters.angleOffsetAverageDeg
+ break
+
+ for m in msgs:
+ if m.which() == 'carState':
+ last_carstate = m
+ break
+
+ print(params)
+ learner = ParamsLearner(CP, params['steerRatio'], params['stiffnessFactor'], math.radians(params['angleOffsetAverageDeg']))
+ msgs = [m for m in tqdm(msgs) if m.which() in ('liveLocationKalman', 'carState', 'liveParameters')]
+ msgs = sorted(msgs, key=lambda m: m.logMonoTime)
+
+ ts = []
+ ts_log = []
+ results = []
+ results_log = []
+ for m in tqdm(msgs):
+ if m.which() == 'carState':
+ last_carstate = m
+
+ elif m.which() == 'liveLocationKalman':
+ t = last_carstate.logMonoTime / 1e9
+ learner.handle_log(t, 'carState', last_carstate.carState)
+
+ t = m.logMonoTime / 1e9
+ learner.handle_log(t, 'liveLocationKalman', m.liveLocationKalman)
+
+ x = learner.kf.x
+ sr = float(x[States.STEER_RATIO])
+ st = float(x[States.STIFFNESS])
+ ao_avg = math.degrees(x[States.ANGLE_OFFSET])
+ ao = ao_avg + math.degrees(x[States.ANGLE_OFFSET_FAST])
+ r = [sr, st, ao_avg, ao]
+ if any(math.isnan(v) for v in r):
+ print("NaN", t)
+
+ ts.append(t)
+ results.append(r)
+
+ elif m.which() == 'liveParameters':
+ t = m.logMonoTime / 1e9
+ mm = m.liveParameters
+
+ r = [mm.steerRatio, mm.stiffnessFactor, mm.angleOffsetAverageDeg, mm.angleOffsetDeg]
+ if any(math.isnan(v) for v in r):
+ print("NaN in log", t)
+ ts_log.append(t)
+ results_log.append(r)
+
+ results = np.asarray(results)
+ results_log = np.asarray(results_log)
+
+ if PLOT:
+ import matplotlib.pyplot as plt
+ plt.figure()
+
+ plt.subplot(3, 2, 1)
+ plt.plot(ts, results[:, 0], label='Steer Ratio')
+ plt.grid()
+ plt.ylim([0, 20])
+ plt.legend()
+
+ plt.subplot(3, 2, 3)
+ plt.plot(ts, results[:, 1], label='Stiffness')
+ plt.ylim([0, 2])
+ plt.grid()
+ plt.legend()
+
+ plt.subplot(3, 2, 5)
+ plt.plot(ts, results[:, 2], label='Angle offset (average)')
+ plt.plot(ts, results[:, 3], label='Angle offset (instant)')
+ plt.ylim([-5, 5])
+ plt.grid()
+ plt.legend()
+
+ plt.subplot(3, 2, 2)
+ plt.plot(ts_log, results_log[:, 0], label='Steer Ratio')
+ plt.grid()
+ plt.ylim([0, 20])
+ plt.legend()
+
+ plt.subplot(3, 2, 4)
+ plt.plot(ts_log, results_log[:, 1], label='Stiffness')
+ plt.ylim([0, 2])
+ plt.grid()
+ plt.legend()
+
+ plt.subplot(3, 2, 6)
+ plt.plot(ts_log, results_log[:, 2], label='Angle offset (average)')
+ plt.plot(ts_log, results_log[:, 3], label='Angle offset (instant)')
+ plt.ylim([-5, 5])
+ plt.grid()
+ plt.legend()
+ plt.show()
+
diff --git a/selfdrive/test/test_car_models.py b/selfdrive/test/test_car_models.py
index 5b0bf81e32..fbf0d382f5 100755
--- a/selfdrive/test/test_car_models.py
+++ b/selfdrive/test/test_car_models.py
@@ -445,6 +445,10 @@ routes = {
'carFingerprint': VOLKSWAGEN.JETTA_MK7,
'enableCamera': True,
},
+ "4d134e099430fba2|2021-03-26--00-26-06": {
+ 'carFingerprint': VOLKSWAGEN.PASSAT_MK8,
+ 'enableCamera': True,
+ },
"2cef8a0b898f331a|2021-03-25--20-13-57": {
'carFingerprint': VOLKSWAGEN.TIGUAN_MK2,
'enableCamera': True,
@@ -453,10 +457,18 @@ routes = {
'carFingerprint': VOLKSWAGEN.AUDI_A3_MK3,
'enableCamera': True,
},
+ "8f205bdd11bcbb65|2021-03-26--01-00-17": {
+ 'carFingerprint': VOLKSWAGEN.SEAT_ATECA_MK1,
+ 'enableCamera': True,
+ },
"90434ff5d7c8d603|2021-03-15--12-07-31": {
'carFingerprint': VOLKSWAGEN.SKODA_KODIAQ_MK1,
'enableCamera': True,
},
+ "026b6d18fba6417f|2021-03-26--09-17-04": {
+ 'carFingerprint': VOLKSWAGEN.SKODA_SCALA_MK1,
+ 'enableCamera': True,
+ },
"3c8f0c502e119c1c|2020-06-30--12-58-02": {
'carFingerprint': SUBARU.ASCENT,
'enableCamera': True,
diff --git a/selfdrive/ui/qt/api.cc b/selfdrive/ui/qt/api.cc
index 48726b989b..5522705c4f 100644
--- a/selfdrive/ui/qt/api.cc
+++ b/selfdrive/ui/qt/api.cc
@@ -76,8 +76,8 @@ QString CommaApi::create_jwt() {
return create_jwt(*(new QVector>()));
}
-RequestRepeater::RequestRepeater(QWidget* parent, QString requestURL, int period_seconds, QVector> payloads, bool disableWithScreen)
- : disableWithScreen(disableWithScreen), QObject(parent) {
+RequestRepeater::RequestRepeater(QWidget* parent, QString requestURL, int period_seconds, const QString &cache_key, QVector> payloads, bool disableWithScreen)
+ : disableWithScreen(disableWithScreen), cache_key(cache_key), QObject(parent) {
networkAccessManager = new QNetworkAccessManager(this);
reply = NULL;
@@ -90,6 +90,12 @@ RequestRepeater::RequestRepeater(QWidget* parent, QString requestURL, int period
networkTimer->setSingleShot(true);
networkTimer->setInterval(20000);
connect(networkTimer, SIGNAL(timeout()), this, SLOT(requestTimeout()));
+
+ if (!cache_key.isEmpty()) {
+ if (std::string cached_resp = Params().get(cache_key.toStdString()); !cached_resp.empty()) {
+ QTimer::singleShot(0, [=]() { emit receivedResponse(QString::fromStdString(cached_resp)); });
+ }
+ }
}
void RequestRepeater::sendRequest(QString requestURL, QVector> payloads){
@@ -126,13 +132,19 @@ void RequestRepeater::requestFinished(){
networkTimer->stop();
QString response = reply->readAll();
if (reply->error() == QNetworkReply::NoError) {
+ // save to cache
+ if (!cache_key.isEmpty()) {
+ Params().write_db_value(cache_key.toStdString(), response.toStdString());
+ }
emit receivedResponse(response);
} else {
- qDebug() << reply->errorString();
+ if (!cache_key.isEmpty()) {
+ Params().delete_db_value(cache_key.toStdString());
+ }
emit failedResponse(reply->errorString());
}
} else {
- emit failedResponse("network timeout");
+ emit timeoutResponse("timeout");
}
reply->deleteLater();
reply = NULL;
diff --git a/selfdrive/ui/qt/api.hpp b/selfdrive/ui/qt/api.hpp
index 43bf7b13ba..3772727be9 100644
--- a/selfdrive/ui/qt/api.hpp
+++ b/selfdrive/ui/qt/api.hpp
@@ -33,7 +33,7 @@ class RequestRepeater : public QObject {
Q_OBJECT
public:
- explicit RequestRepeater(QWidget* parent, QString requestURL, int period = 10, QVector> payloads = *(new QVector>()), bool disableWithScreen = true);
+ explicit RequestRepeater(QWidget* parent, QString requestURL, int period = 10, const QString &cache_key = "", QVector> payloads = *(new QVector>()), bool disableWithScreen = true);
bool active = true;
private:
@@ -41,6 +41,7 @@ private:
QNetworkReply* reply;
QNetworkAccessManager* networkAccessManager;
QTimer* networkTimer;
+ QString cache_key;
void sendRequest(QString requestURL, QVector> payloads);
private slots:
@@ -50,4 +51,5 @@ private slots:
signals:
void receivedResponse(QString response);
void failedResponse(QString errorString);
+ void timeoutResponse(QString errorString);
};
diff --git a/selfdrive/ui/qt/widgets/drive_stats.cc b/selfdrive/ui/qt/widgets/drive_stats.cc
index 1c2af95a9a..7cbcc0aabd 100644
--- a/selfdrive/ui/qt/widgets/drive_stats.cc
+++ b/selfdrive/ui/qt/widgets/drive_stats.cc
@@ -1,8 +1,6 @@
#include
#include
#include
-#include
-#include
#include
#include "api.hpp"
@@ -11,94 +9,62 @@
const double MILE_TO_KM = 1.60934;
-void clearLayouts(QLayout* layout) {
- while (QLayoutItem* item = layout->takeAt(0)) {
- if (QWidget* widget = item->widget()) {
- widget->deleteLater();
- }
- if (QLayout* childLayout = item->layout()) {
- clearLayouts(childLayout);
- }
- delete item;
- }
-}
-
-QLayout* build_stat(QString name, int stat) {
+static QLayout* build_stat_layout(QLabel** metric, const QString& name) {
QVBoxLayout* layout = new QVBoxLayout;
layout->setMargin(0);
-
- QLabel* metric = new QLabel(QString("%1").arg(stat));
- metric->setStyleSheet(R"(
- font-size: 80px;
- font-weight: 600;
- )");
- layout->addWidget(metric, 0, Qt::AlignLeft);
+ *metric = new QLabel("0");
+ (*metric)->setStyleSheet("font-size: 80px; font-weight: 600;");
+ layout->addWidget(*metric, 0, Qt::AlignLeft);
QLabel* label = new QLabel(name);
- label->setStyleSheet(R"(
- font-size: 45px;
- font-weight: 500;
- )");
+ label->setStyleSheet("font-size: 45px; font-weight: 500;");
layout->addWidget(label, 0, Qt::AlignLeft);
-
return layout;
}
-void DriveStats::parseError(QString response) {
- clearLayouts(vlayout);
- vlayout->addWidget(new QLabel("No Internet connection"), 0, Qt::AlignCenter);
-}
-
void DriveStats::parseResponse(QString response) {
- response.chop(1);
- clearLayouts(vlayout);
+ response = response.trimmed();
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
if (doc.isNull()) {
qDebug() << "JSON Parse failed on getting past drives statistics";
return;
}
- bool metric = Params().read_db_bool("IsMetric");
+ auto update = [](const QJsonObject &obj, StatsLabels& labels, bool metric) {
+ labels.routes->setText(QString::number((int)obj["routes"].toDouble()));
+ labels.distance->setText(QString::number(int(obj["distance"].toDouble() * (metric ? MILE_TO_KM : 1))));
+ labels.hours->setText(QString::number((int)(obj["minutes"].toDouble() / 60)));
+ };
+ bool metric = Params().read_db_bool("IsMetric");
QJsonObject json = doc.object();
- auto all = json["all"].toObject();
- auto week = json["week"].toObject();
+ update(json["all"].toObject(), all_, metric);
+ update(json["week"].toObject(), week_, metric);
+}
+
+DriveStats::DriveStats(QWidget* parent) : QWidget(parent) {
+ setStyleSheet("QLabel {font-size: 48px; font-weight: 500;}");
+ auto add_stats_layouts = [&](QGridLayout* gl, StatsLabels& labels, int row, const char* distance_unit) {
+ gl->addLayout(build_stat_layout(&labels.routes, "DRIVES"), row, 0, 3, 1);
+ gl->addLayout(build_stat_layout(&labels.distance, distance_unit), row, 1, 3, 1);
+ gl->addLayout(build_stat_layout(&labels.hours, "HOURS"), row, 2, 3, 1);
+ };
+
+ const char* distance_unit = Params().read_db_bool("IsMetric") ? "KM" : "MILES";
QGridLayout* gl = new QGridLayout();
gl->setMargin(0);
-
- int all_distance = all["distance"].toDouble() * (metric ? MILE_TO_KM : 1);
gl->addWidget(new QLabel("ALL TIME"), 0, 0, 1, 3);
- gl->addLayout(build_stat("DRIVES", all["routes"].toDouble()), 1, 0, 3, 1);
- gl->addLayout(build_stat(metric ? "KM" : "MILES", all_distance), 1, 1, 3, 1);
- gl->addLayout(build_stat("HOURS", all["minutes"].toDouble() / 60), 1, 2, 3, 1);
-
- int week_distance = week["distance"].toDouble() * (metric ? MILE_TO_KM : 1);
+ add_stats_layouts(gl, all_, 1, distance_unit);
gl->addWidget(new QLabel("PAST WEEK"), 6, 0, 1, 3);
- gl->addLayout(build_stat("DRIVES", week["routes"].toDouble()), 7, 0, 3, 1);
- gl->addLayout(build_stat(metric ? "KM" : "MILES", week_distance), 7, 1, 3, 1);
- gl->addLayout(build_stat("HOURS", week["minutes"].toDouble() / 60), 7, 2, 3, 1);
-
- QWidget* q = new QWidget;
- q->setLayout(gl);
- vlayout->addWidget(q);
-}
+ add_stats_layouts(gl, week_, 7, distance_unit);
-DriveStats::DriveStats(QWidget* parent) : QWidget(parent) {
- vlayout = new QVBoxLayout(this);
- vlayout->setMargin(0);
- setLayout(vlayout);
- setStyleSheet(R"(
- QLabel {
- font-size: 48px;
- font-weight: 500;
- }
- )");
+ QVBoxLayout* vlayout = new QVBoxLayout(this);
+ vlayout->addLayout(gl);
// TODO: do we really need to update this frequently?
QString dongleId = QString::fromStdString(Params().get("DongleId"));
QString url = "https://api.commadotai.com/v1.1/devices/" + dongleId + "/stats";
- RequestRepeater* repeater = new RequestRepeater(this, url, 13);
+ RequestRepeater* repeater = new RequestRepeater(this, url, 13, "ApiCache_DriveStats");
QObject::connect(repeater, SIGNAL(receivedResponse(QString)), this, SLOT(parseResponse(QString)));
- QObject::connect(repeater, SIGNAL(failedResponse(QString)), this, SLOT(parseError(QString)));
}
diff --git a/selfdrive/ui/qt/widgets/drive_stats.hpp b/selfdrive/ui/qt/widgets/drive_stats.hpp
index 40cefbb4d9..64b93cfe73 100644
--- a/selfdrive/ui/qt/widgets/drive_stats.hpp
+++ b/selfdrive/ui/qt/widgets/drive_stats.hpp
@@ -1,7 +1,6 @@
#pragma once
-#include
-#include
+#include
class DriveStats : public QWidget {
Q_OBJECT
@@ -10,9 +9,10 @@ public:
explicit DriveStats(QWidget* parent = 0);
private:
- QVBoxLayout* vlayout;
+ struct StatsLabels {
+ QLabel *routes, *distance, *hours;
+ } all_, week_;
private slots:
- void parseError(QString response);
void parseResponse(QString response);
};
diff --git a/selfdrive/ui/qt/widgets/setup.cc b/selfdrive/ui/qt/widgets/setup.cc
index 64a2f68265..32e14f20dd 100644
--- a/selfdrive/ui/qt/widgets/setup.cc
+++ b/selfdrive/ui/qt/widgets/setup.cc
@@ -3,7 +3,7 @@
#include
#include
#include
-#include
+#include
#include
#include
@@ -107,7 +107,7 @@ PrimeUserWidget::PrimeUserWidget(QWidget* parent) : QWidget(parent) {
// TODO: only send the request when widget is shown
QString url = "https://api.commadotai.com/v1/devices/" + dongleId + "/owner";
- RequestRepeater* repeater = new RequestRepeater(this, url, 6);
+ RequestRepeater* repeater = new RequestRepeater(this, url, 6, "ApiCache_Owner");
QObject::connect(repeater, SIGNAL(receivedResponse(QString)), this, SLOT(replyFinished(QString)));
}
@@ -156,7 +156,7 @@ PrimeAdWidget::PrimeAdWidget(QWidget* parent) : QWidget(parent) {
SetupWidget::SetupWidget(QWidget* parent) : QFrame(parent) {
- mainLayout = new QStackedLayout;
+ mainLayout = new QStackedWidget;
// Unpaired, registration prompt layout
@@ -214,7 +214,12 @@ SetupWidget::SetupWidget(QWidget* parent) : QFrame(parent) {
primeUser = new PrimeUserWidget;
mainLayout->addWidget(primeUser);
- setLayout(mainLayout);
+ mainLayout->setCurrentWidget(primeAd);
+
+ QVBoxLayout *layout = new QVBoxLayout;
+ layout->addWidget(mainLayout);
+ setLayout(layout);
+
setStyleSheet(R"(
SetupWidget {
background-color: #292929;
@@ -226,16 +231,23 @@ SetupWidget::SetupWidget(QWidget* parent) : QFrame(parent) {
}
)");
+ // Retain size while hidden
+ QSizePolicy sp_retain = sizePolicy();
+ sp_retain.setRetainSizeWhenHidden(true);
+ setSizePolicy(sp_retain);
+
// set up API requests
QString dongleId = QString::fromStdString(Params().get("DongleId"));
QString url = "https://api.commadotai.com/v1.1/devices/" + dongleId + "/";
- RequestRepeater* repeater = new RequestRepeater(this, url, 5);
+ RequestRepeater* repeater = new RequestRepeater(this, url, 5, "ApiCache_Device");
QObject::connect(repeater, SIGNAL(receivedResponse(QString)), this, SLOT(replyFinished(QString)));
QObject::connect(repeater, SIGNAL(failedResponse(QString)), this, SLOT(parseError(QString)));
+ hide(); // Only show when first request comes back
}
void SetupWidget::parseError(QString response) {
+ show();
showQr = false;
mainLayout->setCurrentIndex(0);
}
@@ -246,6 +258,7 @@ void SetupWidget::showQrCode(){
}
void SetupWidget::replyFinished(QString response) {
+ show();
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
if (doc.isNull()) {
qDebug() << "JSON Parse failed on getting pairing and prime status";
diff --git a/selfdrive/ui/qt/widgets/setup.hpp b/selfdrive/ui/qt/widgets/setup.hpp
index 6cd18b1bc7..0479db8f95 100644
--- a/selfdrive/ui/qt/widgets/setup.hpp
+++ b/selfdrive/ui/qt/widgets/setup.hpp
@@ -1,7 +1,8 @@
#pragma once
#include
-#include
+#include
+#include
#include
#include "api.hpp"
@@ -48,7 +49,7 @@ public:
explicit SetupWidget(QWidget* parent = 0);
private:
- QStackedLayout* mainLayout;
+ QStackedWidget* mainLayout;
CommaApi* api;
PrimeAdWidget *primeAd;
PrimeUserWidget *primeUser;
diff --git a/selfdrive/ui/sidebar.cc b/selfdrive/ui/sidebar.cc
index 150841008e..805e1520cd 100644
--- a/selfdrive/ui/sidebar.cc
+++ b/selfdrive/ui/sidebar.cc
@@ -7,11 +7,7 @@
#include "sidebar.hpp"
static void draw_background(UIState *s) {
-#ifndef QT_GUI_LIB
- const NVGcolor color = COLOR_BLACK_ALPHA(85);
-#else
const NVGcolor color = nvgRGBA(0x39, 0x39, 0x39, 0xff);
-#endif
ui_fill_rect(s->vg, {0, 0, sbr_w, s->fb_h}, color);
}
diff --git a/selfdrive/ui/ui.hpp b/selfdrive/ui/ui.hpp
index b32116e006..e5e8790b1a 100644
--- a/selfdrive/ui/ui.hpp
+++ b/selfdrive/ui/ui.hpp
@@ -74,11 +74,7 @@ typedef enum UIStatus {
} UIStatus;
static std::map bg_colors = {
-#ifndef QT_GUI_LIB
- {STATUS_OFFROAD, nvgRGBA(0x07, 0x23, 0x39, 0xf1)},
-#else
{STATUS_OFFROAD, nvgRGBA(0x0, 0x0, 0x0, 0xff)},
-#endif
{STATUS_DISENGAGED, nvgRGBA(0x17, 0x33, 0x49, 0xc8)},
{STATUS_ENGAGED, nvgRGBA(0x17, 0x86, 0x44, 0xf1)},
{STATUS_WARNING, nvgRGBA(0xDA, 0x6F, 0x25, 0xf1)},