#!/usr/bin/env python3 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								import  os 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								import  numpy  as  np 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								import  capnp 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								from  collections  import  deque 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								from  functools  import  partial 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								import  cereal . messaging  as  messaging 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								from  cereal  import  car ,  log 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								from  cereal . services  import  SERVICE_LIST 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								from  openpilot . common . params  import  Params 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								from  openpilot . common . realtime  import  config_realtime_process 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								from  openpilot . common . swaglog  import  cloudlog 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								from  openpilot . selfdrive . locationd . helpers  import  PoseCalibrator ,  Pose ,  fft_next_good_size ,  parabolic_peak_interp 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								BLOCK_SIZE  =  100 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								BLOCK_NUM  =  50 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								BLOCK_NUM_NEEDED  =  5 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								MOVING_WINDOW_SEC  =  300.0 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								MIN_OKAY_WINDOW_SEC  =  25.0 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								MIN_RECOVERY_BUFFER_SEC  =  2.0 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								MIN_VEGO  =  15.0 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								MIN_ABS_YAW_RATE  =  np . radians ( 1.0 ) 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								MAX_YAW_RATE_SANITY_CHECK  =  1.0 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								MIN_NCC  =  0.95 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								MAX_LAG  =  1.0 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								MAX_LAG_STD  =  0.1 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								MAX_LAT_ACCEL  =  2.0 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								MAX_LAT_ACCEL_DIFF  =  0.6 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  masked_normalized_cross_correlation ( expected_sig :  np . ndarray ,  actual_sig :  np . ndarray ,  mask :  np . ndarray ,  n :  int ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  """ 
  
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  References : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    D .  Padfield .  " Masked FFT registration " .  In  Proc .  Computer  Vision  and 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    Pattern  Recognition ,  pp .  2918 - 2925  ( 2010 ) . 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    : DOI : ` 10.1109 / CVPR .2010 .5540032 ` 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  """ 
  
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  eps  =  np . finfo ( np . float64 ) . eps 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  expected_sig  =  np . asarray ( expected_sig ,  dtype = np . float64 ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  actual_sig  =  np . asarray ( actual_sig ,  dtype = np . float64 ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  expected_sig [ ~ mask ]  =  0.0 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  actual_sig [ ~ mask ]  =  0.0 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  rotated_expected_sig  =  expected_sig [ : : - 1 ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  rotated_mask  =  mask [ : : - 1 ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  fft  =  partial ( np . fft . fft ,  n = n ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  actual_sig_fft  =  fft ( actual_sig ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  rotated_expected_sig_fft  =  fft ( rotated_expected_sig ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  actual_mask_fft  =  fft ( mask . astype ( np . float64 ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  rotated_mask_fft  =  fft ( rotated_mask . astype ( np . float64 ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  number_overlap_masked_samples  =  np . fft . ifft ( rotated_mask_fft  *  actual_mask_fft ) . real 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  number_overlap_masked_samples [ : ]  =  np . round ( number_overlap_masked_samples ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  number_overlap_masked_samples [ : ]  =  np . fmax ( number_overlap_masked_samples ,  eps ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  masked_correlated_actual_fft  =  np . fft . ifft ( rotated_mask_fft  *  actual_sig_fft ) . real 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  masked_correlated_expected_fft  =  np . fft . ifft ( actual_mask_fft  *  rotated_expected_sig_fft ) . real 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  numerator  =  np . fft . ifft ( rotated_expected_sig_fft  *  actual_sig_fft ) . real 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  numerator  - =  masked_correlated_actual_fft  *  masked_correlated_expected_fft  /  number_overlap_masked_samples 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  actual_squared_fft  =  fft ( actual_sig  * *  2 ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  actual_sig_denom  =  np . fft . ifft ( rotated_mask_fft  *  actual_squared_fft ) . real 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  actual_sig_denom  - =  masked_correlated_actual_fft  * *  2  /  number_overlap_masked_samples 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  actual_sig_denom [ : ]  =  np . fmax ( actual_sig_denom ,  0.0 ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  rotated_expected_squared_fft  =  fft ( rotated_expected_sig  * *  2 ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  expected_sig_denom  =  np . fft . ifft ( actual_mask_fft  *  rotated_expected_squared_fft ) . real 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  expected_sig_denom  - =  masked_correlated_expected_fft  * *  2  /  number_overlap_masked_samples 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  expected_sig_denom [ : ]  =  np . fmax ( expected_sig_denom ,  0.0 ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  denom  =  np . sqrt ( actual_sig_denom  *  expected_sig_denom ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  # zero-out samples with very small denominators 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  tol  =  1e3  *  eps  *  np . max ( np . abs ( denom ) ,  keepdims = True ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  nonzero_indices  =  denom  >  tol 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  ncc  =  np . zeros_like ( denom ,  dtype = np . float64 ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  ncc [ nonzero_indices ]  =  numerator [ nonzero_indices ]  /  denom [ nonzero_indices ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  np . clip ( ncc ,  - 1 ,  1 ,  out = ncc ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  ncc 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								class  Points : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  __init__ ( self ,  num_points :  int ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . times  =  deque [ float ] ( [ 0.0 ]  *  num_points ,  maxlen = num_points ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . okay  =  deque [ bool ] ( [ False ]  *  num_points ,  maxlen = num_points ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . desired  =  deque [ float ] ( [ 0.0 ]  *  num_points ,  maxlen = num_points ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . actual  =  deque [ float ] ( [ 0.0 ]  *  num_points ,  maxlen = num_points ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  @property 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  num_points ( self ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  len ( self . desired ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  @property 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  num_okay ( self ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  np . count_nonzero ( self . okay ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  update ( self ,  t :  float ,  desired :  float ,  actual :  float ,  okay :  bool ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . times . append ( t ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . okay . append ( okay ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . desired . append ( desired ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . actual . append ( actual ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  get ( self )  - >  tuple [ np . ndarray ,  np . ndarray ,  np . ndarray ,  np . ndarray ] : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  np . array ( self . times ) ,  np . array ( self . desired ) ,  np . array ( self . actual ) ,  np . array ( self . okay ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								class  BlockAverage : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  __init__ ( self ,  num_blocks :  int ,  block_size :  int ,  valid_blocks :  int ,  initial_value :  float ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . num_blocks  =  num_blocks 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . block_size  =  block_size 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . block_idx  =  valid_blocks  %  num_blocks 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . idx  =  0 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . values  =  np . tile ( initial_value ,  ( num_blocks ,  1 ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . valid_blocks  =  valid_blocks 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  update ( self ,  value :  float ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . values [ self . block_idx ]  =  ( self . idx  *  self . values [ self . block_idx ]  +  value )  /  ( self . idx  +  1 ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . idx  =  ( self . idx  +  1 )  %  self . block_size 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  self . idx  ==  0 : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      self . block_idx  =  ( self . block_idx  +  1 )  %  self . num_blocks 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      self . valid_blocks  =  min ( self . valid_blocks  +  1 ,  self . num_blocks ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  def  get ( self )  - >  tuple [ float ,  float ,  float ,  float ] : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    valid_block_idx  =  [ i  for  i  in  range ( self . valid_blocks )  if  i  !=  self . block_idx ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    valid_and_current_idx  =  valid_block_idx  +  ( [ self . block_idx ]  if  self . idx  >  0  else  [ ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    if  len ( valid_block_idx )  >  0 : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      valid_mean  =  float ( np . mean ( self . values [ valid_block_idx ] ,  axis = 0 ) . item ( ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      valid_std  =  float ( np . std ( self . values [ valid_block_idx ] ,  axis = 0 ) . item ( ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    else : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      valid_mean ,  valid_std  =  float ( ' nan ' ) ,  float ( ' nan ' ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  len ( valid_and_current_idx )  >  0 : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      current_mean  =  float ( np . mean ( self . values [ valid_and_current_idx ] ,  axis = 0 ) . item ( ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      current_std  =  float ( np . std ( self . values [ valid_and_current_idx ] ,  axis = 0 ) . item ( ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    else : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      current_mean ,  current_std  =  float ( ' nan ' ) ,  float ( ' nan ' ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  valid_mean ,  valid_std ,  current_mean ,  current_std 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								class  LateralLagEstimator : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  inputs  =  { " carControl " ,  " carState " ,  " controlsState " ,  " liveCalibration " ,  " livePose " } 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  __init__ ( self ,  CP :  car . CarParams ,  dt :  float , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								               block_count :  int  =  BLOCK_NUM ,  min_valid_block_count :  int  =  BLOCK_NUM_NEEDED ,  block_size :  int  =  BLOCK_SIZE , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								               window_sec :  float  =  MOVING_WINDOW_SEC ,  okay_window_sec :  float  =  MIN_OKAY_WINDOW_SEC ,  min_recovery_buffer_sec :  float  =  MIN_RECOVERY_BUFFER_SEC , 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								               min_vego :  float  =  MIN_VEGO ,  min_yr :  float  =  MIN_ABS_YAW_RATE ,  min_ncc :  float  =  MIN_NCC , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								               max_lat_accel :  float  =  MAX_LAT_ACCEL ,  max_lat_accel_diff :  float  =  MAX_LAT_ACCEL_DIFF ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . dt  =  dt 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . window_sec  =  window_sec 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . okay_window_sec  =  okay_window_sec 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . min_recovery_buffer_sec  =  min_recovery_buffer_sec 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . initial_lag  =  CP . steerActuatorDelay  +  0.2 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . block_size  =  block_size 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . block_count  =  block_count 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . min_valid_block_count  =  min_valid_block_count 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . min_vego  =  min_vego 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . min_yr  =  min_yr 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . min_ncc  =  min_ncc 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    self . max_lat_accel  =  max_lat_accel 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . max_lat_accel_diff  =  max_lat_accel_diff 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . t  =  0.0 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . lat_active  =  False 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . steering_pressed  =  False 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . steering_saturated  =  False 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . desired_curvature  =  0.0 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . v_ego  =  0.0 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . yaw_rate  =  0.0 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    self . yaw_rate_std  =  0.0 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . pose_valid  =  False 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . last_lat_inactive_t  =  0.0 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . last_steering_pressed_t  =  0.0 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . last_steering_saturated_t  =  0.0 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    self . last_pose_invalid_t  =  0.0 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . last_estimate_t  =  0.0 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . calibrator  =  PoseCalibrator ( ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . reset ( self . initial_lag ,  0 ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  reset ( self ,  initial_lag :  float ,  valid_blocks :  int ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    window_len  =  int ( self . window_sec  /  self . dt ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . points  =  Points ( window_len ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . block_avg  =  BlockAverage ( self . block_count ,  self . block_size ,  valid_blocks ,  initial_lag ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  get_msg ( self ,  valid :  bool ,  debug :  bool  =  False )  - >  capnp . _DynamicStructBuilder : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    msg  =  messaging . new_message ( ' liveDelay ' ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    msg . valid  =  valid 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    liveDelay  =  msg . liveDelay 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    valid_mean_lag ,  valid_std ,  current_mean_lag ,  current_std  =  self . block_avg . get ( ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  self . block_avg . valid_blocks  > =  self . min_valid_block_count  and  not  np . isnan ( valid_mean_lag )  and  not  np . isnan ( valid_std ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      if  valid_std  >  MAX_LAG_STD : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        liveDelay . status  =  log . LiveDelayData . Status . invalid 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      else : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        liveDelay . status  =  log . LiveDelayData . Status . estimated 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    else : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      liveDelay . status  =  log . LiveDelayData . Status . unestimated 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  liveDelay . status  ==  log . LiveDelayData . Status . estimated : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      liveDelay . lateralDelay  =  valid_mean_lag 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    else : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      liveDelay . lateralDelay  =  self . initial_lag 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  not  np . isnan ( current_mean_lag )  and  not  np . isnan ( current_std ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      liveDelay . lateralDelayEstimate  =  current_mean_lag 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      liveDelay . lateralDelayEstimateStd  =  current_std 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    else : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      liveDelay . lateralDelayEstimate  =  self . initial_lag 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      liveDelay . lateralDelayEstimateStd  =  0.0 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    liveDelay . validBlocks  =  self . block_avg . valid_blocks 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  debug : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      liveDelay . points  =  self . block_avg . values . flatten ( ) . tolist ( ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  msg 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  handle_log ( self ,  t :  float ,  which :  str ,  msg :  capnp . _DynamicStructReader ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  which  ==  " carControl " : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      self . lat_active  =  msg . latActive 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    elif  which  ==  " carState " : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      self . steering_pressed  =  msg . steeringPressed 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      self . v_ego  =  msg . vEgo 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    elif  which  ==  " controlsState " : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      self . steering_saturated  =  getattr ( msg . lateralControlState ,  msg . lateralControlState . which ( ) ) . saturated 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      self . desired_curvature  =  msg . desiredCurvature 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    elif  which  ==  " liveCalibration " : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      self . calibrator . feed_live_calib ( msg ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    elif  which  ==  " livePose " : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      device_pose  =  Pose . from_live_pose ( msg ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      calibrated_pose  =  self . calibrator . build_calibrated_pose ( device_pose ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      self . yaw_rate  =  calibrated_pose . angular_velocity . yaw 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      self . yaw_rate_std  =  calibrated_pose . angular_velocity . yaw_std 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      self . pose_valid  =  msg . angularVelocityDevice . valid  and  msg . posenetOK  and  msg . inputsOK 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . t  =  t 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  points_enough ( self ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  self . points . num_points  > =  int ( self . okay_window_sec  /  self . dt ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  points_valid ( self ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  self . points . num_okay  > =  int ( self . okay_window_sec  /  self . dt ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  update_points ( self ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    la_desired  =  self . desired_curvature  *  self . v_ego  *  self . v_ego 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    la_actual_pose  =  self . yaw_rate  *  self . v_ego 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    fast  =  self . v_ego  >  self . min_vego 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    turning  =  np . abs ( self . yaw_rate )  > =  self . min_yr 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    sensors_valid  =  self . pose_valid  and  np . abs ( self . yaw_rate )  <  MAX_YAW_RATE_SANITY_CHECK  and  self . yaw_rate_std  <  MAX_YAW_RATE_SANITY_CHECK 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    la_valid  =  np . abs ( la_actual_pose )  < =  self . max_lat_accel  and  np . abs ( la_desired  -  la_actual_pose )  < =  self . max_lat_accel_diff 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    calib_valid  =  self . calibrator . calib_valid 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  not  self . lat_active : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      self . last_lat_inactive_t  =  self . t 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  self . steering_pressed : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      self . last_steering_pressed_t  =  self . t 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  self . steering_saturated : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      self . last_steering_saturated_t  =  self . t 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    if  not  sensors_valid  or  not  la_valid : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      self . last_pose_invalid_t  =  self . t 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    has_recovered  =  all (  # wait for recovery after !lat_active, steering_pressed, steering_saturated, !sensors/la_valid 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      self . t  -  last_t  > =  self . min_recovery_buffer_sec 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      for  last_t  in  [ self . last_lat_inactive_t ,  self . last_steering_pressed_t ,  self . last_steering_saturated_t ,  self . last_pose_invalid_t ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    okay  =  self . lat_active  and  not  self . steering_pressed  and  not  self . steering_saturated  and  \
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								           fast  and  turning  and  has_recovered  and  calib_valid  and  sensors_valid  and  la_valid 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . points . update ( self . t ,  la_desired ,  la_actual_pose ,  okay ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  update_estimate ( self ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  not  self . points_enough ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      return 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    times ,  desired ,  actual ,  okay  =  self . points . get ( ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    # check if there are any new valid data points since the last update 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    is_valid  =  self . points_valid ( ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  self . last_estimate_t  !=  0  and  times [ 0 ]  < =  self . last_estimate_t : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      new_values_start_idx  =  next ( - i  for  i ,  t  in  enumerate ( reversed ( times ) )  if  t  < =  self . last_estimate_t ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      is_valid  =  is_valid  and  not  ( new_values_start_idx  ==  0  or  not  np . any ( okay [ new_values_start_idx : ] ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    delay ,  corr  =  self . actuator_delay ( desired ,  actual ,  okay ,  self . dt ,  MAX_LAG ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  corr  <  self . min_ncc  or  not  is_valid : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      return 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . block_avg . update ( delay ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . last_estimate_t  =  self . t 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  actuator_delay ( self ,  expected_sig :  np . ndarray ,  actual_sig :  np . ndarray ,  mask :  np . ndarray ,  dt :  float ,  max_lag :  float )  - >  tuple [ float ,  float ] : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    assert  len ( expected_sig )  ==  len ( actual_sig ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    max_lag_samples  =  int ( max_lag  /  dt ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    padded_size  =  fft_next_good_size ( len ( expected_sig )  +  max_lag_samples ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    ncc  =  masked_normalized_cross_correlation ( expected_sig ,  actual_sig ,  mask ,  padded_size ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    # only consider lags from 0 to max_lag 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    roi_ncc  =  ncc [ len ( expected_sig )  -  1 :  len ( expected_sig )  -  1  +  max_lag_samples ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    max_corr_index  =  np . argmax ( roi_ncc ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    corr  =  roi_ncc [ max_corr_index ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    lag  =  parabolic_peak_interp ( roi_ncc ,  max_corr_index )  *  dt 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  lag ,  corr 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  retrieve_initial_lag ( params_reader :  Params ,  CP :  car . CarParams ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  last_lag_data  =  params_reader . get ( " LiveDelay " ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  last_carparams_data  =  params_reader . get ( " CarParamsPrevRoute " ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  last_lag_data  is  not  None : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    try : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      with  log . Event . from_bytes ( last_lag_data )  as  last_lag_msg ,  car . CarParams . from_bytes ( last_carparams_data )  as  last_CP : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        ld  =  last_lag_msg . liveDelay 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  last_CP . carFingerprint  !=  CP . carFingerprint : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          raise  Exception ( " Car model mismatch " ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        lag ,  valid_blocks ,  status  =  ld . lateralDelayEstimate ,  ld . validBlocks ,  ld . status 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        assert  valid_blocks  < =  BLOCK_NUM ,  " Invalid number of valid blocks " 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        assert  status  !=  log . LiveDelayData . Status . invalid ,  " Lag estimate is invalid " 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  lag ,  valid_blocks 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    except  Exception  as  e : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      cloudlog . error ( f " Failed to retrieve initial lag:  { e } " ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  None 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  main ( ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  config_realtime_process ( [ 0 ,  1 ,  2 ,  3 ] ,  5 ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  DEBUG  =  bool ( int ( os . getenv ( " DEBUG " ,  " 0 " ) ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  pm  =  messaging . PubMaster ( [ ' liveDelay ' ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  sm  =  messaging . SubMaster ( [ ' livePose ' ,  ' liveCalibration ' ,  ' carState ' ,  ' controlsState ' ,  ' carControl ' ] ,  poll = ' livePose ' ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  params_reader  =  Params ( ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  CP  =  messaging . log_from_bytes ( params_reader . get ( " CarParams " ,  block = True ) ,  car . CarParams ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  lag_learner  =  LateralLagEstimator ( CP ,  1.  /  SERVICE_LIST [ ' livePose ' ] . frequency ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  if  ( initial_lag_params  :=  retrieve_initial_lag ( params_reader ,  CP ) )  is  not  None : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    lag ,  valid_blocks  =  initial_lag_params 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    lag_learner . reset ( lag ,  valid_blocks ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  while  True : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    sm . update ( ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  sm . all_checks ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      for  which  in  sorted ( sm . updated . keys ( ) ,  key = lambda  x :  sm . logMonoTime [ x ] ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  sm . updated [ which ] : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          t  =  sm . logMonoTime [ which ]  *  1e-9 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          lag_learner . handle_log ( t ,  which ,  sm [ which ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      lag_learner . update_points ( ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    # 4Hz driven by livePose 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  sm . frame  %  5  ==  0 : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      lag_learner . update_estimate ( ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      lag_msg  =  lag_learner . get_msg ( sm . all_checks ( ) ,  DEBUG ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      lag_msg_dat  =  lag_msg . to_bytes ( ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      pm . send ( ' liveDelay ' ,  lag_msg_dat ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      if  sm . frame  %  1200  ==  0 :  # cache every 60 seconds 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        params_reader . put_nonblocking ( " LiveDelay " ,  lag_msg_dat )