|  |  |  | @ -1,5 +1,6 @@ | 
			
		
	
		
			
				
					|  |  |  |  | #!/usr/bin/env python3 | 
			
		
	
		
			
				
					|  |  |  |  | import json | 
			
		
	
		
			
				
					|  |  |  |  | import os | 
			
		
	
		
			
				
					|  |  |  |  | import time | 
			
		
	
		
			
				
					|  |  |  |  | from collections import defaultdict | 
			
		
	
		
			
				
					|  |  |  |  | from concurrent.futures import Future, ProcessPoolExecutor | 
			
		
	
	
		
			
				
					|  |  |  | @ -32,8 +33,10 @@ class Laikad: | 
			
		
	
		
			
				
					|  |  |  |  |                save_ephemeris=False, last_known_position=None): | 
			
		
	
		
			
				
					|  |  |  |  |     self.astro_dog = AstroDog(valid_const=valid_const, auto_update=auto_update, valid_ephem_types=valid_ephem_types, clear_old_ephemeris=True) | 
			
		
	
		
			
				
					|  |  |  |  |     self.gnss_kf = GNSSKalman(GENERATED_DIR) | 
			
		
	
		
			
				
					|  |  |  |  |     self.orbit_fetch_executor = ProcessPoolExecutor() | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     self.orbit_fetch_executor: Optional[ProcessPoolExecutor] = None | 
			
		
	
		
			
				
					|  |  |  |  |     self.orbit_fetch_future: Optional[Future] = None | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     self.last_fetch_orbits_t = None | 
			
		
	
		
			
				
					|  |  |  |  |     self.last_cached_t = None | 
			
		
	
		
			
				
					|  |  |  |  |     self.save_ephemeris = save_ephemeris | 
			
		
	
	
		
			
				
					|  |  |  | @ -44,9 +47,13 @@ class Laikad: | 
			
		
	
		
			
				
					|  |  |  |  |     self.last_pos_fix_t = None | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   def load_cache(self): | 
			
		
	
		
			
				
					|  |  |  |  |     if not self.save_ephemeris: | 
			
		
	
		
			
				
					|  |  |  |  |       return | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     cache = Params().get(EPHEMERIS_CACHE) | 
			
		
	
		
			
				
					|  |  |  |  |     if not cache: | 
			
		
	
		
			
				
					|  |  |  |  |       return | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     try: | 
			
		
	
		
			
				
					|  |  |  |  |       cache = json.loads(cache, object_hook=deserialize_hook) | 
			
		
	
		
			
				
					|  |  |  |  |       self.astro_dog.add_orbits(cache['orbits']) | 
			
		
	
	
		
			
				
					|  |  |  | @ -71,7 +78,7 @@ class Laikad: | 
			
		
	
		
			
				
					|  |  |  |  |         self.last_pos_residual = pos_fix_residual | 
			
		
	
		
			
				
					|  |  |  |  |         self.last_pos_fix_t = t | 
			
		
	
		
			
				
					|  |  |  |  |     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 | 
			
		
	
	
		
			
				
					|  |  |  | @ -152,17 +159,22 @@ class Laikad: | 
			
		
	
		
			
				
					|  |  |  |  |   def fetch_orbits(self, t: GPSTime, block): | 
			
		
	
		
			
				
					|  |  |  |  |     if t not in self.astro_dog.orbit_fetched_times and (self.last_fetch_orbits_t is None or t - self.last_fetch_orbits_t > SECS_IN_HR): | 
			
		
	
		
			
				
					|  |  |  |  |       astro_dog_vars = self.astro_dog.valid_const, self.astro_dog.auto_update, self.astro_dog.valid_ephem_types | 
			
		
	
		
			
				
					|  |  |  |  |       if self.orbit_fetch_future is None: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |       ret = None | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |       if block: | 
			
		
	
		
			
				
					|  |  |  |  |         ret = get_orbit_data(t, *astro_dog_vars) | 
			
		
	
		
			
				
					|  |  |  |  |       elif self.orbit_fetch_future is None: | 
			
		
	
		
			
				
					|  |  |  |  |         self.orbit_fetch_executor = ProcessPoolExecutor(max_workers=1) | 
			
		
	
		
			
				
					|  |  |  |  |         self.orbit_fetch_future = self.orbit_fetch_executor.submit(get_orbit_data, t, *astro_dog_vars) | 
			
		
	
		
			
				
					|  |  |  |  |         if block: | 
			
		
	
		
			
				
					|  |  |  |  |           self.orbit_fetch_future.result() | 
			
		
	
		
			
				
					|  |  |  |  |       if self.orbit_fetch_future.done(): | 
			
		
	
		
			
				
					|  |  |  |  |         ret = self.orbit_fetch_future.result() | 
			
		
	
		
			
				
					|  |  |  |  |       elif self.orbit_fetch_future.done(): | 
			
		
	
		
			
				
					|  |  |  |  |         self.last_fetch_orbits_t = t | 
			
		
	
		
			
				
					|  |  |  |  |         if ret: | 
			
		
	
		
			
				
					|  |  |  |  |           self.astro_dog.orbits, self.astro_dog.orbit_fetched_times = ret | 
			
		
	
		
			
				
					|  |  |  |  |           self.cache_ephemeris(t=t) | 
			
		
	
		
			
				
					|  |  |  |  |         self.orbit_fetch_future = None | 
			
		
	
		
			
				
					|  |  |  |  |         ret = self.orbit_fetch_future.result() | 
			
		
	
		
			
				
					|  |  |  |  |         self.orbit_fetch_executor = self.orbit_fetch_future = None | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |       if ret is not None: | 
			
		
	
		
			
				
					|  |  |  |  |         self.astro_dog.orbits, self.astro_dog.orbit_fetched_times = ret | 
			
		
	
		
			
				
					|  |  |  |  |         self.cache_ephemeris(t=t) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | def get_orbit_data(t: GPSTime, valid_const, auto_update, valid_ephem_types): | 
			
		
	
	
		
			
				
					|  |  |  | @ -174,7 +186,7 @@ def get_orbit_data(t: GPSTime, valid_const, auto_update, valid_ephem_types): | 
			
		
	
		
			
				
					|  |  |  |  |     astro_dog.get_orbit_data(t, only_predictions=True) | 
			
		
	
		
			
				
					|  |  |  |  |     data = (astro_dog.orbits, astro_dog.orbit_fetched_times) | 
			
		
	
		
			
				
					|  |  |  |  |   except RuntimeError as e: | 
			
		
	
		
			
				
					|  |  |  |  |     cloudlog.info(f"No orbit data found. {e}") | 
			
		
	
		
			
				
					|  |  |  |  |     cloudlog.warning(f"No orbit data found. {e}") | 
			
		
	
		
			
				
					|  |  |  |  |   cloudlog.info(f"Done parsing orbits. Took {time.monotonic() - start_time:.1f}s") | 
			
		
	
		
			
				
					|  |  |  |  |   return data | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -255,17 +267,21 @@ class EphemerisSourceType(IntEnum): | 
			
		
	
		
			
				
					|  |  |  |  |   glonassIacUltraRapid = 2 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | def main(): | 
			
		
	
		
			
				
					|  |  |  |  |   sm = messaging.SubMaster(['ubloxGnss']) | 
			
		
	
		
			
				
					|  |  |  |  |   pm = messaging.PubMaster(['gnssMeasurements']) | 
			
		
	
		
			
				
					|  |  |  |  | def main(sm=None, pm=None): | 
			
		
	
		
			
				
					|  |  |  |  |   if sm is None: | 
			
		
	
		
			
				
					|  |  |  |  |     sm = messaging.SubMaster(['ubloxGnss']) | 
			
		
	
		
			
				
					|  |  |  |  |   if pm is None: | 
			
		
	
		
			
				
					|  |  |  |  |     pm = messaging.PubMaster(['gnssMeasurements']) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   replay = "REPLAY" in os.environ | 
			
		
	
		
			
				
					|  |  |  |  |   # todo get last_known_position | 
			
		
	
		
			
				
					|  |  |  |  |   laikad = Laikad(save_ephemeris=True) | 
			
		
	
		
			
				
					|  |  |  |  |   laikad = Laikad(save_ephemeris=not replay) | 
			
		
	
		
			
				
					|  |  |  |  |   while True: | 
			
		
	
		
			
				
					|  |  |  |  |     sm.update() | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     if sm.updated['ubloxGnss']: | 
			
		
	
		
			
				
					|  |  |  |  |       ublox_msg = sm['ubloxGnss'] | 
			
		
	
		
			
				
					|  |  |  |  |       msg = laikad.process_ublox_msg(ublox_msg, sm.logMonoTime['ubloxGnss']) | 
			
		
	
		
			
				
					|  |  |  |  |       msg = laikad.process_ublox_msg(ublox_msg, sm.logMonoTime['ubloxGnss'], block=replay) | 
			
		
	
		
			
				
					|  |  |  |  |       if msg is not None: | 
			
		
	
		
			
				
					|  |  |  |  |         pm.send('gnssMeasurements', msg) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | 
 |