def test_TimeProfile_interpolate_by_distance(self): profile_0 = TimeProfile(START_TIME, DISTANCES, ELAPSED_TIMES) distances_0 = np.array([0., 2 * NM, 8 * NM, DISTANCES[2]]) times_0 = profile_0.interpolate_by_distance(distances_0) self.assertEqual(len(times_0), len(distances_0)) self.assertEqual(times_0[0], ELAPSED_TIMES[0]) self.assertEqual(times_0[-1], ELAPSED_TIMES[2])
def test_TimeProfile_interpolate_by_elapsed_time(self): profile_0 = TimeProfile(START_TIME, DISTANCES, ELAPSED_TIMES) times_0 = np.array([0.0, 292.0, 500.0, 718.0]) distances_0 = profile_0.interpolate_by_elapsed_time(times_0) self.assertEqual(len(distances_0), len(times_0)) self.assertEqual(distances_0[0], DISTANCES[0]) assert_almost_equal(distances_0[2], 51.053368441692193 * NM) self.assertEqual(distances_0[-1], DISTANCES[2])
def test_TimeProfile_json(self): profile_0 = TimeProfile(START_TIME, DISTANCES, ELAPSED_TIMES) s = profile_0.dumps() # print(s) json_1 = json.loads(s) profile_1 = TimeProfile.loads(json_1) self.assertEqual(profile_1.start_time, START_TIME) assert_array_almost_equal(profile_1.distances, profile_0.distances) assert_array_almost_equal(profile_1.elapsed_times, profile_0.elapsed_times)
def analyse_times(distances, times, duplicate_positions, method=LM): """ Create an TimeProfile and quality metrics. Cruise positions are calculated and removed from the arrays of distances and altitudes. Parameters ---------- distances : numpy float array An array of distances [Nautical Miles] times: numpy datetime64 array An array of datetimes [seconds] duplicate_positions: numpy bool array An array indicating duplicate distance positions. Returns ------- TimeProfile: the time profile. time_sd: the time standard deviation. max_time_diff: the maximum time difference. """ # calculate time differences from non-duplicate positions elapsed_times = calculate_elapsed_times(times[~duplicate_positions], times[0]) # dicatnces between non-duplicate positions valid_distances = distances[~duplicate_positions] # attempt to fit a curve to the distances and times # Using the Levenberg-Marquardt algorithm def polynomial_5d(x, a, b, c, d, e, f): return a * x**5 + b * x**4 + c * x**3 + d * x**2 + e * x + f popt, pcov = scipy.optimize.curve_fit(polynomial_5d, valid_distances, elapsed_times, method=method) # calculate time standard deviation time_sd = np.sqrt(np.sum(np.diag(pcov))) # Adjust times to smoothed times and output quality metrics smoothed_times = polynomial_5d(valid_distances, *popt) # calculate the maximum time difference delta_times = smoothed_times - elapsed_times max_time_diff, max_time_index = find_most_extreme_value(delta_times) # Don't output duplicate positions in the time profile return TimeProfile(times[0], valid_distances, smoothed_times), \ time_sd, max_time_diff, max_time_index
def test_SmoothedTrajectories(self): path_0 = HorizontalPath(ROUTE_LATS, ROUTE_LONS, TURN_DISTANCES) timep_0 = TimeProfile(START_TIME, TIME_DISTANCES, ELAPSED_TIMES) altp_0 = AltitudeProfile(ALT_DISTANCES, ALTITUDES) traj_0 = SmoothedTrajectory('123-456-789', path_0, timep_0, altp_0) trajs_0 = [traj_0, traj_0, traj_0] s = dumps_SmoothedTrajectories(trajs_0, 'mas', 0.5, 5, 3, 120.0) # print(s) json_1 = json.loads(s) trajs_1 = loads_SmoothedTrajectories(json_1) self.assertEqual(len(trajs_1), len(trajs_0))
def test_TimeProfile_calculate_average_period(self): profile_0 = TimeProfile(START_TIME, DISTANCES, ELAPSED_TIMES) period_0 = profile_0.calculate_average_period(75. * NM, 75. * NM) self.assertEqual(period_0, 0.0) period_1 = profile_0.calculate_average_period(0.0, 75. * NM) self.assertEqual(period_1, 0.0) period_2 = profile_0.calculate_average_period(0.0, 225. * NM) assert_almost_equal(period_2, 1125.5) period_3 = profile_0.calculate_average_period(225. * NM, 233.645865 * NM) assert_almost_equal(period_3, 26.571428571428573) period_4 = profile_0.calculate_average_period(225. * NM, 235.0 * NM) assert_almost_equal(period_4, 26.571428571428573)
def test_find_3D_airspace_intersections(self): path_0 = HorizontalPath(ROUTE_LATS, ROUTE_LONS, TURN_DISTANCES) timep_0 = TimeProfile(START_TIME, DISTANCES, ELAPSED_TIMES) altp_0 = ALTITUDE_PROFILE traj_0 = SmoothedTrajectory('123-456-789', path_0, timep_0, altp_0) LATS = np.array([0., 20. / 60., 40. / 60., 60. / 60.]) LONS = np.zeros(4, dtype=float) sector_ids = ['1', '2', '3', '1'] points_0 = global_Point3d(LATS, LONS) df_3d = find_3D_airspace_intersections(traj_0, path_0.ecef_path(), points_0, sector_ids, SECTORS) self.assertEqual(df_3d.shape[0], 6)
def test_find_3D_airspace_no_intersections(self): path_0 = HorizontalPath(ROUTE_LATS, ROUTE_LONS, TURN_DISTANCES) timep_0 = TimeProfile(START_TIME, DISTANCES, ELAPSED_TIMES) # Create a altitude profile above the sectors HIGH_ALTITUDES = np.array([ 10000., 11800., 13000., 13600., 14200., 15400., 16000., 16000., 16000., 16000., 15400., 14200. ]) alt_p = AltitudeProfile(DISTANCES, HIGH_ALTITUDES) traj_0 = SmoothedTrajectory('123-456-789', path_0, timep_0, alt_p) LATS = np.array([0., 20. / 60., 40. / 60., 60. / 60.]) LONS = np.zeros(4, dtype=float) sector_ids = ['1', '2', '3', '1'] points_0 = global_Point3d(LATS, LONS) df_3d = find_3D_airspace_intersections(traj_0, path_0.ecef_path(), points_0, sector_ids, SECTORS) self.assertEqual(df_3d.shape[0], 0)
def test_SmoothedTrajectory(self): path_0 = HorizontalPath(ROUTE_LATS, ROUTE_LONS, TURN_DISTANCES) timep_0 = TimeProfile(START_TIME, TIME_DISTANCES, ELAPSED_TIMES) altp_0 = AltitudeProfile(ALT_DISTANCES, ALTITUDES) traj_0 = SmoothedTrajectory('123-456-789', path_0, timep_0, altp_0) s_list = traj_0.dumps() s = ''.join(s_list) # print(s) json_1 = json.loads(s) traj_1 = SmoothedTrajectory.loads(json_1) self.assertEqual(traj_1.flight_id, '123-456-789') assert_array_almost_equal(traj_1.path.lats, ROUTE_LATS) assert_array_almost_equal(traj_1.path.lons, ROUTE_LONS) assert_array_almost_equal(traj_1.path.tids, TURN_DISTANCES) self.assertEqual(traj_1.timep.start_time, START_TIME) assert_array_almost_equal(traj_1.timep.distances, TIME_DISTANCES) assert_array_almost_equal(traj_1.timep.elapsed_times, ELAPSED_TIMES) assert_array_almost_equal(traj_1.altp.distances, ALT_DISTANCES) assert_array_almost_equal(traj_1.altp.altitudes, ALTITUDES)
def analyse_speeds(distances, times, duplicate_positions, N=5, M=5, max_duration=DEFAULT_SPEED_MAX_DURATION): """ Create an TimeProfile and quality metrics. Cruise positions are calculated and removed from the arrays of distances and altitudes. Parameters ---------- distances : numpy float array An array of distances [Nautical Miles] times: numpy datetime64 array An array of datetimes [seconds] duplicate_positions: numpy bool array An array indicating duplicate distance positions. N : integer The number of samples to consider for the moving median filter, default 5. M : integer The number of samples to consider for the moving average filter, default 5. max_duration: float The maximum time between points to smooth, default 120 [Seconds]. Returns ------- TimeProfile: the time profile. time_sd: the time standard deviation. max_time_diff: the maximum time difference. """ # calculate time differences from non-duplicate positions elapsed_times = calculate_elapsed_times(times[~duplicate_positions], times[0]) # dicatnces between non-duplicate positions valid_distances = distances[~duplicate_positions] # Smooth the times smoothed_times = smooth_times(valid_distances, elapsed_times, N, M, max_duration) # Adjust for any offset introduced by smoothing delta_times = smoothed_times - elapsed_times mean_delta = np.sum(delta_times) / len(delta_times) smoothed_times = smoothed_times - mean_delta # Then calculate deltas from the adjusted mean times delta_times = smoothed_times - elapsed_times time_sd = delta_times.std() max_time_diff, max_time_index = find_most_extreme_value(delta_times) # Don't output duplicate positions in the time profile return TimeProfile(times[0], valid_distances, smoothed_times), \ time_sd, max_time_diff, max_time_index