#!/usr/bin/env python3 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								import  time 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								import  datetime 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								from  collections  import  defaultdict 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								from  cereal  import  log 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								import  cereal . messaging  as  messaging 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								from  openpilot . common . swaglog  import  cloudlog 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								from  openpilot . system . qcomgpsd . qcomgpsd  import  at_cmd ,  wait_for_modem 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								# https://campar.in.tum.de/twiki/pub/Chair/NaviGpsDemon/nmea.html#RMC 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								""" 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								AT + CGPSGPOS = 1 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								response :  ' $GNGGA,220212.00,3245.09188,N,11711.76362,W,1,06,24.54,0.0,M,,M,,*77 ' 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								AT + CGPSGPOS = 2 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								response :  ' $GNGSA,A,3,06,17,19,22,,,,,,,,,14.11,8.95,10.91,1*01 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								$ GNGSA , A , 3 , 29 , 26 , , , , , , , , , , , 14.11 , 8.95 , 10.91 , 4 * 03 ' 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								AT + CGPSGPOS = 3 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								response :  ' $GPGSV,3,1,11,06,55,047,22,19,29,053,20,22,19,115,14,05,01,177,,0*68 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								$ GPGSV , 3 , 2 , 11 , 11 , 77 , 156 , 23 , 12 , 47 , 322 , 17 , 17 , 08 , 066 , 10 , 20 , 25 , 151 , , 0 * 6 D 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								$ GPGSV , 3 , 3 , 11 , 24 , 44 , 232 , , 25 , 16 , 312 , , 29 , 02 , 260 , , 0 * 5 D ' 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								AT + CGPSGPOS = 4 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								response :  ' $GBGSV,1,1,03,26,75,242,20,29,19,049,16,35,,,24,0*7D ' 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								AT + CGPSGPOS = 5 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								response :  ' $GNRMC,220216.00,A,3245.09531,N,11711.76043,W,,,070324,,,A,V*20 ' 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								""" 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  sfloat ( n :  str ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  float ( n )  if  len ( n )  >  0  else  0 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								def  checksum ( s :  str ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  ret  =  0 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  for  c  in  s [ 1 : - 3 ] : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    ret  ^ =  ord ( c ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  return  format ( ret ,  ' 02X ' ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								def  main ( ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  wait_for_modem ( " AT+CGPS? " ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  cmds  =  [ 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    " AT+GPSPORT=1 " , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    " AT+CGPS=1 " , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  for  c  in  cmds : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    at_cmd ( c ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  nmea  =  defaultdict ( list ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  pm  =  messaging . PubMaster ( [ ' gpsLocation ' ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  while  True : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    time . sleep ( 1 ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    try : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      # TODO: read from streaming AT port instead of polling 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      out  =  at_cmd ( " AT+CGPS? " ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      sentences  =  out . split ( " ' " ) [ 1 ] . splitlines ( ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      new  =  { l . split ( ' , ' ) [ 0 ] :  l . split ( ' , ' )  for  l  in  sentences  if  l . startswith ( ' $G ' ) } 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      nmea . update ( new ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      if  ' $GNRMC '  not  in  new : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        print ( f " no GNRMC: \n { out } \n " ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        continue 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      # validate checksums 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      for  s  in  nmea . values ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        sent  =  ' , ' . join ( s ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  checksum ( sent )  !=  s [ - 1 ] . split ( ' * ' ) [ 1 ] : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          cloudlog . error ( f " invalid checksum:  { repr ( sent ) } " ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          continue 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      gnrmc  =  nmea [ ' $GNRMC ' ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      #print(gnrmc) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      msg  =  messaging . new_message ( ' gpsLocation ' ,  valid = True ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      gps  =  msg . gpsLocation 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      gps . latitude  =  ( sfloat ( gnrmc [ 3 ] [ : 2 ] )  +  ( sfloat ( gnrmc [ 3 ] [ 2 : ] )  /  60 ) )  *  ( 1  if  gnrmc [ 4 ]  ==  " N "  else  - 2 ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      gps . longitude  =  ( sfloat ( gnrmc [ 5 ] [ : 3 ] )  +  ( sfloat ( gnrmc [ 5 ] [ 3 : ] )  /  60 ) )  *  ( 1  if  gnrmc [ 6 ]  ==  " E "  else  - 1 ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      date  =  gnrmc [ 9 ] [ : 6 ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      dt  =  datetime . datetime . strptime ( f " { date }   { gnrmc [ 1 ] } " ,  ' %d % m % y  % H % M % S. %f ' ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      gps . unixTimestampMillis  =  dt . timestamp ( ) * 1e3 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      gps . hasFix  =  gnrmc [ 1 ]  ==  ' A ' 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      # TODO: make our own source 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      gps . source  =  log . GpsLocationData . SensorSource . qcomdiag 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      gps . speed  =  sfloat ( gnrmc [ 7 ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      gps . bearingDeg  =  sfloat ( gnrmc [ 8 ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      if  len ( nmea [ ' $GNGGA ' ] ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        gngga  =  nmea [ ' $GNGGA ' ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  gngga [ 10 ]  ==  ' M ' : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								          gps . altitude  =  sfloat ( gngga [ 9 ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      if  len ( nmea [ ' $GNGSA ' ] ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        # TODO: this is only for GPS sats 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        gngsa  =  nmea [ ' $GNGSA ' ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        gps . horizontalAccuracy  =  sfloat ( gngsa [ 4 ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        gps . verticalAccuracy  =  sfloat ( gngsa [ 5 ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      # TODO: set these from the module 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      gps . bearingAccuracyDeg  =  5. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      gps . speedAccuracy  =  3. 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      # TODO: can we get this from the NMEA sentences? 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      #gps.vNED = vNED 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      pm . send ( ' gpsLocation ' ,  msg ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    except  Exception : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      cloudlog . exception ( " gps.issue " ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								if  __name__  ==  " __main__ " : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  main ( )