def test_reduce_poi_with_access_file(self, test_data): """Test reduce_poi with access file""" access_file, interval, error_threshold = test_data interval = TimeInterval(*interval) # Get access information access_info = self.parse_access_file(access_file) trimmed_accesses = time_intervals.trim_poi_segments( access_info.accesses, interval) # Gather data for every 24 hour period of the input interval q_mag = Satellite.get_by_name(access_info.sat_name)[0].maximum_altitude sat_platform_id = Satellite.get_by_name( access_info.sat_name)[0].platform_id sat_irp = Interpolator(sat_platform_id) poi_list = [ TimeInterval(start, start + 24 * 60 * 60) for start in range(interval[0], interval[1], 24 * 60 * 60) ] sampling_time_list = [time.start for time in poi_list] sampling_time_list.append(interval[1]) sat_pos_ecef_list, sat_vel_ecef_list = map( list, zip(*[sat_irp.interpolate(t) for t in sampling_time_list])) sat_position_velocity_pairs = ecef_to_eci( np.transpose(np.asarray(sat_pos_ecef_list)), np.transpose(np.asarray(sat_vel_ecef_list)), sampling_time_list) # Run viewing cone reduced_poi_list = [ reduced_poi for idx, poi in enumerate(poi_list) for reduced_poi in view_cone.reduce_poi( access_info.target, sat_position_velocity_pairs[idx:idx + 2], q_mag, poi) ] reduced_poi_list = time_intervals.fuse_neighbor_intervals( reduced_poi_list) # Check coverage for poi in reduced_poi_list: trimmed_accesses = filter( lambda access: not ( (poi.start - error_threshold < access.start) and (poi.end + error_threshold > access.end)), trimmed_accesses) if trimmed_accesses: print("Remaining accesses: ", trimmed_accesses) raise Exception("Some accesses are not covered")
def test_full_visibility(self, test_data): """Tests that the visibility finder produces the same results as the access file. Args: test_data (tuple): A three tuple containing the: 1 - The path of KAOS access test file 2 - A tuple of the desired test duration 3 - The maximum tolerated deviation in seconds Note: Ranges provided must not start or end at an access boundary. This is a result of the strict checking method provided. """ access_file, interval, max_error = test_data access_info = self.parse_access_file(access_file) finder = VisibilityFinder( Satellite.get_by_name(access_info.sat_name)[0].platform_id, access_info.target, interval) access_times = finder.determine_visibility() for predicted_access in access_times: found = False for actual_access in access_info.accesses: if (interval[0] > actual_access[0]) and (interval[1] < actual_access[1]): if ((abs(interval[0] - predicted_access[0]) < max_error) and (abs(interval[1] - predicted_access[1]) < max_error)): found = True break if interval[0] > actual_access[0]: if ((abs(interval[0] - predicted_access[0]) < max_error) and (abs(actual_access[1] - predicted_access[1]) < max_error)): found = True break elif interval[1] < actual_access[1]: if ((abs(actual_access[0] - predicted_access[0]) < max_error) and (abs(interval[1] - predicted_access[1]) < max_error)): found = True break if ((abs(actual_access[0] - predicted_access[0]) < max_error) and (abs(actual_access[1] - predicted_access[1]) < max_error)): found = True break if not found: raise Exception('Wrong access: {}'.format(predicted_access))
def parse_ephemeris_file(filename): """Parse the given ephemeris file and store the orbital data in OrbitRecords. We assume that each row in the ephemeris file is a 7-tuple containing an orbital point, formatted as: time posx posy posz velx vely velz Calculate the maximum distance from earth center to the position if the satellite. Insert it in Satellite. """ sat_name = os.path.splitext(os.path.basename(filename))[0].split('.')[0] existing_sat = Satellite.get_by_name(sat_name) if not existing_sat: sat = Satellite(platform_name=sat_name) sat.save() DB.session.commit() sat_id = -1 else: sat = existing_sat[0] max_distance = 0 with open(filename, "rU") as f: segment_boundaries = [] segment_tuples = [] start_time = float(0) read_segment_boundaries = False read_orbital_data = False # Remember the last seen segment boundary while reading the ephemeris rows. Needed to # differentiate between the beginning of a new segment and the end of en existing segment of # data last_seen_segment_boundary = 0 for line in f: line = line.rstrip('\n') if "Epoch in JDate format:" in line: start_time = float(line.split(':')[1]) start_time = jdate_to_unix(start_time) last_seen_segment_boundary = start_time # For now, we assume that the coord system will always be J2000 # if "CoordinateSystem" in line: # coord_system = str(line.split()[1]) if "END SegmentBoundaryTimes" in line: read_segment_boundaries = False if read_segment_boundaries: line = line.strip() if line: segment_boundaries.append(start_time + float(line)) if "BEGIN SegmentBoundaryTimes" in line: read_segment_boundaries = True if "END Ephemeris" in line: add_segment_to_db(segment_tuples, sat.platform_id) read_orbital_data = False if read_orbital_data: line = line.strip() if line: ephemeris_row = [float(num) for num in line.split()] orbit_tuple = OrbitPoint(start_time + ephemeris_row[0], ephemeris_row[1:4], ephemeris_row[4:7]) segment_tuples.append(orbit_tuple) # Keep track of the magnitude of the position vector and update with a bigger # value max_distance = max(max_distance, np.linalg.norm(ephemeris_row[1:4])) # The line we just read is a segment boundary, So first check that this is the # *end* of a segment, not the beginning of a new one, and then add this segment # to the db. if (orbit_tuple.time in segment_boundaries and last_seen_segment_boundary != orbit_tuple.time): last_seen_segment_boundary = orbit_tuple.time sat_id = add_segment_to_db(segment_tuples, sat.platform_id) segment_tuples = [] if "EphemerisTimePosVel" in line: read_orbital_data = True # After getting the q_max, insert it into Satellite""" sat.maximum_altitude = max_distance sat.save() DB.session.commit() return sat_id
def test_visibility(self, test_data): """Tests that the visibility finder produces the same results as the access file. Args: test_data (tuple): A three tuple containing the: 1 - The path of KAOS access test file 2 - A tuple of the desired test duration 3 - The maximum tolerated deviation in seconds Note: Ranges provided must not start or end at an access boundary. This is a result of the strict checking method provided. """ access_file, interval, max_error = test_data posix_interval = (utc_to_unix(interval[0]), utc_to_unix(interval[1])) access_info = self.parse_access_file(access_file, posix_interval) satellite_id = Satellite.get_by_name( access_info.sat_name)[0].platform_id request = { 'Target': access_info.target, 'POI': { 'startTime': interval[0], 'endTime': interval[1] }, 'PlatformID': [satellite_id] } with self.app.test_client() as client: response = client.post('/visibility/search', json=request) self.assertTrue(response.is_json) self.assertEqual(response.status_code, 200) self.assertEqual(len(response.json['Opportunities']), len(access_info.accesses)) for oppertunity in response.json['Opportunities']: self.assertEqual(satellite_id, oppertunity['PlatformID']) predicted_access = (oppertunity['start_time'], oppertunity['end_time']) interval = posix_interval found = False for actual_access in access_info.accesses: if (interval[0] > actual_access[0]) and (interval[1] < actual_access[1]): if ((abs(interval[0] - predicted_access[0]) < max_error) and (abs(interval[1] - predicted_access[1]) < max_error)): found = True break if interval[0] > actual_access[0]: if ((abs(interval[0] - predicted_access[0]) < max_error) and (abs(actual_access[1] - predicted_access[1]) < max_error)): found = True break elif interval[1] < actual_access[1]: if ((abs(actual_access[0] - predicted_access[0]) < max_error) and (abs(interval[1] - predicted_access[1]) < max_error)): found = True break if ((abs(actual_access[0] - predicted_access[0]) < max_error) and (abs(actual_access[1] - predicted_access[1]) < max_error)): found = True break if not found: raise Exception('Wrong access: {}'.format(predicted_access))
def test_visibility_perf(self, test_data): """Tests that the visibility finder produces the same results as the access file and prints accuracy and performance measurements at the end. This test is meant to be run manually (not as part of the automated CI tests). Use: pytest -s test/algorithm/perf_visibiliry_finder.py Args: test_data (tuple): A three tuple containing the: 1 - The path of KAOS access test file 2 - A tuple of the desired test duration """ # Use viewing cone algorithm or not use_view_cone = True # Do viewing cone coordinate conversion in series or as a vector vectorized = True access_file, interval = test_data interval = TimeInterval(interval[0], interval[1]) access_info = self.parse_access_file(access_file) sat_id = Satellite.get_by_name(access_info.sat_name)[0].platform_id max_q = Satellite.get_by_name(access_info.sat_name)[0].maximum_altitude sat_irp = Interpolator(sat_id) print("\n") if use_view_cone is False: finder = VisibilityFinder(sat_id, access_info.target, interval) # NOTE: change to burte_force=True to switch to brute-force method. access_times = np.asarray( finder.profile_determine_visibility(brute_force=False)) else: import cProfile import pstats import sys profile = cProfile.Profile() profile.enable() # Vectorized if vectorized is True: poi_list = [ TimeInterval(start, start + 24 * 60 * 60) for start in range(interval[0], interval[1], 24 * 60 * 60) ] sampling_time_list = [time.start for time in poi_list] sampling_time_list.append(interval[1]) sat_pos_ecef_list, sat_vel_ecef_list = map( list, zip(*[sat_irp.interpolate(t) for t in sampling_time_list])) sat_position_velocity_pairs = ecef_to_eci( np.transpose(np.asarray(sat_pos_ecef_list)), np.transpose(np.asarray(sat_vel_ecef_list)), sampling_time_list) reduced_poi_list = [ reduced_poi for idx, poi in enumerate(poi_list) for reduced_poi in view_cone.reduce_poi( access_info.target, sat_position_velocity_pairs[idx:idx + 2], max_q, poi) ] # NON vectorized else: reduced_poi_list = [] for start_time in range(interval[0], interval[1], 24 * 60 * 60): poi = TimeInterval(start_time, start_time + 24 * 60 * 60) # get sat at start_time sat_pos_ecef, sat_vel_ecef = sat_irp.interpolate( start_time + 12 * 60 * 60) sat_pos, sat_vel = ecef_to_eci(np.asarray(sat_pos_ecef), np.asarray(sat_vel_ecef), start_time + 12 * 60 * 60) reduced_poi_list.extend( view_cone.reduce_poi(access_info.target, sat_pos[0], sat_vel[0], max_q, poi)) trimmed_reduced_poi_list = interval_utils.fuse_neighbor_intervals( reduced_poi_list) access_times = [] for reduced_poi in trimmed_reduced_poi_list: finder = VisibilityFinder(sat_id, access_info.target, reduced_poi) access_times.extend(np.asarray(finder.determine_visibility())) profile.disable() stats = pstats.Stats(profile, stream=sys.stdout) stats.strip_dirs().sort_stats('cumtime').print_stats(50) reduced_time = 0 for time in trimmed_reduced_poi_list: reduced_time += time[1] - time[0] print("Viewing cone stats:") print("Reduced time is: {}".format(mp.nstr(reduced_time, 12))) print("Input time is: {}".format(interval[1] - interval[0])) interval = TimeInterval(*interval) expected_accesses = interval_utils.trim_poi_segments( access_info.accesses, interval) # Check the visibility times fail = False total_error = 0 print( "=============================== visibility report ===============================" ) for exp_start, exp_end in expected_accesses: idx = (np.abs(np.transpose(access_times)[0] - exp_start)).argmin() error = abs(exp_start - access_times[idx][0]) + abs(exp_end - access_times[idx][1]) if error > 600: fail = True print("start, {}, ------------, {}".format( exp_start, mp.nstr(access_times[idx][0] - exp_start, 6))) print("end, {}, ------------, {}".format( exp_end, mp.nstr(access_times[idx][1] - exp_end, 6))) else: print("start, {}, {}, {}".format( exp_start, mp.nstr(access_times[idx][0], 11), mp.nstr(access_times[idx][0] - exp_start, 6))) print("end , {}, {}, {}".format( exp_end, mp.nstr(access_times[idx][1], 11), mp.nstr(access_times[idx][1] - exp_end, 6))) total_error += error access_times = np.delete(access_times, idx, axis=0) print("\nTotal Error: {}".format(mp.nstr(total_error, 12))) print("Average Error: {}".format( mp.nstr(total_error / (len(expected_accesses) * 2)))) if fail: raise Exception( "Missing accesses. Unmatched: {}".format(access_times)) if access_times.size > 0: raise Exception( "Extra accesses. Unmatched: {}".format(access_times))