You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							185 lines
						
					
					
						
							6.3 KiB
						
					
					
				
			
		
		
	
	
							185 lines
						
					
					
						
							6.3 KiB
						
					
					
				| import math
 | |
| 
 | |
| from cereal import car
 | |
| from collections import namedtuple
 | |
| from dataclasses import dataclass
 | |
| from enum import Enum
 | |
| from typing import Dict, List, Optional, Union, no_type_check
 | |
| 
 | |
| TACO_TORQUE_THRESHOLD = 2.5  # m/s^2
 | |
| GREAT_TORQUE_THRESHOLD = 1.4  # m/s^2
 | |
| GOOD_TORQUE_THRESHOLD = 1.0  # m/s^2
 | |
| 
 | |
| 
 | |
| class Tier(Enum):
 | |
|   GOLD = "The best openpilot experience. Great highway driving and beyond."
 | |
|   SILVER = "A solid highway driving experience, but is limited by stock longitudinal. May be upgraded in the future."
 | |
|   BRONZE = "A good highway experience, but may have limited performance in traffic and on sharp turns."
 | |
| 
 | |
| 
 | |
| class Column(Enum):
 | |
|   MAKE = "Make"
 | |
|   MODEL = "Model"
 | |
|   PACKAGE = "Supported Package"
 | |
|   LONGITUDINAL = "openpilot ACC"
 | |
|   FSR_LONGITUDINAL = "Stop and Go"
 | |
|   FSR_STEERING = "Steer to 0"
 | |
|   STEERING_TORQUE = "Steering Torque"
 | |
|   MAINTAINED = "Actively Maintained"
 | |
| 
 | |
| 
 | |
| class Star(Enum):
 | |
|   FULL = "full"
 | |
|   HALF = "half"
 | |
|   EMPTY = "empty"
 | |
| 
 | |
| 
 | |
| StarColumns = list(Column)[3:]
 | |
| CarFootnote = namedtuple("CarFootnote", ["text", "column", "star"], defaults=[None])
 | |
| 
 | |
| 
 | |
| def get_footnote(footnotes: Optional[List[Enum]], column: Column) -> Optional[Enum]:
 | |
|   # Returns applicable footnote given current column
 | |
|   if footnotes is not None:
 | |
|     for fn in footnotes:
 | |
|       if fn.value.column == column:
 | |
|         return fn
 | |
|   return None
 | |
| 
 | |
| 
 | |
| @dataclass
 | |
| class CarInfo:
 | |
|   name: str
 | |
|   package: str
 | |
|   video_link: Optional[str] = None
 | |
|   footnotes: Optional[List[Enum]] = None
 | |
|   min_steer_speed: Optional[float] = None
 | |
|   min_enable_speed: Optional[float] = None
 | |
|   good_torque: bool = False
 | |
|   harness: Optional[Enum] = None
 | |
| 
 | |
|   def init(self, CP: car.CarParams, non_tested_cars: List[str], all_footnotes: Dict[Enum, int]):
 | |
|     # TODO: set all the min steer speeds in carParams and remove this
 | |
|     min_steer_speed = CP.minSteerSpeed
 | |
|     if self.min_steer_speed is not None:
 | |
|       min_steer_speed = self.min_steer_speed
 | |
|       assert CP.minSteerSpeed == 0, f"{CP.carFingerprint}: Minimum steer speed set in both CarInfo and CarParams"
 | |
| 
 | |
|     assert self.harness is not None, f"{CP.carFingerprint}: Need to specify car harness"
 | |
| 
 | |
|     # TODO: set all the min enable speeds in carParams correctly and remove this
 | |
|     min_enable_speed = CP.minEnableSpeed
 | |
|     if self.min_enable_speed is not None:
 | |
|       min_enable_speed = self.min_enable_speed
 | |
| 
 | |
|     self.car_name = CP.carName
 | |
|     self.make, self.model = self.name.split(' ', 1)
 | |
|     self.row = {
 | |
|       Column.MAKE: self.make,
 | |
|       Column.MODEL: self.model,
 | |
|       Column.PACKAGE: self.package,
 | |
|       # StarColumns
 | |
|       Column.LONGITUDINAL: Star.FULL if CP.openpilotLongitudinalControl and not CP.radarOffCan else Star.EMPTY,
 | |
|       Column.FSR_LONGITUDINAL: Star.FULL if min_enable_speed <= 0. else Star.EMPTY,
 | |
|       Column.FSR_STEERING: Star.FULL if min_steer_speed <= 0. else Star.EMPTY,
 | |
|       Column.STEERING_TORQUE: Star.FULL if self.good_torque else Star.EMPTY,  # TODO: remove hardcoding and use maxLateralAccel
 | |
|       Column.MAINTAINED: Star.FULL if CP.carFingerprint not in non_tested_cars and self.harness is not Harness.none else Star.EMPTY,
 | |
|     }
 | |
| 
 | |
|     if not math.isnan(CP.maxLateralAccel):
 | |
|       if CP.maxLateralAccel >= GREAT_TORQUE_THRESHOLD:
 | |
