def crossed_line(self, fix1, fix2): distance1 = calculate_distance(fix1, self.fix) distance2 = calculate_distance(fix2, self.fix) if not self.is_line: raise ValueError('Calling crossed_line on a sector!') else: if distance2 > self.r_max and distance1 > self.r_max: return False else: # either both within circle or only one, leading to small amount of false positives bearing1 = calculate_bearing(self.fix, fix1) bearing2 = calculate_bearing(self.fix, fix2) angle_wrt_orientation1 = abs( calculate_bearing_difference(self.orientation_angle, bearing1)) angle_wrt_orientation2 = abs( calculate_bearing_difference(self.orientation_angle, bearing2)) if self.sector_orientation == "next": # start line return angle_wrt_orientation1 < 90 < angle_wrt_orientation2 elif self.sector_orientation == "previous": # finish line return angle_wrt_orientation2 < 90 < angle_wrt_orientation1 else: raise ValueError( "A line with this orientation is not implemented!")
def determine_outlanding_distance(self, outlanding_leg, fix): previous_waypoint = self.waypoints[outlanding_leg] next_waypoint = self.waypoints[outlanding_leg + 1] # outlanding distance = distance between tps minus distance from next tp to outlanding outlanding_dist = calculate_distance(previous_waypoint.fix, next_waypoint.fix) outlanding_dist -= calculate_distance(next_waypoint.fix, fix) return outlanding_dist if outlanding_dist > 0 else 0
def _calculate_distance_completed_leg(self, leg, start_tp_fix, end_tp_fix): if leg == 0: # take start-point of task start = self.waypoints[0] distance = calculate_distance(start.fix, end_tp_fix) if start.distance_correction == 'shorten_legs': distance -= start.r_max elif leg == self.no_legs - 1: # take finish-point of task finish = self.waypoints[-1] distance = calculate_distance(start_tp_fix, finish.fix) if finish.distance_correction == 'shorten_legs': distance -= finish.r_max else: distance = calculate_distance(start_tp_fix, end_tp_fix) return distance
def _calculate_distance_outlanding_leg(self, leg, start_tp_fix, outlanding_fix): if leg == 0: tp1 = self.waypoints[leg + 1] bearing = calculate_bearing(start_tp_fix, outlanding_fix) closest_area_fix = calculate_destination(start_tp_fix, tp1.r_max, bearing) distance = calculate_distance(self.start.fix, closest_area_fix) distance -= calculate_distance(outlanding_fix, closest_area_fix) elif leg == self.no_legs - 1: # take finish-point of task distance = calculate_distance(start_tp_fix, self.finish.fix) distance -= calculate_distance(self.finish.fix, outlanding_fix) else: tp1 = self.waypoints[leg + 1] bearing = calculate_bearing(tp1.fix, outlanding_fix) closest_area_fix = calculate_destination(tp1.fix, tp1.r_max, bearing) if leg == 0: distance = calculate_distance(self.start.fix, closest_area_fix) else: distance = calculate_distance(start_tp_fix, closest_area_fix) distance -= calculate_distance(outlanding_fix, closest_area_fix) return distance
def calculate_task_distances(self): distances = list() for leg in range(self.no_legs): begin = self.waypoints[leg] end = self.waypoints[leg+1] # next is built in name distance = calculate_distance(begin.fix, end.fix) if begin.distance_correction is "shorten_legs": if end.distance_correction is "shorten_legs": distance = Task.distance_shortened_leg(distance, begin, end, "begin") distance = Task.distance_shortened_leg(distance, begin, end, "end") elif end.distance_correction is "move_tp": distance = Task.distance_moved_turnpoint(distance, begin, end, "end") distance = Task.distance_shortened_leg(distance, begin, end, "begin") elif end.distance_correction is None: distance = Task.distance_shortened_leg(distance, begin, end, "begin") else: raise ValueError("This distance correction does not exist: %s" % end.distance_correction) elif begin.distance_correction is "move_tp": if end.distance_correction is "shorten_legs": distance = Task.distance_moved_turnpoint(distance, begin, end, "begin") distance = Task.distance_shortened_leg(distance, begin, end, "end") elif end.distance_correction is "move_tp": distance = Task.distance_moved_turnpoint(distance, begin, end, "begin") distance = Task.distance_moved_turnpoint(distance, begin, end, "both_end") elif end.distance_correction is None: distance = Task.distance_moved_turnpoint(distance, begin, end, "begin") else: raise ValueError("This distance correction does not exist: %s" % end.distance_correction) elif begin.distance_correction is None: if end.distance_correction is "shorten_legs": distance = Task.distance_shortened_leg(distance, begin, end, "end") elif end.distance_correction is "move_tp": distance = Task.distance_moved_turnpoint(distance, begin, end, "end") elif end.distance_correction is None: pass else: raise ValueError("This distance correction does not exist: %s" % end.distance_correction) else: raise ValueError("This distance correction does not exist: %s" % self.waypoints[leg].distance_correction) distances.append(distance) return distances
def inside_sector(self, fix): distance = calculate_distance(fix, self.fix) bearing = calculate_bearing(self.fix, fix) angle_wrt_orientation = abs(calculate_bearing_difference(self.orientation_angle, bearing)) if self.is_line: raise ValueError('Calling inside_sector on a line') elif self.r_min is not None: inside_outer_sector = self.r_min - self.SEEYOU_SECTOR_MARGIN < distance < self.r_max + self.SEEYOU_SECTOR_MARGIN and angle_wrt_orientation < self.angle_max inside_inner_sector = distance < self.r_min and angle_wrt_orientation < self.angle_min return inside_outer_sector or inside_inner_sector else: # self.r_min is None return distance < self.r_max + self.SEEYOU_SECTOR_MARGIN and (180 - angle_wrt_orientation) < self.angle_max
def distance_moved_turnpoint(distance, begin, end, moved_point, move_direction='reduce'): from math import sqrt, cos, pi, acos if moved_point == "begin": moved = begin other = end angle_reduction = 0 elif moved_point == "end": moved = end other = begin angle_reduction = 0 elif moved_point == "both_end": moved = end other = begin original_distance = calculate_distance(begin.fix, end.fix) distance_moved_current = begin.r_max if begin.angle_max == 180 else begin.r_min angle_reduction = abs( acos((distance_moved_current**2 - distance**2 - original_distance**2) / (-2 * distance * original_distance))) * 180 / pi else: raise ValueError("Displaced point is not recognized: %s" % moved_point) displacement_dist = moved.r_max if moved.angle_max == 180 else moved.r_min bearing1 = moved.orientation_angle bearing2 = calculate_bearing(other.fix, other.fix) if move_direction == 'increase': angle = 180 - abs(calculate_bearing_difference( bearing1, bearing2)) - angle_reduction else: angle = abs(calculate_bearing_difference( bearing1, bearing2)) - angle_reduction distance = sqrt(distance**2 + displacement_dist**2 - 2 * distance * displacement_dist * cos(angle * pi / 180)) return distance
def _calculate_nominal_distances(self): distances = list() for start_waypoint, end_waypoint in double_iterator(self.waypoints): distance = calculate_distance(start_waypoint.fix, end_waypoint.fix) distances.append(distance) return distances
def analyse(self, trace): # To prevent circular import with flight_phases from opensoar.thermals.flight_phases import Phase cruise = True possible_thermal_fixes = list() possible_cruise_fixes = list() sharp_thermal_entry_found = False turning_left = True total_bearing_change = 0 # Start with first phase phases = [Phase(cruise, trace[0:2])] for fix_minus2, fix_minus1, fix in triple_iterator(trace): time_minus2 = fix_minus2['time'] time_minus1 = fix_minus1['time'] time = fix['time'] bearing_change = calculate_bearing_change(fix_minus2, fix_minus1, fix) delta_t = (0.5 * seconds_time_difference(time_minus1, time) + 0.5 * seconds_time_difference(time_minus2, time)) bearing_change_rate = bearing_change / delta_t if cruise: continuing_left = turning_left and bearing_change_rate < self.MINIMUM_BEARING_CHANGE_RATE continuing_right = not turning_left and bearing_change_rate > -self.MINIMUM_BEARING_CHANGE_RATE if continuing_left or continuing_right: total_bearing_change += bearing_change if len(possible_thermal_fixes) == 0: possible_thermal_fixes = [fix] else: if not sharp_thermal_entry_found and abs( bearing_change_rate ) > self.CRUISE_THRESHOLD_BEARINGRATE: sharp_thermal_entry_found = True phases[-1].fixes.extend(possible_thermal_fixes) possible_thermal_fixes = [fix] else: possible_thermal_fixes.append(fix) else: # sign change total_bearing_change = bearing_change sharp_thermal_entry_found = False if len(possible_thermal_fixes) == 0: phases[-1].fixes.append(fix) else: phases[-1].fixes.extend([*possible_thermal_fixes, fix]) possible_thermal_fixes = list() turning_left = bearing_change_rate < 0 if abs(total_bearing_change ) > self.CRUISE_THRESHOLD_BEARINGTOT: cruise = False phases[-1].fixes.append(possible_thermal_fixes[0]) phases.append(Phase(cruise, possible_thermal_fixes)) possible_thermal_fixes = list() sharp_thermal_entry_found = False total_bearing_change = 0 else: # thermal if abs(bearing_change_rate ) > self.THERMAL_THRESHOLD_BEARINGRATE: if len(possible_cruise_fixes) != 0: phases[-1].fixes.extend([*possible_cruise_fixes, fix]) possible_cruise_fixes = list() else: phases[-1].fixes.append(fix) else: # possible cruise if len(possible_cruise_fixes) == 0: possible_cruise_fixes = [fix] total_bearing_change = bearing_change else: possible_cruise_fixes.append(fix) total_bearing_change += bearing_change delta_t = seconds_time_difference( possible_cruise_fixes[0]['time'], time) cruise_distance = calculate_distance( possible_cruise_fixes[0], fix) temp_bearing_rate_avg = 0 if delta_t == 0 else total_bearing_change / delta_t if (cruise_distance > self.THERMAL_THRESHOLD_DISTANCE and abs(temp_bearing_rate_avg) < self.THERMAL_THRESHOLD_BEARINGRATE_AVG): cruise = True phases[-1].fixes.append(possible_cruise_fixes[0]) phases.append(Phase(cruise, possible_cruise_fixes)) possible_cruise_fixes = list() total_bearing_change = 0 # add possible fixes at the end if cruise: if len(possible_thermal_fixes) != 0: phases[-1].fixes.extend(possible_thermal_fixes) else: if len(possible_cruise_fixes) != 0: phases[-1].fixes.extend(possible_cruise_fixes) return phases
def test_calculate_distance_equal_fixes(self): fix1 = dict(lat=52.331783333333334, lon=6.249083333333333) fix2 = dict(lat=52.331783333333334, lon=6.249083333333333) self.assertEqual(calculate_distance(fix1, fix2), 0)
def determine_performance(self, task, trip, phases, gps_altitude): thermal_altitude_gain_tot = 0 thermal_altitude_loss_tot = 0 thermal_time_tot = 0 thermal_distance_tot = 0 thermal_drift_tot = 0 cruise_time_tot = 0 cruise_distance_tot = 0 cruise_height_diff_tot = 0 task_time_tot = 0 for leg in range(trip.completed_legs()): thermal_altitude_gain = 0 thermal_altitude_loss = 0 thermal_time = 0 thermal_distance = 0 thermal_drift = 0 cruise_time = 0 cruise_distance = 0 cruise_height_diff = 0 task_time = 0 for i, phase in enumerate(phases.all_phases(leg)): # take refined start if leg == 0 and i == 0: phase_duration = seconds_time_difference( trip.refined_start_time, phase.fixes[-1]['time']) else: phase_duration = seconds_time_difference_fixes( phase.fixes[0], phase.fixes[-1]) phase_distance_traveled = total_distance_travelled(phase.fixes) task_time += phase_duration if phase.is_cruise: cruise_time += phase_duration cruise_distance += phase_distance_traveled cruise_height_diff += height_difference_fixes( phase.fixes[0], phase.fixes[-1], gps_altitude) else: thermal_time += phase_duration thermal_distance += phase_distance_traveled gain, loss = altitude_gain_and_loss( phase.fixes, gps_altitude) thermal_altitude_gain += gain thermal_altitude_loss += loss thermal_drift += calculate_distance( phase.fixes[0], phase.fixes[-1]) # write to total performance values thermal_altitude_gain_tot += thermal_altitude_gain thermal_altitude_loss_tot += thermal_altitude_loss thermal_time_tot += thermal_time thermal_distance_tot += thermal_distance thermal_drift_tot += thermal_drift cruise_time_tot += cruise_time cruise_distance_tot += cruise_distance cruise_height_diff_tot += cruise_height_diff task_time_tot += task_time self.write_perfs(leg, thermal_altitude_gain, thermal_altitude_loss, thermal_time, thermal_distance, thermal_drift, cruise_time, cruise_distance, cruise_height_diff, task_time) if isinstance(task, AAT) and task_time_tot < task.t_min.total_seconds(): task_time_tot = task.t_min.total_seconds() self.write_perfs(-1, thermal_altitude_gain_tot, thermal_altitude_loss_tot, thermal_time_tot, thermal_distance_tot, thermal_drift_tot, cruise_time_tot, cruise_distance_tot, cruise_height_diff_tot, task_time_tot)