diff --git a/.gitignore b/.gitignore index f247c5651..5bf9742e9 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,7 @@ openpilot notebooks xx +/selfdrive/tinklad/params +/selfdrive/tinklad/bb_openpilot.cfg +/selfdrive/tinklad/tinklad-cache +/selfdrive/tinklad/bb_openpilot_config.cfg diff --git a/.python-version b/.python-version index 35b46aeac..c1e43e6d4 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -2.7.16 +3.7.3 diff --git a/README.md b/README.md index fe82e2c5c..13788d793 100644 --- a/README.md +++ b/README.md @@ -67,15 +67,15 @@ Supported Cars | ---------------------| ---------------------------| ---------------------| --------| ---------------| -----------------| ---------------|-------------------| | Acura | ILX 2016-18 | AcuraWatch Plus | Yes | Yes | 25mph1| 25mph | Nidec | | Acura | RDX 2016-18 | AcuraWatch Plus | Yes | Yes | 25mph1| 12mph | Nidec | -| Buick3 | Regal 2018 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom7| -| Chevrolet3| Malibu 2017 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom7| -| Chevrolet3| Volt 2017-18 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom7| -| Cadillac3 | ATS 2018 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom7| +| Buick3 | Regal 2018 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom6| +| Chevrolet3| Malibu 2017 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom6| +| Chevrolet3| Volt 2017-18 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom6| +| Cadillac3 | ATS 2018 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom6| | Chrysler | Pacifica 2017-18 | Adaptive Cruise | Yes | Stock | 0mph | 9mph | FCA | | Chrysler | Pacifica Hybrid 2017-18 | Adaptive Cruise | Yes | Stock | 0mph | 9mph | FCA | | Chrysler | Pacifica Hybrid 2019 | Adaptive Cruise | Yes | Stock | 0mph | 39mph | FCA | -| GMC3 | Acadia Denali 2018 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom7| -| Holden3 | Astra 2017 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom7| +| GMC3 | Acadia Denali 2018 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom6| +| Holden3 | Astra 2017 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom6| | Honda | Accord 2018-19 | All | Yes | Stock | 0mph | 3mph | Bosch | | Honda | Accord Hybrid 2018-19 | All | Yes | Stock | 0mph | 3mph | Bosch | | Honda | Civic Sedan/Coupe 2016-18 | Honda Sensing | Yes | Yes | 0mph | 12mph | Nidec | @@ -90,30 +90,32 @@ Supported Cars | Honda | Pilot 2016-18 | Honda Sensing | Yes | Yes | 25mph1| 12mph | Nidec | | Honda | Pilot 2019 | All | Yes | Yes | 25mph1| 12mph | Inverted Nidec | | Honda | Ridgeline 2017-19 | Honda Sensing | Yes | Yes | 25mph1| 12mph | Nidec | -| Hyundai | Santa Fe 2019 | All | Yes | Stock | 0mph | 0mph | Custom6| -| Hyundai | Elantra 2017-19 | SCC + LKAS | Yes | Stock | 19mph | 34mph | Custom6| -| Hyundai | Genesis 2018 | All | Yes | Stock | 19mph | 34mph | Custom6| +| Hyundai | Santa Fe 2019 | All | Yes | Stock | 0mph | 0mph | Custom5| +| Hyundai | Elantra 2017-19 | SCC + LKAS | Yes | Stock | 19mph | 34mph | Custom5| +| Hyundai | Genesis 2018 | All | Yes | Stock | 19mph | 34mph | Custom5| | Jeep | Grand Cherokee 2016-18 | Adaptive Cruise | Yes | Stock | 0mph | 9mph | FCA | | Jeep | Grand Cherokee 2019 | Adaptive Cruise | Yes | Stock | 0mph | 39mph | FCA | -| Kia | Optima 2019 | SCC + LKAS | Yes | Stock | 0mph | 0mph | Custom6| -| Kia | Sorento 2018 | All | Yes | Stock | 0mph | 0mph | Custom6| -| Kia | Stinger 2018 | SCC + LKAS | Yes | Stock | 0mph | 0mph | Custom6| +| Kia | Optima 2019 | SCC + LKAS | Yes | Stock | 0mph | 0mph | Custom5| +| Kia | Sorento 2018 | All | Yes | Stock | 0mph | 0mph | Custom5| +| Kia | Stinger 2018 | SCC + LKAS | Yes | Stock | 0mph | 0mph | Custom5| | Lexus | CT Hybrid 2017-18 | All | Yes | Yes2| 0mph | 0mph | Toyota | +| Lexus | ES 2019 | All | Yes | Yes | 0mph | 0mph | Toyota | | Lexus | ES Hybrid 2019 | All | Yes | Yes | 0mph | 0mph | Toyota | | Lexus | RX Hybrid 2016-19 | All | Yes | Yes2| 0mph | 0mph | Toyota | | Lexus | IS 2017-2019 | All | Yes | Stock | 22mph | 0mph | Toyota | | Lexus | IS Hybrid 2017 | All | Yes | Stock | 0mph | 0mph | Toyota | -| Subaru | Crosstrek 2018 | EyeSight | Yes | Stock | 0mph | 0mph | Custom4| -| Subaru | Impreza 2019 | EyeSight | Yes | Stock | 0mph | 0mph | Custom4| +| Subaru | Crosstrek 2018-19 | EyeSight | Yes | Stock | 0mph | 0mph | Subaru | +| Subaru | Impreza 2019-20 | EyeSight | Yes | Stock | 0mph | 0mph | Subaru | | Toyota | Avalon 2016 | TSS-P | Yes | Yes2| 20mph1| 0mph | Toyota | | Toyota | Avalon 2017-18 | All | Yes | Yes2| 20mph1| 0mph | Toyota | -| Toyota | Camry 2018-19 | All | Yes | Stock | 0mph5 | 0mph | Toyota | -| Toyota | Camry Hybrid 2018-19 | All | Yes | Stock | 0mph5 | 0mph | Toyota | +| Toyota | Camry 2018-19 | All | Yes | Stock | 0mph4 | 0mph | Toyota | +| Toyota | Camry Hybrid 2018-19 | All | Yes | Stock | 0mph4 | 0mph | Toyota | | Toyota | C-HR 2017-19 | All | Yes | Stock | 0mph | 0mph | Toyota | | Toyota | C-HR Hybrid 2017-19 | All | Yes | Stock | 0mph | 0mph | Toyota | | Toyota | Corolla 2017-19 | All | Yes | Yes2| 20mph1| 0mph | Toyota | | Toyota | Corolla 2020 | All | Yes | Yes | 0mph | 0mph | Toyota | | Toyota | Corolla Hatchback 2019 | All | Yes | Yes | 0mph | 0mph | Toyota | +| Toyota | Corolla Hybrid 2019 | All | Yes | Yes | 0mph | 0mph | Toyota | | Toyota | Highlander 2017-19 | All | Yes | Yes2| 0mph | 0mph | Toyota | | Toyota | Highlander Hybrid 2017-19 | All | Yes | Yes2| 0mph | 0mph | Toyota | | Toyota | Prius 2016 | TSS-P | Yes | Yes2| 0mph | 0mph | Toyota | @@ -129,20 +131,19 @@ Supported Cars 1[Comma Pedal](https://community.comma.ai/wiki/index.php/Comma_Pedal) is used to provide stop-and-go capability to some of the openpilot-supported cars that don't currently support stop-and-go. Here is how to [build a Comma Pedal](https://medium.com/@jfrux/comma-pedal-building-with-macrofab-6328bea791e8). ***NOTE: The Comma Pedal is not officially supported by [comma.ai](https://comma.ai).***
2When disconnecting the Driver Support Unit (DSU), otherwise longitudinal control is stock ACC. For DSU locations, see [Toyota Wiki page](https://community.comma.ai/wiki/index.php/Toyota). ***NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).***
3[GM installation guide](https://zoneos.com/volt/). ***NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).***
-4Subaru Giraffe is DIY.
-528mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control.
-6Open sourced [Hyundai Giraffe](https://github.com/commaai/neo/tree/master/giraffe/hyundai) is designed for the 2019 Sante Fe; pinout may differ for other Hyundais.
-7Community built Giraffe, find more information [here](https://zoneos.com/shop/).
+428mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control.
+5Open sourced [Hyundai Giraffe](https://github.com/commaai/neo/tree/master/giraffe/hyundai) is designed for the 2019 Sante Fe; pinout may differ for other Hyundais.
+6Community built Giraffe, find more information [here](https://zoneos.com/shop/).
Community Maintained Cars ------ | Make | Model | Supported Package | Lateral | Longitudinal | No Accel Below | No Steer Below | Giraffe | | ---------------------| -------------------------| ---------------------| --------| ---------------| -----------------| ---------------|-------------------| -| Tesla | Model S 2012-13 | All | Yes | Not yet | Not applicable | 0mph | Custom8| +| Tesla | Model S 2012-13 | All | Yes | Not yet | Not applicable | 0mph | Custom7| [[Tesla Model S Pull Request]](https://github.com/commaai/openpilot/pull/246)
-8Community built Giraffe, find more information here [Community Tesla Giraffe](https://github.com/jeankalud/neo/tree/tesla_giraffe/giraffe/tesla)
+7Community built Giraffe, find more information here [Community Tesla Giraffe](https://github.com/jeankalud/neo/tree/tesla_giraffe/giraffe/tesla)
Community Maintained Cars are not confirmed by comma.ai to meet our [safety model](https://github.com/commaai/openpilot/blob/devel/SAFETY.md). Be extra cautious using them. diff --git a/cereal/tinkla.capnp b/cereal/tinkla.capnp index 9e71cb789..ab35b430b 100644 --- a/cereal/tinkla.capnp +++ b/cereal/tinkla.capnp @@ -7,7 +7,7 @@ $Java.outerClassname("Tinkla"); @0xfc8dda643156b95d; -const interfaceVersion :Float32 = 2.2; +const interfaceVersion :Float32 = 3; # Use integer values here struct Interface { diff --git a/common/dbc.py b/common/dbc.py index e65ee38be..aa52e4067 100755 --- a/common/dbc.py +++ b/common/dbc.py @@ -101,13 +101,8 @@ def __init__(self, fn): defvals = defvals.replace("?",r"\?") #escape sequence in C++ defvals = defvals.split('"')[:-1] - defs = defvals[1::2] - #cleanup, convert to UPPER_CASE_WITH_UNDERSCORES - for i,d in enumerate(defs): - d = defs[i].strip().upper() - defs[i] = d.replace(" ","_") - - defvals[1::2] = defs + # convert strings to UPPER_CASE_WITH_UNDERSCORES + defvals[1::2] = [d.strip().upper().replace(" ","_") for d in defvals[1::2]] defvals = '"'+"".join(str(i) for i in defvals)+'"' self.def_vals[ids].append((sgname, defvals)) @@ -152,22 +147,20 @@ def encode(self, msg_id, dd): ival = dd.get(s.name) if ival is not None: - b2 = s.size - if s.is_little_endian: - b1 = s.start_bit - else: - b1 = (s.start_bit // 8) * 8 + (-s.start_bit - 1) % 8 - bo = 64 - (b1 + s.size) - ival = (ival / s.factor) - s.offset ival = int(round(ival)) if s.is_signed and ival < 0: - ival = (1 << b2) + ival + ival = (1 << s.size) + ival + + if s.is_little_endian: + shift = s.start_bit + else: + b1 = (s.start_bit // 8) * 8 + (-s.start_bit - 1) % 8 + shift = 64 - (b1 + s.size) - shift = b1 if s.is_little_endian else bo - mask = ((1 << b2) - 1) << shift - dat = (ival & ((1 << b2) - 1)) << shift + mask = ((1 << s.size) - 1) << shift + dat = (ival & ((1 << s.size) - 1)) << shift if s.is_little_endian: mask = self.reverse_bytes(mask) @@ -227,30 +220,24 @@ def decode(self, x, arr=None, debug=False): factor = s[5] offset = s[6] - b2 = signal_size - if little_endian: - b1 = start_bit - else: - b1 = (start_bit // 8) * 8 + (-start_bit - 1) % 8 - bo = 64 - (b1 + signal_size) - if little_endian: if le is None: le = struct.unpack("Q", st)[0] - shift_amount = bo tmp = be + b1 = (start_bit // 8) * 8 + (-start_bit - 1) % 8 + shift_amount = 64 - (b1 + signal_size) if shift_amount < 0: continue - tmp = (tmp >> shift_amount) & ((1 << b2) - 1) - if signed and (tmp >> (b2 - 1)): - tmp -= (1 << b2) + tmp = (tmp >> shift_amount) & ((1 << signal_size) - 1) + if signed and (tmp >> (signal_size - 1)): + tmp -= (1 << signal_size) tmp = tmp * factor + offset diff --git a/common/params.py b/common/params.py index 7397347db..b2c7618e5 100755 --- a/common/params.py +++ b/common/params.py @@ -81,6 +81,7 @@ class UnknownKeyName(Exception): "Passive": [TxType.PERSISTENT], "RecordFront": [TxType.PERSISTENT], "ReleaseNotes": [TxType.PERSISTENT], + "SafetyModelLock": [TxType.PERSISTENT], "ShouldDoUpdate": [TxType.CLEAR_ON_MANAGER_START], "SpeedLimitOffset": [TxType.PERSISTENT], "SubscriberInfo": [TxType.PERSISTENT], diff --git a/opendbc/generator/toyota/lexus_is_2018_pt.dbc b/opendbc/generator/toyota/lexus_is_2018_pt.dbc index e1aff7c4f..ffcd949f0 100644 --- a/opendbc/generator/toyota/lexus_is_2018_pt.dbc +++ b/opendbc/generator/toyota/lexus_is_2018_pt.dbc @@ -11,7 +11,7 @@ BO_ 705 GAS_PEDAL: 8 XXX SG_ GAS_PEDAL : 55|8@0+ (0.005,0) [0|1] "" XXX BO_ 608 STEER_TORQUE_SENSOR: 8 XXX - SG_ STEER_TORQUE_EPS : 47|16@0- (0.66,0) [-20000|20000] "" XXX + SG_ STEER_TORQUE_EPS : 47|16@0- (0.77,0) [-20000|20000] "" XXX SG_ STEER_TORQUE_DRIVER : 15|16@0- (1,0) [-32768|32767] "" XXX SG_ STEER_OVERRIDE : 0|1@0+ (1,0) [0|1] "" XXX SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX diff --git a/opendbc/honda_accord_touring_2016_can.dbc b/opendbc/honda_accord_touring_2016_can.dbc index e8f388de0..744e55a9b 100644 --- a/opendbc/honda_accord_touring_2016_can.dbc +++ b/opendbc/honda_accord_touring_2016_can.dbc @@ -177,14 +177,14 @@ BO_ 597 ROUGH_WHEEL_SPEED: 8 VSA SG_ SET_TO_X55_2 : 47|8@0+ (1,0) [0|255] "" NEO SG_ LONG_COUNTER : 55|8@0+ (1,0) [0|255] "" XXX SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - SG_ CHECKSUM : 59|4@1+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX BO_ 660 SCM_COMMANDS: 8 SCM SG_ RIGHT_BLINKER : 6|1@0+ (1,0) [0|1] "" NEO SG_ LEFT_BLINKER : 5|1@0+ (1,0) [0|1] "" NEO SG_ WIPERS_SPEED : 4|2@0+ (1,0) [0|3] "" NEO SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" XXX - SG_ CHECKSUM : 59|4@1+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" XXX BO_ 661 XXX_10: 4 XXX SG_ COUNTER : 29|2@0+ (1,0) [0|3] "" XXX diff --git a/opendbc/lexus_is_2018_pt_generated.dbc b/opendbc/lexus_is_2018_pt_generated.dbc index 9e4eb5145..ac6bdf383 100644 --- a/opendbc/lexus_is_2018_pt_generated.dbc +++ b/opendbc/lexus_is_2018_pt_generated.dbc @@ -370,7 +370,7 @@ BO_ 705 GAS_PEDAL: 8 XXX SG_ GAS_PEDAL : 55|8@0+ (0.005,0) [0|1] "" XXX BO_ 608 STEER_TORQUE_SENSOR: 8 XXX - SG_ STEER_TORQUE_EPS : 47|16@0- (0.66,0) [-20000|20000] "" XXX + SG_ STEER_TORQUE_EPS : 47|16@0- (0.77,0) [-20000|20000] "" XXX SG_ STEER_TORQUE_DRIVER : 15|16@0- (1,0) [-32768|32767] "" XXX SG_ STEER_OVERRIDE : 0|1@0+ (1,0) [0|1] "" XXX SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX diff --git a/panda/board/drivers/can.h b/panda/board/drivers/can.h index 528f07f1d..c45a3fe8d 100644 --- a/panda/board/drivers/can.h +++ b/panda/board/drivers/can.h @@ -43,7 +43,7 @@ int can_live = 0, pending_can_live = 0, can_loopback = 0, can_silent = ALL_CAN_S CAN_FIFOMailBox_TypeDef elems_##x[size]; \ can_ring can_##x = { .w_ptr = 0, .r_ptr = 0, .fifo_size = size, .elems = (CAN_FIFOMailBox_TypeDef *)&elems_##x }; -can_buffer(rx_q, 0x1500) +can_buffer(rx_q, 0x1000) can_buffer(tx1_q, 0x100) can_buffer(tx2_q, 0x100) can_buffer(tx3_q, 0x100) diff --git a/panda/tests/pedal/enter_canloader.py b/panda/tests/pedal/enter_canloader.py index 8d263a372..f76ac9597 100755 --- a/panda/tests/pedal/enter_canloader.py +++ b/panda/tests/pedal/enter_canloader.py @@ -25,7 +25,7 @@ def _handle_timeout(signum, frame): finally: signal.alarm(0) - #print "R:",ret.encode("hex") + # "R:",ret.encode("hex") return ret def controlWrite(self, request_type, request, value, index, data, timeout=0): @@ -58,7 +58,7 @@ def bulkRead(self, endpoint, length, timeout=0): while 1: if len(p.can_recv()) == 0: break - print "entering bootloader mode" + print("entering bootloader mode") if args.recover: p.can_send(0x200, b"\xce\xfa\xad\xde\x1e\x0b\xb0\x02", 0) p.can_send(0x551, b"\xce\xfa\xad\xde\x1e\x0b\xb0\x02", 0) diff --git a/phonelibs/json11/json11.o b/phonelibs/json11/json11.o index 516adf373..160a55256 100644 Binary files a/phonelibs/json11/json11.o and b/phonelibs/json11/json11.o differ diff --git a/selfdrive/boardd/boardd.cc b/selfdrive/boardd/boardd.cc index 24e01e552..a703fdf4c 100644 --- a/selfdrive/boardd/boardd.cc +++ b/selfdrive/boardd/boardd.cc @@ -49,6 +49,7 @@ const uint32_t NO_IGNITION_CNT_MAX = 2 * 60 * 60 * 24 * 3; // turn off charge a uint32_t no_ignition_cnt = 0; bool connected_once = false; uint8_t ignition_last = 0; +bool safety_model_locked = false; pthread_t safety_setter_thread_handle = -1; pthread_t pigeon_thread_handle = -1; @@ -74,12 +75,12 @@ void *safety_setter_thread(void *s) { } LOGW("got CarVin %s", value_vin); - pthread_mutex_lock(&usb_lock); - // VIN query done, stop listening to OBDII - libusb_control_transfer(dev_handle, 0x40, 0xdc, (uint16_t)(cereal::CarParams::SafetyModel::NO_OUTPUT), 0, NULL, 0, TIMEOUT); - - pthread_mutex_unlock(&usb_lock); + if (!safety_model_locked) { + pthread_mutex_lock(&usb_lock); + libusb_control_transfer(dev_handle, 0x40, 0xdc, (uint16_t)(cereal::CarParams::SafetyModel::NO_OUTPUT), 0, NULL, 0, TIMEOUT); + pthread_mutex_unlock(&usb_lock); + } char *value; size_t value_sz = 0; @@ -125,6 +126,11 @@ void *safety_setter_thread(void *s) { bool usb_connect() { int err; unsigned char hw_query[1] = {0}; + char *value_safety_model; + size_t value_safety_model_sz = 0; + int safety_model; + const int result = read_db_value(NULL, "SafetyModelLock", &value_safety_model, &value_safety_model_sz); + ignition_last = 0; dev_handle = libusb_open_device_with_vid_pid(ctx, 0xbbaa, 0xddcc); @@ -140,6 +146,16 @@ bool usb_connect() { libusb_control_transfer(dev_handle, 0xc0, 0xe5, 1, 0, NULL, 0, TIMEOUT); } + // check if safety mode is forced (needed to support gm) + if (value_safety_model_sz > 0) { + sscanf(value_safety_model, "%d", &safety_model); + // sanity check that we are not setting all output + assert(safety_model != (int)(cereal::CarParams::SafetyModel::ALL_OUTPUT)); + safety_model_locked = true; + LOGW("Setting Locked Safety Model %s", value_safety_model); + libusb_control_transfer(dev_handle, 0x40, 0xdc, (uint16_t)(cereal::CarParams::SafetyModel(safety_model)), 0, NULL, 0, TIMEOUT); + } + // power off ESP libusb_control_transfer(dev_handle, 0xc0, 0xd9, 0, 0, NULL, 0, TIMEOUT); @@ -297,10 +313,11 @@ void can_health(void *s) { assert((result == 0) || (result == ERR_NO_VALUE)); // diagnostic only is the default, needed for VIN query - pthread_mutex_lock(&usb_lock); - libusb_control_transfer(dev_handle, 0x40, 0xdc, (uint16_t)(cereal::CarParams::SafetyModel::ELM327), 0, NULL, 0, TIMEOUT); - pthread_mutex_unlock(&usb_lock); - + if (!safety_model_locked) { + pthread_mutex_lock(&usb_lock); + libusb_control_transfer(dev_handle, 0x40, 0xdc, (uint16_t)(cereal::CarParams::SafetyModel::ELM327), 0, NULL, 0, TIMEOUT); + pthread_mutex_unlock(&usb_lock); + } if (safety_setter_thread_handle == -1) { err = pthread_create(&safety_setter_thread_handle, NULL, safety_setter_thread, NULL); assert(err == 0); diff --git a/selfdrive/can/can_define.py b/selfdrive/can/can_define.py index d6ffc5c66..eb708869f 100644 --- a/selfdrive/can/can_define.py +++ b/selfdrive/can/can_define.py @@ -35,5 +35,5 @@ def __init__(self, dbc_name): self.dv[msgname] = {} # two ways to lookup: address or msg name - self.dv[address][sgname] = {v: d for v, d in zip(values, defs)} #build dict + self.dv[address][sgname] = dict(zip(values, defs)) self.dv[msgname][sgname] = self.dv[address][sgname] diff --git a/selfdrive/can/common.h b/selfdrive/can/common.h index 4f0972284..d6c2e4760 100644 --- a/selfdrive/can/common.h +++ b/selfdrive/can/common.h @@ -7,11 +7,13 @@ #define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0])) - unsigned int honda_checksum(unsigned int address, uint64_t d, int l); unsigned int toyota_checksum(unsigned int address, uint64_t d, int l); unsigned int pedal_checksum(unsigned int address, uint64_t d, int l); +void init_crc_lookup_tables(); +unsigned int volkswagen_crc(unsigned int address, uint64_t d, int l); + struct SignalPackValue { const char* name; double value; @@ -44,6 +46,8 @@ enum SignalType { TOYOTA_CHECKSUM, PEDAL_CHECKSUM, PEDAL_COUNTER, + VOLKSWAGEN_CHECKSUM, + VOLKSWAGEN_COUNTER, }; struct Signal { diff --git a/selfdrive/can/dbc_template.cc b/selfdrive/can/dbc_template.cc index 776403b22..be249012e 100644 --- a/selfdrive/can/dbc_template.cc +++ b/selfdrive/can/dbc_template.cc @@ -31,6 +31,10 @@ const Signal sigs_{{address}}[] = { .type = SignalType::PEDAL_CHECKSUM, {% elif address in [512, 513] and sig.name == "COUNTER_PEDAL" %} .type = SignalType::PEDAL_COUNTER, + {% elif checksum_type == "volkswagen" and sig.name == "CHECKSUM" %} + .type = SignalType::VOLKSWAGEN_CHECKSUM, + {% elif checksum_type == "volkswagen" and sig.name == "COUNTER" %} + .type = SignalType::VOLKSWAGEN_COUNTER, {% else %} .type = SignalType::DEFAULT, {% endif %} diff --git a/selfdrive/can/libdbc_py.py b/selfdrive/can/libdbc_py.py index fbf36a3d4..36713eab0 100644 --- a/selfdrive/can/libdbc_py.py +++ b/selfdrive/can/libdbc_py.py @@ -41,6 +41,8 @@ TOYOTA_CHECKSUM, PEDAL_CHECKSUM, PEDAL_COUNTER, + VOLKSWAGEN_CHECKSUM, + VOLKSWAGEN_COUNTER, } SignalType; typedef struct { diff --git a/selfdrive/can/packer.cc b/selfdrive/can/packer.cc index d0e841978..88fca77da 100644 --- a/selfdrive/can/packer.cc +++ b/selfdrive/can/packer.cc @@ -51,6 +51,8 @@ namespace { signal_lookup[std::make_pair(msg->address, std::string(sig->name))] = *sig; } } + + init_crc_lookup_tables(); } uint64_t pack(uint32_t address, const std::vector &signals, int counter) { @@ -82,23 +84,30 @@ namespace { } auto sig = sig_it->second; - if (sig.type != SignalType::HONDA_COUNTER){ + if ((sig.type != SignalType::HONDA_COUNTER) && (sig.type != SignalType::VOLKSWAGEN_COUNTER)) { WARN("COUNTER signal type not valid\n"); } ret = set_value(ret, sig, counter); } - auto sig_it = signal_lookup.find(std::make_pair(address, "CHECKSUM")); - if (sig_it != signal_lookup.end()) { - auto sig = sig_it->second; - if (sig.type == SignalType::HONDA_CHECKSUM){ + auto sig_it_checksum = signal_lookup.find(std::make_pair(address, "CHECKSUM")); + if (sig_it_checksum != signal_lookup.end()) { + auto sig = sig_it_checksum->second; + if (sig.type == SignalType::HONDA_CHECKSUM) { unsigned int chksm = honda_checksum(address, ret, message_lookup[address].size); ret = set_value(ret, sig, chksm); } - else if (sig.type == SignalType::TOYOTA_CHECKSUM){ + else if (sig.type == SignalType::TOYOTA_CHECKSUM) { unsigned int chksm = toyota_checksum(address, ret, message_lookup[address].size); ret = set_value(ret, sig, chksm); + } + else if (sig.type == SignalType::VOLKSWAGEN_CHECKSUM) { + // FIXME: Hackish fix for an endianness issue. The message is in reverse byte order + // until later in the pack process. Checksums can be run backwards, CRCs not so much. + // The correct fix is unclear but this works for the moment. + unsigned int chksm = volkswagen_crc(address, ReverseBytes(ret), message_lookup[address].size); + ret = set_value(ret, sig, chksm); } else { //WARN("CHECKSUM signal type not valid\n"); } diff --git a/selfdrive/can/packer_impl.pyx b/selfdrive/can/packer_impl.pyx index 7429338ac..d7c6119b7 100644 --- a/selfdrive/can/packer_impl.pyx +++ b/selfdrive/can/packer_impl.pyx @@ -20,7 +20,9 @@ ctypedef enum SignalType: HONDA_COUNTER, TOYOTA_CHECKSUM, PEDAL_CHECKSUM, - PEDAL_COUNTER + PEDAL_COUNTER, + VOLKSWAGEN_CHECKSUM, + VOLKSWAGEN_COUNTER cdef struct Signal: const char* name diff --git a/selfdrive/can/parser.cc b/selfdrive/can/parser.cc index 6b9cf5758..d925a0efb 100644 --- a/selfdrive/can/parser.cc +++ b/selfdrive/can/parser.cc @@ -24,9 +24,11 @@ // #define DEBUG printf #define INFO printf - #define MAX_BAD_COUNTER 5 +// Static lookup table for fast computation of CRC8 poly 0x2F, aka 8H2F/AUTOSAR +uint8_t crc8_lut_8h2f[256]; + unsigned int honda_checksum(unsigned int address, uint64_t d, int l) { d >>= ((8-l)*8); // remove padding d >>= 4; // remove checksum @@ -75,6 +77,98 @@ unsigned int pedal_checksum(unsigned int address, uint64_t d, int l) { return crc; } +void gen_crc_lookup_table(uint8_t poly, uint8_t crc_lut[]) +{ + uint8_t crc; + int i, j; + + for (i = 0; i < 256; i++) { + crc = i; + for (j = 0; j < 8; j++) { + if ((crc & 0x80) != 0) + crc = (uint8_t)((crc << 1) ^ poly); + else + crc <<= 1; + } + crc_lut[i] = crc; + } +} + +void init_crc_lookup_tables() +{ + // At init time, set up static lookup tables for fast CRC computation. + + gen_crc_lookup_table(0x2F, crc8_lut_8h2f); // CRC-8 8H2F/AUTOSAR for Volkswagen +} + +unsigned int volkswagen_crc(unsigned int address, uint64_t d, int l) +{ + // Volkswagen uses standard CRC8 8H2F/AUTOSAR, but they compute it with + // a magic variable padding byte tacked onto the end of the payload. + // https://www.autosar.org/fileadmin/user_upload/standards/classic/4-3/AUTOSAR_SWS_CRCLibrary.pdf + + uint8_t *dat = (uint8_t *)&d; + uint8_t crc = 0xFF; // Standard init value for CRC8 8H2F/AUTOSAR + + // CRC the payload first, skipping over the first byte where the CRC lives. + for (int i = 1; i < l; i++) { + crc ^= dat[i]; + crc = crc8_lut_8h2f[crc]; + } + + // Look up and apply the magic final CRC padding byte, which permutes by CAN + // address, and additionally (for SOME addresses) by the message counter. + uint8_t counter = dat[1] & 0x0F; + switch(address) { + case 0x86: // LWI_01 Steering Angle + crc ^= (uint8_t[]){0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86}[counter]; + break; + case 0x9F: // EPS_01 Electric Power Steering + crc ^= (uint8_t[]){0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5}[counter]; + break; + case 0xAD: // Getriebe_11 Automatic Gearbox + crc ^= (uint8_t[]){0x3F,0x69,0x39,0xDC,0x94,0xF9,0x14,0x64,0xD8,0x6A,0x34,0xCE,0xA2,0x55,0xB5,0x2C}[counter]; + break; + case 0xFD: // ESP_21 Electronic Stability Program + crc ^= (uint8_t[]){0xB4,0xEF,0xF8,0x49,0x1E,0xE5,0xC2,0xC0,0x97,0x19,0x3C,0xC9,0xF1,0x98,0xD6,0x61}[counter]; + break; + case 0x106: // ESP_05 Electronic Stability Program + crc ^= (uint8_t[]){0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07}[counter]; + break; + case 0x117: // ACC_10 Automatic Cruise Control + crc ^= (uint8_t[]){0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC}[counter]; + break; + case 0x122: // ACC_06 Automatic Cruise Control + crc ^= (uint8_t[]){0x37,0x7D,0xF3,0xA9,0x18,0x46,0x6D,0x4D,0x3D,0x71,0x92,0x9C,0xE5,0x32,0x10,0xB9}[counter]; + break; + case 0x126: // HCA_01 Heading Control Assist + crc ^= (uint8_t[]){0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA}[counter]; + break; + case 0x12B: // GRA_ACC_01 Steering wheel controls for ACC + crc ^= (uint8_t[]){0x6A,0x38,0xB4,0x27,0x22,0xEF,0xE1,0xBB,0xF8,0x80,0x84,0x49,0xC7,0x9E,0x1E,0x2B}[counter]; + break; + case 0x187: // EV_Gearshift "Gear" selection data for EVs with no gearbox + crc ^= (uint8_t[]){0x7F,0xED,0x17,0xC2,0x7C,0xEB,0x44,0x21,0x01,0xFA,0xDB,0x15,0x4A,0x6B,0x23,0x05}[counter]; + break; + case 0x30C: // ACC_02 Automatic Cruise Control + crc ^= (uint8_t[]){0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F}[counter]; + break; + case 0x3C0: // Klemmen_Status_01 ignition and starting status + crc ^= (uint8_t[]){0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3}[counter]; + break; + case 0x65D: // ESP_20 Electronic Stability Program + crc ^= (uint8_t[]){0xAC,0xB3,0xAB,0xEB,0x7A,0xE1,0x3B,0xF7,0x73,0xBA,0x7C,0x9E,0x06,0x5F,0x02,0xD9}[counter]; + break; + default: // As-yet undefined CAN message, CRC check expected to fail + INFO("Attempt to CRC check undefined Volkswagen message 0x%02X\n", address); + crc ^= (uint8_t[]){0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}[counter]; + break; + } + crc = crc8_lut_8h2f[crc]; + + return crc ^ 0xFF; // Return after standard final XOR for CRC8 8H2F/AUTOSAR +} + namespace { uint64_t read_u64_be(const uint8_t* v) { @@ -129,16 +223,11 @@ struct MessageState { tmp -= (tmp >> (sig.b2-1)) ? (1ULL << sig.b2) : 0; //signed } - // Testing both little and big endian signals (Tesla messages) - //if ( (address == 0x318) || (address == 0x118)) { - // INFO("parse %X %s -> %f, dat -> %lX\n", address, sig.name, tmp * sig.factor + sig.offset, dat); - //} - - DEBUG("parse %X %s -> %lld\n", address, sig.name, tmp); + DEBUG("parse 0x%X %s -> %lld\n", address, sig.name, tmp); if (sig.type == SignalType::HONDA_CHECKSUM) { if (honda_checksum(address, dat, size) != tmp) { - INFO("%X CHECKSUM FAIL\n", address); + INFO("0x%X CHECKSUM FAIL\n", address); return false; } } else if (sig.type == SignalType::HONDA_COUNTER) { @@ -147,12 +236,21 @@ struct MessageState { } } else if (sig.type == SignalType::TOYOTA_CHECKSUM) { if (toyota_checksum(address, dat, size) != tmp) { - INFO("%X CHECKSUM FAIL\n", address); + INFO("0x%X CHECKSUM FAIL\n", address); + return false; + } + } else if (sig.type == SignalType::VOLKSWAGEN_CHECKSUM) { + if (volkswagen_crc(address, dat, size) != tmp) { + INFO("0x%X CRC FAIL\n", address); + return false; + } + } else if (sig.type == SignalType::VOLKSWAGEN_COUNTER) { + if (!update_counter_generic(tmp, sig.b2)) { return false; } } else if (sig.type == SignalType::PEDAL_CHECKSUM) { if (pedal_checksum(address, dat, size) != tmp) { - INFO("%X PEDAL CHECKSUM FAIL\n", address); + INFO("0x%X PEDAL CHECKSUM FAIL\n", address); return false; } } else if (sig.type == SignalType::PEDAL_COUNTER) { @@ -176,7 +274,7 @@ struct MessageState { if (((old_counter+1) & ((1 << cnt_size) -1)) != v) { counter_fail += 1; if (counter_fail > 1) { - INFO("%X COUNTER FAIL %d -- %d vs %d\n", address, counter_fail, old_counter, (int)v); + INFO("0x%X COUNTER FAIL %d -- %d vs %d\n", address, counter_fail, old_counter, (int)v); } if (counter_fail >= MAX_BAD_COUNTER) { return false; @@ -228,7 +326,9 @@ class CANParser { } dbc = dbc_lookup(dbc_name); - assert(dbc); + assert(dbc); + init_crc_lookup_tables(); + for (const auto& op : options) { MessageState state = { .address = op.address, diff --git a/selfdrive/can/parser_pyx.pxd b/selfdrive/can/parser_pyx.pxd index 28048bbc1..4d7879e1c 100644 --- a/selfdrive/can/parser_pyx.pxd +++ b/selfdrive/can/parser_pyx.pxd @@ -14,7 +14,9 @@ ctypedef enum SignalType: HONDA_COUNTER, TOYOTA_CHECKSUM, PEDAL_CHECKSUM, - PEDAL_COUNTER + PEDAL_COUNTER, + VOLKSWAGEN_CHECKSUM, + VOLKSWAGEN_COUNTER cdef struct Signal: const char* name diff --git a/selfdrive/can/plant_can_parser.py b/selfdrive/can/plant_can_parser.py index a75435a4c..03aaf10c5 100644 --- a/selfdrive/can/plant_can_parser.py +++ b/selfdrive/can/plant_can_parser.py @@ -21,7 +21,7 @@ def __init__(self, dbc_f, signals, checks=None): # - frequency is the frequency at which health should be monitored. checks = [] if checks is None else checks - self.msgs_ck = set([check[0] for check in checks]) + self.msgs_ck = {check[0] for check in checks} self.frqs = dict(checks) self.can_valid = False # start with False CAN assumption # list of received msg we want to monitor counter and checksum for diff --git a/selfdrive/can/process_dbc.py b/selfdrive/can/process_dbc.py index 187d41358..6da527f78 100755 --- a/selfdrive/can/process_dbc.py +++ b/selfdrive/can/process_dbc.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -from __future__ import print_function import os import glob import sys @@ -39,50 +38,78 @@ def main(): if dbc_mtime < out_mtime and template_mtime < out_mtime and this_file_mtime < out_mtime: continue #skip output is newer than template and dbc - msgs = [(address, msg_name, msg_size, sorted(msg_sigs, key=lambda s: s.name not in (b"COUNTER", b"CHECKSUM"))) # process counter and checksums first + msgs = [(address, msg_name, msg_size, sorted(msg_sigs, key=lambda s: s.name not in ("COUNTER", "CHECKSUM"))) # process counter and checksums first for address, ((msg_name, msg_size), msg_sigs) in sorted(can_dbc.msgs.items()) if msg_sigs] def_vals = {a: set(b) for a,b in can_dbc.def_vals.items()} #remove duplicates - def_vals = [(address, sig) for address, sig in sorted(def_vals.items())] + def_vals = sorted(def_vals.items()) - if can_dbc.name.startswith("honda") or can_dbc.name.startswith("acura"): + if can_dbc.name.startswith(("honda_", "acura_")): checksum_type = "honda" checksum_size = 4 - elif can_dbc.name.startswith("toyota") or can_dbc.name.startswith("lexus"): + counter_size = 2 + checksum_start_bit = 3 + counter_start_bit = 5 + little_endian = False + elif can_dbc.name.startswith(("toyota_", "lexus_")): checksum_type = "toyota" checksum_size = 8 + counter_size = None + checksum_start_bit = 7 + counter_start_bit = None + little_endian = False + elif can_dbc.name.startswith(("vw_", "volkswagen_", "audi_", "seat_", "skoda_")): + checksum_type = "volkswagen" + checksum_size = 8 + counter_size = 4 + checksum_start_bit = 0 + counter_start_bit = 0 + little_endian = True else: checksum_type = None + checksum_size = None + counter_size = None + checksum_start_bit = None + counter_start_bit = None + little_endian = None + # sanity checks on expected COUNTER and CHECKSUM rules, as packer and parser auto-compute those signals for address, msg_name, msg_size, sigs in msgs: + dbc_msg_name = dbc_name + " " + msg_name for sig in sigs: - if checksum_type is not None and sig.name == b"CHECKSUM": - if sig.size != checksum_size: - sys.exit("CHECKSUM is not %d bits longs %s" % (checksum_size, msg_name)) - if checksum_type == "honda" and sig.start_bit % 8 != 3: - sys.exit("CHECKSUM starts at wrong bit %s" % msg_name) - if checksum_type == "toyota" and sig.start_bit % 8 != 7: - sys.exit("CHECKSUM starts at wrong bit %s" % msg_name) - if checksum_type == "honda" and sig.name == b"COUNTER": - if sig.size != 2: - sys.exit("COUNTER is not 2 bits longs %s" % msg_name) - if sig.start_bit % 8 != 5: - sys.exit("COUNTER starts at wrong bit %s" % msg_name) + if checksum_type is not None: + # checksum rules + if sig.name == "CHECKSUM": + if sig.size != checksum_size: + sys.exit("%s: CHECKSUM is not %d bits long" % (dbc_msg_name, checksum_size)) + if sig.start_bit % 8 != checksum_start_bit: + sys.exit("%s: CHECKSUM starts at wrong bit" % dbc_msg_name) + if little_endian != sig.is_little_endian: + sys.exit("%s: CHECKSUM has wrong endianess" % dbc_msg_name) + # counter rules + if sig.name == "COUNTER": + if counter_size is not None and sig.size != counter_size: + sys.exit("%s: COUNTER is not %d bits long" % (dbc_msg_name, counter_size)) + if counter_start_bit is not None and sig.start_bit % 8 != counter_start_bit: + print(counter_start_bit, sig.start_bit) + sys.exit("%s: COUNTER starts at wrong bit" % dbc_msg_name) + if little_endian != sig.is_little_endian: + sys.exit("%s: COUNTER has wrong endianess" % dbc_msg_name) + # pedal rules if address in [0x200, 0x201]: - if sig.name == b"COUNTER_PEDAL" and sig.size != 4: - sys.exit("PEDAL COUNTER is not 4 bits longs %s" % msg_name) - if sig.name == b"CHECKSUM_PEDAL" and sig.size != 8: - sys.exit("PEDAL CHECKSUM is not 8 bits longs %s" % msg_name) + if sig.name == "COUNTER_PEDAL" and sig.size != 4: + sys.exit("%s: PEDAL COUNTER is not 4 bits long" % dbc_msg_name) + if sig.name == "CHECKSUM_PEDAL" and sig.size != 8: + sys.exit("%s: PEDAL CHECKSUM is not 8 bits long" % dbc_msg_name) # Fail on duplicate message names c = Counter([msg_name for address, msg_name, msg_size, sigs in msgs]) for name, count in c.items(): if count > 1: - sys.exit("Duplicate message name in DBC file %s" % name) + sys.exit("%s: Duplicate message name in DBC file %s" % (dbc_name, name)) parser_code = template.render(dbc=can_dbc, checksum_type=checksum_type, msgs=msgs, def_vals=def_vals, len=len) - with open(out_fn, "w") as out_f: out_f.write(parser_code) diff --git a/selfdrive/car/lock_safety_model.py b/selfdrive/car/lock_safety_model.py new file mode 100755 index 000000000..055d8c0c2 --- /dev/null +++ b/selfdrive/car/lock_safety_model.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +import sys +from cereal import car +from common.params import Params + +# This script locks the safety model to a given value. +# When the safety model is locked, boardd will preset panda to the locked safety model + +# run example: +# ./lock_safety_model.py gm + +if __name__ == "__main__": + + params = Params() + + if len(sys.argv) < 2: + params.delete("SafetyModelLock") + print("Clear locked safety model") + + else: + safety_model = getattr(car.CarParams.SafetyModel, sys.argv[1]) + if type(safety_model) != int: + raise Exception("Invalid safety model: " + sys.argv[1]) + if safety_model == car.CarParams.SafetyModel.allOutput: + raise Exception("Locking the safety model to allOutput is not allowed") + params.put("SafetyModelLock", str(safety_model)) + print("Locked safety model: " + sys.argv[1]) diff --git a/selfdrive/car/modules/ALCA_module.py b/selfdrive/car/modules/ALCA_module.py index d12f8f49c..b686126cc 100644 --- a/selfdrive/car/modules/ALCA_module.py +++ b/selfdrive/car/modules/ALCA_module.py @@ -31,6 +31,7 @@ HISTORY ------- +v4.1 - OP 0.6.5 operating model v4.0 - integrated into model_parser.py v3.6 - moved parameters to carstate.py v3.5 - changing the start angle to keep turning until we reach MAX_ANGLE_DELTA @@ -56,73 +57,90 @@ WAIT_TIME_AFTER_TURN = 2.0 #ALCA -ALCA_line_check_low_limit = 0.25 -ALCA_line_check_high_limit = 0.75 ALCA_line_min_prob = 0.01 ALCA_release_distance = 0.3 -ALCA_line_prob_low = 0.1 +ALCA_line_prob_low = 0.2 ALCA_line_prob_high = 0.4 -ALCA_zero_line = 2.5 #meters in front where to check for distance vs line +ALCA_distance_jump = 1.1 +ALCA_lane_change_coefficient = 0.8 +ITERATIONS_AHEAD_TO_ESTIMATE = 4 +ALCA_duration_seconds = 5. +ALCA_right_lane_multiplier = 1.1 -ALCA_DEBUG = True +ALCA_DEBUG = False DEBUG_INFO = "step {step} of {total_steps}: direction = {ALCA_direction} | using visual = {ALCA_use_visual} | over line = {ALCA_over_line} | lane width = {ALCA_lane_width} | left to move = {left_to_move} | from center = {from_center} | C2 offset = {ALCA_OFFSET_C2} | C1 offset = {ALCA_OFFSET_C1} | Prob Low = {prob_low} | Prob High = {prob_high}" + class ALCAController(): def __init__(self,carcontroller,alcaEnabled,steerByAngle): #import settings + self.frame = 0 self.CC = carcontroller # added to start, will see if we need it actually # variables for lane change self.angle_offset = 0. #added when one needs to compensate for missalignment self.alcaEnabled = alcaEnabled - self.alca_duration = [2., 3.5, 5.] - self.laneChange_strStartFactor = 2. - self.laneChange_strStartMultiplier = 1.5 - self.laneChange_steerByAngle = steerByAngle # steer only by angle; do not call PID - self.laneChange_last_actuator_angle = 0. - self.laneChange_last_actuator_delta = 0. - self.laneChange_last_sent_angle = 0. self.laneChange_over_the_line = 0 # did we cross the line? - self.laneChange_avg_angle = 0. # used if we do average entry angle over x frames - self.laneChange_avg_count = 0. # used if we do average entry angle over x frames self.laneChange_enabled = 1 # set to zero for no lane change self.laneChange_counter = 0 # used to count frames during lane change - self.laneChange_min_duration = 2. # min time to wait before looking for next lane - self.laneChange_duration = 5.6 # how many max seconds to actually do the move; if lane not found after this then send error - self.laneChange_after_lane_duration_mult = 1. # multiplier for time after we cross the line before we let OP take over; multiplied with CL_TIMEA_T self.laneChange_wait = 1 # how many seconds to wait before it starts the change - self.laneChange_lw = 3.7 # lane width in meters - self.laneChange_angle = 0. # saves the last angle from actuators before lane change starts - self.laneChange_angled = 0. # angle delta - self.laneChange_steerr = 15.75 # steer ratio for lane change starting with the Tesla one self.laneChange_direction = 0 # direction of the lane change self.prev_right_blinker_on = False # local variable for prev position self.prev_left_blinker_on = False # local variable for prev position - self.keep_angle = False #local variable to keep certain angle delta vs. actuator - self.last10delta = [] self.laneChange_cancelled = False self.laneChange_cancelled_counter = 0 - self.last_time_enabled = 0 + self.alcaStatusSocket = messaging.pub_sock(service_list['alcaStatus'].port) + + def debug_alca(self,message): + if ALCA_DEBUG: + print (message) + + def send_status(self,CS): + CS.ALCA_enabled = (self.laneChange_enabled > 1) and self.alcaEnabled + CS.ALCA_total_steps = int(20 * ALCA_duration_seconds) + if self.laneChange_enabled == 3: + CS.ALCA_direction = -self.laneChange_direction + else: + CS.ALCA_direction = 0 + if not (self.frame % 20 == 0): + return + alca_status = tesla.ALCAStatus.new_message() + alca_status.alcaEnabled = bool(CS.ALCA_enabled) + alca_status.alcaTotalSteps = int(CS.ALCA_total_steps) + alca_status.alcaDirection = int(CS.ALCA_direction) + alca_status.alcaError = bool(CS.ALCA_error) + self.alcaStatusSocket.send(alca_status.to_bytes()) def update_status(self,alcaEnabled): self.alcaEnabled = alcaEnabled - def stop_ALCA(self, CS): + def stop_ALCA(self, CS, isDone): # something is not right; ALCAModelParser is not engaged; cancel - CS.UE.custom_alert_message(3,"Auto Lane Change Canceled! (d)",200,5) - self.laneChange_cancelled = True - self.laneChange_cancelled_counter = 200 + self.debug_alca("ALCA canceled: stop_ALCA called") + if not isDone: + CS.UE.custom_alert_message(3,"Auto Lane Change Canceled! (d)",200,5) + self.laneChange_cancelled = True + self.laneChange_cancelled_counter = 200 + else: + self.laneChange_cancelled = False + self.laneChange_cancelled_counter = 0 self.laneChange_enabled = 1 self.laneChange_counter = 0 self.laneChange_direction = 0 CS.cstm_btns.set_button_status("alca",1) + self.send_status(CS) - def update(self,enabled,CS,actuators): + def update(self,enabled,CS,actuators,alcaStateData,frame): cl_min_v = CS.CL_MIN_V cl_max_a = CS.CL_MAX_A - alca_mode = CS.cstm_btns.get_button_label2_index("alca") + self.frame = frame + + if (alcaStateData is not None) and (((self.laneChange_direction != 0) and alcaStateData.alcaError) or (alcaStateData.alcaDirection == 100)): + self.debug_alca("ALCA canceled: stop_ALCA called (1)") + self.stop_ALCA(CS,alcaStateData.alcaDirection == 100) + return 0, False if self.laneChange_cancelled_counter > 0: self.laneChange_cancelled_counter -= 1 @@ -131,11 +149,11 @@ def update(self,enabled,CS,actuators): # Basic highway lane change logic actuator_delta = 0. - laneChange_angle = 0. turn_signal_needed = 0 # send 1 for left, 2 for right 0 for not needed if (not CS.right_blinker_on) and (not CS.left_blinker_on) and \ (self.laneChange_enabled == 4): + self.debug_alca("ALCA reset: resetting 4 -> 1") self.laneChange_enabled =1 self.laneChange_counter =0 self.laneChange_direction =0 @@ -158,49 +176,48 @@ def update(self,enabled,CS,actuators): else: CS.cstm_btns.set_button_status("alca",1) - if self.alcaEnabled and enabled and (((not self.prev_right_blinker_on) and CS.right_blinker_on) or \ - ((not self.prev_left_blinker_on) and CS.left_blinker_on)) and \ + if self.alcaEnabled and enabled and (self.laneChange_enabled > 1) and \ ((CS.v_ego < cl_min_v) or (abs(actuators.steerAngle) >= cl_max_a) or (abs(CS.angle_steers) >=cl_max_a)): # something is not right, the speed or angle is limitting - CS.UE.custom_alert_message(3,"Auto Lane Change Unavailable!",500,3) + self.debug_alca("ALCA Unavailable (2)") + CS.UE.custom_alert_message(3,"Auto Lane Change Unavailable!",200,3) CS.cstm_btns.set_button_status("alca",9) - + self.stop_ALCA(CS, False) + return 0, False if self.alcaEnabled and enabled and (((not self.prev_right_blinker_on) and CS.right_blinker_on) or \ ((not self.prev_left_blinker_on) and CS.left_blinker_on)) and \ - (CS.v_ego >= cl_min_v) and (abs(actuators.steerAngle) < cl_max_a): + (CS.v_ego >= cl_min_v) and (abs(actuators.steerAngle) < cl_max_a) and (self.laneChange_enabled == 1): # start blinker, speed and angle is within limits, let's go laneChange_direction = 1 # changing lanes if CS.left_blinker_on: laneChange_direction = -1 + self.debug_alca("ALCA blinker on detected") - if (self.laneChange_enabled > 1) and (self.laneChange_direction != laneChange_direction): - # something is not right; signal in oposite direction; cancel - CS.UE.custom_alert_message(3,"Auto Lane Change Canceled! (s)",200,5) - self.laneChange_cancelled = True - self.laneChange_cancelled_counter = 200 - self.laneChange_enabled = 1 - self.laneChange_counter = 0 - self.laneChange_direction = 0 - CS.cstm_btns.set_button_status("alca",1) - elif (self.laneChange_enabled == 1) : - # compute angle delta for lane change - CS.UE.custom_alert_message(2,"Auto Lane Change Engaged!",100) - self.laneChange_enabled = 2 - self.laneChange_counter = 1 - self.laneChange_direction = laneChange_direction - CS.cstm_btns.set_button_status("alca",2) + CS.UE.custom_alert_message(2,"Auto Lane Change Engaged!",100) + self.debug_alca("ALCA engaged") + self.laneChange_enabled = 2 + self.laneChange_counter = 1 + self.laneChange_direction = laneChange_direction + CS.cstm_btns.set_button_status("alca",2) + + self.prev_right_blinker_on = CS.right_blinker_on + self.prev_left_blinker_on = CS.left_blinker_on if (not self.alcaEnabled) and self.laneChange_enabled > 1: + self.debug_alca("ALCA canceled: not enabled") self.laneChange_enabled = 1 self.laneChange_counter = 0 self.laneChange_direction = 0 + self.stop_ALCA(CS, False) + return 0, False # lane change in progress if self.laneChange_enabled > 1: if (CS.steer_override or (CS.v_ego < cl_min_v)): CS.UE.custom_alert_message(4,"Auto Lane Change Canceled! (u)",200,3) + self.debug_alca("ALCA canceled: steer override") self.laneChange_cancelled = True self.laneChange_cancelled_counter = 200 # if any steer override cancel process or if speed less than min speed @@ -208,33 +225,34 @@ def update(self,enabled,CS,actuators): self.laneChange_enabled = 1 self.laneChange_direction = 0 CS.cstm_btns.set_button_status("alca",1) + self.stop_ALCA(CS, False) + return 0, False if self.laneChange_enabled == 2: if self.laneChange_counter == 1: CS.UE.custom_alert_message(2,"Auto Lane Change Engaged! (1)",self.laneChange_wait * 100) self.laneChange_counter += 1 + self.debug_alca("ALCA phase 2: " + str(self.laneChange_counter)) if self.laneChange_counter == self.laneChange_wait * 100: self.laneChange_enabled = 3 self.laneChange_counter = 0 if self.laneChange_enabled ==3: if self.laneChange_counter == 1: - CS.UE.custom_alert_message(2,"Auto Lane Change Engaged! (2)",int(self.alca_duration[alca_mode] * 100)) + CS.UE.custom_alert_message(2,"Auto Lane Change Engaged! (2)",int(ALCA_duration_seconds * 100)) self.laneChange_counter += 1 - if self.laneChange_counter >= self.alca_duration[alca_mode] * 100: + self.debug_alca("ALCA phase 3: " + str(self.laneChange_counter)) + if self.laneChange_counter >= ALCA_duration_seconds * 100.: self.laneChange_enabled = 4 self.laneChange_counter = 0 if self.laneChange_enabled == 4: + self.debug_alca("ALCA phase 4: " +str(self.laneChange_counter)) if self.laneChange_counter == 1: CS.UE.custom_alert_message(2,"Auto Lane Change Complete!",100) self.laneChange_enabled = 1 self.laneChange_counter = 0 + self.stop_ALCA(CS, True) + return 0, False - CS.ALCA_enabled = (self.laneChange_enabled > 1) and self.alcaEnabled - CS.ALCA_total_steps = int(20 * self.alca_duration[alca_mode]) - if self.laneChange_enabled == 3: - CS.ALCA_direction = -self.laneChange_direction - else: - CS.ALCA_direction = 0 - + self.send_status(CS) return turn_signal_needed, self.laneChange_enabled > 1 class ALCAModelParser(): @@ -242,9 +260,9 @@ def __init__(self): #ALCA params self.ALCA_error = False self.ALCA_lane_width = 3.6 - self.ALCA_direction = 0 # left 1, right -1 + self.ALCA_direction = 100 #none 0, left 1, right -1,reset 100 self.ALCA_step = 0 - self.ALCA_total_steps = 20 * 5 #20 Hz, 5 seconds, wifey mode + self.ALCA_total_steps = 20 * ALCA_duration_seconds #20 Hz, 5 seconds, wifey mode self.ALCA_cancelling = False self.ALCA_enabled = False self.ALCA_OFFSET_C3 = 0. @@ -267,18 +285,18 @@ def __init__(self): self.prev_distance_to_line_R = 100. - def reset_alca (self): + def reset_alca (self,v_ego): self.ALCA_step = 0 - self.ALCA_direction = 0 + self.ALCA_direction = 100 self.ALCA_cancelling = False - self.ALCA_error = True + self.ALCA_error = False self.ALCA_enabled = False self.ALCA_OFFSET_C3 = 0. self.ALCA_OFFSET_C2 = 0. self.ALCA_OFFSET_C1 = 0. self.ALCA_over_line = False self.ALCA_use_visual = True - self.ALCA_vego_prev = 0. + self.ALCA_vego_prev = v_ego self.alcas = None self.hit_prob_low = False self.hit_prob_high = False @@ -286,6 +304,7 @@ def reset_alca (self): self.prev_distance_to_line_L = 100. self.distance_to_line_R = 100. self.prev_distance_to_line_R = 100. + self.send_state() def debug_alca(self,message): if ALCA_DEBUG: @@ -309,126 +328,141 @@ def update(self, v_ego, md, r_poly, l_poly, r_prob, l_prob, lane_width, p_poly): if socket is self.alcaStatus: self.alcas = tesla.ALCAStatus.from_bytes(socket.recv()) - #if we don't have yet ALCA status, return same values if self.alcas is None: self.send_state() return np.array(r_poly),np.array(l_poly),r_prob, l_prob, lane_width, p_poly - - self.ALCA_direction = self.alcas.alcaDirection - self.ALCA_enabled = self.alcas.alcaEnabled - self.ALCA_total_steps = self.alcas.alcaTotalSteps - self.ALCA_error = self.ALCA_error or (self.alcas.alcaError and not self.prev_CS_ALCA_error) - self.prev_CS_ALCA_error = self.alcas.alcaError + if self.alcas.alcaDirection == 0: + self.ALCA_direction = 0 - if not self.ALCA_enabled: - self.send_state() - return np.array(r_poly),np.array(l_poly),r_prob, l_prob, lane_width, p_poly + if self.ALCA_direction < 100: + self.ALCA_direction = self.alcas.alcaDirection + + self.ALCA_enabled = self.alcas.alcaEnabled + + self.ALCA_total_steps = self.alcas.alcaTotalSteps + self.ALCA_error = self.ALCA_error or (self.alcas.alcaError and not self.prev_CS_ALCA_error) + self.prev_CS_ALCA_error = self.alcas.alcaError + + if self.ALCA_enabled: + if self.ALCA_direction == 0: + self.ALCA_lane_width = lane_width + else: + lane_width = self.ALCA_lane_width #if error but no direction, the carcontroller component is fine and we need to reset if self.ALCA_error and (self.ALCA_direction == 0): self.ALCA_error = False - self.hit_prob_low = (self.ALCA_direction != 0) and (self.hit_prob_low or ((self.ALCA_direction == 1) and (l_prob < ALCA_line_prob_low)) or ((self.ALCA_direction == -1) and (r_prob < ALCA_line_prob_low))) + if (not self.ALCA_enabled) or (self.ALCA_direction == 100) or (self.ALCA_direction == 0): + self.ALCA_vego_prev = v_ego + self.send_state() + return np.array(r_poly),np.array(l_poly),r_prob, l_prob, lane_width,np.array(p_poly) + + self.hit_prob_low = self.hit_prob_low and ((self.ALCA_direction != 0) and (self.hit_prob_low or ((self.ALCA_direction == 1) and (l_prob < ALCA_line_prob_low)) or ((self.ALCA_direction == -1) and (r_prob < ALCA_line_prob_low)))) self.hit_prob_high = (self.ALCA_direction != 0) and (self.hit_prob_low) and (self.hit_prob_high or ((self.ALCA_direction == 1) and (r_prob > ALCA_line_prob_high)) or ((self.ALCA_direction == -1) and (l_prob < ALCA_line_prob_high))) if self.hit_prob_high: self.debug_alca("Hit high probability for line, ALCA done, releasing...") - self.reset_alca() + self.reset_alca(v_ego) + return np.array(r_poly),np.array(l_poly),r_prob, l_prob, lane_width,np.array(p_poly) #where are we in alca as % ALCA_perc_complete = float(self.ALCA_step) / float(self.ALCA_total_steps) if self.ALCA_error and self.ALCA_cancelling: self.debug_alca(" Error and Cancelling -> resetting...") - self.reset_alca() + self.reset_alca(v_ego) + return np.array(r_poly),np.array(l_poly),r_prob, l_prob, lane_width,np.array(p_poly) if self.ALCA_error and not self.ALCA_cancelling: if (ALCA_perc_complete < 0.1) or (ALCA_perc_complete > 0.9): self.debug_alca(" Error and less than 10% -> resetting...") - self.reset_alca() + self.reset_alca(v_ego) + return np.array(r_poly),np.array(l_poly),r_prob, l_prob, lane_width,np.array(p_poly) else: self.debug_alca(" Error and not Cancelling -> rewinding...") self.ALCA_cancelling = True self.ALCA_error = False - if self.ALCA_enabled and not (self.ALCA_direction == 0): - self.ALCA_step += 1 #ALCA_increment - if (self.ALCA_step < 0) or (self.ALCA_step >= self.ALCA_total_steps): - #done so end ALCA - self.debug_alca(" step out of bounds -> resetting...") - self.reset_alca() - - #compute offset - from_center = 0. - left_to_move = 0. - if self.ALCA_enabled and not (self.ALCA_direction == 0): - #compute distances to lines - self.distance_to_line_L = abs(np.polyval(l_poly,ALCA_zero_line)) - self.distance_to_line_R = abs(np.polyval(r_poly,ALCA_zero_line)) - if ((self.ALCA_direction == 1) and ((self.distance_to_line_L > 1.1 * self.prev_distance_to_line_L) or (self.distance_to_line_R < self.ALCA_lane_width / 3.))) or \ - ((self.ALCA_direction == -1) and ((self.distance_to_line_R > 1.1 * self.prev_distance_to_line_R) or (self.distance_to_line_L < self.ALCA_lane_width / 3.))): - self.ALCA_over_line = True - left_to_move = self.ALCA_lane_width / 5. - if self.ALCA_over_line: - left_to_move = self.ALCA_lane_width / 2. - (self.distance_to_line_L if self.ALCA_direction == 1 else self.distance_to_line_R) - if left_to_move < ALCA_release_distance: - self.reset_alca() - self.send_state() - return np.array(r_poly),np.array(l_poly),r_prob, l_prob, lane_width, p_poly - distance_left = float((self.ALCA_total_steps) * 0.05 * (self.ALCA_vego_prev + v_ego) / 2.) #5m + distance left - d1 = np.polyval(p_poly,distance_left) - d2 = np.polyval(p_poly,distance_left + 1) - cos = 1. - if abs(d2 - d1) > 0.1: - cos = abs(np.cos(np.arctan(1/abs(d2-d1)))) - ltm = cos * left_to_move - #compute offsets - self.ALCA_OFFSET_C1 = 0. - self.ALCA_OFFSET_C2 = float(self.ALCA_direction * ltm) / (distance_left ) - self.prev_distance_to_line_R = self.distance_to_line_R - self.prev_distance_to_line_L = self.distance_to_line_L - if ALCA_DEBUG: - debug_string = DEBUG_INFO.format(step=self.ALCA_step,total_steps=self.ALCA_total_steps,ALCA_direction=self.ALCA_direction,ALCA_use_visual=self.ALCA_use_visual,ALCA_over_line=self.ALCA_over_line,ALCA_lane_width=self.ALCA_lane_width, left_to_move=left_to_move, from_center=from_center, ALCA_OFFSET_C2=self.ALCA_OFFSET_C2, ALCA_OFFSET_C1=self.ALCA_OFFSET_C1,prob_low=self.hit_prob_low,prob_high=self.hit_prob_high) - self.debug_alca(debug_string) - else: - self.ALCA_OFFSET_C2 = 0. - self.ALCA_OFFSET_C1 = 0. - - - if (not self.ALCA_error) and self.ALCA_use_visual: - if self.ALCA_over_line: - if (self.ALCA_total_steps - self.ALCA_step <= 1) or (self.ALCA_over_line and ((self.ALCA_direction == 1) and (r_poly[3] < -ALCA_release_distance)) or ((self.ALCA_direction == -1) and (l_poly[3] > ALCA_release_distance))): - self.reset_alca() - self.ALCA_error = False - - if l_prob > r_prob: - r_poly = np.array(l_poly) - if l_prob > ALCA_line_prob_low: - l_prob = 1 - r_prob = l_prob - else: - l_poly = np.array(r_poly) - if r_prob > ALCA_line_prob_low: - r_prob = 1 - l_prob = r_prob - l_poly[3] = self.ALCA_lane_width / 2 - r_poly[3] = -self.ALCA_lane_width / 2 - l_poly[2] += self.ALCA_OFFSET_C2 - r_poly[2] += self.ALCA_OFFSET_C2 - l_poly[1] += self.ALCA_OFFSET_C1 - r_poly[1] += self.ALCA_OFFSET_C1 + self.ALCA_step += 1 #ALCA_increment + + + if (self.ALCA_step < 0) or (self.ALCA_step >= self.ALCA_total_steps): + #done so end ALCA + self.debug_alca(" step out of bounds -> resetting...") + self.reset_alca(v_ego) + return np.array(r_poly),np.array(l_poly),r_prob, l_prob, lane_width,np.array(p_poly) + + #compute offset + from_center = 0. + left_to_move = 0. + + #compute distances to lines + self.distance_to_line_L = abs(l_poly[3]) + self.distance_to_line_R = abs(r_poly[3]) + distance_left = float((self.ALCA_total_steps) * 0.05 * (self.ALCA_vego_prev + v_ego) / 2.) #5m + distance left + distance_estimate = float(ITERATIONS_AHEAD_TO_ESTIMATE * 0.05 * (self.ALCA_vego_prev + v_ego) / 2.) + estimate_curv_at = distance_estimate / distance_left + left_to_move = self.ALCA_lane_width * estimate_curv_at + # if ((self.ALCA_direction == 1) and ((self.distance_to_line_L > ALCA_distance_jump * self.prev_distance_to_line_L) or (self.distance_to_line_R < self.ALCA_lane_width / 3.))) or \ + # ((self.ALCA_direction == -1) and ((self.distance_to_line_R > ALCA_distance_jump * self.prev_distance_to_line_R) or (self.distance_to_line_L < self.ALCA_lane_width / 3.))): + # self.ALCA_over_line = True + if ((self.ALCA_direction == 1) and (self.distance_to_line_R < self.ALCA_lane_width / 3.)) or \ + ((self.ALCA_direction == -1) and (self.distance_to_line_L < self.ALCA_lane_width / 3.)): + self.ALCA_over_line = True + left_to_move = self.ALCA_lane_width * estimate_curv_at + if self.ALCA_over_line: + left_to_move2 = (self.distance_to_line_L if self.ALCA_direction == 1 else self.distance_to_line_R) - self.ALCA_lane_width / 2. + if left_to_move2 < ALCA_release_distance: + self.reset_alca(v_ego) + return np.array(r_poly),np.array(l_poly),r_prob, l_prob, lane_width, p_poly + + d1 = np.polyval(p_poly,distance_estimate -1) + d2 = np.polyval(p_poly,distance_estimate + 1) + cos = 0. + if abs(d2 - d1) > 0.001: + #cos = abs(np.cos(np.arctan(2/abs(d2-d1)))) + cos = np.cos(np.arctan(2/(d2-d1))) + d0 = (d2 + d1) / 2.0 - np.polyval(p_poly,distance_estimate) + ltm = left_to_move # * (1 - cos * self.ALCA_direction) + #compute offsets + self.ALCA_OFFSET_C1 = 0. + lane_multiplier = 1. if self.ALCA_direction == 1 else ALCA_right_lane_multiplier + self.ALCA_OFFSET_C2 = lane_multiplier * ALCA_lane_change_coefficient * float(self.ALCA_direction * ltm) / (distance_estimate ) + self.prev_distance_to_line_R = self.distance_to_line_R + self.prev_distance_to_line_L = self.distance_to_line_L + if ALCA_DEBUG: + debug_string = DEBUG_INFO.format(step=self.ALCA_step,total_steps=self.ALCA_total_steps,ALCA_direction=self.ALCA_direction,ALCA_use_visual=self.ALCA_use_visual,ALCA_over_line=self.ALCA_over_line,ALCA_lane_width=self.ALCA_lane_width, left_to_move=left_to_move/estimate_curv_at, from_center=from_center, ALCA_OFFSET_C2=self.ALCA_OFFSET_C2, ALCA_OFFSET_C1=self.ALCA_OFFSET_C1,prob_low=self.hit_prob_low,prob_high=self.hit_prob_high) + self.debug_alca(debug_string) + + if (not self.ALCA_error) and self.ALCA_use_visual: + if self.ALCA_over_line: + if (self.ALCA_total_steps - self.ALCA_step <= 1) or (self.ALCA_over_line and ((self.ALCA_direction == 1) and (r_poly[3] < -ALCA_release_distance)) or ((self.ALCA_direction == -1) and (l_poly[3] > ALCA_release_distance))): + self.ALCA_error = False + self.reset_alca(v_ego) + return np.array(r_poly),np.array(l_poly),r_prob, l_prob, lane_width, p_poly + + if l_prob > r_prob: + r_poly = np.array(l_poly) + if l_prob > ALCA_line_prob_low: + l_prob = 1 + r_prob = l_prob else: - self.reset_alca() - self.ALCA_error = False + l_poly = np.array(r_poly) + if r_prob > ALCA_line_prob_low: + r_prob = 1 + l_prob = r_prob + l_poly[3] = self.ALCA_lane_width / 2 + r_poly[3] = -self.ALCA_lane_width / 2 + l_poly[2] += self.ALCA_OFFSET_C2 + r_poly[2] += self.ALCA_OFFSET_C2 + l_poly[1] += self.ALCA_OFFSET_C1 + r_poly[1] += self.ALCA_OFFSET_C1 + p_poly[3] = 0 + p_poly[2] += self.ALCA_OFFSET_C2 + p_poly[1] += self.ALCA_OFFSET_C1 self.ALCA_vego_prev = v_ego - - if self.ALCA_enabled: - if self.ALCA_direction == 0: - self.ALCA_lane_width = lane_width - else: - lane_width = self.ALCA_lane_width - self.send_state() - return np.array(r_poly),np.array(l_poly),r_prob, l_prob, self.ALCA_lane_width, p_poly + return np.array(r_poly),np.array(l_poly),r_prob, l_prob, self.ALCA_lane_width, np.array(p_poly) diff --git a/selfdrive/car/subaru/interface.py b/selfdrive/car/subaru/interface.py index 0fa12cf7d..e2195b00a 100644 --- a/selfdrive/car/subaru/interface.py +++ b/selfdrive/car/subaru/interface.py @@ -3,9 +3,9 @@ from selfdrive.config import Conversions as CV from selfdrive.controls.lib.drive_helpers import create_event, EventTypes as ET from selfdrive.controls.lib.vehicle_model import VehicleModel -from selfdrive.car.subaru.values import CAR, FINGERPRINTS, ECU_FINGERPRINT, ECU +from selfdrive.car.subaru.values import CAR from selfdrive.car.subaru.carstate import CarState, get_powertrain_can_parser, get_camera_can_parser -from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, is_ecu_disconnected +from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint from selfdrive.car.interfaces import CarInterfaceBase ButtonType = car.CarState.ButtonEvent.Type @@ -48,7 +48,9 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), vin="", has_relay ret.enableCruise = True ret.steerLimitAlert = True - ret.enableCamera = is_ecu_disconnected(fingerprint[0], FINGERPRINTS, ECU_FINGERPRINT, candidate, ECU.CAM) or has_relay + # force openpilot to fake the stock camera, since car harness is not supported yet and old style giraffe (with switches) + # was never released + ret.enableCamera = True ret.steerRateCost = 0.7 diff --git a/selfdrive/car/tesla/ACC_module.py b/selfdrive/car/tesla/ACC_module.py index 4445a5173..3b76968bf 100644 --- a/selfdrive/car/tesla/ACC_module.py +++ b/selfdrive/car/tesla/ACC_module.py @@ -357,7 +357,7 @@ def _calc_follow_button(self, CS, lead_car,speed_limit_kph, speed_limit_valid, s print ("Ratio: {0:.1f}% lead: {1:.1f}m avail: {2:.1f}kph vRel: {3:.1f}kph Angle: {4:.1f}deg".format( ratio, lead_dist_m, available_speed_kph, lead_car.vRel * CV.MS_TO_KPH, CS.angle_steers)) self.last_update_time = current_time_ms - if msg != None: + if msg is not None: print ("ACC: " + msg) return button @@ -371,7 +371,7 @@ def _should_autoengage_cc(self, CS, lead_car=None): and CS.v_ego >= self.MIN_CRUISE_SPEED_MS and _current_time_millis() > self.fast_decel_time + 2000) - slow_lead = lead_car and lead_car.dRel > 0 and lead_car.vRel < 0 or self._fast_decel_required(CS, lead_car) + slow_lead = lead_car and lead_car.dRel > 0 and lead_car.vRel < 0 or self._fast_decel_required(CS, lead_car) # pylint: disable=chained-comparison # "Autoresume" mode allows cruise to engage even after brake events, but # shouldn't trigger DURING braking. diff --git a/selfdrive/car/tesla/HSO_module.py b/selfdrive/car/tesla/HSO_module.py index fba539b5f..daa209351 100644 --- a/selfdrive/car/tesla/HSO_module.py +++ b/selfdrive/car/tesla/HSO_module.py @@ -1,7 +1,6 @@ #human steer override module import time -MAX_TIME_BLINKER_ON = 150 # in 0.01 seconds TIME_REMEMBER_LAST_BLINKER = 50 # in 0.01 seconds def _current_time_millis(): @@ -9,7 +8,6 @@ def _current_time_millis(): class HSOController(): def __init__(self,carcontroller): - self.CC = carcontroller self.human_control = False self.frame_humanSteered = 0 self.turn_signal_needed = 0 # send 1 for left, 2 for right 0 for not needed @@ -18,6 +16,7 @@ def __init__(self,carcontroller): self.frame_blinker_on = 0 self.last_human_blinker_on = 0 self.frame_human_blinker_on = 0 + self.HSO_frame_blinker_on = 0 def update_stat(self,CC,CS,enabled,actuators,frame): @@ -29,6 +28,10 @@ def update_stat(self,CC,CS,enabled,actuators,frame): elif CS.left_blinker_on: self.blinker_on = 1 self.frame_human_blinker_on = frame + if (self.last_blinker_on == 0) and (self.blinker_on > 0) and (CC.ALCA.laneChange_enabled <= 1): + self.HSO_frame_blinker_on = frame + if (self.last_blinker_on == 0) and (self.blinker_on == 0): + self.HSO_frame_blinker_on = 0 if self.blinker_on > 0: self.frame_blinker_on = frame self.last_human_blinker_on = self.blinker_on @@ -37,24 +40,24 @@ def update_stat(self,CC,CS,enabled,actuators,frame): self.blinker_on = self.last_human_blinker_on else: self.last_human_blinker_on = 0 - if frame - self.frame_human_blinker_on > MAX_TIME_BLINKER_ON: + if frame - self.frame_human_blinker_on > 100 * CS.hsoBlinkerExtender: self.blinker_on = 0 self.turn_signal_needed = 0 self.last_blinker_on = 0 if CS.enableHSO and enabled: #if steering but not by ALCA - if (CS.right_blinker_on or CS.left_blinker_on) and (self.CC.ALCA.laneChange_enabled <= 1):# and (self.last_blinker_on != self.blinker_on): + if (CS.right_blinker_on or CS.left_blinker_on) and (CC.ALCA.laneChange_enabled <= 1):# and (self.last_blinker_on != self.blinker_on): self.frame_humanSteered = frame - if (CS.steer_override > 0) and (frame - self.frame_humanSteered > 50): + if (CS.steer_override > 0): # and (frame - self.frame_humanSteered > 50): #let's try with human touch only self.frame_humanSteered = frame else: - if (frame - self.frame_humanSteered < 50): # Need more human testing of handoff timing + if (CC.ALCA.laneChange_enabled <= 1) and (frame - self.frame_humanSteered < 50): # Need more human testing of handoff timing # Find steering difference between visiond model and human (no need to do every frame if we run out of CPU): steer_current=(CS.angle_steers) # Formula to convert current steering angle to match apply_steer calculated number apply_steer = int(-actuators.steerAngle) angle = abs(apply_steer-steer_current) - if angle > 15.: + if frame < (self.HSO_frame_blinker_on + int(100 * CS.hsoNumbPeriod)) or angle > 15.: self.frame_humanSteered = frame if enabled: if CS.enableHSO: @@ -74,7 +77,7 @@ def update_stat(self,CC,CS,enabled,actuators,frame): CC.DAS_219_lcTempUnavailableSpeed = 0 CC.warningNeeded = 1 self.turn_signal_needed = 0 - if (self.CC.ALCA.laneChange_enabled > 1): + if (CC.ALCA.laneChange_enabled > 1): self.turn_signal_needed = 0 self.blinker_on = 0 self.last_blinker_on = 0 diff --git a/selfdrive/car/tesla/PCC_module.py b/selfdrive/car/tesla/PCC_module.py index aec40a03f..bf286b4ae 100644 --- a/selfdrive/car/tesla/PCC_module.py +++ b/selfdrive/car/tesla/PCC_module.py @@ -15,6 +15,7 @@ from collections import OrderedDict from common.params import Params from selfdrive.car.tesla.movingaverage import MovingAverage +import json _DT = 0.05 # 10Hz in our case, since we don't want to process more than once the same radarState message _DT_MPC = _DT @@ -49,6 +50,8 @@ # Pull the cruise stalk twice in this many ms for a 'double pull' STALK_DOUBLE_PULL_MS = 750 +V_PID_FILE = '/data/params/pidParams' + class Mode(): label = None @@ -177,6 +180,32 @@ def __init__(self,carcontroller): self.params = Params() self.average_speed_over_x_suggestions = 3 #10x a second self.maxsuggestedspeed_avg = MovingAverage(self.average_speed_over_x_suggestions) + + def load_pid(self): + try: + v_pid_json = open(V_PID_FILE) + data = json.load(v_pid_json) + if (self.LoC): + if self.LoC.pid: + self.LoC.pid.p = data['p'] + self.LoC.pid.i = data['i'] + self.LoC.pid.f = data['f'] + else: + print("self.LoC not initialized!") + except IOError: + print("file not present, creating at next reset") + + #Helper function for saving the PCC pid constants across drives + def save_pid(self, pid): + data = {} + data['p'] = pid.p + data['i'] = pid.i + data['f'] = pid.f + try: + with open(V_PID_FILE , 'w') as outfile : + json.dump(data, outfile) + except IOError: + print("PDD pid parameters could not be saved to file") def max_v_by_speed_limit(self,pedal_set_speed_ms ,speed_limit_ms, CS): # if more than 10 kph / 2.78 ms, consider we have speed limit @@ -195,12 +224,19 @@ def max_v_by_speed_limit(self,pedal_set_speed_ms ,speed_limit_ms, CS): return pedal_set_speed_ms def reset(self, v_pid): + #save the pid parameters to params file + self.save_pid(self.LoC.pid) + if self.LoC and RESET_PID_ON_DISENGAGE: self.LoC.reset(v_pid) def update_stat(self, CS, enabled): if not self.LoC: self.LoC = LongControl(CS.CP, tesla_compute_gb) + # Get v_id from the stored file when initiating the LoC and reset_on_disengage==false + if (not RESET_PID_ON_DISENGAGE): + self.load_pid() + can_sends = [] if CS.pedal_interceptor_available and not CS.cstm_btns.get_button_status("pedal"): @@ -252,7 +288,7 @@ def update_stat(self, CS, enabled): if ready and double_pull: # A double pull enables ACC. updating the max ACC speed if necessary. self.enable_pedal_cruise = True - self.LoC.reset(CS.v_ego) + self.reset(CS.v_ego) # Increase PCC speed to match current, if applicable. self.pedal_speed_kph = max(CS.v_ego * CV.MS_TO_KPH, self.speed_limit_kph) # Handle pressing the cancel button. @@ -379,6 +415,7 @@ def update_pdl(self, enabled, CS, frame, actuators, pcm_speed, speed_limit_ms, s #################################################################### if PCCModes.is_selected(FollowMode(), CS.cstm_btns): self.v_pid = self.calc_follow_speed_ms(CS,alca_enabled) + if mapd is not None: v_curve = max_v_in_mapped_curve_ms(mapd.liveMapData, self.pedal_speed_kph) if v_curve: @@ -394,7 +431,7 @@ def update_pdl(self, enabled, CS, frame, actuators, pcm_speed, speed_limit_ms, s self.pedal_state = CS.pedal_interceptor_value > 10 #reset PID if we just lifted foot of accelerator if (not self.pedal_state) and self.prev_pedal_state: - self.LoC.reset(v_pid=CS.v_ego) + self.reset(v_pid=CS.v_ego) if self.enable_pedal_cruise and (not self.pedal_state): jerk_min, jerk_max = _jerk_limits(CS.v_ego, self.lead_1, self.v_pid * CV.MS_TO_KPH, self.lead_last_seen_time_ms, CS) @@ -432,7 +469,7 @@ def update_pdl(self, enabled, CS, frame, actuators, pcm_speed, speed_limit_ms, s output_gb = t_go - t_brake #print ("Output GB Follow:", output_gb) else: - self.LoC.reset(v_pid=CS.v_ego) + self.reset(v_pid=CS.v_ego) #print ("PID reset") output_gb = 0. starting = self.LoC.long_control_state == LongCtrlState.starting @@ -534,7 +571,7 @@ def calc_follow_speed_ms(self, CS, alca_enabled): elif lead_dist_m < MIN_SAFE_DIST_M: new_speed_kph = MIN_PCC_V_KPH # In a 10 meter cruise zone, lets match the car in front - elif lead_dist_m > MIN_SAFE_DIST_M and lead_dist_m < safe_dist_m + 2: # BB we might want to try this and rel_speed_kph > 0: + elif safe_dist_m + 2 > lead_dist_m > MIN_SAFE_DIST_M: # BB we might want to try this and rel_speed_kph > 0: min_vrel_kph_map = OrderedDict([ # (distance in m, min allowed relative kph) (0.5 * safe_dist_m, 3.0), @@ -592,7 +629,7 @@ def calc_follow_speed_ms(self, CS, alca_enabled): # Don't accelerate during manual turns, curves or ALCA. new_speed_kph = min(new_speed_kph, self.last_speed_kph) #BB Last safety check. Zero if below MIN_SAFE_DIST_M - if (lead_dist_m > 0) and (lead_dist_m < MIN_SAFE_DIST_M) and (rel_speed_kph < 3.): + if (MIN_SAFE_DIST_M > lead_dist_m > 0) and (rel_speed_kph < 3.): new_speed_kph = MIN_PCC_V_KPH self.last_speed_kph = new_speed_kph return new_speed_kph * CV.KPH_TO_MS diff --git a/selfdrive/car/tesla/carcontroller.py b/selfdrive/car/tesla/carcontroller.py index e8b63a5b9..c12d70646 100644 --- a/selfdrive/car/tesla/carcontroller.py +++ b/selfdrive/car/tesla/carcontroller.py @@ -4,6 +4,7 @@ from collections import namedtuple from common.numpy_fast import clip, interp from common.realtime import DT_CTRL +from common import realtime from selfdrive.car.tesla import teslacan from selfdrive.car.tesla.values import AH, CM from selfdrive.can.packer import CANPacker @@ -25,10 +26,6 @@ ANGLE_DELTA_BP = [0., 5., 15.] ANGLE_DELTA_V = [5., .8, .25] # windup limit ANGLE_DELTA_VU = [5., 3.5, 0.8] # unwind limit -#steering adjustment with speed -DES_ANGLE_ADJUST_FACTOR_BP = [0.,13., 44.] -DES_ANGLE_ADJUST_FACTOR = [1.0, 1.0, 1.0] - #LDW WARNING LEVELS LDW_WARNING_1 = 0.9 LDW_WARNING_2 = 0.5 @@ -161,11 +158,8 @@ def __init__(self, dbc_name): self.curv2 = 0. self.curv3 = 0. self.visionCurvC0 = 0. - self.laneRange = 50 #max is 160m but OP has issues with precision beyond 50 - self.useZeroC0 = False - self.useMap = False - self.clipC0 = False - self.useMapOnly = False + self.laneRange = 75 #max is 160m but OP has issues with precision beyond 50 + self.laneWidth = 0. self.stopSign_visible = False @@ -196,7 +190,11 @@ def __init__(self, dbc_name): self.prev_ldwStatus = 0 self.radarVin_idx = 0 - + self.LDW_ENABLE_SPEED = 16 + self.should_ldw = False + self.ldw_numb_frame_start = 0 + self.prev_changing_lanes = False + self.isMetric = (self.params.get("IsMetric") == "1") def reset_traffic_events(self): @@ -317,7 +315,25 @@ def update(self, enabled, CS, frame, actuators, \ # Basic highway lane change logic changing_lanes = CS.right_blinker_on or CS.left_blinker_on - + + if (frame % 5 == 0): + if (changing_lanes and not self.prev_changing_lanes): #we have a transition from blinkers off to blinkers on, save the frame + self.ldw_numb_frame_start = frame + if (CS.v_ego > self.LDW_ENABLE_SPEED): + CS.UE.custom_alert_message(3, "LDW Disabled", 150, 4) + + # update the previous state of the blinkers (chaning_lanes if (self.ALCA.laneChange_enabled > 1): + self.ldw_numb_frame_start = frame + self.prev_changing_lanes = changing_lanes + if self.alca_enabled: + self.ldw_numb_frame_start = frame + 200 #we don't want LDW for 2 seconds after ALCA finishes + #Determine if we should have LDW or not + self.should_ldw = (frame > (self.ldw_numb_frame_start + int( 50 * CS.ldwNumbPeriod)) and CS.v_ego > self.LDW_ENABLE_SPEED) + + if self.should_ldw and self.ldw_numb_frame_start != 0: + self.ldw_numb_frame_start = 0 + CS.UE.custom_alert_message(2, "LDW Enabled", 150, 4) + #upodate custom UI buttons and alerts CS.UE.update_custom_ui() @@ -337,11 +353,11 @@ def update(self, enabled, CS, frame, actuators, \ self.speed_limit_offset = 0. if not self.isMetric: self.speed_limit_offset = self.speed_limit_offset * CV.MPH_TO_MS - if CS.useTeslaGPS: + if CS.useTeslaGPS and (frame % 10 == 0): if self.gpsLocationExternal is None: self.gpsLocationExternal = messaging.pub_sock(service_list['gpsLocationExternal'].port) sol = gen_solution(CS) - sol.logMonoTime = int(frame * DT_CTRL * 1e9) + sol.logMonoTime = int(realtime.sec_since_boot() * 1e9) self.gpsLocationExternal.send(sol.to_bytes()) #get pitch/roll/yaw every 0.1 sec @@ -351,7 +367,6 @@ def update(self, enabled, CS, frame, actuators, \ # Update statuses for custom buttons every 0.1 sec. if (frame % 10 == 0): - #self.ALCA.update_status(False) self.ALCA.update_status((CS.cstm_btns.get_button_status("alca") > 0) and ((CS.enableALCA and not CS.hasTeslaIcIntegration) or (CS.hasTeslaIcIntegration and CS.alcaEnabled))) pedal_can_sends = [] @@ -362,8 +377,7 @@ def update(self, enabled, CS, frame, actuators, \ self.ACC.enable_adaptive_cruise = False else: # Update ACC module info. - if frame % 5 == 0: - self.ACC.update_stat(CS, True) + self.ACC.update_stat(CS, True) self.PCC.enable_pedal_cruise = False # Update HSO module info. @@ -380,7 +394,7 @@ def update(self, enabled, CS, frame, actuators, \ else: CS.v_cruise_pcm = max(0.,CS.v_ego * CV.MS_TO_KPH +0.5) * speed_uom_kph # Get the turn signal from ALCA. - turn_signal_needed, self.alca_enabled = self.ALCA.update(enabled, CS, actuators) + turn_signal_needed, self.alca_enabled = self.ALCA.update(enabled, CS, actuators, self.alcaStateData, frame) apply_angle = -actuators.steerAngle # Tesla is reversed vs OP. human_control,turn_signal_needed_hso = self.HSO.update_stat(self,CS, enabled, actuators, frame) if turn_signal_needed == 0: @@ -399,11 +413,8 @@ def update(self, enabled, CS, frame, actuators, \ else: angle_rate_lim = interp(CS.v_ego, ANGLE_DELTA_BP, ANGLE_DELTA_VU) - des_angle_factor = interp(CS.v_ego, DES_ANGLE_ADJUST_FACTOR_BP, DES_ANGLE_ADJUST_FACTOR ) - if self.alca_enabled or not CS.enableSpeedVariableDesAngle: - des_angle_factor = 1. #BB disable limits to test 0.5.8 - # apply_angle = clip(apply_angle * des_angle_factor, self.last_angle - angle_rate_lim, self.last_angle + angle_rate_lim) + # apply_angle = clip(apply_angle , self.last_angle - angle_rate_lim, self.last_angle + angle_rate_lim) # If human control, send the steering angle as read at steering wheel. if human_control: apply_angle = CS.angle_steers @@ -472,46 +483,6 @@ def update(self, enabled, CS, frame, actuators, \ can_messages = self.handleTrafficEvents(trafficEventsSocket = socket) can_sends.extend(can_messages) - if (CS.roadCurvRange > 20) and self.useMap: - if self.useZeroC0: - self.curv0 = 0. - elif self.clipC0: - self.curv0 = -clip(CS.roadCurvC0,-0.5,0.5) - #else: - # self.curv0 = -CS.roadCurvC0 - #if CS.v_ego > 9: - # self.curv1 = -CS.roadCurvC1 - #else: - # self.curv1 = 0. - self.curv2 = -CS.roadCurvC2 - self.curv3 = -CS.roadCurvC3 - self.laneRange = CS.roadCurvRange - #else: - # self.curv0 = 0. - # self.curv1 = 0. - # self.curv2 = 0. - # self.curv3 = 0. - # self.laneRange = 0 - - if (CS.csaRoadCurvRange > 2.) and self.useMap and not self.useMapOnly: - self.curv2 = -CS.csaRoadCurvC2 - self.curv3 = -CS.csaRoadCurvC3 - #if self.laneRange > 0: - # self.laneRange = min(self.laneRange,CS.csaRoadCurvRange) - #else: - self.laneRange = CS.csaRoadCurvRange - elif (CS.csaOfframpCurvRange > 2.) and self.useMap and not self.useMapOnly: - #self.curv2 = -CS.csaOfframpCurvC2 - #self.curv3 = -CS.csaOfframpCurvC3 - #self.curv0 = 0. - #self.curv1 = 0. - #if self.laneRange > 0: - # self.laneRange = min(self.laneRange,CS.csaOfframpCurvRange) - #else: - self.laneRange = CS.csaOfframpCurvRange - else: - self.laneRange = 50 - self.laneRange = int(clip(self.laneRange,0,159)) op_status = 0x02 hands_on_state = 0x00 forward_collision_warning = 0 #1 if needed @@ -726,19 +697,16 @@ def handlePathPlanSocketForCurvatureOnIC(self, pathPlanSocket, alcaStateData, CS self.curv2 = -clip(pp.dPoly[1],-0.0025,0.0025) #self.curv2Matrix.add(-clip(pp.cPoly[1],-0.0025,0.0025)) self.curv3 = -clip(pp.dPoly[0],-0.00003,0.00003) #self.curv3Matrix.add(-clip(pp.cPoly[0],-0.00003,0.00003)) self.laneWidth = pp.laneWidth - self.laneRange = 50 # it is fixed in OP at 50m pp.viewRange self.visionCurvC0 = self.curv0 self.prev_ldwStatus = self.ldwStatus self.ldwStatus = 0 - if (self.ALCA.laneChange_direction != 0) and alcaStateData.alcaError: - self.ALCA.stop_ALCA(CS) - if self.alca_enabled: + if alcaStateData.alcaEnabled: #exagerate position a little during ALCA to make lane change look smoother on IC - if self.ALCA.laneChange_over_the_line: - self.curv0 = self.ALCA.laneChange_direction * self.laneWidth - self.curv0 + self.curv1 = 0.0 #straighten the turn for ALCA + self.curv0 = -self.ALCA.laneChange_direction * alcaStateData.alcaLaneWidth * alcaStateData.alcaStep / alcaStateData.alcaTotalSteps #animas late change on IC self.curv0 = clip(self.curv0, -3.5, 3.5) else: - if CS.enableLdw and (not CS.blinker_on) and (CS.v_ego > 15.6) and (turn_signal_needed == 0): + if self.should_ldw and (CS.enableLdw and (not CS.blinker_on) and (turn_signal_needed == 0)): if pp.lProb > LDW_LANE_PROBAB: lLaneC0 = -pp.lPoly[3] if abs(lLaneC0) < LDW_WARNING_2: diff --git a/selfdrive/car/tesla/carstate.py b/selfdrive/car/tesla/carstate.py index 383176a71..72ac38ea6 100644 --- a/selfdrive/car/tesla/carstate.py +++ b/selfdrive/car/tesla/carstate.py @@ -201,7 +201,7 @@ def __init__(self, CP): self.CL_MIN_V = 8.9 self.CL_MAX_A = 20. # labels for buttons - self.btns_init = [["alca", "ALC", ["MadMax", "Normal", "Calm"]], + self.btns_init = [["alca", "ALC", [""]], [ACCMode.BUTTON_NAME, ACCMode.BUTTON_ABREVIATION, ACCMode.labels()], ["dsp", "DSP", ["OP","MIN","OFF","GYRO"]], ["", "", [""]], @@ -215,9 +215,6 @@ def __init__(self, CP): self.enableALCA = True self.enableDasEmulation = True self.enableRadarEmulation = True - self.enableSpeedVariableDesAngle = False - self.enableRollAngleCorrection = False - self.enableFeedForwardAngleCorrection = True self.enableDriverMonitor = True self.enableShowCar = True self.enableShowLogo = True @@ -240,9 +237,10 @@ def __init__(self, CP): self.radarEpasType = 0 self.fix1916 = False self.forceFingerprintTesla = False - self.eonToFront = 0.1 self.spinnerText = "" - self.enableParamLearner = False + self.hsoNumbPeriod = 1.5 + self.ldwNumbPeriod = 1.5 + self.hsoBlinkerExtender = 1.0 #read config file read_config_file(self) ### END OF MAIN CONFIG OPTIONS ### diff --git a/selfdrive/car/tesla/interface.py b/selfdrive/car/tesla/interface.py index 796e73311..8eaa7ac3f 100644 --- a/selfdrive/car/tesla/interface.py +++ b/selfdrive/car/tesla/interface.py @@ -1,7 +1,7 @@ #!/usr/bin/env python from cereal import car, tesla from common.numpy_fast import clip, interp -from common.realtime import sec_since_boot, DT_CTRL +from common.realtime import DT_CTRL from selfdrive.config import Conversions as CV from selfdrive.controls.lib.drive_helpers import create_event, EventTypes as ET, get_events from selfdrive.controls.lib.vehicle_model import VehicleModel @@ -35,8 +35,6 @@ def __init__(self, CP, CarController): self.gas_pressed_prev = False self.brake_pressed_prev = False self.can_invalid_count = 0 - self.alca = messaging.pub_sock(service_list['alcaStatus'].port) - # *** init the major players *** @@ -217,6 +215,7 @@ def get_params(candidate, fingerprint, vin="", is_panda_black=False): ret.steerLimitAlert = False ret.startAccel = 0.5 ret.steerRateCost = 1.0 + ret.radarOffCan = not CarSettings().get_value("useTeslaRadar") return ret @@ -450,10 +449,10 @@ def update(self, c, can_strings): # NO_ENTRY events, so controlsd will display alerts. Also not send enable events # too close in time, so a no_entry will not be followed by another one. # TODO: button press should be the only thing that triggers enble - if ((cur_time - self.last_enable_pressed) < 0.2 and + if ((cur_time - self.last_enable_pressed) < 0.2 and # pylint: disable=chained-comparison (cur_time - self.last_enable_sent) > 0.2 and ret.cruiseState.enabled) or \ - (enable_pressed and get_events(events, [ET.NO_ENTRY])): + (enable_pressed and get_events(events, [ET.NO_ENTRY])): if ret.seatbeltUnlatched: self.CC.DAS_211_accNoSeatBelt = 1 self.CC.warningCounter = 300 @@ -494,15 +493,6 @@ def update(self, c, can_strings): self.gas_pressed_prev = ret.gasPressed self.brake_pressed_prev = self.CS.brake_pressed != 0 - #pass ALCA status - alca_status = tesla.ALCAStatus.new_message() - - alca_status.alcaEnabled = bool(self.CS.ALCA_enabled) - alca_status.alcaTotalSteps = int(self.CS.ALCA_total_steps) - alca_status.alcaDirection = int(self.CS.ALCA_direction) - alca_status.alcaError = bool(self.CS.ALCA_error) - - self.alca.send(alca_status.to_bytes()) # cast to reader so it can't be modified return ret.as_reader() diff --git a/selfdrive/car/tesla/radar_interface.py b/selfdrive/car/tesla/radar_interface.py index b3111c286..5ce6c6dbe 100644 --- a/selfdrive/car/tesla/radar_interface.py +++ b/selfdrive/car/tesla/radar_interface.py @@ -47,6 +47,7 @@ def _create_radard_can_parser(): checks = list(zip(RADAR_A_MSGS + RADAR_B_MSGS, [6]*(msg_a_n + msg_b_n))) + return CANParser(os.path.splitext(dbc_f)[0].encode('utf8'), signals, checks, 1) @@ -55,6 +56,7 @@ class RadarInterface(RadarInterfaceBase): tinklaClient = TinklaClient() def __init__(self,CP): + super().__init__(self) # radar self.pts = {} self.extPts = {} @@ -82,7 +84,8 @@ def update(self, can_strings): if not self.useTeslaRadar: time.sleep(0.05) return car.RadarData.new_message(),self.extPts.values() - if can_strings != None: + + if can_strings is not None: vls = self.rcp.update_strings(can_strings) self.updated_messages.update(vls) diff --git a/selfdrive/car/tesla/readconfig.py b/selfdrive/car/tesla/readconfig.py index bf89f0b45..50a60cda0 100644 --- a/selfdrive/car/tesla/readconfig.py +++ b/selfdrive/car/tesla/readconfig.py @@ -21,9 +21,11 @@ def read(self, into, config_path): print("no config file, creating with defaults...") main_section = 'OP_CONFIG' + pref_section = 'OP_PREFERENCES' logging_section = 'LOGGING' config = configparser.RawConfigParser(allow_no_value=True) config.add_section(main_section) + config.add_section(pref_section) config.add_section(logging_section) #user_handle -> userHandle @@ -44,15 +46,6 @@ def read(self, into, config_path): ) file_changed |= didUpdate - #eon_to_front -> eonToFront - into.eonToFront, didUpdate = self.read_config_entry( - config, configr, prev_file_contents, section = main_section, - entry = 'eon_to_front', entry_type = float, - default_value = 0.9, - comment = 'Distance between EON plane and front of the car.' - ) - file_changed |= didUpdate - #force_pedal_over_cc -> forcePedalOverCC into.forcePedalOverCC, didUpdate = self.read_config_entry( config, configr, prev_file_contents, section = main_section, @@ -71,7 +64,7 @@ def read(self, into, config_path): ) file_changed |= didUpdate - #enable_das_emulation -> enableDasEmulation + #enable_alca -> enableALCA into.enableALCA, didUpdate = self.read_config_entry( config, configr, prev_file_contents, section = main_section, entry = 'enable_alca', entry_type = bool, @@ -98,33 +91,6 @@ def read(self, into, config_path): ) file_changed |= didUpdate - #enable_roll_angle_correction -> enableRollAngleCorrection - into.enableSpeedVariableDesAngle, didUpdate = self.read_config_entry( - config, configr, prev_file_contents, section = main_section, - entry = 'enable_speed_variable_angle', entry_type = bool, - default_value = True, - comment = '' - ) - file_changed |= didUpdate - - #enable_roll_angle_correction -> enableRollAngleCorrection - into.enableRollAngleCorrection, didUpdate = self.read_config_entry( - config, configr, prev_file_contents, section = main_section, - entry = 'enable_roll_angle_correction', entry_type = bool, - default_value = False, - comment = '' - ) - file_changed |= didUpdate - - #enable_feed_forward_angle_correction -> enableFeedForwardAngleCorrection - into.enableFeedForwardAngleCorrection, didUpdate = self.read_config_entry( - config, configr, prev_file_contents, section = main_section, - entry = 'enable_feed_forward_angle_correction', entry_type = bool, - default_value = True, - comment = '' - ) - file_changed |= didUpdate - #enable_driver_monitor -> enableDriverMonitor into.enableDriverMonitor, didUpdate = self.read_config_entry( config, configr, prev_file_contents, section = main_section, @@ -134,24 +100,6 @@ def read(self, into, config_path): ) file_changed |= didUpdate - #enable_show_car -> enableShowCar - into.enableShowCar, didUpdate = self.read_config_entry( - config, configr, prev_file_contents, section = main_section, - entry = 'enable_show_car', entry_type = bool, - default_value = True, - comment = 'Shows a Tesla car in the limitted UI mode instead of the triangle that identifies the lead car; this is only used if you do not have IC/CID integration' - ) - file_changed |= didUpdate - - #enable_show_logo -> enableShowLogo - into.enableShowLogo, didUpdate = self.read_config_entry( - config, configr, prev_file_contents, section = main_section, - entry = 'enable_show_logo', entry_type = bool, - default_value = True, - comment = 'Shows a Tesla red logo on the EON screen when OP is not enabled' - ) - file_changed |= didUpdate - #has_noctua_fan -> hasNoctuaFan into.hasNoctuaFan, didUpdate = self.read_config_entry( config, configr, prev_file_contents, section = main_section, @@ -264,15 +212,6 @@ def read(self, into, config_path): into.radarVIN = default_radar_vin file_changed = True - #enable_ldw = enableLdw - into.enableLdw, didUpdate = self.read_config_entry( - config, configr, prev_file_contents, section = main_section, - entry = 'enable_ldw', entry_type = bool, - default_value = True, - comment = 'Enable the Lane Departure Warning (LDW) feature; this feature warns the driver is the car gets too close to one of the lines when driving above 45 MPH (72 km/h) without touching the steering wheel and when the turn signal is off' - ) - file_changed |= didUpdate - #radar_offset -> radarOffset into.radarOffset, didUpdate = self.read_config_entry( config, configr, prev_file_contents, section = main_section, @@ -320,19 +259,64 @@ def read(self, into, config_path): #spiner_text -> spinnerText into.spinnerText, didUpdate = self.read_config_entry( - config, configr, prev_file_contents, section = main_section, + config, configr, prev_file_contents, section = pref_section, entry = 'spinner_text', entry_type = str, default_value = '%d', comment = 'The text that is shown for the spinner when spawning the managed services.' ) file_changed |= didUpdate - #enable_param_learner -> enableParamLearner - into.enableParamLearner, didUpdate = self.read_config_entry( - config, configr, prev_file_contents, section = main_section, - entry = 'enable_param_learner', entry_type = bool, - default_value = False, - comment = 'Set this setting to True if you want OP to relearn steering rate automatically or will be using fixed rate when False' + #hso_numb_period -> hsoNumbPeriod + into.hsoNumbPeriod, didUpdate = self.read_config_entry( + config, configr, prev_file_contents, section = pref_section, + entry = 'hso_numb_period', entry_type = float, + default_value = 1.5, + comment = 'Period to delay (in seconds) the reengagement of LKAS after human turn signal has been used. Time starts when the turn signal is turned on.' + ) + file_changed |= didUpdate + + #enable_ldw = enableLdw + into.enableLdw, didUpdate = self.read_config_entry( + config, configr, prev_file_contents, section = pref_section, + entry = 'enable_ldw', entry_type = bool, + default_value = True, + comment = 'Enable the Lane Departure Warning (LDW) feature; this feature warns the driver is the car gets too close to one of the lines when driving above 45 MPH (72 km/h) without touching the steering wheel and when the turn signal is off' + ) + file_changed |= didUpdate + + #ldw_numb_period -> ldwNumbPeriod + into.ldwNumbPeriod, didUpdate = self.read_config_entry( + config, configr, prev_file_contents, section = pref_section, + entry = 'ldw_numb_period', entry_type = float, + default_value = 1.5, + comment = 'Period to delay (in seconds) the LDW warnings after human turn signal has been used. Time starts when the turn signal is turned on.' + ) + file_changed |= didUpdate + + #hso_blinker_extender -> hsoBlinkerExtender + into.hsoBlinkerExtender, didUpdate = self.read_config_entry( + config, configr, prev_file_contents, section = pref_section, + entry = 'hso_blinker_extender', entry_type = float, + default_value = 0.0, + comment = 'Period to keep the blinker on (in seconds). Time starts when the turn signal is turned off. If LKA is reengaged, the signal is turned off automatically.' + ) + file_changed |= didUpdate + + #enable_show_car -> enableShowCar + into.enableShowCar, didUpdate = self.read_config_entry( + config, configr, prev_file_contents, section = pref_section, + entry = 'enable_show_car', entry_type = bool, + default_value = True, + comment = 'Shows a Tesla car in the limitted UI mode instead of the triangle that identifies the lead car; this is only used if you do not have IC/CID integration' + ) + file_changed |= didUpdate + + #enable_show_logo -> enableShowLogo + into.enableShowLogo, didUpdate = self.read_config_entry( + config, configr, prev_file_contents, section = pref_section, + entry = 'enable_show_logo', entry_type = bool, + default_value = True, + comment = 'Shows a Tesla red logo on the EON screen when OP is not enabled' ) file_changed |= didUpdate @@ -391,15 +375,11 @@ class CarSettings(): userHandle = None forceFingerprintTesla = None - eonToFront = None forcePedalOverCC = None enableHSO = None enableALCA = None enableDasEmulation = None enableRadarEmulation = None - enableSpeedVariableDesAngle = None - enableRollAngleCorrection = None - enableFeedForwardAngleCorrection = None enableDriverMonitor = None enableShowCar = None enableShowLogo = None @@ -424,7 +404,9 @@ class CarSettings(): spinnerText = None shouldLogProcessCommErrors = None shouldLogCanErrors = None - enableParamLearner = None + hsoNumbPeriod = None + ldwNumbPeriod = None + hsoBlinkerExtender = None def __init__(self, optional_config_file_path = default_config_file_path): config_file = ConfigFile() @@ -436,4 +418,5 @@ def get_value(self, name_of_variable): # Legacy support def read_config_file(into, config_path = default_config_file_path): config_file = ConfigFile() - config_file.read(into, config_path) \ No newline at end of file + config_file.read(into, config_path) + diff --git a/selfdrive/car/tesla/test_readconfig.py b/selfdrive/car/tesla/test_readconfig.py index 40a67ad42..10110209c 100755 --- a/selfdrive/car/tesla/test_readconfig.py +++ b/selfdrive/car/tesla/test_readconfig.py @@ -187,9 +187,6 @@ def check_defaults(self, cs): self.assertEqual(cs.enableALCA, True) self.assertEqual(cs.enableDasEmulation, False) self.assertEqual(cs.enableRadarEmulation, False) - self.assertEqual(cs.enableSpeedVariableDesAngle, True) - self.assertEqual(cs.enableRollAngleCorrection, False) - self.assertEqual(cs.enableFeedForwardAngleCorrection, True) self.assertEqual(cs.enableDriverMonitor, True) self.assertEqual(cs.enableShowCar, True) self.assertEqual(cs.enableShowLogo, True) diff --git a/selfdrive/car/toyota/interface.py b/selfdrive/car/toyota/interface.py index 4c1f81694..3dcb3e395 100755 --- a/selfdrive/car/toyota/interface.py +++ b/selfdrive/car/toyota/interface.py @@ -180,7 +180,7 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), vin="", has_relay ret.mass = 3370. * CV.LB_TO_KG + STD_CARGO_KG ret.lateralTuning.pid.kf = 0.00007818594 - elif candidate == CAR.COROLLA_TSS2: + elif candidate in [CAR.COROLLA_TSS2, CAR.COROLLAH_TSS2]: stop_and_go = True ret.safetyParam = 73 ret.wheelbase = 2.63906 @@ -190,7 +190,7 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), vin="", has_relay ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.1]] ret.lateralTuning.pid.kf = 0.00007818594 - elif candidate == CAR.LEXUS_ESH_TSS2: + elif candidate in [CAR.LEXUS_ES_TSS2, CAR.LEXUS_ESH_TSS2]: stop_and_go = True ret.safetyParam = 73 ret.wheelbase = 2.8702 @@ -212,7 +212,7 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), vin="", has_relay elif candidate == CAR.LEXUS_IS: stop_and_go = False - ret.safetyParam = 66 + ret.safetyParam = 77 ret.wheelbase = 2.79908 ret.steerRatio = 13.3 tire_stiffness_factor = 0.444 diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index 4ec85d51e..e3bacab9c 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -15,6 +15,8 @@ class CAR: AVALON = "TOYOTA AVALON 2016" RAV4_TSS2 = "TOYOTA RAV4 2019" COROLLA_TSS2 = "TOYOTA COROLLA TSS2 2019" + COROLLAH_TSS2 = "TOYOTA COROLLA HYBRID TSS2 2019" + LEXUS_ES_TSS2 = "LEXUS ES 2019" LEXUS_ESH_TSS2 = "LEXUS ES 300H 2019" SIENNA = "TOYOTA SIENNA XLE 2018" LEXUS_IS = "LEXUS IS300 2018" @@ -162,7 +164,8 @@ class ECU: 36: 8, 37: 8, 170: 8, 180: 8, 296: 8, 426: 6, 452: 8, 466: 8, 467: 8, 550: 8, 552: 4, 560: 7, 581: 5, 608: 8, 610: 5, 643: 7, 713: 8, 740: 5, 800: 8, 835: 8, 836: 8, 849: 4, 869: 7, 870: 7, 871: 2, 896: 8, 897: 8, 900: 6, 902: 6, 905: 8, 911: 8, 916: 3, 918: 7, 921: 8, 933: 8, 944: 8, 945: 8, 950: 8, 951: 8, 953: 3, 955: 8, 956: 8, 979: 2, 998: 5, 999: 7, 1000: 8, 1001: 8, 1005: 2, 1014: 8, 1017: 8, 1020: 8, 1041: 8, 1042: 8, 1043: 8, 1044: 8, 1056: 8, 1059: 1, 1112: 8, 1114: 8, 1161: 8, 1162: 8, 1163: 8, 1176: 8, 1177: 8, 1178: 8, 1179: 8, 1180: 8, 1181: 8, 1184: 8, 1185: 8, 1186: 8, 1189: 8, 1190: 8, 1191: 8, 1192: 8, 1196: 8, 1197: 8, 1198: 8, 1199: 8, 1206: 8, 1212: 8, 1227: 8, 1232: 8, 1235: 8, 1237: 8, 1263: 8, 1264: 8, 1279: 8, 1552: 8, 1553: 8, 1554: 8, 1556: 8, 1557: 8, 1561: 8, 1562: 8, 1568: 8, 1569: 8, 1570: 8, 1571: 8, 1572: 8, 1584: 8, 1589: 8, 1592: 8, 1593: 8, 1595: 8, 1599: 8, 1656: 8, 1728: 8, 1745: 8, 1779: 8, 1904: 8, 1912: 8, 1990: 8, 1998: 8 }, { - 36: 8, 37: 8, 170: 8, 180: 8, 296: 8, 426: 6, 452: 8, 466: 8, 467: 8, 550: 8, 552: 4, 560: 7, 581: 5, 608: 8, 610: 5, 643: 7, 713: 8, 740: 5, 800: 8, 835: 8, 836: 8, 849: 4, 869: 7, 870: 7, 871: 2, 896: 8, 897: 8, 900: 6, 902: 6, 905: 8, 911: 8, 916: 3, 921: 8, 933: 8, 944: 8, 945: 8, 950: 8, 951: 8, 953: 3, 955: 8, 956: 8, 979: 2, 992: 8, 998: 5, 999: 7, 1000: 8, 1001: 8, 1014: 8, 1017: 8, 1020: 8, 1041: 8, 1042: 8, 1043: 8, 1044: 8, 1056: 8, 1057: 8, 1059: 1, 1076: 8, 1077: 8, 1114: 8, 1161: 8, 1162: 8, 1163: 8, 1176: 8, 1177: 8, 1178: 8, 1179: 8, 1180: 8, 1181: 8, 1184: 8, 1185: 8, 1186: 8, 1189: 8, 1190: 8, 1191: 8, 1192: 8, 1196: 8, 1197: 8, 1198: 8, 1199: 8, 1206: 8, 1212: 8, 1227: 8, 1232: 8, 1237: 8, 1279: 8, 1552: 8, 1553: 8, 1554: 8, 1556: 8, 1557: 8, 1561: 8, 1562: 8, 1568: 8, 1569: 8, 1570: 8, 1571: 8, 1572: 8, 1584: 8, 1589: 8, 1592: 8, 1593: 8, 1595: 8, 1599: 8, 1656: 8, 1728: 8, 1745: 8, 1779: 8, 1904: 8, 1912: 8, 1990: 8, 1998: 8 + # 2019 Highlander Hybrid Limited Platinum + 36: 8, 37: 8, 170: 8, 180: 8, 296: 8, 426: 6, 452: 8, 466: 8, 467: 8, 550: 8, 552: 4, 560: 7, 581: 5, 608: 8, 610: 5, 643: 7, 713: 8, 740: 5, 800: 8, 835: 8, 836: 8, 849: 4, 869: 7, 870: 7, 871: 2, 896: 8, 897: 8, 900: 6, 902: 6, 905: 8, 911: 8, 916: 3, 918: 7, 921: 8, 933: 8, 944: 8, 945: 8, 950: 8, 951: 8, 953: 3, 955: 8, 956: 8, 979: 2, 992: 8, 998: 5, 999: 7, 1000: 8, 1001: 8, 1014: 8, 1017: 8, 1020: 8, 1041: 8, 1042: 8, 1043: 8, 1044: 8, 1056: 8, 1057: 8, 1059: 1, 1076: 8, 1077: 8, 1112: 8, 1114: 8, 1161: 8, 1162: 8, 1163: 8, 1176: 8, 1177: 8, 1178: 8, 1179: 8, 1180: 8, 1181: 8, 1184: 8, 1185: 8, 1186: 8, 1189: 8, 1190: 8, 1191: 8, 1192: 8, 1196: 8, 1197: 8, 1198: 8, 1199: 8, 1206: 8, 1212: 8, 1227: 8, 1232: 8, 1235: 8, 1237: 8, 1263: 8, 1279: 8, 1552: 8, 1553: 8, 1554: 8, 1556: 8, 1557: 8, 1561: 8, 1562: 8, 1568: 8, 1569: 8, 1570: 8, 1571: 8, 1572: 8, 1584: 8, 1589: 8, 1592: 8, 1593: 8, 1595: 8, 1599: 8, 1656: 8, 1666: 8, 1667: 8, 1728: 8, 1745: 8, 1779: 8, 1904: 8, 1912: 8, 1990: 8, 1998: 8 }], CAR.AVALON: [{ 36: 8, 37: 8, 170: 8, 180: 8, 426: 6, 452: 8, 464: 8, 466: 8, 467: 8, 547: 8, 550: 8, 552: 4, 562: 6, 608: 8, 610: 5, 643: 7, 705: 8, 740: 5, 800: 8, 835: 8, 836: 8, 849: 4, 869: 7, 870: 7, 871: 2, 896: 8, 897: 8, 905: 8, 911: 1, 916: 2, 921: 8, 933: 6, 944: 8, 945: 8, 951: 8, 955: 8, 956: 8, 979: 2, 1005: 2, 1014: 8, 1017: 8, 1041: 8, 1042: 8, 1044: 8, 1056: 8, 1059: 1, 1114: 8, 1161: 8, 1162: 8, 1163: 8, 1176: 8, 1177: 8, 1178: 8, 1179: 8, 1180: 8, 1181: 8, 1190: 8, 1191: 8, 1192: 8, 1196: 8, 1200: 8, 1201: 8, 1202: 8, 1203: 8, 1206: 8, 1227: 8, 1235: 8, 1237: 8, 1264: 8, 1279: 8, 1552: 8, 1553: 8, 1554: 8, 1555: 8, 1556: 8, 1557: 8, 1558: 8, 1561: 8, 1562: 8, 1568: 8, 1569: 8, 1570: 8, 1571: 8, 1572: 8, 1584: 8, 1589: 8, 1592: 8, 1593: 8, 1595: 8, 1596: 8, 1597: 8, 1664: 8, 1728: 8, 1779: 8, 1904: 8, 1912: 8, 1990: 8, 1998: 8 @@ -181,12 +184,26 @@ class ECU: { 36: 8, 37: 8, 114: 5, 170: 8, 180: 8, 186: 4, 401: 8, 426: 6, 452: 8, 464: 8, 466: 8, 467: 8, 544: 4, 550: 8, 552: 4, 562: 6, 608: 8, 610: 8, 643: 7, 705: 8, 728: 8, 740: 5, 742: 8, 743: 8, 761: 8, 764: 8, 765: 8, 800: 8, 810: 2, 812: 8, 824: 8, 829: 2, 830: 7, 835: 8, 836: 8, 865: 8, 869: 7, 870: 7, 871: 2, 877: 8, 881: 8, 896: 8, 898: 8, 900: 6, 902: 6, 905: 8, 913: 8, 921: 8, 933: 8, 934: 8, 935: 8, 944: 8, 945: 8, 951: 8, 955: 8, 956: 8, 976: 1, 998: 5, 999: 7, 1000: 8, 1001: 8, 1002: 8, 1014: 8, 1017: 8, 1020: 8, 1041: 8, 1042: 8, 1044: 8, 1056: 8, 1059: 1, 1076: 8, 1077: 8, 1114: 8, 1161: 8, 1162: 8, 1163: 8, 1164: 8, 1165: 8, 1166: 8, 1167: 8, 1172: 8, 1235: 8, 1237: 8, 1279: 8, 1541: 8, 1552: 8, 1553: 8, 1556: 8, 1557: 8, 1568: 8, 1570: 8, 1571: 8, 1572: 8, 1592: 8, 1595: 8, 1649: 8, 1745: 8, 1775: 8, 1779: 8, 1786: 8, 1787: 8, 1788: 8, 1789: 8, 1808: 8, 1809: 8, 1816: 8, 1817: 8, 1840: 8, 1848: 8, 1904: 8, 1912: 8, 1940: 8, 1941: 8, 1948: 8, 1949: 8, 1952: 8, 1960: 8, 1981: 8, 1986: 8, 1990: 8, 1994: 8, 1998: 8, 2004: 8 }], + CAR.COROLLAH_TSS2: [ + # 2019 Taiwan Altis Hybrid + { + 36: 8, 37: 8, 166: 8, 170: 8, 180: 8, 295: 8, 296: 8, 401: 8, 426: 6, 452: 8, 466: 8, 467: 8, 550: 8, 552: 4, 560: 7, 562: 6, 581: 5, 608: 8, 610: 8, 643: 7, 713: 8, 728: 8, 740: 5, 742: 8, 743: 8, 761: 8, 765: 8, 800: 8, 810: 2, 829: 2, 830: 7, 835: 8, 836: 8, 865: 8, 869: 7, 870: 7, 871: 2, 877: 8, 881: 8, 885: 8, 896: 8, 898: 8, 918: 7, 921: 8, 944: 8, 945: 8, 950: 8, 951: 8, 953: 8, 955: 8, 956: 8, 971: 7, 975: 5, 987: 8, 993: 8, 1002: 8, 1014: 8, 1017: 8, 1020: 8, 1041: 8, 1042: 8, 1044: 8, 1056: 8, 1057: 8, 1059: 1, 1071: 8, 1082: 8, 1112: 8, 1114: 8, 1161: 8, 1162: 8, 1163: 8, 1172: 8, 1235: 8, 1237: 8, 1279: 8, 1541: 8, 1552: 8, 1553: 8, 1556: 8, 1557: 8, 1568: 8, 1570: 8, 1571: 8, 1572: 8, 1592: 8, 1594: 8, 1595: 8, 1745: 8, 1775: 8, 1779: 8 + }], + CAR.LEXUS_ES_TSS2: [ + { + 36: 8, 37: 8, 114: 5, 119: 6, 120: 4, 170: 8, 180: 8, 186: 4, 401: 8, 426: 6, 452: 8, 464: 8, 466: 8, 467: 8, 544: 4, 550: 8, 552: 4, 562: 6, 608: 8, 610: 8, 643: 7, 658: 8, 705: 8, 728: 8, 740: 5, 761: 8, 764: 8, 765: 8, 800: 8, 810: 2, 812: 8, 814: 8, 818: 8, 824: 8, 830: 7, 835: 8, 836: 8, 865: 8, 869: 7, 870: 7, 871: 2, 877: 8, 881: 8, 882: 8, 885: 8, 889: 8, 896: 8, 898: 8, 900: 6, 902: 6, 905: 8, 913: 8, 918: 8, 921: 8, 933: 8, 934: 8, 935: 8, 944: 8, 945: 8, 951: 8, 955: 8, 956: 8, 976: 1, 987: 8, 998: 5, 999: 7, 1000: 8, 1001: 8, 1002: 8, 1014: 8, 1017: 8, 1020: 8, 1041: 8, 1042: 8, 1044: 8, 1056: 8, 1059: 1, 1076: 8, 1077: 8, 1082: 8, 1114: 8, 1161: 8, 1162: 8, 1163: 8, 1164: 8, 1165: 8, 1166: 8, 1167: 8, 1172: 8, 1228: 8, 1235: 8, 1237: 8, 1264: 8, 1279: 8, 1541: 8, 1552: 8, 1553: 8, 1556: 8, 1557: 8, 1568: 8, 1570: 8, 1571: 8, 1572: 8, 1575: 8, 1592: 8, 1594: 8, 1595: 8, 1649: 8, 1696: 8, 1775: 8, 1777: 8, 1779: 8, 1786: 8, 1787: 8, 1788: 8, 1789: 8, 1904: 8, 1912: 8 + }], CAR.LEXUS_ESH_TSS2: [ { 36: 8, 37: 8, 166: 8, 170: 8, 180: 8, 295: 8, 296: 8, 401: 8, 426: 6, 452: 8, 466: 8, 467: 8, 550: 8, 552: 4, 560: 7, 562: 6, 581: 5, 608: 8, 610: 8, 643: 7, 658: 8, 713: 8, 728: 8, 740: 5, 742: 8, 743: 8, 744: 8, 761: 8, 764: 8, 765: 8, 800: 8, 810: 2, 812: 8, 814: 8, 818: 8, 824: 8, 829: 2, 830: 7, 835: 8, 836: 8, 863: 8, 865: 8, 869: 7, 870: 7, 871: 2, 877: 8, 881: 8, 882: 8, 885: 8, 889: 8, 896: 8, 898: 8, 900: 6, 902: 6, 905: 8, 913: 8, 918: 8, 921: 8, 933: 8, 934: 8, 935: 8, 944: 8, 945: 8, 950: 8, 951: 8, 953: 8, 955: 8, 956: 8, 971: 7, 975: 5, 987: 8, 993: 8, 998: 5, 999: 7, 1000: 8, 1001: 8, 1002: 8, 1017: 8, 1020: 8, 1041: 8, 1042: 8, 1056: 8, 1057: 8, 1059: 1, 1071: 8, 1076: 8, 1077: 8, 1082: 8, 1084: 8, 1085: 8, 1086: 8, 1114: 8, 1132: 8, 1161: 8, 1162: 8, 1163: 8, 1164: 8, 1165: 8, 1166: 8, 1167: 8, 1172: 8, 1228: 8, 1235: 8, 1264: 8, 1279: 8, 1541: 8, 1552: 8, 1553: 8, 1556: 8, 1557: 8, 1568: 8, 1570: 8, 1571: 8, 1572: 8, 1575: 8, 1592: 8, 1594: 8, 1595: 8, 1649: 8, 1696: 8, 1775: 8, 1777: 8, 1779: 8, 1786: 8, 1787: 8, 1788: 8, 1789: 8, 1904: 8, 1912: 8, 1990: 8, 1998: 8 }], - CAR.SIENNA: [{ + CAR.SIENNA: [ + { 36: 8, 37: 8, 114: 5, 119: 6, 120: 4, 170: 8, 180: 8, 186: 4, 426: 6, 452: 8, 464: 8, 466: 8, 467: 8, 544: 4, 545: 5, 548: 8, 550: 8, 552: 4, 562: 4, 608: 8, 610: 5, 643: 7, 705: 8, 725: 2, 740: 5, 764: 8, 800: 8, 824: 8, 835: 8, 836: 8, 849: 4, 869: 7, 870: 7, 871: 2, 888: 8, 896: 8, 900: 6, 902: 6, 905: 8, 911: 8, 916: 1, 918: 7, 921: 8, 933: 8, 944: 6, 945: 8, 951: 8, 955: 8, 956: 8, 979: 2, 992: 8, 998: 5, 999: 7, 1000: 8, 1001: 8, 1002: 8, 1008: 2, 1014: 8, 1017: 8, 1041: 8, 1042: 8, 1043: 8, 1056: 8, 1059: 1, 1076: 8, 1077: 8, 1114: 8, 1160: 8, 1161: 8, 1162: 8, 1163: 8, 1164: 8, 1165: 8, 1166: 8, 1167: 8, 1176: 8, 1177: 8, 1178: 8, 1179: 8, 1180: 8, 1181: 8, 1182: 8, 1183: 8, 1191: 8, 1192: 8, 1196: 8, 1197: 8, 1198: 8, 1199: 8, 1200: 8, 1201: 8, 1202: 8, 1203: 8, 1212: 8, 1227: 8, 1228: 8, 1235: 8, 1237: 8, 1279: 8, 1552: 8, 1553: 8, 1555: 8, 1556: 8, 1557: 8, 1561: 8, 1562: 8, 1568: 8, 1569: 8, 1570: 8, 1571: 8, 1572: 8, 1584: 8, 1589: 8, 1592: 8, 1593: 8, 1595: 8, 1656: 8, 1664: 8, 1666: 8, 1667: 8, 1728: 8, 1745: 8, 1779: 8, 1904: 8, 1912: 8, 1990: 8, 1998: 8 + }, + # XLE AWD 2018 + { + 36: 8, 37: 8, 114: 5, 119: 6, 120: 4, 170: 8, 180: 8, 186: 4, 238: 4, 426: 6, 452: 8, 464: 8, 466: 8, 467: 8, 544: 4, 545: 5, 548: 8, 550: 8, 552: 4, 562: 4, 608: 8, 610: 5, 643: 7, 705: 8, 725: 2, 740: 5, 764: 8, 800: 8, 824: 8, 835: 8, 836: 8, 849: 4, 869: 7, 870: 7, 871: 2, 896: 8, 900: 6, 902: 6, 905: 8, 911: 8, 916: 1, 921: 8, 933: 8, 944: 6, 945: 8, 951: 8, 955: 8, 956: 8, 979: 2, 992: 8, 998: 5, 999: 7, 1000: 8, 1001: 8, 1002: 8, 1008: 2, 1014: 8, 1017: 8, 1041: 8, 1042: 8, 1043: 8, 1056: 8, 1059: 1, 1076: 8, 1077: 8, 1114: 8, 1160: 8, 1161: 8, 1162: 8, 1163: 8, 1164: 8, 1165: 8, 1166: 8, 1167: 8, 1176: 8, 1177: 8, 1178: 8, 1179: 8, 1180: 8, 1181: 8, 1182: 8, 1183: 8, 1191: 8, 1192: 8, 1196: 8, 1197: 8, 1198: 8, 1199: 8, 1200: 8, 1201: 8, 1202: 8, 1203: 8, 1212: 8, 1227: 8, 1235: 8, 1237: 8, 1279: 8, 1552: 8, 1553: 8, 1555: 8, 1556: 8, 1557: 8, 1561: 8, 1562: 8, 1568: 8, 1569: 8, 1570: 8, 1571: 8, 1572: 8, 1584: 8, 1589: 8, 1592: 8, 1593: 8, 1595: 8, 1656: 8, 1664: 8, 1666: 8, 1667: 8, 1728: 8, 1745: 8, 1779: 8, 1904: 8, 1912: 8, 1990: 8, 1998: 8 }], CAR.LEXUS_IS: [ # IS300 2018 @@ -219,12 +236,14 @@ class ECU: CAR.AVALON: dbc_dict('toyota_avalon_2017_pt_generated', 'toyota_adas'), CAR.RAV4_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.COROLLA_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), + CAR.COROLLAH_TSS2: dbc_dict('toyota_nodsu_hybrid_pt_generated', 'toyota_tss2_adas'), + CAR.LEXUS_ES_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.LEXUS_ESH_TSS2: dbc_dict('toyota_nodsu_hybrid_pt_generated', 'toyota_tss2_adas'), CAR.SIENNA: dbc_dict('toyota_sienna_xle_2018_pt_generated', 'toyota_adas'), CAR.LEXUS_IS: dbc_dict('lexus_is_2018_pt_generated', 'toyota_adas'), CAR.LEXUS_CTH: dbc_dict('lexus_ct200h_2018_pt_generated', 'toyota_adas'), } -NO_DSU_CAR = [CAR.CHR, CAR.CHRH, CAR.CAMRY, CAR.CAMRYH, CAR.RAV4_TSS2, CAR.COROLLA_TSS2, CAR.LEXUS_ESH_TSS2] -TSS2_CAR = [CAR.RAV4_TSS2, CAR.COROLLA_TSS2, CAR.LEXUS_ESH_TSS2] -NO_STOP_TIMER_CAR = [CAR.RAV4H, CAR.HIGHLANDERH, CAR.HIGHLANDER, CAR.RAV4_TSS2, CAR.COROLLA_TSS2, CAR.LEXUS_ESH_TSS2, CAR.SIENNA] # no resume button press required +NO_DSU_CAR = [CAR.CHR, CAR.CHRH, CAR.CAMRY, CAR.CAMRYH, CAR.RAV4_TSS2, CAR.COROLLA_TSS2, CAR.COROLLAH_TSS2, CAR.LEXUS_ES_TSS2, CAR.LEXUS_ESH_TSS2] +TSS2_CAR = [CAR.RAV4_TSS2, CAR.COROLLA_TSS2, CAR.COROLLAH_TSS2, CAR.LEXUS_ES_TSS2, CAR.LEXUS_ESH_TSS2] +NO_STOP_TIMER_CAR = [CAR.RAV4H, CAR.HIGHLANDERH, CAR.HIGHLANDER, CAR.RAV4_TSS2, CAR.COROLLA_TSS2, CAR.COROLLAH_TSS2, CAR.LEXUS_ES_TSS2, CAR.LEXUS_ESH_TSS2, CAR.SIENNA] # no resume button press required diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 79001ec85..863869953 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -522,7 +522,7 @@ def controlsd_thread(sm=None, pm=None, can_sock=None): # FIXME: offroad alerts should not be created with negative severity connectivity_alert = params.get("Offroad_ConnectivityNeeded", encoding='utf8') - internet_needed = connectivity_alert is not None and json.loads(connectivity_alert.replace("'", "\""))["severity"] >= 0 + internet_needed = connectivity_alert is not None and json.loads(connectivity_alert)["severity"] >= 0 prof = Profiler(False) # off by default diff --git a/selfdrive/controls/lib/lane_planner.py b/selfdrive/controls/lib/lane_planner.py index 454c8454f..2042cefd6 100644 --- a/selfdrive/controls/lib/lane_planner.py +++ b/selfdrive/controls/lib/lane_planner.py @@ -27,7 +27,7 @@ def calc_d_poly(l_poly, r_poly, p_poly, l_prob, r_prob, lane_width): path_from_right_lane = r_poly.copy() path_from_right_lane[3] += lane_width / 2.0 - lr_prob = l_prob * r_prob + lr_prob = l_prob + r_prob - l_prob * r_prob d_poly_lane = (l_prob * path_from_left_lane + r_prob * path_from_right_lane) / (l_prob + r_prob + 0.0001) return lr_prob * d_poly_lane + (1.0 - lr_prob) * p_poly diff --git a/selfdrive/controls/lib/pathplanner.py b/selfdrive/controls/lib/pathplanner.py index 8acdc2745..a7ecfee7f 100644 --- a/selfdrive/controls/lib/pathplanner.py +++ b/selfdrive/controls/lib/pathplanner.py @@ -6,13 +6,12 @@ from selfdrive.controls.lib.drive_helpers import MPC_COST_LAT from selfdrive.controls.lib.lane_planner import LanePlanner import selfdrive.messaging as messaging -from selfdrive.car.tesla.readconfig import CarSettings LOG_MPC = os.environ.get('LOG_MPC', False) -def calc_states_after_delay(states, v_ego, steer_angle, curvature_factor, steer_ratio, delay, long_camera_offset): - states[0].x = max(v_ego * delay + long_camera_offset, 0.0) +def calc_states_after_delay(states, v_ego, steer_angle, curvature_factor, steer_ratio, delay): + states[0].x = max(v_ego * delay, 0.0) states[0].psi = v_ego * curvature_factor * math.radians(steer_angle) / steer_ratio * delay return states @@ -26,7 +25,6 @@ def __init__(self, CP): self.setup_mpc(CP.steerRateCost) self.solution_invalid_cnt = 0 self.path_offset_i = 0.0 - self.eonToFront = CarSettings().get_value("eonToFront") def setup_mpc(self, steer_rate_cost): self.libmpc = libmpc_py.libmpc @@ -70,7 +68,7 @@ def update(self, sm, pm, CP, VM): # account for actuation delay - self.cur_state = calc_states_after_delay(self.cur_state, v_ego, angle_steers - angle_offset, curvature_factor, VM.sR, CP.steerActuatorDelay, self.eonToFront) + self.cur_state = calc_states_after_delay(self.cur_state, v_ego, angle_steers - angle_offset, curvature_factor, VM.sR, CP.steerActuatorDelay) v_ego_mpc = max(v_ego, 5.0) # avoid mpc roughness due to low speed self.libmpc.run_mpc(self.cur_state, self.mpc_solution, diff --git a/selfdrive/controls/radard.py b/selfdrive/controls/radard.py index 8d66b4f71..f45f9fa5c 100755 --- a/selfdrive/controls/radard.py +++ b/selfdrive/controls/radard.py @@ -72,7 +72,10 @@ def get_lead(v_ego, ready, clusters, lead_msg, low_speed_override=True,use_tesla cluster = None lead_dict = {'status': False} - lead_dict_ext = {'trackId': 1, 'oClass': 0, 'length': 0.} + lead_dict_ext = {'trackId': 1, 'oClass': 1, 'length': 0.} + # temporary for development purposes: we set the default lead vehicle type to truck (=0) to distinguish between vision (truck) and radar leads (car) in IC + if use_tesla_radar: + lead_dict_ext['oClass'] = 0 if cluster is not None: lead_dict,lead_dict_ext = cluster.get_RadarState(lead_msg.prob) elif (cluster is None) and ready and (lead_msg.prob > .5): diff --git a/selfdrive/locationd/params_learner.cc b/selfdrive/locationd/params_learner.cc index 778dc7096..f66977e1e 100644 --- a/selfdrive/locationd/params_learner.cc +++ b/selfdrive/locationd/params_learner.cc @@ -28,6 +28,8 @@ ParamsLearner::ParamsLearner(cereal::CarParams::Reader car_params, cF0 = car_params.getTireStiffnessFront(); cR0 = car_params.getTireStiffnessRear(); + prev_u = 0; + l = car_params.getWheelbase(); m = car_params.getMass(); @@ -43,53 +45,31 @@ ParamsLearner::ParamsLearner(cereal::CarParams::Reader car_params, alpha3 = 0.1 * learning_rate; alpha4 = 1.0 * learning_rate; cs_sr = car_params.getSteerRatio(); - learnerEnabled = false; - char line[500]; - int linenum=0; - FILE *stream; - stream = fopen("/data/bb_openpilot.cfg","r"); - while(fgets(line, 500, stream) != NULL) - { - char setting[256], value[256], oper[10]; - - linenum++; - if(line[0] == '#') continue; - if(sscanf(line, "%s %s %s", setting, oper , value) != 3) - { - continue; - } - if (strcmp("enable_param_learner",setting) == 0) { - //printf("Found [%s] %s [%s]\n", setting, oper, value); - learnerEnabled = (strcmp("True",value) == 0); - printf("Set learnerEnabled to [%s]\n", learnerEnabled ? "true" : "false"); - sR = cs_sr; - printf("Setting steerRatio to [%f]\n", sR); - } - } - fclose(stream); } bool ParamsLearner::update(double psi, double u, double sa) { - if (u > 10.0 && fabs(sa) < (DEGREES_TO_RADIANS * 90.)) { + //BB only learn when speed is constant; accel and decel in turns can affect learner + if (u > 10.0 && fabs(sa) < (DEGREES_TO_RADIANS * 90.)) { double ao_diff = 2.0*cF0*cR0*l*u*x*(1.0*cF0*cR0*l*u*x*(ao - sa) + psi*sR*(cF0*cR0*pow(l, 2)*x - m*pow(u, 2)*(aF*cF0 - aR*cR0)))/(pow(sR, 2)*pow(cF0*cR0*pow(l, 2)*x - m*pow(u, 2)*(aF*cF0 - aR*cR0), 2)); double new_ao = ao - alpha1 * ao_diff; double slow_ao_diff = 2.0*cF0*cR0*l*u*x*(1.0*cF0*cR0*l*u*x*(slow_ao - sa) + psi*sR*(cF0*cR0*pow(l, 2)*x - m*pow(u, 2)*(aF*cF0 - aR*cR0)))/(pow(sR, 2)*pow(cF0*cR0*pow(l, 2)*x - m*pow(u, 2)*(aF*cF0 - aR*cR0), 2)); double new_slow_ao = slow_ao - alpha2 * slow_ao_diff; - + double new_x = x - alpha3 * (-2.0*cF0*cR0*l*m*pow(u, 3)*(slow_ao - sa)*(aF*cF0 - aR*cR0)*(1.0*cF0*cR0*l*u*x*(slow_ao - sa) + psi*sR*(cF0*cR0*pow(l, 2)*x - m*pow(u, 2)*(aF*cF0 - aR*cR0)))/(pow(sR, 2)*pow(cF0*cR0*pow(l, 2)*x - m*pow(u, 2)*(aF*cF0 - aR*cR0), 3))); double new_sR = sR - alpha4 * (-2.0*cF0*cR0*l*u*x*(slow_ao - sa)*(1.0*cF0*cR0*l*u*x*(slow_ao - sa) + psi*sR*(cF0*cR0*pow(l, 2)*x - m*pow(u, 2)*(aF*cF0 - aR*cR0)))/(pow(sR, 3)*pow(cF0*cR0*pow(l, 2)*x - m*pow(u, 2)*(aF*cF0 - aR*cR0), 2))); - ao = new_ao; - slow_ao = new_slow_ao; - x = new_x; - if (learnerEnabled) { + + //only consider if acceleration [abs(prev_speed - speed) * frequency] is less than MAX_ACCEL + double a = abs(prev_u - u) * FREQUENCY; + if (a < MAX_ACCEL) { + ao = new_ao; + slow_ao = new_slow_ao; + x = new_x; sR = new_sR; - } else { - sR = cs_sr; } } - + prev_u = u; #ifdef DEBUG std::cout << "Instant AO: " << (RADIANS_TO_DEGREES * ao) << "\tAverage AO: " << (RADIANS_TO_DEGREES * slow_ao); std::cout << "\tStiffness: " << x << "\t sR: " << sR << std::endl; diff --git a/selfdrive/locationd/params_learner.h b/selfdrive/locationd/params_learner.h index a433275fc..aab0f712c 100644 --- a/selfdrive/locationd/params_learner.h +++ b/selfdrive/locationd/params_learner.h @@ -12,6 +12,9 @@ #define MIN_SR_TH 0.55 #define MAX_SR_TH 1.9 +#define FREQUENCY 100.0 //BB learner is called at 100 Hz +#define MAX_ACCEL 0.2 //BB maximum acceleration m/s^2 accepted before ignoring the calculations + class ParamsLearner { double cF0, cR0; double aR, aF; @@ -20,12 +23,12 @@ class ParamsLearner { double min_sr, max_sr, min_sr_th, max_sr_th; double alpha1, alpha2, alpha3, alpha4; double cs_sr; - bool learnerEnabled; public: double ao; double slow_ao; double x, sR; + double prev_u; //BB previous speed so we only learn when speed is constant between iterations ParamsLearner(cereal::CarParams::Reader car_params, double angle_offset, diff --git a/selfdrive/manager.py b/selfdrive/manager.py index 9cda418a0..80048f1d8 100755 --- a/selfdrive/manager.py +++ b/selfdrive/manager.py @@ -5,6 +5,7 @@ import errno import signal import subprocess +import time from selfdrive.tinklad.tinkla_interface import TinklaClient from cereal import tinkla from selfdrive.car.tesla.readconfig import CarSettings @@ -321,7 +322,7 @@ def system(cmd): output=e.output[-1024:], returncode=e.returncode) -def sendUserInfoToTinkla(params): +def sendUserInfoToTinkla(params, tinklaClient): carSettings = CarSettings() gitRemote = params.get("GitRemote") gitBranch = params.get("GitBranch") @@ -368,9 +369,9 @@ def manager_thread(): logger_dead = False # Tinkla interface - global tinklaClient + last_tinklad_send_attempt_time = 0 tinklaClient = TinklaClient() - sendUserInfoToTinkla(params) + sendUserInfoToTinkla(params=params, tinklaClient=tinklaClient) while 1: msg = messaging.recv_sock(thermal_sock, wait=True) @@ -382,7 +383,11 @@ def manager_thread(): start_managed_process("uploader") # Attempt to send pending messages if there's any that queued while offline - tinklaClient.attemptToSendPendingMessages() + # Seems this loop runs every second or so, throttle to once every 30s + now = time.time() + if now - last_tinklad_send_attempt_time >= 30: + tinklaClient.attemptToSendPendingMessages() + last_tinklad_send_attempt_time = now if msg.thermal.freeSpace < 0.05: logger_dead = True diff --git a/selfdrive/registration.py b/selfdrive/registration.py index 1b1705fe5..e3f1ab728 100644 --- a/selfdrive/registration.py +++ b/selfdrive/registration.py @@ -55,14 +55,6 @@ def get_subscriber_info(): return "" return ret -def get_git_remote(): - try: - local_branch = subprocess.run(["git", "name-rev", "--name-only", "HEAD"], capture_output=True).stdout.decode("utf-8").strip() - tracking_remote = subprocess.run(["git", "config", "branch."+local_branch+".remote"],capture_output=True).stdout.decode("utf-8").strip() - return subprocess.run(["git", "config", "remote."+tracking_remote+".url"],capture_output=True).stdout.decode("utf-8").strip() - except subprocess.CalledProcessError: - return "" - def register(): params = Params() params.put("Version", version) diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 557be7d88..780589159 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -8def7e7391802f2e86498d764c953f487361f6a1 \ No newline at end of file +a64d824f47e2f794512f1c9439a04e242e3d1676 \ No newline at end of file diff --git a/selfdrive/test/test_car_models.py b/selfdrive/test/test_car_models.py index fc4605f94..ab95fe7d0 100755 --- a/selfdrive/test/test_car_models.py +++ b/selfdrive/test/test_car_models.py @@ -281,6 +281,11 @@ def get_route_logs(route_name): 'enableCamera': True, 'enableDsu': True, }, + "e6a24be49a6cd46e|2019-10-29--10-52-42": { + 'carFingerprint': TOYOTA.LEXUS_ES_TSS2, + 'enableCamera': True, + 'enableDsu': True, + }, "f49e8041283f2939|2019-05-29--13-48-33": { 'carFingerprint': TOYOTA.LEXUS_ESH_TSS2, 'enableCamera': False, diff --git a/selfdrive/thermald.py b/selfdrive/thermald.py old mode 100755 new mode 100644 index f728b3107..20291a20a --- a/selfdrive/thermald.py +++ b/selfdrive/thermald.py @@ -162,7 +162,6 @@ def thermald_thread(): off_ts = None started_ts = None - ignition_seen = False started_seen = False thermal_status = ThermalStatus.green thermal_status_prev = ThermalStatus.green @@ -188,7 +187,6 @@ def thermald_thread(): # clear car params when panda gets disconnected if health is None and health_prev is not None: params.panda_disconnect() - ignition_seen = False health_prev = health if health is not None: @@ -273,12 +271,6 @@ def thermald_thread(): # start constellation of processes when the car starts ignition = health is not None and health.health.started - # print "Ignition from panda: ", ignition - ignition_seen = ignition_seen or ignition - - # add voltage check for ignition - #if not ignition_seen and health is not None and health.health.voltage > 13500: - # ignition = True do_uninstall = params.get("DoUninstall") == b"1" accepted_terms = params.get("HasAcceptedTerms") == terms_version diff --git a/selfdrive/tinklad/airtable_publisher.py b/selfdrive/tinklad/airtable_publisher.py index 0d17224f3..2ff3f7764 100644 --- a/selfdrive/tinklad/airtable_publisher.py +++ b/selfdrive/tinklad/airtable_publisher.py @@ -1,3 +1,7 @@ +#!/usr/bin/env python3 +# Created by Raf 5/2019 + +import asyncio AIRTABLE_API_KEY = 'keyvqdsl2VIIr9Q2A' AIRTABLE_BASE_ID = 'appht7GB4aJS2A0LD' @@ -34,7 +38,7 @@ class Publisher(): latest_info_dict = None # current info published userRecordId = None - def send_info(self, info, isData= False): + async def send_info(self, info, isData= False): data_dict = None if isData: data_dict = info @@ -42,23 +46,23 @@ def send_info(self, info, isData= False): data_dict = self.__generate_airtable_user_info_dict(info) # Early return if no changes - if self.latest_info_dict != None: + if self.latest_info_dict is not None: print(LOG_PREFIX + "latest_info. data=%s" % (self.latest_info_dict)) if data_dict == self.latest_info_dict: print(LOG_PREFIX + "send_info no update necessary*") return print(LOG_PREFIX + "Sending info. data=%s" % (data_dict)) - if self.userRecordId != None: - self.__update_user(data_dict) + if self.userRecordId is not None: + await self.__update_user(data_dict) - if info.openPilotId != None and info.openPilotId != '': + if (info.openPilotId is not None) and info.openPilotId != '': self.openPilotId = info.openPilotId - response = self.at.get(USERS_TABLE, limit=1, filter_by_formula=("{openPilotId} = '%s'" % (self.openPilotId))) + response = await self.at.get(USERS_TABLE, limit=1, filter_by_formula=("{openPilotId} = '%s'" % (self.openPilotId))) if self.__is_notfound_response(response): # Not found, create: print(LOG_PREFIX + "Creating record for openPilotId='%s'" % (info.openPilotId)) - response = self.at.create(USERS_TABLE, data_dict) + response = await self.at.create(USERS_TABLE, data_dict) if self.__is_error_response(response): raise Exception(response) elif self.__is_error_response(response): #Unsupported error @@ -66,25 +70,24 @@ def send_info(self, info, isData= False): raise Exception(response) else: self.userRecordId = response["records"][0]["id"] - self.__update_user(data_dict) + await self.__update_user(data_dict) self.latest_info_dict = data_dict print(LOG_PREFIX + "*send_info competed*") - def send_event(self, event): - if self.openPilotId is None and self.latest_info_dict != None: + async def send_event(self, event): + if self.openPilotId is None and self.latest_info_dict is not None: self.openPilotId = self.latest_info_dict[self.userKeys.openPilotId] event_dict = self.__generate_airtable_user_event_dict(event) print(LOG_PREFIX + "Sending event. data=%s" % (event_dict)) - response = self.at.create(EVENTS_TABLE, event_dict) + response = await self.at.create(EVENTS_TABLE, event_dict) if self.__is_error_response(response): print(LOG_PREFIX + "Error sending airtable event. %s" % (response)) raise Exception(response) print(LOG_PREFIX + "*send_event competed*") - def __generate_airtable_user_info_dict(self, info): dictionary = info.to_dict() dictionary.pop(self.userKeys.timestamp, None) @@ -100,29 +103,29 @@ def __generate_airtable_user_event_dict(self, event): value = event.value.intValue elif value == self.eventValueTypes.floatValue: value = event.value.floatValue - openPilotId = self.openPilotId if (self.openPilotId != None) else "" + openPilotId = event.openPilotId if (event.openPilotId is not None) else (self.openPilotId if (self.openPilotId is not None) else "") dictionary = event.to_dict() dictionary[self.eventKeys.value] = value dictionary[self.eventKeys.openPilotId] = openPilotId # dictionary.pop("timestamp", None) return dictionary - def __update_user(self, data): + async def __update_user(self, data): print(LOG_PREFIX + "Updating userRecordId='%s'" % (self.userRecordId)) - response = self.at.update(USERS_TABLE, self.userRecordId, data) + response = await self.at.update(USERS_TABLE, self.userRecordId, data) if self.__is_error_response(response): raise Exception(response) def __is_notfound_response(self, response): try: - return response["error"] != None and response["error"]["code"] == 422 + return response["error"] is not None and response["error"]["code"] == 422 except: # pylint: disable=bare-except - count = response["records"].__len__() - return count == 0 + count = response["records"].__len__() + return count == 0 def __is_error_response(self, response): try: - return response["error"] != None + return response["error"] is not None except: # pylint: disable=bare-except return False @@ -176,7 +179,7 @@ def create_payload(data): return {'fields': data} -class Airtable(object): +class Airtable(): def __init__(self, base_id, api_key, dict_class=OrderedDict): """Create a client to connect to an Airtable Base. @@ -193,14 +196,28 @@ def __init__(self, base_id, api_key, dict_class=OrderedDict): self.headers = {'Authorization': 'Bearer %s' % api_key} self._dict_class = dict_class - def __request(self, method, url, params=None, payload=None): + def __perform_request(self, method, url, params, data, headers): + return requests.request( + method, + url, + params=params, + data=data, + headers=headers + ) + + async def __request(self, method, url, params=None, payload=None): if method in ['POST', 'PUT', 'PATCH']: self.headers.update({'Content-type': 'application/json'}) - r = requests.request(method, - posixpath.join(self.base_url, url), - params=params, - data=payload, - headers=self.headers) + loop = asyncio.get_event_loop() + r = await loop.run_in_executor( + None, + self.__perform_request, + method, + posixpath.join(self.base_url, url), + params, + payload, + self.headers + ) if r.status_code == requests.codes.ok: # pylint: disable=no-member return r.json(object_pairs_hook=self._dict_class) else: @@ -272,27 +289,27 @@ def iterate( # pylint: disable=dangerous-default-value else: break - def create(self, table_name, data): # pylint: disable=inconsistent-return-statements + async def create(self, table_name, data): # pylint: disable=inconsistent-return-statements if check_string(table_name): payload = create_payload(data) - return self.__request('POST', table_name, + return await self.__request('POST', table_name, payload=json.dumps(payload)) - def update(self, table_name, record_id, data): # pylint: disable=inconsistent-return-statements + async def update(self, table_name, record_id, data): # pylint: disable=inconsistent-return-statements if check_string(table_name) and check_string(record_id): url = posixpath.join(table_name, record_id) payload = create_payload(data) - return self.__request('PATCH', url, + return await self.__request('PATCH', url, payload=json.dumps(payload)) - def update_all(self, table_name, record_id, data): # pylint: disable=inconsistent-return-statements + async def update_all(self, table_name, record_id, data): # pylint: disable=inconsistent-return-statements if check_string(table_name) and check_string(record_id): url = posixpath.join(table_name, record_id) payload = create_payload(data) - return self.__request('PUT', url, + return await self.__request('PUT', url, payload=json.dumps(payload)) - def delete(self, table_name, record_id): # pylint: disable=inconsistent-return-statements + async def delete(self, table_name, record_id): # pylint: disable=inconsistent-return-statements if check_string(table_name) and check_string(record_id): url = posixpath.join(table_name, record_id) - return self.__request('DELETE', url) + return await self.__request('DELETE', url) diff --git a/selfdrive/tinklad/pqueue.py b/selfdrive/tinklad/pqueue.py index 691cc88d3..de5c241e4 100644 --- a/selfdrive/tinklad/pqueue.py +++ b/selfdrive/tinklad/pqueue.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3.7 +#!/usr/bin/env python3 # # https://github.com/balena/python-pqueue @@ -73,7 +73,7 @@ def _destroy(self): shutil.rmtree(self.path) os.makedirs(self.path) - def _qsize(self, len=len): # pylint: disable=redefined-builtin + def _qsize(self, len=len): # pylint: disable=redefined-builtin,arguments-differ return self.info['size'] def _put(self, item): diff --git a/selfdrive/tinklad/tinkla_interface.py b/selfdrive/tinklad/tinkla_interface.py index e6768913b..cc117bf4a 100644 --- a/selfdrive/tinklad/tinkla_interface.py +++ b/selfdrive/tinklad/tinkla_interface.py @@ -1,11 +1,11 @@ -#!/usr/bin/env python3.7 +#!/usr/bin/env python3 +# Created by Raf 5/2019 from cereal import tinkla import os import zmq import datetime -#import tinklad -from selfdrive.tinklad.tinklad import TinklaInterfaceEventCategoryKeys, TinklaInterfaceMessageKeys, TinklaInterfaceActions +from selfdrive.tinklad import tinklad import time ## For helpers: @@ -13,6 +13,7 @@ from selfdrive.car.tesla.readconfig import CarSettings from common.params import Params +LOG_PREFIX = "tinklad client: " tinklaClient = None @@ -25,9 +26,9 @@ class TinklaClient(): lastCanErrorTimestamp = 0 lastProcessErrorTimestamp = 0 - eventCategoryKeys = TinklaInterfaceEventCategoryKeys() - messageTypeKeys = TinklaInterfaceMessageKeys() - actions = TinklaInterfaceActions() + eventCategoryKeys = tinklad.TinklaInterfaceEventCategoryKeys() + messageTypeKeys = tinklad.TinklaInterfaceMessageKeys() + actions = tinklad.TinklaInterfaceActions() # Configurable: # Note: If throttling, events are dropped @@ -50,7 +51,6 @@ def start_client(self): print("Unable to connect to tinklad") self.sock = None - def setUserInfo(self, info): self.start_client() if self.sock is None: @@ -167,8 +167,14 @@ def print_msg(self, message): print(message) def __init__(self): - params = Params() - self.carSettings = CarSettings() + try: + params = Params() + except OSError: + params = Params(db="./params") + try: + self.carSettings = CarSettings() + except IOError: + self.carSettings = CarSettings(optional_config_file_path="./bb_openpilot.cfg") self.openPilotId = params.get("DongleId") self.userHandle = self.carSettings.userHandle self.gitRemote = params.get("GitRemote") diff --git a/selfdrive/tinklad/tinklad.py b/selfdrive/tinklad/tinklad.py index aa6c22cfa..6a046748b 100644 --- a/selfdrive/tinklad/tinklad.py +++ b/selfdrive/tinklad/tinklad.py @@ -1,4 +1,5 @@ -#!/usr/bin/env python3.7 +#!/usr/bin/env python3 +# Created by Raf 5/2019 import zmq import cereal @@ -6,7 +7,8 @@ from selfdrive.tinklad.airtable_publisher import Publisher import requests import time -import os +import os +import asyncio LOG_PREFIX = "tinklad: " @@ -73,7 +75,7 @@ class TinklaServer(): last_attempt_time = 0 - def attemptToSendPendingMessages(self): + async def attemptToSendPendingMessages(self): # Throttle to once per minute now = time.time() if now - self.last_attempt_time < 60: @@ -85,14 +87,15 @@ def attemptToSendPendingMessages(self): if not self.isOnline(): return print(LOG_PREFIX + "Attempting to send pending messages") - self.publish_pending_userinfo() - self.publish_pending_events() + await self.publish_pending_userinfo() + await self.publish_pending_events() - def setUserInfo(self, info, **kwargs): + async def setUserInfo(self, info, **kwargs): + print(LOG_PREFIX + "Pushing user info to cache") self.userInfoCache.push(info) - self.publish_pending_userinfo() + await self.publish_pending_userinfo() - def publish_pending_userinfo(self): + async def publish_pending_userinfo(self): if self.userInfoCache.count() == 0: return @@ -107,15 +110,17 @@ def publish_pending_userinfo(self): print(LOG_PREFIX + "Sending info to publisher: %s" % (info.to_dict())) self.info = info try: - self.publisher.send_info(info) + await self.publisher.send_info(info) + self.userInfoCache.task_done() except Exception as error: # pylint: disable=broad-except - print(LOG_PREFIX + "Error attempting to publish user info (%s)" % (error)) + self.userInfoCache.push(info) + print(LOG_PREFIX + "Error attempting to publish user info (%s) (Cache has %d elements)" % (error, self.userInfoCache.count())) - def logUserEvent(self, event, **kwargs): + async def logUserEvent(self, event, **kwargs): self.eventCache.push(event) - self.publish_pending_events() + await self.publish_pending_events() - def publish_pending_events(self): + async def publish_pending_events(self): if self.eventCache.count() > 0: print(LOG_PREFIX + 'Cache has %d elements, attempting to publish...' %(self.eventCache.count())) @@ -126,7 +131,7 @@ def publish_pending_events(self): return try: print(LOG_PREFIX + "Sending event to publisher: %s" % (event.to_dict())) - self.publisher.send_event(event) + await self.publisher.send_event(event) self.eventCache.task_done() except AssertionError as error: self.eventCache.push(event) @@ -147,42 +152,52 @@ def isOnline(self): return False return False - def __init__(self): - self.publisher = Publisher() - # set persitent cache for bad network / offline - self.eventCache = Cache("events") - self.userInfoCache = Cache("user_info") - self.publish_pending_userinfo() - self.publish_pending_events() + async def messageLoop(self, sock): messageKeys = TinklaInterfaceMessageKeys() actions = TinklaInterfaceActions() - # Start server: - ctx = zmq.Context() - sock = ctx.socket(zmq.PULL) - sock.bind("ipc:///tmp/tinklad") - context = zmq.Context() - while True: - tmp = sock.recv_multipart() data = b''.join(sock.recv_multipart()) + #print(LOG_PREFIX + "Received Data: " + repr(data) + "'") tinklaInterface = cereal.tinkla.Interface.from_bytes(data) if tinklaInterface.version != cereal.tinkla.interfaceVersion: print(LOG_PREFIX + "Unsupported message version: %0.2f (supported version: %0.2f)" % (tinklaInterface.version, cereal.tinkla.interfaceVersion)) continue messageType = tinklaInterface.message.which() + #if messageType != messageKeys.action: + print(LOG_PREFIX + "> Received message. Type: '%s'" % messageType) if messageType == messageKeys.userInfo: info = tinklaInterface.message.userInfo - self.setUserInfo(info) + await self.setUserInfo(info) elif messageType == messageKeys.event: event = tinklaInterface.message.event - self.logUserEvent(event) + await self.logUserEvent(event) elif messageType == messageKeys.action: if tinklaInterface.message.action == actions.attemptToSendPendingMessages: - self.attemptToSendPendingMessages() + await self.attemptToSendPendingMessages() else: print(LOG_PREFIX + "Unsupported action: %s" % tinklaInterface.message.action) + def __init__(self): + loop = asyncio.get_event_loop() + self.publisher = Publisher() + # set persitent cache for bad network / offline + self.eventCache = Cache("events") + self.userInfoCache = Cache("user_info") + tasks = [ + self.publish_pending_userinfo(), + self.publish_pending_events() + ] + loop.run_until_complete(asyncio.wait(tasks)) + + # Start server: + ctx = zmq.Context() + sock = ctx.socket(zmq.PULL) + sock.bind("ipc:///tmp/tinklad") + context = zmq.Context() + + loop.run_until_complete(self.messageLoop(sock=sock)) + def main(gctx=None): print("Starting tinklad service ...") @@ -190,5 +205,4 @@ def main(gctx=None): if __name__ == "__main__": - main() - \ No newline at end of file + main() diff --git a/selfdrive/tinklad/tinkladTestClient.py b/selfdrive/tinklad/tinkladTestClient.py index 1b0be87d8..6da4a4460 100644 --- a/selfdrive/tinklad/tinkladTestClient.py +++ b/selfdrive/tinklad/tinkladTestClient.py @@ -1,4 +1,5 @@ -#!/usr/bin/env python2.7 +#!/usr/bin/env python3 +# Created by Raf 5/2019 from cereal import tinkla from tinkla_interface import TinklaClient @@ -48,15 +49,25 @@ def __init__(self): print("send crash log") self.tinklaClient.logCrashStackTraceEvent(openPilotId=openPilotId) - print("send can error") - self.tinklaClient.logCANErrorEvent(source=source, canMessage=1, additionalInformation="test can error logging", openPilotId=openPilotId) - time.sleep(1) - self.tinklaClient.logCANErrorEvent(source=source, canMessage=2, additionalInformation="test can error logging", openPilotId=openPilotId) + print("send user info 2") + info = tinkla.Interface.UserInfo.new_message( + openPilotId=openPilotId, + userHandle=userHandle + "2", + gitRemote="test_github.com/something", + gitBranch="test_gitbranch", + gitHash="test_123456" + ) + self.tinklaClient.setUserInfo(info) + + #print("send can error") + #self.tinklaClient.logCANErrorEvent(source=source, canMessage=1, additionalInformation="test can error logging", openPilotId=openPilotId) + #time.sleep(1) + #self.tinklaClient.logCANErrorEvent(source=source, canMessage=2, additionalInformation="test can error logging", openPilotId=openPilotId) - print("send process comm error") - self.tinklaClient.logProcessCommErrorEvent(source=source, processName="processNameWouldBeHere1", count=10, eventType="Not Alive", openPilotId=openPilotId) - time.sleep(1) - self.tinklaClient.logProcessCommErrorEvent(source=source, processName="processNameWouldBeHere2", count=10, eventType="Not Alive", openPilotId=openPilotId) + #print("send process comm error") + #self.tinklaClient.logProcessCommErrorEvent(source=source, processName="processNameWouldBeHere1", count=10, eventType="Not Alive", openPilotId=openPilotId) + #time.sleep(1) + #self.tinklaClient.logProcessCommErrorEvent(source=source, processName="processNameWouldBeHere2", count=10, eventType="Not Alive", openPilotId=openPilotId) if __name__ == "__main__": TinklaTestClient() diff --git a/selfdrive/tinklad/tinkladTestClient.sh b/selfdrive/tinklad/tinkladTestClient.sh index 3c26accdc..32f70dfd4 100755 --- a/selfdrive/tinklad/tinkladTestClient.sh +++ b/selfdrive/tinklad/tinkladTestClient.sh @@ -1 +1 @@ -PYTHONPATH="../../" python2.7 tinkladTestClient.py +PYTHONPATH="../../" python3 tinkladTestClient.py diff --git a/selfdrive/tinklad/tinkladTestServer.sh b/selfdrive/tinklad/tinkladTestServer.sh index 4a7a9d1d0..57b366f9e 100755 --- a/selfdrive/tinklad/tinkladTestServer.sh +++ b/selfdrive/tinklad/tinkladTestServer.sh @@ -1,2 +1,2 @@ mkdir ./tinklad-cache 2> /dev/null -PYTHONPATH="../../" python2.7 tinklad.py +PYTHONPATH="../../" python3 tinklad.py