|  |  |  | @ -92,6 +92,45 @@ def recv_one_retry(sock: SubSocket) -> capnp.lib.capnp._DynamicStructReader: | 
			
		
	
		
			
				
					|  |  |  |  |       return log_from_bytes(dat) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | class FrequencyTracker: | 
			
		
	
		
			
				
					|  |  |  |  |   def __init__(self, service_freq: float, update_freq: float, is_poll: bool): | 
			
		
	
		
			
				
					|  |  |  |  |     freq = max(min(service_freq, update_freq), 1.) | 
			
		
	
		
			
				
					|  |  |  |  |     if is_poll: | 
			
		
	
		
			
				
					|  |  |  |  |       min_freq = max_freq = freq | 
			
		
	
		
			
				
					|  |  |  |  |     else: | 
			
		
	
		
			
				
					|  |  |  |  |       max_freq = min(freq, update_freq) | 
			
		
	
		
			
				
					|  |  |  |  |       if service_freq >= 2 * update_freq: | 
			
		
	
		
			
				
					|  |  |  |  |         min_freq = update_freq | 
			
		
	
		
			
				
					|  |  |  |  |       elif update_freq >= 2* service_freq: | 
			
		
	
		
			
				
					|  |  |  |  |         min_freq = freq | 
			
		
	
		
			
				
					|  |  |  |  |       else: | 
			
		
	
		
			
				
					|  |  |  |  |         min_freq = min(freq, freq / 2.) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     self.min_freq = min_freq * 0.8 | 
			
		
	
		
			
				
					|  |  |  |  |     self.max_freq = max_freq * 1.2 | 
			
		
	
		
			
				
					|  |  |  |  |     self.recv_dts: Deque[float] = deque(maxlen=int(10 * freq)) | 
			
		
	
		
			
				
					|  |  |  |  |     self.prev_time = 0.0 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   def record_recv_time(self, cur_time: float) -> None: | 
			
		
	
		
			
				
					|  |  |  |  |     # TODO: Handle case where cur_time is less than prev_time | 
			
		
	
		
			
				
					|  |  |  |  |     if self.prev_time > 1e-5: | 
			
		
	
		
			
				
					|  |  |  |  |       self.recv_dts.append(cur_time - self.prev_time) | 
			
		
	
		
			
				
					|  |  |  |  |     self.prev_time = cur_time | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   @property | 
			
		
	
		
			
				
					|  |  |  |  |   def valid(self) -> bool: | 
			
		
	
		
			
				
					|  |  |  |  |     if not self.recv_dts: | 
			
		
	
		
			
				
					|  |  |  |  |       return False | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     avg_freq = len(self.recv_dts) / sum(self.recv_dts) | 
			
		
	
		
			
				
					|  |  |  |  |     if self.min_freq <= avg_freq <= self.max_freq: | 
			
		
	
		
			
				
					|  |  |  |  |       return True | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     recent_dts = list(self.recv_dts)[-int(self.recv_dts.maxlen / 10):] | 
			
		
	
		
			
				
					|  |  |  |  |     avg_freq_recent = len(recent_dts) / sum(recent_dts) | 
			
		
	
		
			
				
					|  |  |  |  |     return self.min_freq <= avg_freq_recent <= self.max_freq | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | class SubMaster: | 
			
		
	
		
			
				
					|  |  |  |  |   def __init__(self, services: List[str], poll: Optional[str] = None, | 
			
		
	
		
			
				
					|  |  |  |  |                ignore_alive: Optional[List[str]] = None, ignore_avg_freq: Optional[List[str]] = None, | 
			
		
	
	
		
			
				
					|  |  |  | @ -103,15 +142,12 @@ class SubMaster: | 
			
		
	
		
			
				
					|  |  |  |  |     self.recv_frame = {s: 0 for s in services} | 
			
		
	
		
			
				
					|  |  |  |  |     self.alive = {s: False for s in services} | 
			
		
	
		
			
				
					|  |  |  |  |     self.freq_ok = {s: False for s in services} | 
			
		
	
		
			
				
					|  |  |  |  |     self.recv_dts: Dict[str, Deque[float]] = {} | 
			
		
	
		
			
				
					|  |  |  |  |     self.sock = {} | 
			
		
	
		
			
				
					|  |  |  |  |     self.data = {} | 
			
		
	
		
			
				
					|  |  |  |  |     self.valid = {} | 
			
		
	
		
			
				
					|  |  |  |  |     self.logMonoTime = {} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     self.max_freq = {} | 
			
		
	
		
			
				
					|  |  |  |  |     self.min_freq = {} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     self.freq_tracker: Dict[str, FrequencyTracker] = {} | 
			
		
	
		
			
				
					|  |  |  |  |     self.poller = Poller() | 
			
		
	
		
			
				
					|  |  |  |  |     polled_services = set([poll, ] if poll is not None else services) | 
			
		
	
		
			
				
					|  |  |  |  |     self.non_polled_services = set(services) - polled_services | 
			
		
	
	
		
			
				
					|  |  |  | @ -138,22 +174,7 @@ class SubMaster: | 
			
		
	
		
			
				
					|  |  |  |  |       self.data[s] = getattr(data.as_reader(), s) | 
			
		
	
		
			
				
					|  |  |  |  |       self.logMonoTime[s] = 0 | 
			
		
	
		
			
				
					|  |  |  |  |       self.valid[s] = True  # FIXME: this should default to False | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |       freq = max(min([SERVICE_LIST[s].frequency, self.update_freq]), 1.) | 
			
		
	
		
			
				
					|  |  |  |  |       if s == poll: | 
			
		
	
		
			
				
					|  |  |  |  |         max_freq = freq | 
			
		
	
		
			
				
					|  |  |  |  |         min_freq = freq | 
			
		
	
		
			
				
					|  |  |  |  |       else: | 
			
		
	
		
			
				
					|  |  |  |  |         max_freq = min(freq, self.update_freq) | 
			
		
	
		
			
				
					|  |  |  |  |         if SERVICE_LIST[s].frequency >= 2*self.update_freq: | 
			
		
	
		
			
				
					|  |  |  |  |           min_freq = self.update_freq | 
			
		
	
		
			
				
					|  |  |  |  |         elif self.update_freq >= 2*SERVICE_LIST[s].frequency: | 
			
		
	
		
			
				
					|  |  |  |  |           min_freq = freq | 
			
		
	
		
			
				
					|  |  |  |  |         else: | 
			
		
	
		
			
				
					|  |  |  |  |           min_freq = min(freq, freq / 2.) | 
			
		
	
		
			
				
					|  |  |  |  |       self.max_freq[s] = max_freq*1.2 | 
			
		
	
		
			
				
					|  |  |  |  |       self.min_freq[s] = min_freq*0.8 | 
			
		
	
		
			
				
					|  |  |  |  |       self.recv_dts[s] = deque(maxlen=int(10*freq)) | 
			
		
	
		
			
				
					|  |  |  |  |       self.freq_tracker[s] = FrequencyTracker(SERVICE_LIST[s].frequency, self.update_freq, s == poll) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   def __getitem__(self, s: str) -> capnp.lib.capnp._DynamicStructReader: | 
			
		
	
		
			
				
					|  |  |  |  |     return self.data[s] | 
			
		
	
	
		
			
				
					|  |  |  | @ -182,8 +203,7 @@ class SubMaster: | 
			
		
	
		
			
				
					|  |  |  |  |       self.seen[s] = True | 
			
		
	
		
			
				
					|  |  |  |  |       self.updated[s] = True | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |       if self.recv_time[s] > 1e-5: | 
			
		
	
		
			
				
					|  |  |  |  |         self.recv_dts[s].append(cur_time - self.recv_time[s]) | 
			
		
	
		
			
				
					|  |  |  |  |       self.freq_tracker[s].record_recv_time(cur_time) | 
			
		
	
		
			
				
					|  |  |  |  |       self.recv_time[s] = cur_time | 
			
		
	
		
			
				
					|  |  |  |  |       self.recv_frame[s] = self.frame | 
			
		
	
		
			
				
					|  |  |  |  |       self.data[s] = getattr(msg, s) | 
			
		
	
	
		
			
				
					|  |  |  | @ -194,27 +214,10 @@ class SubMaster: | 
			
		
	
		
			
				
					|  |  |  |  |       if SERVICE_LIST[s].frequency > 1e-5 and not self.simulation: | 
			
		
	
		
			
				
					|  |  |  |  |         # alive if delay is within 10x the expected frequency | 
			
		
	
		
			
				
					|  |  |  |  |         self.alive[s] = (cur_time - self.recv_time[s]) < (10. / SERVICE_LIST[s].frequency) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         # check average frequency; slow to fall, quick to recover | 
			
		
	
		
			
				
					|  |  |  |  |         dts = self.recv_dts[s] | 
			
		
	
		
			
				
					|  |  |  |  |         assert dts.maxlen is not None | 
			
		
	
		
			
				
					|  |  |  |  |         recent_dts = list(dts)[-int(dts.maxlen / 10):] | 
			
		
	
		
			
				
					|  |  |  |  |         try: | 
			
		
	
		
			
				
					|  |  |  |  |           avg_freq = 1 / (sum(dts) / len(dts)) | 
			
		
	
		
			
				
					|  |  |  |  |           avg_freq_recent = 1 / (sum(recent_dts) / len(recent_dts)) | 
			
		
	
		
			
				
					|  |  |  |  |         except ZeroDivisionError: | 
			
		
	
		
			
				
					|  |  |  |  |           avg_freq = 0 | 
			
		
	
		
			
				
					|  |  |  |  |           avg_freq_recent = 0 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         avg_freq_ok = self.min_freq[s] <= avg_freq <= self.max_freq[s] | 
			
		
	
		
			
				
					|  |  |  |  |         recent_freq_ok = self.min_freq[s] <= avg_freq_recent <= self.max_freq[s] | 
			
		
	
		
			
				
					|  |  |  |  |         self.freq_ok[s] = avg_freq_ok or recent_freq_ok | 
			
		
	
		
			
				
					|  |  |  |  |         self.freq_ok[s] = self.freq_tracker[s].valid | 
			
		
	
		
			
				
					|  |  |  |  |       else: | 
			
		
	
		
			
				
					|  |  |  |  |         self.freq_ok[s] = True | 
			
		
	
		
			
				
					|  |  |  |  |         if self.simulation: | 
			
		
	
		
			
				
					|  |  |  |  |           self.alive[s] = self.seen[s] # alive is defined as seen when simulation flag set | 
			
		
	
		
			
				
					|  |  |  |  |         else: | 
			
		
	
		
			
				
					|  |  |  |  |           self.alive[s] = True | 
			
		
	
		
			
				
					|  |  |  |  |         self.alive[s] = self.seen[s] if self.simulation else True | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   def all_alive(self, service_list: Optional[List[str]] = None) -> bool: | 
			
		
	
		
			
				
					|  |  |  |  |     if service_list is None: | 
			
		
	
	
		
			
				
					|  |  |  | 
 |