def thrust_direction(axis: int, force: float, pr: Projectile): if pr.time < 1.2: return spherical_to_planar_coord(axis, force, math.pi / 4, 0) if (pr.position.alt > 100000 or pr.velocities[Z_INDEX] > 4000) and pr.pitch > 0.2: spherical_to_planar_coord(axis, force, pr.pitch - 0.15, pr.yaw) if pr.pitch < 0.15: spherical_to_planar_coord(axis, force, pr.pitch + 0.17, pr.yaw) return follow_path(axis, force, pr)
def follow_path(axis: int, force: float, pr: Projectile) -> float: return spherical_to_planar_coord(axis, force, pr.pitch, pr.yaw)
def update_velocities(self, old_lat: np.float128, old_lon: np.float128, radius: float, distance_m: float) -> None: """ Update velocities: take a look at the old position, current position, how much it has changed and calculate the speed based on that. Effectively moves the local tangent plane. z-speed is not modified as it's unaffected by moving of the tangent plane. :param old_lat: previous latitude :param old_lon: previous longitude :param radius: earth radius + altitude :param distance_m: distance travelled (could be calculated inside the function, but no need atm) :return: nothing; updates the projectile's state """ old_vy = self.velocities[Y_INDEX] self.velocities[Y_INDEX] = (radius * (self.position.lat - old_lat) ) / self.dt # usual case for correcting Vy # our first and largest problem is detecting whether we've crossed the pole. I don't know a nice way for this, # so we use the ugly way: statistics and prayers. # ideally, change_radio should be 0 (meaning Vy won't be corrected) if old_vy != 0: change_ratio = fabs(self.velocities[Y_INDEX] / old_vy - 1) else: change_ratio = fabs(self.velocities[Y_INDEX]) if change_ratio > self.vy_corrective_change_threshold: # oops, we've modified Vy too much actual_distance = haversine(self.position, Position(old_lat, old_lon, 0), radius) if self.crossed_the_pole: # if we've crossed the pole recently, we definitely don't want to do it again print( f"Warning! V_y has too extreme oscillations: {change_ratio}" ) elif actual_distance < self.distance_stats.mean and self.distance_stats.is_outlier( actual_distance): # if we've travelled significantly less than usual (i.e. in y direction), assume we've crossed the pole # conceptually simple, practically not-really-definable, but surprisingly effective print(f"Crossing the pole: change ratio is {change_ratio}f") # so now that we've crossed the pole, we want to reverse Vx and Vy and change longitude so we continue # flying 'straight'. We're basically throwing away this iteration and using the data from the previous. self.crossed_the_pole = True # this will prevent crossing the pole again if we're still too close self.position.lon = divmod((old_lon + pi), (2 * pi))[1] - 2 * pi self.yaw = self.yaw - pi % (sgn(self.yaw - pi) * 2 * pi) self.velocities[X_INDEX] = spherical_to_planar_coord( X_INDEX, self.total_velocity, self.pitch, self.yaw) self.velocities[Y_INDEX] = spherical_to_planar_coord( Y_INDEX, self.total_velocity, self.pitch, self.yaw) return # don't update X velocity! elif actual_distance < self.distance_stats.mean: # Vy changed a lot, but we're far away from the pole; can happen in e.g. polar circle even if we're not # flying over the pole itself. print( f"Warning: Vy has extreme correction, but we're far from poles: {change_ratio}" ) if self.crossed_the_pole: self.crossed_the_pole = False self.velocities[ Y_INDEX] = old_vy # don't correct speed if we've just skipped the pole return self.distance_stats.update( distance_m) # we need to keep track of what's the 'usual' movement # from now on, we take care of updating Vx lon_radius = radius * cos(self.position.lat) if lon_radius == 0: # not much we can do if we're exactly at the pole, just use the last good number, it'll be close enough lon_radius = radius * cos(old_lat) if fabs(self.position.lon - old_lon) < pi: # usual case for correcting Vx self.velocities[X_INDEX] = ( lon_radius * (self.position.lon - old_lon)) / self.dt else: # crossing the antimeridian: we haven't really moved for 2pi, so substract (or add) that self.velocities[X_INDEX] = ( lon_radius * (self.position.lon - old_lon + 2 * pi * sgn(old_lon - self.position.lon))) / self.dt