def test_ephemeris_source_in_msg(self): data_mock = defaultdict(str) data_mock['sv_id'] = 1 gpstime = GPSTime.from_datetime(datetime(2022, month=3, day=1)) laikad = Laikad() laikad.fetch_orbits(gpstime, block=True) meas = get_measurement_mock(gpstime, laikad.astro_dog.orbits['R01'][0]) msg = create_measurement_msg(meas) self.assertEqual(msg.ephemerisSource.type.raw, EphemerisSourceType.glonassIacUltraRapid) # Verify gps satellite returns same source meas = get_measurement_mock(gpstime, laikad.astro_dog.orbits['R01'][0]) msg = create_measurement_msg(meas) self.assertEqual(msg.ephemerisSource.type.raw, EphemerisSourceType.glonassIacUltraRapid) # Test nasa source by using older date gpstime = GPSTime.from_datetime(datetime(2021, month=3, day=1)) laikad = Laikad() laikad.fetch_orbits(gpstime, block=True) meas = get_measurement_mock(gpstime, laikad.astro_dog.orbits['G01'][0]) msg = create_measurement_msg(meas) self.assertEqual(msg.ephemerisSource.type.raw, EphemerisSourceType.nasaUltraRapid) # Test nav source type ephem = GPSEphemeris(data_mock, gpstime) meas = get_measurement_mock(gpstime, ephem) msg = create_measurement_msg(meas) self.assertEqual(msg.ephemerisSource.type.raw, EphemerisSourceType.nav)
def test_one_outside_range(self): start_time = GPSTime.from_datetime(datetime(2020, 4, 28)) end_time = GPSTime.from_datetime(datetime(2020, 5, 2)) time = GPSTime.from_datetime(datetime(2021, 5, 1)) holder = TimeRangeHolder() holder.add(start_time, end_time) self.assertFalse(time in holder)
def test_no_block_satellite_when_get_info_from_not_available_period(self): '''If you first fetch satellite info from period when navigation data isn't available and next from period when navigation data are available then you should get correct result''' prn = "C03" constellations = ["GPS", "BEIDOU"] available_date = GPSTime.from_datetime(datetime(2020, 5, 1, 12, 0)) not_available_date = GPSTime.from_datetime(datetime(2000, 1, 1)) dog = AstroDog(valid_const=constellations) sat_info = dog.get_sat_info(prn, not_available_date) self.assertIsNone(sat_info) sat_info = dog.get_sat_info(prn, available_date) self.assertIsNotNone(sat_info)
def test_extend_range_right(self): merge_range = (GPSTime.from_datetime(datetime(2020, 5, 7)), GPSTime.from_datetime(datetime(2020, 5, 9))) range_ = (GPSTime.from_datetime(datetime(2020, 5, 3)), GPSTime.from_datetime(datetime(2020, 5, 7))) time = GPSTime.from_datetime(datetime(2020, 5, 8)) holder = TimeRangeHolder() holder.add(*range_) self.assertFalse(time in holder) holder.add(*merge_range) self.assertTrue(time in holder)
def test_gps_time_dt_conversion(self): for dt in datetimes: double_converted_dt = GPSTime.from_datetime(dt).as_datetime() delta_sec = (dt - double_converted_dt).total_seconds() np.testing.assert_allclose(0, delta_sec, rtol=0, atol=1e-10) for gps_time, dt in zip(gps_times, datetimes): delta_sec = gps_time - GPSTime.from_datetime(dt) np.testing.assert_allclose(0, delta_sec, rtol=0, atol=1e-10) for gps_time in gps_times: double_converted_gps_time = GPSTime.from_datetime( gps_time.as_datetime()) delta_sec = gps_time - double_converted_gps_time np.testing.assert_allclose(0, delta_sec, rtol=0, atol=1e-10)
def test_get_all_sat_info_gps(self): time = GPSTime.from_datetime(datetime(2020, 5, 1, 12, 0, 0)) all_ephem_types = (EphemerisType.FINAL_ORBIT, EphemerisType.RAPID_ORBIT, EphemerisType.ULTRA_RAPID_ORBIT, EphemerisType.NAV) kwargs_list = [ *[{ "valid_const": ["GPS"], "valid_ephem_types": ephem_type } for ephem_type in all_ephem_types], *[{ "valid_const": ["GLONASS"], "valid_ephem_types": ephem_type } for ephem_type in all_ephem_types], *[{ "valid_const": ["BEIDOU"], "valid_ephem_types": ephem_type } for ephem_type in EphemerisType.all_orbits()], *[{ "valid_const": ["GALILEO"], "valid_ephem_types": ephem_type } for ephem_type in EphemerisType.all_orbits()], *[{ "valid_const": ["QZNSS"], "valid_ephem_types": ephem_type } for ephem_type in EphemerisType.all_orbits()], ] for kwargs in kwargs_list: dog = AstroDog(**kwargs) infos = dog.get_all_sat_info(time) self.assertGreater(len(infos), 0, f"No ephemeris found for {kwargs}")
def test_gps(self): available_date = GPSTime.from_datetime(datetime(2020, 5, 1, 12)) dog = AstroDog(valid_const=["GPS"], valid_ephem_types=EphemerisType.ULTRA_RAPID_ORBIT) dog.get_orbit_data(available_date, only_predictions=True) self.assertGreater(len(dog.orbits.keys()), 0) self.assertTrue(available_date in dog.orbit_fetched_times)
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([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, use_qcom=use_qcom) while True: sm.update() 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_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: laikad.fetch_orbits(t, block=replay)
def test_laika_get_orbits_now(self): laikad = Laikad(auto_update=False) laikad.fetch_orbits(GPSTime.from_datetime(datetime.utcnow()), block=True) prn = "G01" self.assertGreater(len(laikad.astro_dog.orbits[prn]), 0) prn = "R01" self.assertGreater(len(laikad.astro_dog.orbits[prn]), 0) print(min(laikad.astro_dog.orbits[prn], key=lambda e: e.epoch).epoch.as_datetime())
def test_gps_and_glonass_2022(self): # Test GPS and GLONASS separately from the first date that GLONASS Ultra-Rapid prediction orbits were available available_date = GPSTime.from_datetime(datetime(2022, 1, 29, 11, 31)) for t in range(0, 24, 3): check_date = available_date + t * SECS_IN_HR for const in ["GPS", "GLONASS"]: dog = AstroDog( valid_const=const, valid_ephem_types=EphemerisType.ULTRA_RAPID_ORBIT) dog.get_orbit_data(check_date, only_predictions=True) self.assertGreater(len(dog.orbits.keys()), 0) self.assertTrue(check_date in dog.orbit_fetched_times)
def get_satellite_delays(dog, date): dog.get_dcb_data(GPSTime.from_datetime(start_date)) res = {} # published data is in nanoseconds... # ours is in pseudorange (meters) factor = constants.SPEED_OF_LIGHT / 1e9 factor = 0.365 for prn in ['G%02d' % i for i in range(1, 33)]: if hasattr(dog.dcbs[prn][0], 'C1W_C2W'): res[prn] = dog.dcbs[prn][0].C1W_C2W * factor elif hasattr(dog.dcbs[prn][0], 'C1P_C2P'): res[prn] = dog.dcbs[prn][0].C1P_C2P * factor return res
def test_create_msg_without_errors(self): gpstime = GPSTime.from_datetime(datetime.now()) meas = GNSSMeasurement(ConstellationId.GPS, 1, gpstime.week, gpstime.tow, { 'C1C': 0., 'D1C': 0. }, { 'C1C': 0., 'D1C': 0. }) msg = create_measurement_msg(meas) self.assertEqual(msg.constellationId, 'gps')
def test_utc_converter(self): datetimes_strings = [ '2008-04-27 22:22:06', '2012-05-13 08:52:57', '2012-09-17 12:50:05', '2016-04-08 12:28:19', '2017-10-23 06:42:34', '2018-01-18 03:16:27', '2017-07-01 00:00:05' ] gps_times = [ GPSTime.from_datetime( datetime.strptime(dt_str, '%Y-%m-%d %H:%M:%S')) for dt_str in datetimes_strings ] np.testing.assert_allclose((gps_times[0] - gpst_to_utc(gps_times[0])), 14, rtol=0, atol=1e-3) np.testing.assert_allclose((gps_times[1] - gpst_to_utc(gps_times[1])), 15, rtol=0, atol=1e-3) np.testing.assert_allclose((gps_times[2] - gpst_to_utc(gps_times[2])), 16, rtol=0, atol=1e-3) np.testing.assert_allclose((gps_times[3] - gpst_to_utc(gps_times[3])), 17, rtol=0, atol=1e-3) np.testing.assert_allclose((gps_times[4] - gpst_to_utc(gps_times[4])), 18, rtol=0, atol=1e-3) np.testing.assert_allclose((gps_times[5] - gpst_to_utc(gps_times[5])), 18, rtol=0, atol=1e-3) np.testing.assert_allclose((gps_times[6] - gpst_to_utc(gps_times[6])), 17, rtol=0, atol=1e-3) np.testing.assert_allclose( (gps_times[5] - utc_to_gpst(gpst_to_utc(gps_times[5]))), 0, rtol=0, atol=1e-3) np.testing.assert_allclose( (gps_times[6] - utc_to_gpst(gpst_to_utc(gps_times[6]))), 0, rtol=0, atol=1e-3)
def test_station_position(self): print( 'WARNING THIS TAKE CAN TAKE A VERY LONG TIME THE FIRST RUN TO DOWNLOAD' ) dog = AstroDog() # Building this cache takes forever just copy it from repo cache_directory = '/tmp/gnss/cors_coord/' try: os.mkdir('/tmp/gnss/') except OSError: pass try: os.mkdir(cache_directory) except OSError: pass examples_directory = os.path.join( os.path.dirname(os.path.abspath(__file__)), '../examples') copyfile(os.path.join(examples_directory, 'cors_station_positions'), os.path.join(cache_directory, 'cors_station_positions')) station_name = 'sc01' time = GPSTime.from_datetime(datetime(2020, 1, 11)) slac_rinex_obs_file = download_cors_station(time, station_name, dog.cache_dir) obs_data = RINEXFile(slac_rinex_obs_file) sc01_exact_position = get_station_position('sc01') rinex_meas_grouped = raw.read_rinex_obs(obs_data) rinex_corr_grouped = [] for meas in tqdm(rinex_meas_grouped): proc = raw.process_measurements(meas, dog=dog) corr = raw.correct_measurements(meas, sc01_exact_position, dog=dog) rinex_corr_grouped.append(corr) # Using laika's WLS solver we can now calculate position # fixes for every epoch (every 30s) over 24h. ests = [] for corr in tqdm(rinex_corr_grouped[:]): fix, _ = raw.calc_pos_fix(corr) ests.append(fix) ests = np.array(ests) mean_fix = np.mean(ests[:, :3], axis=0) np.testing.assert_allclose(mean_fix, sc01_exact_position, rtol=0, atol=1)
def data_for_station(dog, station_name, date=None): """ Get data from a particular station and time. Station names are CORS names (eg: 'slac') Dates are datetimes (eg: datetime(2020,1,7)) """ if date is None: date = datetime(2020,1,7) time = GPSTime.from_datetime(date) rinex_obs_file = download_cors_station(time, station_name, dog.cache_dir) obs_data = RINEXFile(rinex_obs_file) station_pos = get_station_position(station_name) return station_pos, raw.read_rinex_obs(obs_data)
def run_station_position(self, length): dog = AstroDog() # Building this cache takes forever just copy it from repo cache_directory = '/tmp/gnss/cors_coord/' os.makedirs('/tmp/gnss/', exist_ok=True) os.makedirs(cache_directory, exist_ok=True) examples_directory = os.path.join( os.path.dirname(os.path.abspath(__file__)), '../examples') copyfile(os.path.join(examples_directory, 'cors_station_positions'), os.path.join(cache_directory, 'cors_station_positions')) station_name = 'sc01' time = GPSTime.from_datetime(datetime(2020, 1, 11)) slac_rinex_obs_file = download_cors_station(time, station_name, dog.cache_dir) obs_data = RINEXFile(slac_rinex_obs_file) sc01_exact_position = get_station_position('sc01') rinex_meas_grouped = raw.read_rinex_obs(obs_data) # Select small sample out of ~2800 to reduce computation time rinex_meas_grouped = rinex_meas_grouped[:length] rinex_corr_grouped = [] for meas in tqdm(rinex_meas_grouped): proc = raw.process_measurements(meas, dog=dog) corr = raw.correct_measurements(proc, sc01_exact_position, dog=dog) rinex_corr_grouped.append(corr) # Using laika's WLS solver we can now calculate position # fixes for every epoch (every 30s) over 24h. ests = [] for corr in tqdm(rinex_corr_grouped): ret = raw.calc_pos_fix(corr) if len(ret) > 0: fix, _ = ret ests.append(fix) ests = np.array(ests) mean_fix = np.mean(ests[:, :3], axis=0) np.testing.assert_allclose(mean_fix, sc01_exact_position, rtol=0, atol=1)
def test_fetch_orbits_non_blocking(self): gpstime = GPSTime.from_datetime(datetime(2021, month=3, day=1)) laikad = Laikad() laikad.fetch_orbits(gpstime, block=False) laikad.orbit_fetch_future.result(30) # Get results and save orbits to laikad: laikad.fetch_orbits(gpstime, block=False) ephem = laikad.astro_dog.orbits['G01'][0] self.assertIsNotNone(ephem) laikad.fetch_orbits(gpstime+2*SECS_IN_DAY, block=False) laikad.orbit_fetch_future.result(30) # Get results and save orbits to laikad: laikad.fetch_orbits(gpstime + 2 * SECS_IN_DAY, block=False) ephem2 = laikad.astro_dog.orbits['G01'][0] self.assertIsNotNone(ephem) self.assertNotEqual(ephem, ephem2)
def test_merge_ranges(self): first_range = (GPSTime.from_datetime(datetime(2020, 5, 1)), GPSTime.from_datetime(datetime(2020, 5, 3))) second_range = (GPSTime.from_datetime(datetime(2020, 5, 7)), GPSTime.from_datetime(datetime(2020, 5, 9))) merge_range = (GPSTime.from_datetime(datetime(2020, 5, 2)), GPSTime.from_datetime(datetime(2020, 5, 8))) time = GPSTime.from_datetime(datetime(2020, 5, 5)) holder = TimeRangeHolder() holder.add(*first_range) holder.add(*second_range) self.assertFalse(time in holder) holder.add(*merge_range) self.assertTrue(time in holder)
def data_for_station(dog, station_name, date): """ Get data from a particular station and time. Wraps a number of laika function calls. Station names are CORS names (eg: 'slac') Dates are datetimes (eg: datetime(2020,1,7)) """ time = GPSTime.from_datetime(date) rinex_obs_file = None # handlers for specific networks handlers = {'Korea': download_korean_station} network = station_network_info.get(station_name, None) # no special network, so try using whatever if network is None: try: station_pos = get_station_position(station_name, cache_dir=dog.cache_dir) rinex_obs_file = download_cors_station(time, station_name, cache_dir=dog.cache_dir) except (KeyError, DownloadError): pass if not rinex_obs_file: # station position not in CORS map, try another thing if station_name in extra_station_info: station_pos = numpy.array(extra_station_info[station_name]) rinex_obs_file = download_misc_igs_station( time, station_name, cache_dir=dog.cache_dir) else: raise DownloadError else: station_pos = numpy.array(extra_station_info[station_name]) rinex_obs_file = handlers[network](time, station_name, cache_dir=dog.cache_dir) obs_data = RINEXFile(rinex_obs_file, rate=30) return station_pos, raw.read_rinex_obs(obs_data)
def test_fetch_orbits_with_wrong_clocks(self): laikad = Laikad() def check_has_orbits(): self.assertGreater(len(laikad.astro_dog.orbits), 0) ephem = laikad.astro_dog.orbits['G01'][0] self.assertIsNotNone(ephem) real_current_time = GPSTime.from_datetime(datetime(2021, month=3, day=1)) wrong_future_clock_time = real_current_time + SECS_IN_DAY laikad.fetch_orbits(wrong_future_clock_time, block=True) check_has_orbits() self.assertEqual(laikad.last_fetch_orbits_t, wrong_future_clock_time) # Test fetching orbits with earlier time assert real_current_time < laikad.last_fetch_orbits_t laikad.astro_dog.orbits = {} laikad.fetch_orbits(real_current_time, block=True) check_has_orbits() self.assertEqual(laikad.last_fetch_orbits_t, real_current_time)
def main(sm=None, pm=None): if sm is None: sm = messaging.SubMaster(['ubloxGnss', '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) 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 msg is not None: pm.send('gnssMeasurements', msg) if not laikad.got_first_ublox_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: laikad.fetch_orbits(t, block=replay)
def gather_data(start_time, station_vtecs): ''' Looks for 'coincidences'. A 'coincidence' is a set of observables for various sat/rec pairs that cross into the ionosphere at approximately the same location (lat,lon). Returns four items: final_coicidences (dict) {(lat, lon, i): Observation((lat, lon, i), station, prn, vtec, s_to_v, ...} measurements (list): [Observation 1, ....] sats, recvrs (set): sets of satellite and receiver objects included. ''' # mapping of (lat, lon, time) to [measurement_idx] coincidences = defaultdict(list) measurements = [] svs = set() recvs = set() for cnt, (station, station_dat) in enumerate(station_vtecs.items()): print("gathering data for %3d/%d" % (cnt, len(station_vtecs))) for prn, (locs, dats, slants) in station_dat.items(): if len(locs) == 0: continue # convert locs to lat lon in bulk for much better speed # dirty hack to force numpy to treat the array as 1d locs.append(None) # indices with locations set idxs = numpy.where( numpy.logical_not(numpy.vectorize(is_)(locs, None))) locs.pop(-1) if len(idxs[0]) == 0: continue locs_lla = ecef2geodetic(numpy.stack(numpy.array(locs)[idxs])) prev_coi = None for i, idx in enumerate(idxs[0]): cois = set() lat, lon, _ = locs_lla[i] coi = (round_to_res(lat, lat_res), round_to_res(lon, lon_res), (idx // (time_res / 30)) * 30) if coi == prev_coi: continue prev_coi = coi gpstime = start_time + timedelta(seconds=30 * int(idx)) gpstime = GPSTime.from_datetime(gpstime) obs = Observation(coi, station, prn, dats[idx], slants[idx], gpstime) coincidences[coi].append(obs) final_coincidences = dict() # only include coincidences with >= 2 measurements for coi, obss in coincidences.items(): if (len({obs.station for obs in obss}) > 1 or len({obs.sat for obs in obss}) > 1): final_coincidences[coi] = obss for obs in obss: svs.add(obs.sat) recvs.add(obs.station) measurements.append(obs) return final_coincidences, measurements, svs, recvs
def test_fetch_data_from_distant_future(self): dog = AstroDog() date = GPSTime.from_datetime(datetime(3120, 1, 1)) self.assertRaises(RuntimeError, dog.get_sat_info, "G01", date)
def test_empty(self): time = GPSTime.from_datetime(datetime(2020, 5, 1, 0, 0, 0)) holder = TimeRangeHolder() self.assertFalse(time in holder)
for m in logs: if m.ubloxGnss.which == 'measurementReport': new_meas = read_raw_ublox(m.ubloxGnss.measurementReport) if len(new_meas) > 0: return new_meas[0].recv_time def get_measurement_mock(gpstime, sat_ephemeris): meas = GNSSMeasurement(ConstellationId.GPS, 1, gpstime.week, gpstime.tow, {'C1C': 0., 'D1C': 0.}, {'C1C': 0., 'D1C': 0.}) # Fake measurement being processed meas.observables_final = meas.observables meas.sat_ephemeris = sat_ephemeris return meas GPS_TIME_PREDICTION_ORBITS_RUSSIAN_SRC = GPSTime.from_datetime(datetime(2022, month=1, day=29, hour=12)) class TestLaikad(unittest.TestCase): @classmethod def setUpClass(cls): logs = get_log(range(1)) cls.logs = logs first_gps_time = get_first_gps_time(logs) cls.first_gps_time = first_gps_time def setUp(self): Params().delete(EPHEMERIS_CACHE) def test_fetch_orbits_non_blocking(self):
m = Basemap(projection='mill', lat_0 = 40, lon_0 = 0, resolution = 'c', ax=ax) m.shadedrelief(scale = 0.5) # m.drawcoastlines() # m.drawcountries() # m.drawstates() #m.fillcontinents(color='coral', lake_color='aqua') m.drawparallels(np.arange(-90., 120., 30.)) m.drawmeridians(np.arange(0., 420., 60.)) if dn: m.nightshade(dn) #m.drawmapboundary(fill_color='aqua') return m time = GPSTime.from_datetime(datetime(2019,5,31,11,0,0)) dog = AstroDog() ionex_map = dog.get_ionex(time) # print(dog.get_ionex(time).get_TEC((5,5), time)) # Setup map: fig = plt.figure(1, figsize=(18, 12)) plt.clf() ax = fig.add_subplot(111) m = setup_map(ax) lon_bins = np.linspace(-180, 180, 73) lat_bins = np.linspace(-90, 90, 73)
f107_data = pd.DataFrame(data=list(zip(f107s, dts2)), columns=['f107', 'dt']) f107_data = f107_data.set_index('dt') for idx, timestamp in enumerate(daterange.to_pydatetime()): save_idx = idx % num_save print(timestamp) i_kp = [kp_data.index.get_loc(t, method='nearest') for t in [timestamp]] kp = kp_data.iloc[i_kp, :]['kp'][0] i_f107 = [ f107_data.index.get_loc(t, method='nearest') for t in [timestamp] ] f107 = f107_data.iloc[i_f107, :]['f107'][0] time = GPSTime.from_datetime(timestamp) dog = AstroDog() ionex_map = dog.get_ionex(time) doy = timestamp.timetuple().tm_yday fTime = timestamp.hour + timestamp.minute / 60. sub_idx = 0 for lat in lat_bins: for lon in lon_bins: tec = dog.get_ionex(time).get_TEC((lat, lon), time) data[subbins * save_idx + sub_idx] = lat, lon, f107, kp, fTime, doy, tec sub_idx = sub_idx + 1