|         self.row[Column.STEERING_TORQUE] = Star.FULL
 | |
|       elif CP.maxLateralAccel >= GOOD_TORQUE_THRESHOLD:
 | |
|         self.row[Column.STEERING_TORQUE] = Star.HALF
 | |
|       else:
 | |
|         self.row[Column.STEERING_TORQUE] = Star.EMPTY
 | |
| 
 | |
|     if CP.notCar:
 | |
|       for col in StarColumns:
 | |
|         self.row[col] = Star.FULL
 | |
| 
 | |
|     self.all_footnotes = all_footnotes
 | |
|     for column in StarColumns:
 | |
|       # Demote if footnote specifies a star
 | |
|       footnote = get_footnote(self.footnotes, column)
 | |
|       if footnote is not None and footnote.value.star is not None:
 | |
|         self.row[column] = footnote.value.star
 | |
| 
 | |
|     self.tier = {5: Tier.GOLD, 4: Tier.SILVER}.get(list(self.row.values()).count(Star.FULL), Tier.BRONZE)
 | |
|     return self
 | |
| 
 | |
|   @no_type_check
 | |
|   def get_column(self, column: Column, star_icon: str, footnote_tag: str) -> str:
 | |
|     item: Union[str, Star] = self.row[column]
 | |
|     if column in StarColumns:
 | |
|       item = star_icon.format(item.value)
 | |
| 
 | |
|     footnote = get_footnote(self.footnotes, column)
 | |
|     if footnote is not None:
 | |
|       item += footnote_tag.format(self.all_footnotes[footnote])
 | |
| 
 | |
|     return item
 | |
| 
 | |
| 
 | |
| class Harness(Enum):
 | |
|   nidec = "Honda Nidec"
 | |
|   bosch = "Honda Bosch A"
 | |
|   toyota = "Toyota"
 | |
|   subaru = "Subaru"
 | |
|   fca = "FCA"
 | |
|   vw = "VW"
 | |
|   j533 = "J533"
 | |
|   hyundai_a = "Hyundai A"
 | |
|   hyundai_b = "Hyundai B"
 | |
|   hyundai_c = "Hyundai C"
 | |
|   hyundai_d = "Hyundai D"
 | |
|   hyundai_e = "Hyundai E"
 | |
|   hyundai_f = "Hyundai F"
 | |
|   hyundai_g = "Hyundai G"
 | |
|   hyundai_h = "Hyundai H"
 | |
|   hyundai_i = "Hyundai I"
 | |
|   hyundai_j = "Hyundai J"
 | |
|   hyundai_k = "Hyundai K"
 | |
|   hyundai_l = "Hyundai L"
 | |
|   hyundai_m = "Hyundai M"
 | |
|   hyundai_n = "Hyundai N"
 | |
|   hyundai_o = "Hyundai O"
 | |
|   hyundai_p = "Hyundai P"
 | |
|   custom = "Developer"
 | |
|   obd_ii = "OBD-II"
 | |
|   nissan_a = "Nissan A"
 | |
|   nissan_b = "Nissan B"
 | |
|   mazda = "Mazda"
 | |
|   none = "None"
 | |
| 
 | |
| 
 | |
| STAR_DESCRIPTIONS = {
 | |
|   "Gas & Brakes": {  # icon and row name
 | |
|     "openpilot Adaptive Cruise Control (ACC)": [  # star column
 | |
|       [Star.FULL.value, "openpilot is able to control the gas and brakes."],
 | |
|       [Star.HALF.value, "openpilot is able to control the gas and brakes with some restrictions."],
 | |
|       [Star.EMPTY.value, "The gas and brakes are controlled by the car's stock Adaptive Cruise Control (ACC) system."],
 | |
|     ],
 | |
|     Column.FSR_LONGITUDINAL.value: [
 | |
|       [Star.FULL.value, "Adaptive Cruise Control (ACC) operates down to 0 mph."],
 | |
|       [Star.EMPTY.value, "Adaptive Cruise Control (ACC) available only above certain speeds. See your car's manual for the minimum speed."],
 | |
|     ],
 | |
|   },
 | |
|   "Steering": {
 | |
|     Column.FSR_STEERING.value: [
 | |
|       [Star.FULL.value, "openpilot can control the steering wheel down to 0 mph."],
 | |
|       [Star.EMPTY.value, "No steering control below certain speeds."],
 | |
|     ],
 | |
|     Column.STEERING_TORQUE.value: [
 | |
|       [Star.FULL.value, "Car has enough steering torque to take tighter turns."],
 | |
|       [Star.HALF.value, "Car has enough steering torque for comfortable highway driving."],
 | |
|       [Star.EMPTY.value, "Limited ability to make turns."],
 | |
|     ],
 | |
|   },
 | |
|   "Support": {
 | |
|     Column.MAINTAINED.value: [
 | |
|       [Star.FULL.value, "Mainline software support, harness hardware sold by comma, lots of users, primary development target."],
 | |
|       [Star.EMPTY.value, "Low user count, community maintained, harness hardware not sold by comma."],
 | |
|     ],
 | |
|   },
 | |
| }
 | |
| 
 |