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)