diff --git a/RELEASES.md b/RELEASES.md
index aabe425477..42d5514ff3 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -1,5 +1,6 @@
Version 0.8.12 (202X-XX-XX)
========================
+ * Volkswagen T-Roc 2021 support thanks to jyoung8607!
Version 0.8.11 (2021-11-22)
========================
diff --git a/docs/CARS.md b/docs/CARS.md
index aa23a85cec..e2b846c8f1 100644
--- a/docs/CARS.md
+++ b/docs/CARS.md
@@ -162,6 +162,7 @@
| Volkswagen| Passat 2016-183 | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| Polo 2020 | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| T-Cross 20214 | Driver Assistance | Stock | 0mph | 0mph |
+| Volkswagen| T-Roc 20214 | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| Taos 20224 | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| Tiguan 2020 | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| Touran 2017 | Driver Assistance | Stock | 0mph | 0mph |
diff --git a/panda b/panda
index f3bacee55a..ddb3698f9b 160000
--- a/panda
+++ b/panda
@@ -1 +1 @@
-Subproject commit f3bacee55ae5d7f4197c98b697d6b37113df5199
+Subproject commit ddb3698f9bf03dc6096a754c01529cddd580a976
diff --git a/release/files_common b/release/files_common
index ceaef0b0bf..aeae351dfb 100644
--- a/release/files_common
+++ b/release/files_common
@@ -616,3 +616,4 @@ opendbc/vw_mqb_2010.dbc
opendbc/tesla_can.dbc
opendbc/tesla_radar.dbc
+opendbc/tesla_powertrain.dbc
diff --git a/selfdrive/boardd/panda.cc b/selfdrive/boardd/panda.cc
index 01c9f9fa8f..78b123f698 100644
--- a/selfdrive/boardd/panda.cc
+++ b/selfdrive/boardd/panda.cc
@@ -357,9 +357,9 @@ uint8_t Panda::len_to_dlc(uint8_t len) {
return len;
}
if (len <= 24) {
- return 8 + ((len - 8) / 4) + (len % 4) ? 1 : 0;
+ return 8 + ((len - 8) / 4) + ((len % 4) ? 1 : 0);
} else {
- return 11 + (len / 16) + (len % 16) ? 1 : 0;
+ return 11 + (len / 16) + ((len % 16) ? 1 : 0);
}
}
@@ -433,15 +433,15 @@ bool Panda::can_receive(std::vector& out_vec) {
static uint8_t tail[CANPACKET_MAX_SIZE];
uint8_t tail_size = 0;
uint8_t counter = 0;
- for (int i = 0; i < recv; i += 64) {
+ for (int i = 0; i < recv; i += USBPACKET_MAX_SIZE) {
// Check for counter every 64 bytes (length of USB packet)
if (counter != data[i]) {
LOGE("CAN: MALFORMED USB RECV PACKET");
break;
}
counter++;
- uint8_t chunk_len = ((recv - i) > 64) ? 63 : (recv - i - 1); // as 1 is always reserved for counter
- uint8_t chunk[CANPACKET_MAX_SIZE];
+ uint8_t chunk_len = ((recv - i) > USBPACKET_MAX_SIZE) ? 63 : (recv - i - 1); // as 1 is always reserved for counter
+ uint8_t chunk[USBPACKET_MAX_SIZE + CANPACKET_MAX_SIZE];
memcpy(chunk, tail, tail_size);
memcpy(&chunk[tail_size], &data[i+1], chunk_len);
chunk_len += tail_size;
diff --git a/selfdrive/boardd/panda.h b/selfdrive/boardd/panda.h
index 415bb30103..4be9454cf5 100644
--- a/selfdrive/boardd/panda.h
+++ b/selfdrive/boardd/panda.h
@@ -17,6 +17,7 @@
#define PANDA_BUS_CNT 4
#define RECV_SIZE (0x4000U)
#define USB_TX_SOFT_LIMIT (0x100U)
+#define USBPACKET_MAX_SIZE (0x40U)
#define CANPACKET_HEAD_SIZE 5U
#define CANPACKET_MAX_SIZE 72U
#define CANPACKET_REJECTED (0xC0U)
diff --git a/selfdrive/car/honda/carstate.py b/selfdrive/car/honda/carstate.py
index affe377276..8bf3ac58f6 100644
--- a/selfdrive/car/honda/carstate.py
+++ b/selfdrive/car/honda/carstate.py
@@ -5,7 +5,7 @@ from opendbc.can.can_define import CANDefine
from opendbc.can.parser import CANParser
from selfdrive.config import Conversions as CV
from selfdrive.car.interfaces import CarStateBase
-from selfdrive.car.honda.values import CAR, DBC, STEER_THRESHOLD, SPEED_FACTOR, HONDA_BOSCH, HONDA_NIDEC_ALT_MAIN, HONDA_BOSCH_ALT_BRAKE_SIGNAL
+from selfdrive.car.honda.values import CAR, DBC, STEER_THRESHOLD, SPEED_FACTOR, HONDA_BOSCH, HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_ALT_BRAKE_SIGNAL
TransmissionType = car.CarParams.TransmissionType
@@ -161,7 +161,7 @@ class CarState(CarStateBase):
self.gearbox_msg = "GEARBOX_15T"
self.main_on_sig_msg = "SCM_FEEDBACK"
- if CP.carFingerprint in HONDA_NIDEC_ALT_MAIN:
+ if CP.carFingerprint in HONDA_NIDEC_ALT_SCM_MESSAGES:
self.main_on_sig_msg = "SCM_BUTTONS"
self.shifter_values = can_define.dv[self.gearbox_msg]["GEAR_SHIFTER"]
diff --git a/selfdrive/car/honda/interface.py b/selfdrive/car/honda/interface.py
index 63e3f05770..4aa6ec3815 100755
--- a/selfdrive/car/honda/interface.py
+++ b/selfdrive/car/honda/interface.py
@@ -3,7 +3,7 @@ from cereal import car
from panda import Panda
from common.numpy_fast import interp
from common.params import Params
-from selfdrive.car.honda.values import CarControllerParams, CruiseButtons, CAR, HONDA_BOSCH, HONDA_NIDEC_ALT_MAIN, HONDA_BOSCH_ALT_BRAKE_SIGNAL
+from selfdrive.car.honda.values import CarControllerParams, CruiseButtons, CAR, HONDA_BOSCH, HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_ALT_BRAKE_SIGNAL
from selfdrive.car import STD_CARGO_KG, CivicParams, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config
from selfdrive.car.interfaces import CarInterfaceBase
from selfdrive.car.disable_ecu import disable_ecu
@@ -299,8 +299,8 @@ class CarInterface(CarInterfaceBase):
if candidate in HONDA_BOSCH_ALT_BRAKE_SIGNAL:
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HONDA_ALT_BRAKE
- # These cars use an alternate main on message
- if candidate in HONDA_NIDEC_ALT_MAIN:
+ # These cars use alternate SCM messages (SCM_FEEDBACK AND SCM_BUTTON)
+ if candidate in HONDA_NIDEC_ALT_SCM_MESSAGES:
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HONDA_NIDEC_ALT
if ret.openpilotLongitudinalControl and candidate in HONDA_BOSCH:
diff --git a/selfdrive/car/honda/values.py b/selfdrive/car/honda/values.py
index a50b8ce154..97e0ded663 100644
--- a/selfdrive/car/honda/values.py
+++ b/selfdrive/car/honda/values.py
@@ -1365,7 +1365,7 @@ SPEED_FACTOR = {
}
HONDA_NIDEC_ALT_PCM_ACCEL = set([CAR.ODYSSEY])
-HONDA_NIDEC_ALT_MAIN = set([CAR.ACURA_ILX, CAR.ACURA_RDX, CAR.CRV, CAR.CRV_EU, CAR.FIT, CAR.FREED, CAR.HRV, CAR.ODYSSEY_CHN,
- CAR.PILOT, CAR.PILOT_2019, CAR.PASSPORT, CAR.RIDGELINE])
+HONDA_NIDEC_ALT_SCM_MESSAGES = set([CAR.ACURA_ILX, CAR.ACURA_RDX, CAR.CRV, CAR.CRV_EU, CAR.FIT, CAR.FREED, CAR.HRV, CAR.ODYSSEY_CHN,
+ CAR.PILOT, CAR.PILOT_2019, CAR.PASSPORT, CAR.RIDGELINE])
HONDA_BOSCH = set([CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_5G, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E])
HONDA_BOSCH_ALT_BRAKE_SIGNAL = set([CAR.ACCORD, CAR.CRV_5G, CAR.ACURA_RDX_3G])
diff --git a/selfdrive/car/tesla/carcontroller.py b/selfdrive/car/tesla/carcontroller.py
index df53b9dd9d..7e6a2f2e9a 100644
--- a/selfdrive/car/tesla/carcontroller.py
+++ b/selfdrive/car/tesla/carcontroller.py
@@ -1,14 +1,16 @@
from common.numpy_fast import clip, interp
-from selfdrive.car.tesla.teslacan import TeslaCAN
from opendbc.can.packer import CANPacker
-from selfdrive.car.tesla.values import CANBUS, CarControllerParams
+from selfdrive.car.tesla.teslacan import TeslaCAN
+from selfdrive.car.tesla.values import DBC, CANBUS, CarControllerParams
class CarController():
def __init__(self, dbc_name, CP, VM):
self.CP = CP
self.last_angle = 0
+ self.long_control_counter = 0
self.packer = CANPacker(dbc_name)
- self.tesla_can = TeslaCAN(dbc_name, self.packer)
+ self.pt_packer = CANPacker(DBC[CP.carFingerprint]['pt'])
+ self.tesla_can = TeslaCAN(self.packer, self.pt_packer)
def update(self, enabled, CS, frame, actuators, cruise_cancel):
can_sends = []
@@ -34,6 +36,16 @@ class CarController():
self.last_angle = apply_angle
can_sends.append(self.tesla_can.create_steering_control(apply_angle, lkas_enabled, frame))
+ # Longitudinal control (40Hz)
+ if self.CP.openpilotLongitudinalControl and ((frame % 5) in [0, 2]):
+ target_accel = actuators.accel
+ target_speed = max(CS.out.vEgo + (target_accel * CarControllerParams.ACCEL_TO_SPEED_MULTIPLIER), 0)
+ max_accel = 0 if target_accel < 0 else target_accel
+ min_accel = 0 if target_accel > 0 else target_accel
+
+ can_sends.extend(self.tesla_can.create_longitudinal_commands(CS.acc_state, target_speed, min_accel, max_accel, self.long_control_counter))
+ self.long_control_counter += 1
+
# Cancel on user steering override, since there is no steering torque blending
if hands_on_fault:
cruise_cancel = True
@@ -46,7 +58,7 @@ class CarController():
# Spam every possible counter value, otherwise it might not be accepted
for counter in range(16):
can_sends.append(self.tesla_can.create_action_request(CS.msg_stw_actn_req, cruise_cancel, CANBUS.chassis, counter))
- can_sends.append(self.tesla_can.create_action_request(CS.msg_stw_actn_req, cruise_cancel, CANBUS.autopilot, counter))
+ can_sends.append(self.tesla_can.create_action_request(CS.msg_stw_actn_req, cruise_cancel, CANBUS.autopilot_chassis, counter))
# TODO: HUD control
diff --git a/selfdrive/car/tesla/carstate.py b/selfdrive/car/tesla/carstate.py
index 4bd1d1001c..0a45b6f2bb 100644
--- a/selfdrive/car/tesla/carstate.py
+++ b/selfdrive/car/tesla/carstate.py
@@ -16,6 +16,7 @@ class CarState(CarStateBase):
self.msg_stw_actn_req = None
self.hands_on_level = 0
self.steer_warning = None
+ self.acc_state = 0
def update(self, cp, cp_cam):
ret = car.CarState.new_message()
@@ -57,7 +58,7 @@ class CarState(CarStateBase):
elif speed_units == "MPH":
ret.cruiseState.speed = cp.vl["DI_state"]["DI_digitalSpeed"] * CV.MPH_TO_MS
ret.cruiseState.available = ((cruise_state == "STANDBY") or ret.cruiseState.enabled)
- ret.cruiseState.standstill = (cruise_state == "STANDSTILL")
+ ret.cruiseState.standstill = False # This needs to be false, since we can resume from stop without sending anything special
# Gear
ret.gearShifter = GEAR_MAP[self.can_define.dv["DI_torque2"]["DI_gear"].get(int(cp.vl["DI_torque2"]["DI_gear"]), "DI_GEAR_INVALID")]
@@ -88,6 +89,7 @@ class CarState(CarStateBase):
# Messages needed by carcontroller
self.msg_stw_actn_req = copy.copy(cp.vl["STW_ACTN_RQ"])
+ self.acc_state = cp_cam.vl["DAS_control"]["DAS_accState"]
return ret
@@ -174,8 +176,10 @@ class CarState(CarStateBase):
def get_cam_can_parser(CP):
signals = [
# sig_name, sig_address, default
+ ("DAS_accState", "DAS_control", 0),
]
checks = [
# sig_address, frequency
+ ("DAS_control", 40),
]
- return CANParser(DBC[CP.carFingerprint]['chassis'], signals, checks, CANBUS.autopilot)
+ return CANParser(DBC[CP.carFingerprint]['chassis'], signals, checks, CANBUS.autopilot_chassis)
diff --git a/selfdrive/car/tesla/interface.py b/selfdrive/car/tesla/interface.py
index e264215450..45dc0a7239 100755
--- a/selfdrive/car/tesla/interface.py
+++ b/selfdrive/car/tesla/interface.py
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
from cereal import car
-from selfdrive.car.tesla.values import CAR
+from panda import Panda
+from selfdrive.car.tesla.values import CANBUS, CAR
from selfdrive.car import STD_CARGO_KG, gen_empty_fingerprint, scale_rot_inertia, scale_tire_stiffness, get_safety_config
from selfdrive.car.interfaces import CarInterfaceBase
@@ -10,7 +11,6 @@ class CarInterface(CarInterfaceBase):
def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None):
ret = CarInterfaceBase.get_std_params(candidate, fingerprint)
ret.carName = "tesla"
- ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.tesla)]
# There is no safe way to do steer blending with user torque,
# so the steering behaves like autopilot. This is not
@@ -18,7 +18,28 @@ class CarInterface(CarInterfaceBase):
ret.dashcamOnly = True
ret.steerControlType = car.CarParams.SteerControlType.angle
- ret.openpilotLongitudinalControl = False
+
+ # Set kP and kI to 0 over the whole speed range to have the planner accel as actuator command
+ ret.longitudinalTuning.kpBP = [0]
+ ret.longitudinalTuning.kpV = [0]
+ ret.longitudinalTuning.kiBP = [0]
+ ret.longitudinalTuning.kiV = [0]
+ ret.stopAccel = 0.0
+ ret.startAccel = 0.0
+ ret.longitudinalActuatorDelayUpperBound = 0.5 # s
+ ret.radarTimeStep = (1.0 / 8) # 8Hz
+
+ # Check if we have messages on an auxiliary panda, and that 0x2bf (DAS_control) is present on the AP powertrain bus
+ # If so, we assume that it is connected to the longitudinal harness.
+ if (CANBUS.autopilot_powertrain in fingerprint.keys()) and (0x2bf in fingerprint[CANBUS.autopilot_powertrain].keys()):
+ ret.openpilotLongitudinalControl = True
+ ret.safetyConfigs = [
+ get_safety_config(car.CarParams.SafetyModel.tesla, Panda.FLAG_TESLA_LONG_CONTROL),
+ get_safety_config(car.CarParams.SafetyModel.tesla, Panda.FLAG_TESLA_LONG_CONTROL | Panda.FLAG_TESLA_POWERTRAIN),
+ ]
+ else:
+ ret.openpilotLongitudinalControl = False
+ ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.tesla, 0)]
ret.steerActuatorDelay = 0.1
ret.steerRateCost = 0.5
diff --git a/selfdrive/car/tesla/teslacan.py b/selfdrive/car/tesla/teslacan.py
index 0dee8b182c..1301802860 100644
--- a/selfdrive/car/tesla/teslacan.py
+++ b/selfdrive/car/tesla/teslacan.py
@@ -1,13 +1,13 @@
import copy
import crcmod
-from opendbc.can.can_define import CANDefine
-from selfdrive.car.tesla.values import CANBUS
+from selfdrive.config import Conversions as CV
+from selfdrive.car.tesla.values import CANBUS, CarControllerParams
class TeslaCAN:
- def __init__(self, dbc_name, packer):
- self.can_define = CANDefine(dbc_name)
+ def __init__(self, packer, pt_packer):
self.packer = packer
+ self.pt_packer = pt_packer
self.crc = crcmod.mkCrcFun(0x11d, initCrc=0x00, rev=False, xorOut=0xff)
@staticmethod
@@ -39,3 +39,23 @@ class TeslaCAN:
data = self.packer.make_can_msg("STW_ACTN_RQ", bus, values)[2]
values["CRC_STW_ACTN_RQ"] = self.crc(data[:7])
return self.packer.make_can_msg("STW_ACTN_RQ", bus, values)
+
+ def create_longitudinal_commands(self, acc_state, speed, min_accel, max_accel, cnt):
+ messages = []
+ values = {
+ "DAS_setSpeed": speed * CV.MS_TO_KPH,
+ "DAS_accState": acc_state,
+ "DAS_aebEvent": 0,
+ "DAS_jerkMin": CarControllerParams.JERK_LIMIT_MIN,
+ "DAS_jerkMax": CarControllerParams.JERK_LIMIT_MAX,
+ "DAS_accelMin": min_accel,
+ "DAS_accelMax": max_accel,
+ "DAS_controlCounter": (cnt % 8),
+ "DAS_controlChecksum": 0,
+ }
+
+ for packer, bus in [(self.packer, CANBUS.chassis), (self.pt_packer, CANBUS.powertrain)]:
+ data = packer.make_can_msg("DAS_control", bus, values)[2]
+ values["DAS_controlChecksum"] = self.checksum(0x2b9, data[:7])
+ messages.append(packer.make_can_msg("DAS_control", bus, values))
+ return messages
diff --git a/selfdrive/car/tesla/values.py b/selfdrive/car/tesla/values.py
index 2a7cf9e50b..90bc45c7a5 100644
--- a/selfdrive/car/tesla/values.py
+++ b/selfdrive/car/tesla/values.py
@@ -25,14 +25,20 @@ FINGERPRINTS = {
}
DBC = {
- CAR.AP2_MODELS: dbc_dict(None, 'tesla_radar', chassis_dbc='tesla_can'),
- CAR.AP1_MODELS: dbc_dict(None, 'tesla_radar', chassis_dbc='tesla_can'),
+ CAR.AP2_MODELS: dbc_dict('tesla_powertrain', 'tesla_radar', chassis_dbc='tesla_can'),
+ CAR.AP1_MODELS: dbc_dict('tesla_powertrain', 'tesla_radar', chassis_dbc='tesla_can'),
}
class CANBUS:
+ # Lateral harness
chassis = 0
- autopilot = 2
radar = 1
+ autopilot_chassis = 2
+
+ # Longitudinal harness
+ powertrain = 4
+ private = 5
+ autopilot_powertrain = 6
GEAR_MAP = {
"DI_GEAR_INVALID": car.CarState.GearShifter.unknown,
@@ -58,4 +64,6 @@ BUTTONS = [
class CarControllerParams:
RATE_LIMIT_UP = AngleRateLimit(speed_points=[0., 5., 15.], max_angle_diff_points=[5., .8, .15])
RATE_LIMIT_DOWN = AngleRateLimit(speed_points=[0., 5., 15.], max_angle_diff_points=[5., 3.5, 0.4])
-
+ JERK_LIMIT_MAX = 8
+ JERK_LIMIT_MIN = -8
+ ACCEL_TO_SPEED_MULTIPLIER = 3
diff --git a/selfdrive/car/volkswagen/interface.py b/selfdrive/car/volkswagen/interface.py
index 08b570f2f3..9d2eab7958 100644
--- a/selfdrive/car/volkswagen/interface.py
+++ b/selfdrive/car/volkswagen/interface.py
@@ -125,6 +125,10 @@ class CarInterface(CarInterfaceBase):
ret.wheelbase = 3.00 # SWB, LWB is 3.40, TBD how to detect difference
ret.minSteerSpeed = 14.0
+ elif candidate == CAR.TROC_MK1:
+ ret.mass = 1413 + STD_CARGO_KG
+ ret.wheelbase = 2.63
+
elif candidate == CAR.AUDI_A3_MK3:
ret.mass = 1335 + STD_CARGO_KG
ret.wheelbase = 2.61
diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py
index 52fcd9de6d..9669e0e297 100755
--- a/selfdrive/car/volkswagen/values.py
+++ b/selfdrive/car/volkswagen/values.py
@@ -81,6 +81,7 @@ class CAR:
TIGUAN_MK2 = "VOLKSWAGEN TIGUAN 2ND GEN" # Chassis AD/BW, Mk2 VW Tiguan and variants
TOURAN_MK2 = "VOLKSWAGEN TOURAN 2ND GEN" # Chassis 1T, Mk2 VW Touran and variants
TRANSPORTER_T61 = "VOLKSWAGEN TRANSPORTER T6.1" # Chassis 7H/7L, T6-facelift Transporter/Multivan/Caravelle/California
+ TROC_MK1 = "VOLKSWAGEN T-ROC 1ST GEN" # Chassis A1, Mk1 VW VW T-Roc and variants
AUDI_A3_MK3 = "AUDI A3 3RD GEN" # Chassis 8V/FF, Mk3 Audi A3 and variants
AUDI_Q2_MK1 = "AUDI Q2 1ST GEN" # Chassis GA, Mk1 Audi Q2 (RoW) and Q2L (China only)
SEAT_ATECA_MK1 = "SEAT ATECA 1ST GEN" # Chassis 5F, Mk1 SEAT Ateca and CUPRA Ateca
@@ -472,6 +473,23 @@ FW_VERSIONS = {
b'\xf1\x872Q0907572R \xf1\x890372',
],
},
+ CAR.TROC_MK1: {
+ (Ecu.engine, 0x7e0, None): [
+ b'\xf1\x8705E906018AT\xf1\x899640',
+ ],
+ (Ecu.transmission, 0x7e1, None): [
+ b'\xf1\x870CW300051M \xf1\x891925',
+ ],
+ (Ecu.srs, 0x715, None): [
+ b'\xf1\x875Q0959655CG\xf1\x890421\xf1\x82\x13111100123333003142404M1152119333613100',
+ ],
+ (Ecu.eps, 0x712, None): [
+ b'\xf1\x875Q0909144AB\xf1\x891082\xf1\x82\x0521060405A1',
+ ],
+ (Ecu.fwdRadar, 0x757, None): [
+ b'\xf1\x872Q0907572T \xf1\x890383',
+ ],
+ },
CAR.AUDI_A3_MK3: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8704E906023AN\xf1\x893695',
diff --git a/selfdrive/controls/radard.py b/selfdrive/controls/radard.py
index 5d94804d75..88f39ce431 100755
--- a/selfdrive/controls/radard.py
+++ b/selfdrive/controls/radard.py
@@ -18,16 +18,22 @@ from selfdrive.hardware import TICI
class KalmanParams():
def __init__(self, dt):
# Lead Kalman Filter params, calculating K from A, C, Q, R requires the control library.
- # hardcoding a lookup table to compute K for values of radar_ts between 0.1s and 1.0s
- assert dt > .01 and dt < .1, "Radar time step must be between .01s and 0.1s"
+ # hardcoding a lookup table to compute K for values of radar_ts between 0.01s and 0.2s
+ assert dt > .01 and dt < .2, "Radar time step must be between .01s and 0.2s"
self.A = [[1.0, dt], [0.0, 1.0]]
self.C = [1.0, 0.0]
#Q = np.matrix([[10., 0.0], [0.0, 100.]])
#R = 1e3
#K = np.matrix([[ 0.05705578], [ 0.03073241]])
- dts = [dt * 0.01 for dt in range(1, 11)]
- K0 = [0.12288, 0.14557, 0.16523, 0.18282, 0.19887, 0.21372, 0.22761, 0.24069, 0.2531, 0.26491]
- K1 = [0.29666, 0.29331, 0.29043, 0.28787, 0.28555, 0.28342, 0.28144, 0.27958, 0.27783, 0.27617]
+ dts = [dt * 0.01 for dt in range(1, 21)]
+ K0 = [0.12287673, 0.14556536, 0.16522756, 0.18281627, 0.1988689, 0.21372394,
+ 0.22761098, 0.24069424, 0.253096, 0.26491023, 0.27621103, 0.28705801,
+ 0.29750003, 0.30757767, 0.31732515, 0.32677158, 0.33594201, 0.34485814,
+ 0.35353899, 0.36200124]
+ K1 = [0.29666309, 0.29330885, 0.29042818, 0.28787125, 0.28555364, 0.28342219,
+ 0.28144091, 0.27958406, 0.27783249, 0.27617149, 0.27458948, 0.27307714,
+ 0.27162685, 0.27023228, 0.26888809, 0.26758976, 0.26633338, 0.26511557,
+ 0.26393339, 0.26278425]
self.K = [[interp(dt, dts, K0)], [interp(dt, dts, K1)]]
diff --git a/selfdrive/loggerd/tests/test_uploader.py b/selfdrive/loggerd/tests/test_uploader.py
index 4a44f80b5c..8ebb001c75 100644
--- a/selfdrive/loggerd/tests/test_uploader.py
+++ b/selfdrive/loggerd/tests/test_uploader.py
@@ -11,6 +11,7 @@ from common.xattr import getxattr
from selfdrive.loggerd.tests.loggerd_tests_common import UploaderTestCase
+
class TestLogHandler(logging.Handler):
def __init__(self):
logging.Handler.__init__(self)
@@ -22,7 +23,7 @@ class TestLogHandler(logging.Handler):
def emit(self, record):
try:
- j = json.loads(record.message)
+ j = json.loads(record.getMessage())
if j["event"] == "upload_success":
self.upload_order.append(j["key"])
if j["event"] == "upload_ignored":
@@ -33,6 +34,7 @@ class TestLogHandler(logging.Handler):
log_handler = TestLogHandler()
cloudlog.addHandler(log_handler)
+
class TestUploader(UploaderTestCase):
def setUp(self):
super(TestUploader, self).setUp()
diff --git a/selfdrive/loggerd/uploader.py b/selfdrive/loggerd/uploader.py
index d40c7e1f5c..e99725f0c0 100644
--- a/selfdrive/loggerd/uploader.py
+++ b/selfdrive/loggerd/uploader.py
@@ -147,7 +147,7 @@ class Uploader():
def do_upload(self, key, fn):
try:
- url_resp = self.api.get("v1.3/"+self.dongle_id+"/upload_url/", timeout=10, path=key, access_token=self.api.get_token())
+ url_resp = self.api.get("v1.4/"+self.dongle_id+"/upload_url/", timeout=10, path=key, access_token=self.api.get_token())
if url_resp.status_code == 412:
self.last_resp = url_resp
return
@@ -155,7 +155,7 @@ class Uploader():
url_resp_json = json.loads(url_resp.text)
url = url_resp_json['url']
headers = url_resp_json['headers']
- cloudlog.debug("upload_url v1.3 %s %s", url, str(headers))
+ cloudlog.debug("upload_url v1.4 %s %s", url, str(headers))
if fake_upload:
cloudlog.debug("*** WARNING, THIS IS A FAKE UPLOAD TO %s ***" % url)
diff --git a/selfdrive/manager/manager.py b/selfdrive/manager/manager.py
index af0340af2a..6c71c2a377 100755
--- a/selfdrive/manager/manager.py
+++ b/selfdrive/manager/manager.py
@@ -149,9 +149,10 @@ def manager_thread():
started_prev = started
- running_list = ["%s%s\u001b[0m" % ("\u001b[32m" if p.proc.is_alive() else "\u001b[31m", p.name)
- for p in managed_processes.values() if p.proc]
- cloudlog.debug(' '.join(running_list))
+ running = ' '.join(["%s%s\u001b[0m" % ("\u001b[32m" if p.proc.is_alive() else "\u001b[31m", p.name)
+ for p in managed_processes.values() if p.proc])
+ print(running)
+ cloudlog.debug(running)
# send managerState
msg = messaging.new_message('managerState')
diff --git a/selfdrive/swaglog.py b/selfdrive/swaglog.py
index 08122b0ca4..a987bfe72c 100644
--- a/selfdrive/swaglog.py
+++ b/selfdrive/swaglog.py
@@ -106,7 +106,17 @@ def add_file_handler(log):
cloudlog = log = SwagLogger()
log.setLevel(logging.DEBUG)
+
outhandler = logging.StreamHandler()
+
+print_level = os.environ.get('LOGPRINT', 'warning')
+if print_level == 'debug':
+ outhandler.setLevel(logging.DEBUG)
+elif print_level == 'info':
+ outhandler.setLevel(logging.INFO)
+elif print_level == 'warning':
+ outhandler.setLevel(logging.WARNING)
+
log.addHandler(outhandler)
# logs are sent through IPC before writing to disk to prevent disk I/O blocking
log.addHandler(UnixDomainSocketHandler(SwagFormatter(log)))
diff --git a/selfdrive/test/test_models.py b/selfdrive/test/test_models.py
index 9e9fa6a196..faba823a0e 100755
--- a/selfdrive/test/test_models.py
+++ b/selfdrive/test/test_models.py
@@ -37,7 +37,7 @@ ignore_carstate_check = [
CHRYSLER.PACIFICA_2017_HYBRID,
]
-@parameterized_class(('car_model'), [(car,) for i, car in enumerate(all_known_cars()) if i % NUM_JOBS == JOB_ID])
+@parameterized_class(('car_model'), [(car,) for i, car in enumerate(sorted(all_known_cars())) if i % NUM_JOBS == JOB_ID])
class TestCarModel(unittest.TestCase):
@classmethod
diff --git a/selfdrive/test/test_routes.py b/selfdrive/test/test_routes.py
index 4c4ad9c2fb..7465c8c6fd 100755
--- a/selfdrive/test/test_routes.py
+++ b/selfdrive/test/test_routes.py
@@ -150,6 +150,7 @@ routes = [
TestRoute("2cef8a0b898f331a|2021-03-25--20-13-57", VOLKSWAGEN.TIGUAN_MK2),
TestRoute("a589dcc642fdb10a|2021-06-14--20-54-26", VOLKSWAGEN.TOURAN_MK2),
TestRoute("a459f4556782eba1|2021-09-19--09-48-00", VOLKSWAGEN.TRANSPORTER_T61),
+ TestRoute("0cd0b7f7e31a3853|2021-11-18--00-38-32", VOLKSWAGEN.TROC_MK1),
TestRoute("07667b885add75fd|2021-01-23--19-48-42", VOLKSWAGEN.AUDI_A3_MK3),
TestRoute("6c6b466346192818|2021-06-06--14-17-47", VOLKSWAGEN.AUDI_Q2_MK1),
TestRoute("8f205bdd11bcbb65|2021-03-26--01-00-17", VOLKSWAGEN.SEAT_ATECA_MK1),
diff --git a/selfdrive/thermald/thermald.py b/selfdrive/thermald/thermald.py
index 1f42471455..f68a0accb8 100755
--- a/selfdrive/thermald/thermald.py
+++ b/selfdrive/thermald/thermald.py
@@ -355,10 +355,10 @@ def thermald_thread():
set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", False)
set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", True)
elif dt.days > DAYS_NO_CONNECTIVITY_PROMPT:
- remaining_time = str(max(DAYS_NO_CONNECTIVITY_MAX - dt.days, 0))
+ remaining = max(DAYS_NO_CONNECTIVITY_MAX - dt.days, 1)
set_offroad_alert_if_changed("Offroad_UpdateFailed", False)
set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", False)
- set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", True, extra_text=f"{remaining_time} days.")
+ set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", True, extra_text=f"{remaining} day{'' if remaining == 1 else 's'}.")
else:
set_offroad_alert_if_changed("Offroad_UpdateFailed", False)
set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", False)
diff --git a/selfdrive/ui/qt/api.cc b/selfdrive/ui/qt/api.cc
index af959e6cd9..6339884aec 100644
--- a/selfdrive/ui/qt/api.cc
+++ b/selfdrive/ui/qt/api.cc
@@ -90,6 +90,7 @@ void HttpRequest::sendRequest(const QString &requestURL, const HttpRequest::Meth
QNetworkRequest request;
request.setUrl(QUrl(requestURL));
+ request.setRawHeader("User-Agent", getUserAgent().toUtf8());
if (!token.isEmpty()) {
request.setRawHeader(QByteArray("Authorization"), ("JWT " + token).toUtf8());
diff --git a/selfdrive/ui/qt/maps/map.cc b/selfdrive/ui/qt/maps/map.cc
index ecb5c122ae..7bee7f7ca5 100644
--- a/selfdrive/ui/qt/maps/map.cc
+++ b/selfdrive/ui/qt/maps/map.cc
@@ -4,6 +4,7 @@
#include
#include
+#include
#include "selfdrive/common/swaglog.h"
#include "selfdrive/ui/ui.h"
@@ -21,6 +22,8 @@ const float MAX_PITCH = 50;
const float MIN_PITCH = 0;
const float MAP_SCALE = 2;
+const QString ICON_SUFFIX = ".png";
+
MapWindow::MapWindow(const QMapboxGLSettings &settings) :
m_settings(settings), velocity_filter(0, 10, 0.05) {
sm = new SubMaster({"liveLocationKalman", "navInstruction", "navRoute"});
@@ -324,6 +327,7 @@ void MapWindow::offroadTransition(bool offroad) {
}
MapInstructions::MapInstructions(QWidget * parent) : QWidget(parent) {
+ is_rhd = Params().getBool("IsRHD");
QHBoxLayout *main_layout = new QHBoxLayout(this);
main_layout->setContentsMargins(11, 50, 11, 11);
{
@@ -442,10 +446,22 @@ void MapInstructions::updateInstructions(cereal::NavInstruction::Reader instruct
if (!modifier.isEmpty()) {
fn += "_" + modifier;
}
- fn += + ".png";
+ fn += ICON_SUFFIX;
fn = fn.replace(' ', '_');
+ // for rhd, reflect direction and then flip
+ if (is_rhd) {
+ if (fn.contains("left")) {
+ fn.replace(QString("left"), QString("right"));
+ } else if (fn.contains("right")) {
+ fn.replace(QString("right"), QString("left"));
+ }
+ }
+
QPixmap pix(fn);
+ if (is_rhd) {
+ pix = pix.transformed(QTransform().scale(-1, 1));
+ }
icon_01->setPixmap(pix.scaledToWidth(200, Qt::SmoothTransformation));
icon_01->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
icon_01->setVisible(true);
@@ -476,7 +492,7 @@ void MapInstructions::updateInstructions(cereal::NavInstruction::Reader instruct
fn += "turn_straight";
}
- QPixmap pix(fn + ".png");
+ QPixmap pix(fn + ICON_SUFFIX);
auto icon = new QLabel;
int wh = active ? 125 : 75;
icon->setPixmap(pix.scaled(wh, wh, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
diff --git a/selfdrive/ui/qt/maps/map.h b/selfdrive/ui/qt/maps/map.h
index a5e333fdf5..b74f5bf29d 100644
--- a/selfdrive/ui/qt/maps/map.h
+++ b/selfdrive/ui/qt/maps/map.h
@@ -36,6 +36,7 @@ private:
QWidget *lane_widget;
QHBoxLayout *lane_layout;
bool error = false;
+ bool is_rhd = false;
public:
MapInstructions(QWidget * parent=nullptr);
diff --git a/selfdrive/ui/qt/util.cc b/selfdrive/ui/qt/util.cc
index 93bb2b0e79..1661c6c5d0 100644
--- a/selfdrive/ui/qt/util.cc
+++ b/selfdrive/ui/qt/util.cc
@@ -8,12 +8,21 @@
#include "selfdrive/common/swaglog.h"
#include "selfdrive/hardware/hw.h"
+QString getVersion() {
+ static QString version = QString::fromStdString(Params().get("Version"));
+ return version;
+}
+
QString getBrand() {
return Params().getBool("Passive") ? "dashcam" : "openpilot";
}
QString getBrandVersion() {
- return getBrand() + " v" + QString::fromStdString(Params().get("Version")).left(14).trimmed();
+ return getBrand() + " v" + getVersion().left(14).trimmed();
+}
+
+QString getUserAgent() {
+ return "openpilot-" + getVersion();
}
std::optional getDongleId() {
diff --git a/selfdrive/ui/qt/util.h b/selfdrive/ui/qt/util.h
index ad28b51166..588dac704f 100644
--- a/selfdrive/ui/qt/util.h
+++ b/selfdrive/ui/qt/util.h
@@ -9,8 +9,10 @@
#include
#include
+QString getVersion();
QString getBrand();
QString getBrandVersion();
+QString getUserAgent();
std::optional getDongleId();
void configFont(QPainter &p, const QString &family, int size, const QString &style);
void clearLayout(QLayout* layout);
diff --git a/tools/plotjuggler/juggle.py b/tools/plotjuggler/juggle.py
index 3157ff2880..151e4a49c4 100755
--- a/tools/plotjuggler/juggle.py
+++ b/tools/plotjuggler/juggle.py
@@ -116,4 +116,4 @@ if __name__ == "__main__":
if args.stream:
start_juggler(layout=args.layout)
else:
- juggle_route(args.route_name, args.segment_number, args.segment_count, args.qlog, args.can, args.layout)
+ juggle_route(args.route_name.strip(), args.segment_number, args.segment_count, args.qlog, args.can, args.layout)