Example #1
0
 def total_angle(self):
     """
     The total angle in radians from start_point to end_point
     """
     if self._total_angle is None:
         self._total_angle = Vector.angle_between(self(0), self(1))
     return self._total_angle
Example #2
0
    def __init__(
            self, initial_coordinate: ArrayLike,
            final_coordinate: ArrayLike,
            atmosphere_params: ArrayLike,
            operating_frequency: float,
            point_number: Optional[int] = None,
            use_high_ray: Optional[bool] = True,
    ):
        """
        supply initial and final points, atmosphere parameters (f_0, r_m, y_m) and operating_frequency (f)

        :param initial_coordinate : array_like, shape (3,)
            the cartesian coordinates of the path start
        :param final_coordinate : array_like, shape (3,)
            the cartesian coordinates of the path end
        :param atmosphere_params : Tuple, shape (3,)
            the atmosphere parameters as a tuple of
            (max_plasma_frequency (f_0), height_of_max_plasma_frequency(r_m), layer_semi_width(y_m))
        :param operating_frequency: float
            The operating frequency of the ray
        :param point_number : int
            The number of points to interpolate between in the final path
        :param use_high_ray : bool
            Whether or not to use high ray
        """
        super().__init__()

        # Initial and final points are normalized, but poly_fit(0) and poly_fit(1) will return the radial component
        # for the initial and final point
        initial_rad, final_rad = linalg.norm(initial_coordinate), linalg.norm(final_coordinate)
        self.initial_point = initial_coordinate / linalg.norm(initial_coordinate)
        self.final_point = final_coordinate / linalg.norm(final_coordinate)

        self.normal_vec = np.cross(self.initial_point, self.final_point)
        self.normal_vec = self.normal_vec / linalg.norm(self.normal_vec)

        angle_between = Vector.angle_between(initial_coordinate, final_coordinate)

        # Parameters for quasi-parabolic path only depend on atmospheric model and ignore magnetic field

        self._parameters = self.calculate_parameters(atmosphere_params, operating_frequency)
        # Point number is the number of points to calculate. All points in between are interpolated from PC spline
        if point_number is not None:
            self.point_number = point_number
        else:
            self.point_number = int(angle_between * EARTH_RADIUS)

        # Each point is evenly spaced along the great circle path connecting initial and final coordinates
        # Each point is a 2-vector holding its angular value in radians (along great circle path)
        # and its radial value at each point (in km)
        self.points = np.zeros((self.point_number, 2))
        self.points[:, 0] = np.linspace(0, angle_between, self.point_number)
        self.points[:, 1] = np.linspace(initial_rad, final_rad, self.point_number)

        # Real representation of the line will be a poly fit of radius vs angle along great circle
        self._poly_fit = None
        self._total_angle = None
        self.using_high_ray = use_high_ray
Example #3
0
    def interpolate_params(self, radial=False, degree=3):
        self._total_angle = Vector.angle_between(self.initial_point, self.final_point)

        self._poly_fit_angular = UnivariateSpline(
            self._angular_parameters[:, 0],
            self._angular_parameters[:, 1],
            k=min(degree, len(self._angular_parameters) - 1),
            s=0,
            ext=0
        )
        if radial:
            self._poly_fit_radial = UnivariateSpline(
                self._radial_parameters[:, 0],
                self._radial_parameters[:, 1],
                k=degree,
                s=0,
                ext=0
            )
        else:
            cartesian_points = np.zeros((len(self._radial_parameters), 3))
            for index in range(len(self._radial_parameters)):
                alpha = self._radial_parameters[index, 0]
                r_1 = Rotation.from_rotvec(self.normal_vec * alpha * self.total_angle)
                v_1 = r_1.apply(Vector.unit_vector(self.initial_point))
                rotation_vec_2 = Vector.unit_vector(np.cross(self.normal_vec, v_1))
                rotation_vec_2 *= self._poly_fit_angular(alpha)
                r_2 = Rotation.from_rotvec(rotation_vec_2)
                v_2 = r_2.apply(v_1)
                v_2 *= self._radial_parameters[index, 1]
                cartesian_points[index] = v_2
            self._poly_fit_cartesian = []
            for index in range(3):
                self._poly_fit_cartesian.append(
                    UnivariateSpline(
                        self._radial_parameters[:, 0],
                        cartesian_points[:, index],
                        k=degree,
                        s=0
                    )
                )
