@ -19,7 +19,7 @@ from laika.downloader import DownloadFailed 
			
		
	
		
			
				
					from  laika . ephemeris  import  Ephemeris ,  EphemerisType ,  convert_ublox_ephem  
			
		
	
		
			
				
					from  laika . gps_time  import  GPSTime  
			
		
	
		
			
				
					from  laika . helpers  import  ConstellationId  
			
		
	
		
			
				
					from  laika . raw_gnss  import  GNSSMeasurement ,  correct_measurements ,  process_measurements ,  read_raw_ublox  
			
		
	
		
			
				
					from  laika . raw_gnss  import  GNSSMeasurement ,  correct_measurements ,  process_measurements ,  read_raw_ublox ,  read_raw_qcom  
			
		
	
		
			
				
					from  selfdrive . locationd . laikad_helpers  import  calc_pos_fix_gauss_newton ,  get_posfix_sympy_fun  
			
		
	
		
			
				
					from  selfdrive . locationd . models . constants  import  GENERATED_DIR ,  ObservationKind  
			
		
	
		
			
				
					from  selfdrive . locationd . models . gnss_kf  import  GNSSKalman  
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -36,7 +36,7 @@ POS_FIX_RESIDUAL_THRESHOLD = 100.0 
			
		
	
		
			
				
					class  Laikad :  
			
		
	
		
			
				
					  def  __init__ ( self ,  valid_const = ( " GPS " ,  " GLONASS " ) ,  auto_fetch_orbits = True ,  auto_update = False ,   
			
		
	
		
			
				
					               valid_ephem_types = ( EphemerisType . ULTRA_RAPID_ORBIT ,  EphemerisType . NAV ) ,   
			
		
	
		
			
				
					               save_ephemeris = False ) :   
			
		
	
		
			
				
					               save_ephemeris = False ,  use_qcom = False ) :   
			
		
	
		
			
				
					    """   
			
		
	
		
			
				
					    valid_const :  GNSS  constellation  which  can  be  used   
			
		
	
		
			
				
					    auto_fetch_orbits :  If  true  fetch  orbits  from  internet  when  needed   
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -45,14 +45,14 @@ class Laikad: 
			
		
	
		
			
				
					    save_ephemeris :  If  true  saves  and  loads  nav  and  orbit  ephemeris  to  cache .   
			
		
	
		
			
				
					    """   
			
		
	
		
			
				
					    self . astro_dog  =  AstroDog ( valid_const = valid_const ,  auto_update = auto_update ,  valid_ephem_types = valid_ephem_types ,  clear_old_ephemeris = True ,  cache_dir = DOWNLOADS_CACHE_FOLDER )   
			
		
	
		
			
				
					    self . gnss_kf  =  GNSSKalman ( GENERATED_DIR ,  cython = True )   
			
		
	
		
			
				
					    self . gnss_kf  =  GNSSKalman ( GENERATED_DIR ,  cython = True ,  erratic_clock = use_qcom )   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					    self . auto_fetch_orbits  =  auto_fetch_orbits   
			
		
	
		
			
				
					    self . orbit_fetch_executor :  Optional [ ProcessPoolExecutor ]  =  None   
			
		
	
		
			
				
					    self . orbit_fetch_future :  Optional [ Future ]  =  None   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					    self . last_fetch_orbits_t  =  None   
			
		
	
		
			
				
					    self . got_first_ublox _msg  =  False   
			
		
	
		
			
				
					    self . got_first_gnss _msg  =  False   
			
		
	
		
			
				
					    self . last_cached_t  =  None   
			
		
	
		
			
				
					    self . save_ephemeris  =  save_ephemeris   
			
		
	
		
			
				
					    self . load_cache ( )   
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -61,6 +61,7 @@ class Laikad: 
			
		
	
		
			
				
					    self . last_pos_fix  =  [ ]   
			
		
	
		
			
				
					    self . last_pos_residual  =  [ ]   
			
		
	
		
			
				
					    self . last_pos_fix_t  =  None   
			
		
	
		
			
				
					    self . use_qcom  =  use_qcom   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  def  load_cache ( self ) :   
			
		
	
		
			
				
					    if  not  self . save_ephemeris :   
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -105,17 +106,40 @@ class Laikad: 
			
		
	
		
			
				
					          cloudlog . debug ( f " Pos fix failed with median:  { residual_median . round ( ) } . All residuals:  { np . round ( pos_fix_residual ) } " )   
			
		
	
		
			
				
					    return  self . last_pos_fix   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  def  process_ublox_msg ( self ,  ublox_msg ,  ublox_mono_time :  int ,  block = False ) :   
			
		
	
		
			
				
					    if  ublox_msg . which  ==  ' measurementReport ' :   
			
		
	
		
			
				
					      t  =  ublox_mono_time  *  1e-9   
			
		
	
		
			
				
					      report  =  ublox_msg . measurementReport   
			
		
	
		
			
				
					      if  report . gpsWeek  >  0 :   
			
		
	
		
			
				
					        self . got_first_ublox_msg  =  True   
			
		
	
		
			
				
					        latest_msg_t  =  GPSTime ( report . gpsWeek ,  report . rcvTow )   
			
		
	
		
			
				
					  def  is_good_report ( self ,  gnss_msg ) :   
			
		
	
		
			
				
					    if  gnss_msg . which  ==  ' drMeasurementReport '  and  self . use_qcom :   
			
		
	
		
			
				
					      constellation_id  =  ConstellationId . from_qcom_source ( gnss_msg . drMeasurementReport . source )   
			
		
	
		
			
				
					      # TODO support GLONASS   
			
		
	
		
			
				
					      return  constellation_id  in  [ ConstellationId . GPS ,  ConstellationId . SBAS ]   
			
		
	
		
			
				
					    elif  gnss_msg . which  ==  ' measurementReport '  and  not  self . use_qcom :   
			
		
	
		
			
				
					      return  True   
			
		
	
		
			
				
					    else :   
			
		
	
		
			
				
					      return  False   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  def  read_report ( self ,  gnss_msg ) :   
			
		
	
		
			
				
					    if  self . use_qcom :   
			
		
	
		
			
				
					      report  =  gnss_msg . drMeasurementReport   
			
		
	
		
			
				
					      week  =  report . gpsWeek   
			
		
	
		
			
				
					      tow  =  report . gpsMilliseconds  /  1000.0   
			
		
	
		
			
				
					      new_meas  =  read_raw_qcom ( report )   
			
		
	
		
			
				
					    else :   
			
		
	
		
			
				
					      report  =  gnss_msg . measurementReport   
			
		
	
		
			
				
					      week  =  report . gpsWeek   
			
		
	
		
			
				
					      tow  =  report . rcvTow   
			
		
	
		
			
				
					      new_meas  =  read_raw_ublox ( report )   
			
		
	
		
			
				
					    return  week ,  tow ,  new_meas   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  def  process_gnss_msg ( self ,  gnss_msg ,  gnss_mono_time :  int ,  block = False ) :   
			
		
	
		
			
				
					    if  self . is_good_report ( gnss_msg ) :   
			
		
	
		
			
				
					      week ,  tow ,  new_meas  =  self . read_report ( gnss_msg )   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					      t  =  gnss_mono_time  *  1e-9   
			
		
	
		
			
				
					      if  week  >  0 :   
			
		
	
		
			
				
					        self . got_first_gnss_msg  =  True   
			
		
	
		
			
				
					        latest_msg_t  =  GPSTime ( week ,  tow )   
			
		
	
		
			
				
					        if  self . auto_fetch_orbits :   
			
		
	
		
			
				
					          self . fetch_orbits ( latest_msg_t ,  block )   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					      new_meas  =  read_raw_ublox ( report )   
			
		
	
		
			
				
					      # Filter measurements with unexpected pseudoranges for GPS and GLONASS satellites   
			
		
	
		
			
				
					      new_meas  =  [ m  for  m  in  new_meas  if  1e7  <  m . observables [ ' C1C ' ]  <  3e7 ]   
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -123,7 +147,7 @@ class Laikad: 
			
		
	
		
			
				
					      est_pos  =  self . get_est_pos ( t ,  processed_measurements )   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					      corrected_measurements  =  correct_measurements ( processed_measurements ,  est_pos ,  self . astro_dog )  if  len ( est_pos )  >  0  else  [ ]   
			
		
	
		
			
				
					      if  ublox _mono_time%  10  ==  0 :   
			
		
	
		
			
				
					      if  gnss _mono_time%  10  ==  0 :   
			
		
	
		
			
				
					        cloudlog . debug ( f " Measurements Incoming/Processed/Corrected:  { len ( new_meas ) ,  len ( processed_measurements ) ,  len ( corrected_measurements ) } " )   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					      self . update_localizer ( est_pos ,  t ,  corrected_measurements )   
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -139,20 +163,21 @@ class Laikad: 
			
		
	
		
			
				
					      dat  =  messaging . new_message ( " gnssMeasurements " )   
			
		
	
		
			
				
					      measurement_msg  =  log . LiveLocationKalman . Measurement . new_message   
			
		
	
		
			
				
					      dat . gnssMeasurements  =  {   
			
		
	
		
			
				
					        " gpsWeek " :  report . gpsW eek,   
			
		
	
		
			
				
					        " gpsTimeOfWeek " :  repor t. rcvT ow,   
			
		
	
		
			
				
					        " gpsWeek " :  w eek,   
			
		
	
		
			
				
					        " gpsTimeOfWeek " :  tow ,   
			
		
	
		
			
				
					        " positionECEF " :  measurement_msg ( value = ecef_pos . tolist ( ) ,  std = pos_std . tolist ( ) ,  valid = kf_valid ) ,   
			
		
	
		
			
				
					        " velocityECEF " :  measurement_msg ( value = ecef_vel . tolist ( ) ,  std = vel_std . tolist ( ) ,  valid = kf_valid ) ,   
			
		
	
		
			
				
					        " positionFixECEF " :  measurement_msg ( value = self . last_pos_fix ,  std = self . last_pos_residual ,  valid = self . last_pos_fix_t  ==  t ) ,   
			
		
	
		
			
				
					        " ubloxMonoTime " :  ublox _mono_time,   
			
		
	
		
			
				
					        " ubloxMonoTime " :  gnss _mono_time,   
			
		
	
		
			
				
					        " correctedMeasurements " :  meas_msgs   
			
		
	
		
			
				
					      }   
			
		
	
		
			
				
					      return  dat   
			
		
	
		
			
				
					    elif  ublox_msg . which  ==  ' ephemeris ' :   
			
		
	
		
			
				
					      ephem  =  convert_ublox_ephem ( ublox_msg . ephemeris )   
			
		
	
		
			
				
					    # TODO this only works on GLONASS, qcom needs live ephemeris parsing too   
			
		
	
		
			
				
					    elif  gnss_msg . which  ==  ' ephemeris ' :   
			
		
	
		
			
				
					      ephem  =  convert_ublox_ephem ( gnss_msg . ephemeris )   
			
		
	
		
			
				
					      self . astro_dog . add_navs ( { ephem . prn :  [ ephem ] } )   
			
		
	
		
			
				
					      self . cache_ephemeris ( t = ephem . epoch )   
			
		
	
		
			
				
					    # elif ublox _msg.which == 'ionoData':   
			
		
	
		
			
				
					    #elif gnss _msg.which == 'ionoData':   
			
		
	
		
			
				
					    # todo add this. Needed to better correct messages offline. First fix ublox_msg.cc to sent them.   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  def  update_localizer ( self ,  est_pos ,  t :  float ,  measurements :  List [ GNSSMeasurement ] ) :   
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -303,24 +328,30 @@ class EphemerisSourceType(IntEnum): 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					def  main ( sm = None ,  pm = None ) :  
			
		
	
		
			
				
					  use_qcom  =  os . path . isfile ( " /persist/comma/use-quectel-rawgps " )   
			
		
	
		
			
				
					  if  use_qcom :   
			
		
	
		
			
				
					    raw_gnss_socket  =  " qcomGnss "   
			
		
	
		
			
				
					  else :   
			
		
	
		
			
				
					    raw_gnss_socket  =  " ubloxGnss "   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  if  sm  is  None :   
			
		
	
		
			
				
					    sm  =  messaging . SubMaster ( [ ' ubloxGnss ' ,  ' clocks ' ] )   
			
		
	
		
			
				
					    sm  =  messaging . SubMaster ( [ raw_gnss_socket ,  ' clocks ' ] )   
			
		
	
		
			
				
					  if  pm  is  None :   
			
		
	
		
			
				
					    pm  =  messaging . PubMaster ( [ ' gnssMeasurements ' ] )   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  replay  =  " REPLAY "  in  os . environ   
			
		
	
		
			
				
					  use_internet  =  " LAIKAD_NO_INTERNET "  not  in  os . environ   
			
		
	
		
			
				
					  laikad  =  Laikad ( save_ephemeris = not  replay ,  auto_fetch_orbits = use_internet )   
			
		
	
		
			
				
					  laikad  =  Laikad ( save_ephemeris = not  replay ,  auto_fetch_orbits = use_internet ,  use_qcom = use_qcom )   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  while  True :   
			
		
	
		
			
				
					    sm . update ( )   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					    if  sm . updated [ ' ubloxGnss ' ] :   
			
		
	
		
			
				
					      ublox_msg  =  sm [ ' ubloxGnss ' ]   
			
		
	
		
			
				
					      msg  =  laikad . process_ublox_msg ( ublox _msg ,  sm . logMonoTime [ ' ubloxGnss ' ] ,  block = replay )   
			
		
	
		
			
				
					    if  sm . updated [ raw_gnss_socket ] :   
			
		
	
		
			
				
					      gnss_msg  =  sm [ raw_gnss_socket ]   
			
		
	
		
			
				
					      msg  =  laikad . process_gnss_msg ( gnss _msg ,  sm . logMonoTime [ raw_gnss_socket ] ,  block = replay )   
			
		
	
		
			
				
					      if  msg  is  not  None :   
			
		
	
		
			
				
					        pm . send ( ' gnssMeasurements ' ,  msg )   
			
		
	
		
			
				
					    if  not  laikad . got_first_ublox _msg  and  sm . updated [ ' clocks ' ] :   
			
		
	
		
			
				
					    if  not  laikad . got_first_gnss _msg  and  sm . updated [ ' clocks ' ] :   
			
		
	
		
			
				
					      clocks_msg  =  sm [ ' clocks ' ]   
			
		
	
		
			
				
					      t  =  GPSTime . from_datetime ( datetime . utcfromtimestamp ( clocks_msg . wallTimeNanos  *  1E-9 ) )   
			
		
	
		
			
				
					      if  laikad . auto_fetch_orbits :