Example #4
0
    def compile_points(self):
        fc, rm, rb, ym, f = self._parameters
        total_angle = Vector.angle_between(self.initial_point, self.final_point)

        points_unprocessed = quasi_parabolic_core.get_quasi_parabolic_path(
            total_angle * EARTH_RADIUS, f, rm + EARTH_RADIUS, ym, fc ** 2
        )
        if self.using_high_ray or len(points_unprocessed) == 1:
            points_unprocessed = points_unprocessed[0]
        else:
            points_unprocessed = points_unprocessed[1]

        points_unprocessed[:, 1] = points_unprocessed[:, 1]
        points_unprocessed[:, 0] = points_unprocessed[:, 0] / EARTH_RADIUS
        self.points = points_unprocessed

        self._total_angle = total_angle

        self._poly_fit = UnivariateSpline(
            points_unprocessed[:, 0], points_unprocessed[:, 1],
            k=3, s=0, ext=0
        )
Example #5
0
    def visualize(self,
                  initial_point: np.ndarray,
                  final_point: np.ndarray,
                  fig: plt.Figure = None,
                  ax: plt.Axes = None,
                  show: bool = False,
                  **kwargs) -> Optional[Tuple[plt.Figure, plt.Axes]]:
        """
        Given an initial and final point  (and some formatting parameters), visualize the
        atmosphere on a matplotlib graph
        :param initial_point : np.ndarray, shape (3, )
            3-vec corresponding to the starting coordinate of the path in
            cartesian coordinates. Used to determine the interval in which
            to calculate the atmosphere
        :param final_point : np.ndarray, shape (3, )
            3-vec corresponding to the starting coordinate of the path in
            cartesian coordinates. Used to determine the interval in which
            to calculate the atmosphere
        :param fig : Figure, optional
            If fig and ax are both provided,
            display the atmosphere colormap on top of the old axes
            otherwise, create a new figure and axes to work with
        :param ax : Axes, optional
            If fig and ax are both provided,
            display the atmosphere colormap on top of the old axes
            otherwise, create a new figure and axes to work with
        :param show : boolean, optional
            If show is true, display the plotted atmosphere immediately and return nothing.
            Otherwise, don't display and instead return the computed figure and axes
        :param kwargs : dict, optional
            Any additional kwargs are passed to the imshow function
        :returns : (Figure, Axes), optional
            If show is False, return the computed figure and axes, otherwise return nothing.
        """
        total_angle = Vector.angle_between(initial_point, final_point)
        normal_vec = Vector.unit_vector(np.cross(initial_point, final_point))
        point_number = 500

        if ax is None or fig is None:
            fig, ax = plt.subplots(1, 1, figsize=(6, 4.5))
        radii = np.linspace(Constants.EARTH_RADIUS,
                            Constants.EARTH_RADIUS + 400E3, point_number)
        alpha = np.linspace(0, total_angle, point_number)
        r_1 = Rotation.from_rotvec(normal_vec * alpha.reshape(-1, 1))
        v_1 = r_1.apply(initial_point)
        v_1 = Vector.cartesian_to_spherical(v_1)
        frequency_grid = np.zeros((point_number, point_number))
        for i in range(point_number):
            plotted_vecs = np.repeat(v_1[i].reshape(-1, 1),
                                     point_number,
                                     axis=1).T
            plotted_vecs[:, 0] = radii
            frequency_grid[:, i] = self.plasma_frequency(
                Vector.spherical_to_cartesian(plotted_vecs)) / 1E6
        image = ax.imshow(
            frequency_grid,
            cmap='gist_rainbow',
            interpolation='bilinear',
            origin='lower',
            alpha=1,
            aspect='auto',
            extent=[0, total_angle * Constants.EARTH_RADIUS / 1000, 0, 400],
            **kwargs)
        ax.yaxis.set_ticks_position('both')
        color_bar = fig.colorbar(image, ax=ax)
        color_bar.set_label("Plasma Frequency (MHz)")

        if show:
            ax.set_title("Chapman Layers Atmosphere")
            plt.show()
        else:
            return fig, ax