Exemple #1
0
    def test_core_trans_kwargs_deprecated(self):
        """Test deprecated keywords in wrapped transformation functions."""

        self.warn_msgs = [
            "".join([
                "`method` must be a string value in ",
                "v1.0.0+. Setting to function default."
            ])
        ]
        self.warn_msgs = np.array(self.warn_msgs)

        # Catch the warnings.
        with warnings.catch_warnings(record=True) as war:
            OMMBV.python_ecef_to_geodetic(np.array([0.]),
                                          np.array([0.]),
                                          np.array([550.]),
                                          method=None)

        # Ensure the minimum number of warnings were raised.
        assert len(war) >= len(self.warn_msgs)

        # Test the warning messages, ensuring each attribute is present.
        eval_warnings(war, self.warn_msgs)

        return
Exemple #2
0
    def test_geodetic_to_ecef_to_geodetic(self):
        """Geodetic to ECEF and back"""
        lats, longs, alts = gen_data_fixed_alt(550.)
        ecf_x, ecf_y, ecf_z = OMMBV.geodetic_to_ecef(lats,
                                                    longs,
                                                    alts)
        lat, elong, alt = OMMBV.ecef_to_geodetic(ecf_x, ecf_y, ecf_z)
        lat2, elong2, alt2 = OMMBV.python_ecef_to_geodetic(ecf_x, ecf_y, ecf_z)

        idx, = np.where(elong < 0)
        elong[idx] += 360.

        idx, = np.where(elong2 < 0)
        elong2[idx] += 360.

        d_lat = lat - lats
        d_long = elong - longs
        d_alt = alt - alts

        assert np.all(np.abs(d_lat) < 1.E-5)
        assert np.all(np.abs(d_long) < 1.E-5)
        assert np.all(np.abs(d_alt) < 1.E-5)

        d_lat = lat2 - lats
        d_long = elong2 - longs
        d_alt = alt2 - alts

        assert np.all(np.abs(d_lat) < 1.E-5)
        assert np.all(np.abs(d_long) < 1.E-5)
        assert np.all(np.abs(d_alt) < 1.E-5)
Exemple #3
0
    def test_core_scalars_for_mapping(self, param):
        """Test deprecated inputs, `scalars_for_mapping_ion_drifts`."""

        self.warn_msgs = [
            " ".join([
                param, "is deprecated, non-functional,",
                "and will be removed after OMMBV v1.0.0."
            ])
        ]
        self.warn_msgs = np.array(self.warn_msgs)

        # Prep input
        kwargs = {param: 1}

        # Catch the warnings.
        with warnings.catch_warnings(record=True) as war:
            OMMBV.scalars_for_mapping_ion_drifts([0.], [0.], [550.],
                                                 [self.date], **kwargs)

        # Ensure the minimum number of warnings were raised.
        assert len(war) >= len(self.warn_msgs)

        # Test the warning messages, ensuring each attribute is present.
        eval_warnings(war, self.warn_msgs)

        return
Exemple #4
0
    def test_igrf_end_to_ecef_back_to_end(self):
        """Check consistency ENU-ECEF and IGRF implementation"""
        # import pdb
        vx = 0.9
        vy = 0.1
        vz = np.sqrt(1. - vx ** 2 + vy ** 2)
        vz = -vz
        lats, longs, alts = gen_data_fixed_alt(550.)
        for lat, lon, alt in zip(lats, longs, alts):
            # print(vx, vy, vz, lat, lon)
            # pdb.set_trace()
            # input here is co-latitude, not latitude
            # inputs to fortran are in radians
            vxx, vyy, vzz = igrf.end_vector_to_ecef(vx, vy, vz, np.deg2rad(90. - lat), np.deg2rad(lon))
            vx2, vy2, vz2 = OMMBV.enu_to_ecef_vector(vx, vy, -vz, lat, lon)
            # print ('end check ', vxx, vyy, vzz, vx2, vy2, vz2)
            asseq(vxx, vx2, 9)
            asseq(vyy, vy2, 9)
            asseq(vzz, vz2, 9)

            vxx, vyy, vzz = OMMBV.ecef_to_enu_vector(vxx, vyy, vzz, lat, lon)
            # convert upward component back to down
            vzz = -vzz
            # compare original inputs to outputs
            asseq(vx, vxx, 9)
            asseq(vy, vyy, 9)
            asseq(vz, vzz, 9)
Exemple #5
0
    def test_geodetic_to_ecef_to_geocentric_to_ecef_to_geodetic(self):
        """geodetic to ecef and geocentric transformations"""
        lats, longs, alts = gen_data_fixed_alt(550.)
        ecf_x, ecf_y, ecf_z = OMMBV.geodetic_to_ecef(lats,
                                                    longs,
                                                    alts)
        geo_lat, geo_long, geo_alt = OMMBV.ecef_to_geocentric(ecf_x, ecf_y, ecf_z)

        ecfs_x, ecfs_y, ecfs_z = OMMBV.geocentric_to_ecef(geo_lat, geo_long, geo_alt)

        lat, elong, alt = OMMBV.ecef_to_geodetic(ecfs_x, ecfs_y, ecfs_z)

        idx, = np.where(elong < 0)
        elong[idx] += 360.

        d_lat = lat - lats
        d_long = elong - longs
        d_alt = alt - alts

        assert np.all(np.abs(d_lat) < 1.E-5)
        assert np.all(np.abs(d_long) < 1.E-5)
        assert np.all(np.abs(d_alt) < 1.E-5)
        assert np.all(np.abs(ecf_x - ecfs_x) < 1.E-5)
        assert np.all(np.abs(ecf_y - ecfs_y) < 1.E-5)
        assert np.all(np.abs(ecf_z - ecfs_z) < 1.E-5)
Exemple #6
0
    def test_tracing_accuracy_w_recursion(self):
        """Establish performance field-line vs max_steps"""
        lats, longs, alts = gen_trace_data_fixed_alt(550.)
        ecf_x, ecf_y, ecf_z = OMMBV.geodetic_to_ecef(lats,
                                                    longs,
                                                    alts)
        # step size to be tried
        steps_goal = np.array([5.] * 13)
        # max number of steps (fixed)
        max_steps_goal = np.arange(13)
        max_steps_goal = 100000. / 2 ** max_steps_goal

        date = datetime.datetime(2000, 1, 1)
        dx = []
        dy = []
        dz = []
        for x, y, z in zip(ecf_x, ecf_y, ecf_z):
            out = []
            for steps, max_steps in zip(steps_goal, max_steps_goal):
                trace_n = OMMBV.field_line_trace(np.array([x, y, z]), date, 1., 0.,
                                                step_size=steps,
                                                max_steps=max_steps)
                pt = trace_n[-1, :]
                out.append(pt)

            final_pt = pds.DataFrame(out, columns=['x', 'y', 'z'])
            dx.append(np.abs(final_pt.ix[1:, 'x'].values - final_pt.ix[:, 'x'].values[:-1]))
            dy.append(np.abs(final_pt.ix[1:, 'y'].values - final_pt.ix[:, 'y'].values[:-1]))
            dz.append(np.abs(final_pt.ix[1:, 'z'].values - final_pt.ix[:, 'z'].values[:-1]))
        dx = pds.DataFrame(dx)
        dy = pds.DataFrame(dy)
        dz = pds.DataFrame(dz)

        try:
            plt.figure()
            yerrx = np.nanstd(np.log10(dx), axis=0)
            yerry = np.nanstd(np.log10(dy), axis=0)
            yerrz = np.nanstd(np.log10(dz), axis=0)
            plt.errorbar(np.log10(max_steps_goal[1:]), np.log10(dx.mean(axis=0)),
                         yerr=yerrx,
                         label='x')
            plt.errorbar(np.log10(max_steps_goal[1:]), np.log10(dy.mean(axis=0)),
                         yerr=yerry,
                         label='y')
            plt.errorbar(np.log10(max_steps_goal[1:]), np.log10(dz.mean(axis=0)),
                         yerr=yerrz,
                         label='z')
            plt.xlabel('Log Number of Steps per Run')
            plt.ylabel('Change in Foot Point Position (km)')
            plt.title("Change in Final ECEF Position, Recursive Calls")
            plt.legend()
            plt.tight_layout()
            plt.ylabel('Log Change in Foot Point Position (km)')
            plt.savefig('Footpoint_position_vs_max_steps_recursion.pdf')
            plt.close()
        except:
            pass
Exemple #7
0
 def test_enu_to_ecef_back_to_enu(self):
     """Test ENU-ECEF-ENU"""
     vx = 0.9
     vy = 0.1
     vz = np.sqrt(1. - vx ** 2 + vy ** 2)
     lats, longs, alts = gen_data_fixed_alt(550.)
     for lat, lon, alt in zip(lats, longs, alts):
         vxx, vyy, vzz = OMMBV.enu_to_ecef_vector(vx, vy, vz, lat, lon)
         vxx, vyy, vzz = OMMBV.ecef_to_enu_vector(vxx, vyy, vzz, lat, lon)
         asseq(vx, vxx, 9)
         asseq(vy, vyy, 9)
         asseq(vz, vzz, 9)
Exemple #8
0
    def test_plot_apex_heights(self):
        """Check meridional vector along max in apex height gradient"""
        date = pysat.datetime(2010, 1, 1)

        delta = 1.

        ecef_x, ecef_y, ecef_z = OMMBV.geodetic_to_ecef([0.], [320.], [550.])

        # get basis vectors
        zx, zy, zz, _, _, _, mx, my, mz = OMMBV.calculate_mag_drift_unit_vectors_ecef(ecef_x, ecef_y, ecef_z,
                                                                                     [date], ecef_input=True)

        # get apex height for step along meridional directions, then around that direction
        _, _, _, _, _, nominal_max = OMMBV.apex_location_info(ecef_x + delta * mx,
                                                              ecef_y + delta * my,
                                                              ecef_z + delta * mz,
                                                              [date],
                                                              ecef_input=True,
                                                              return_geodetic=True)

        steps = (np.arange(101) - 50.) * delta / 10000.
        output_max = []
        for step in steps:
            del_x = delta * mx + step * zx
            del_y = delta * my + step * zy
            del_z = delta * mz + step * zz
            norm = np.sqrt(del_x ** 2 + del_y ** 2 + del_z ** 2)
            del_x /= norm
            del_y /= norm
            del_z /= norm
            _, _, _, _, _, loop_h = OMMBV.apex_location_info(ecef_x + del_x,
                                                             ecef_y + del_y,
                                                             ecef_z + del_z,
                                                             [date],
                                                             ecef_input=True,
                                                             return_geodetic=True)
            output_max.append(loop_h)

        try:
            plt.figure()
            plt.plot(steps, output_max)
            plt.plot([0], nominal_max, color='r', marker='o', markersize=12)
            plt.ylabel('Apex Height (km)')
            plt.xlabel('Distance along Zonal Direction (km)')
            plt.savefig('comparison_apex_heights_and_meridional.pdf')
            plt.close()
        except:
            pass

        # make sure meridional direction is correct
        assert np.all(np.max(output_max) == nominal_max)
Exemple #9
0
    def test_basic_enu_to_ecef_rotations(self):
        """Test ENU to ECEF rotations"""
        # test basic transformations first
        # vector pointing east at 0, 0 is along y
        vx, vy, vz = OMMBV.enu_to_ecef_vector(1., 0., 0., 0., 0.)
        # print ('{:9f}, {:9f}, {:9f}'.format(vx, vy, vz))
        asseq(vx, 0.0, 9)
        asseq(vy, 1.0, 9)
        asseq(vz, 0.0, 9)
        # vector pointing up at 0, 0 is along x
        vx, vy, vz = OMMBV.enu_to_ecef_vector(0., 0., 1., 0., 0.)
        asseq(vx, 1.0, 9)
        asseq(vy, 0.0, 9)
        asseq(vz, 0.0, 9)
        # vector pointing north at 0, 0 is along z
        vx, vy, vz = OMMBV.enu_to_ecef_vector(0., 1., 0., 0., 0.)
        asseq(vx, 0.0, 9)
        asseq(vy, 0.0, 9)
        asseq(vz, 1.0, 9)

        # east vector at 0, 90 long points along -x
        vx, vy, vz = OMMBV.enu_to_ecef_vector(1., 0., 0., 0., 90.)
        # print ('{:9f}, {:9f}, {:9f}'.format(vx, vy, vz))
        asseq(vx, -1.0, 9)
        asseq(vy, 0.0, 9)
        asseq(vz, 0.0, 9)
        # vector pointing up at 0, 90 is along y
        vx, vy, vz = OMMBV.enu_to_ecef_vector(0., 0., 1., 0., 90.)
        asseq(vx, 0.0, 9)
        asseq(vy, 1.0, 9)
        asseq(vz, 0.0, 9)
        # vector pointing north at 0, 90 is along z
        vx, vy, vz = OMMBV.enu_to_ecef_vector(0., 1., 0., 0., 90.)
        asseq(vx, 0.0, 9)
        asseq(vy, 0.0, 9)
        asseq(vz, 1.0, 9)

        # vector pointing east at 0, 0 is along y
        vx, vy, vz = OMMBV.enu_to_ecef_vector(1., 0., 0., 0., 180.)
        # print ('{:9f}, {:9f}, {:9f}'.format(vx, vy, vz))
        asseq(vx, 0.0, 9)
        asseq(vy, -1.0, 9)
        asseq(vz, 0.0, 9)
        # vector pointing up at 0, 180 is along -x
        vx, vy, vz = OMMBV.enu_to_ecef_vector(0., 0., 1., 0., 180.)
        asseq(vx, -1.0, 9)
        asseq(vy, 0.0, 9)
        asseq(vz, 0.0, 9)
        # vector pointing north at 0, 180 is along z
        vx, vy, vz = OMMBV.enu_to_ecef_vector(0., 1., 0., 0., 180.)
        asseq(vx, 0.0, 9)
        asseq(vy, 0.0, 9)
        asseq(vz, 1.0, 9)
Exemple #10
0
    def test_geodetic_to_ecef_to_geocentric_to_ecef_to_geodetic(self):
        """Test geodetic to ecef and geocentric transformations"""

        ecf_x, ecf_y, ecf_z = OMMBV.trans.geodetic_to_ecef(
            self.lats, self.longs, self.alts)

        geo_lat, geo_long, geo_alt = OMMBV.trans.ecef_to_geocentric(
            ecf_x, ecf_y, ecf_z)

        ecfs_x, ecfs_y, ecfs_z = OMMBV.trans.geocentric_to_ecef(
            geo_lat, geo_long, geo_alt)

        lat, elong, alt = OMMBV.ecef_to_geodetic(ecfs_x, ecfs_y, ecfs_z)

        idx, = np.where(elong < 0)
        elong[idx] += 360.

        assert_difference_tol(lat, self.lats)
        assert_difference_tol(elong, self.longs)
        assert_difference_tol(alt, self.alts)

        assert_difference_tol(ecf_x, ecfs_x)
        assert_difference_tol(ecf_y, ecfs_y)
        assert_difference_tol(ecf_z, ecfs_z)

        return
Exemple #11
0
    def test_igrf_ecef_to_geodetic_back_to_ecef(self):
        """Test IGRF_ECEF - Geodetic - and Back"""
        lats, longs, alts = gen_data_fixed_alt(550.)
        ecf_x, ecf_y, ecf_z = OMMBV.geodetic_to_ecef(lats,
                                                    longs,
                                                    alts)
        for ecef_x, ecef_y, ecef_z, geo_lat, geo_lon, geo_alt in zip(ecf_x, ecf_y,
                                                                     ecf_z, lats, longs, alts):
            pos = np.array([ecef_x, ecef_y, ecef_z])
            lat, elong, alt = igrf.ecef_to_geodetic(pos)
            lat = np.rad2deg(lat)
            elong = np.rad2deg(elong)
            if (elong < 0):
                elong = elong + 360.

            d_lat = lat - geo_lat
            d_long = elong - geo_lon
            d_alt = alt - geo_alt

            # print ('Word', ecef_x, ecef_y, ecef_z)
            # print (geo_lat, geo_lon, geo_alt)
            # print (lat, elong, alt)
            assert np.all(np.abs(d_lat) < 1.E-5)
            assert np.all(np.abs(d_long) < 1.E-5)
            assert np.all(np.abs(d_alt) < 1.E-5)
Exemple #12
0
    def test_geocentric_to_ecef_to_geocentric(self):
        """Geocentric and ECEF transformations"""
        lats, longs, alts = gen_data_fixed_alt(550.)
        ecf_x, ecf_y, ecf_z = OMMBV.geocentric_to_ecef(lats,
                                                      longs,
                                                      alts)
        lat, elong, alt = OMMBV.ecef_to_geocentric(ecf_x, ecf_y, ecf_z)

        idx, = np.where(elong < 0)
        elong[idx] += 360.

        d_lat = lat - lats
        d_long = elong - longs
        d_alt = alt - alts

        assert np.all(np.abs(d_lat) < 1.E-5)
        assert np.all(np.abs(d_long) < 1.E-5)
        assert np.all(np.abs(d_alt) < 1.E-5)
Exemple #13
0
    def test_simple_geomagnetic_basis_interface(self):
        """Ensure simple geomagnetic basis interface runs."""

        for i, p_lat in enumerate(self.lats):
            out_d = OMMBV.calculate_geomagnetic_basis(
                [p_lat] * len(self.longs), self.longs, self.alts,
                [self.date] * len(self.longs))

        assert True
        return
Exemple #14
0
    def test_geodetic_to_ecef_to_geodetic_via_different_methods(self):
        """Multiple techniques for geodetic to ECEF to geodetic"""
        lats, longs, alts = gen_data_fixed_alt(550.)
        ecf_x, ecf_y, ecf_z = OMMBV.geodetic_to_ecef(lats,
                                                     longs,
                                                     alts)
        methods = ['closed', 'iterative']
        for method in methods:
            lat, elong, alt = OMMBV.python_ecef_to_geodetic(ecf_x, ecf_y, ecf_z,
                                                            method=method)

            idx, = np.where(elong < 0)
            elong[idx] += 360.

            d_lat = lat - lats
            d_long = elong - longs
            d_alt = alt - alts

            assert np.all(np.abs(d_lat) < 1.E-5)
            assert np.all(np.abs(d_long) < 1.E-5)
            assert np.all(np.abs(d_alt) < 1.E-5)
Exemple #15
0
    def test_igrf_ecef_to_geographic_with_colatitude(self):
        """Test IGRF_ECEF - Geographic"""
        lats, longs, alts = gen_data_fixed_alt(550.)
        ecf_x, ecf_y, ecf_z = OMMBV.geodetic_to_ecef(lats,
                                                    longs,
                                                    alts)
        for ecef_x, ecef_y, ecef_z, geo_lat, geo_lon, geo_alt in zip(ecf_x, ecf_y,
                                                                     ecf_z, lats, longs, alts):
            pos = np.array([ecef_x, ecef_y, ecef_z])

            colat, lon, r = igrf.ecef_to_colat_long_r(pos)
            # results are returned in radians
            lat = 90. - np.rad2deg(colat)
            lon = np.rad2deg(lon)

            lat2, lon2, h2 = OMMBV.ecef_to_geocentric(*pos, ref_height=0)

            # print(lat, lon, r, lat2, lon2, h2)
            asseq(r, h2, 9)
            asseq(lat, lat2, 9)
            asseq(lon, lon2, 9)
Exemple #16
0
    def test_vectors_at_pole(self):
        """Ensure np.nan returned at magnetic pole for scalars and vectors."""
        out = OMMBV.calculate_mag_drift_unit_vectors_ecef([80.97], [250.],
                                                          [550.], [self.date],
                                                          full_output=True,
                                                          pole_tol=1.E-4)
        # Confirm vectors are np.nan
        assert np.all(np.isnan(out[0:3]))
        assert np.all(np.isnan(out[6:9]))

        for item in self.map_labels:
            assert np.isnan(out[-1][item])

        return
Exemple #17
0
def project_ecef_vector_onto_sc(inst,
                                x_label,
                                y_label,
                                z_label,
                                new_x_label,
                                new_y_label,
                                new_z_label,
                                meta=None):
    """Express input vector using s/c attitude directions

    x - ram pointing
    y - generally southward
    z - generally nadir

    Parameters
    ----------
    x_label : string
        Label used to get ECEF-X component of vector to be projected
    y_label : string
        Label used to get ECEF-Y component of vector to be projected
    z_label : string
        Label used to get ECEF-Z component of vector to be projected
    new_x_label : string
        Label used to set X component of projected vector
    new_y_label : string
        Label used to set Y component of projected vector
    new_z_label : string
        Label used to set Z component of projected vector
    meta : array_like of dicts (None)
        Dicts contain metadata to be assigned.
    """

    # TODO: add checks for existence of ecef labels in inst

    x, y, z = OMMBV.project_ecef_vector_onto_basis(
        inst[x_label], inst[y_label], inst[z_label], inst['sc_xhat_ecef_x'],
        inst['sc_xhat_ecef_y'], inst['sc_xhat_ecef_z'], inst['sc_yhat_ecef_x'],
        inst['sc_yhat_ecef_y'], inst['sc_yhat_ecef_z'], inst['sc_zhat_ecef_x'],
        inst['sc_zhat_ecef_y'], inst['sc_zhat_ecef_z'])
    inst[new_x_label] = x
    inst[new_y_label] = y
    inst[new_z_label] = z

    if meta is not None:
        inst.meta[new_x_label] = meta[0]
        inst.meta[new_y_label] = meta[1]
        inst.meta[new_z_label] = meta[2]

    return
Exemple #18
0
    def test_apex_heights(self):
        """Check meridional vector along max in apex height gradient"""
        date = dt.datetime(2010, 1, 1)

        delta = 1.

        ecef_x, ecef_y, ecef_z = OMMBV.trans.geodetic_to_ecef([0.], [320.],
                                                              [550.])
        # Get basis vectors
        (zx, zy, zz, _, _, _, mx, my,
         mz) = OMMBV.calculate_mag_drift_unit_vectors_ecef(ecef_x,
                                                           ecef_y,
                                                           ecef_z, [date],
                                                           ecef_input=True)

        # Get apex height for step along meridional directions,
        # then around that direction
        (_, _, _, _, _,
         nominal_max) = OMMBV.trace.apex_location_info(ecef_x + delta * mx,
                                                       ecef_y + delta * my,
                                                       ecef_z + delta * mz,
                                                       [date],
                                                       ecef_input=True,
                                                       return_geodetic=True)

        steps = (np.arange(101) - 50.) * delta / 10000.
        output_max = []
        for step in steps:
            del_x = delta * mx + step * zx
            del_y = delta * my + step * zy
            del_z = delta * mz + step * zz
            del_x, del_y, del_z = OMMBV.vector.normalize(del_x, del_y, del_z)
            (_, _, _, _, _,
             loop_h) = OMMBV.trace.apex_location_info(ecef_x + del_x,
                                                      ecef_y + del_y,
                                                      ecef_z + del_z, [date],
                                                      ecef_input=True,
                                                      return_geodetic=True)
            output_max.append(loop_h)

        # Make sure meridional direction is correct
        assert np.all(np.abs(np.max(output_max) - nominal_max) < 1.E-4)
        return
Exemple #19
0
    def test_geomag_efield_scalars(self, kwargs):
        """Test electric field and drift mapping values."""

        data = {}
        for i, p_lat in enumerate(self.lats):
            templ = [p_lat] * len(self.longs)
            tempd = [self.date] * len(self.longs)
            scalars = OMMBV.scalars_for_mapping_ion_drifts(
                templ, self.longs, self.alts, tempd, **kwargs)
            for scalar in scalars:
                if scalar not in data:
                    data[scalar] = np.full((len(self.lats), len(self.longs)),
                                           np.nan)

                data[scalar][i, :] = scalars[scalar]

        assert len(scalars.keys()) == 12

        for scalar in scalars:
            assert np.all(np.isfinite(scalars[scalar]))

        return
Exemple #20
0
def add_mag_drift_unit_vectors(inst,
                               lat_label='latitude',
                               long_label='longitude',
                               alt_label='altitude',
                               **kwargs):
    """Add unit vectors expressing the ion drift coordinate system
    organized by the geomagnetic field. Unit vectors are expressed
    in S/C coordinates.

    Interally, routine calls add_mag_drift_unit_vectors_ecef.
    See function for input parameter description.
    Requires the orientation of the S/C basis vectors in ECEF using naming,
    'sc_xhat_x' where *hat (*=x,y,z) is the S/C basis vector and _* (*=x,y,z)
    is the ECEF direction.

    Parameters
    ----------
    inst : pysat.Instrument object
        Instrument object to be modified
    max_steps : int
        Maximum number of steps taken for field line integration
    **kwargs
        Passed along to calculate_mag_drift_unit_vectors_ecef

    Returns
    -------
    None
        Modifies instrument object in place. Adds 'unit_zon_*' where * = x,y,z
        'unit_fa_*' and 'unit_mer_*' for zonal, field aligned, and meridional
        directions. Note that vector components are expressed in the S/C basis.

    """

    # vectors are returned in geo/ecef coordinate system
    add_mag_drift_unit_vectors_ecef(inst,
                                    lat_label=lat_label,
                                    long_label=long_label,
                                    alt_label=alt_label,
                                    **kwargs)
    # convert them to S/C using transformation supplied by OA
    inst['unit_zon_x'], inst['unit_zon_y'], inst[
        'unit_zon_z'] = OMMBV.project_ecef_vector_onto_basis(
            inst['unit_zon_ecef_x'], inst['unit_zon_ecef_y'],
            inst['unit_zon_ecef_z'], inst['sc_xhat_x'], inst['sc_xhat_y'],
            inst['sc_xhat_z'], inst['sc_yhat_x'], inst['sc_yhat_y'],
            inst['sc_yhat_z'], inst['sc_zhat_x'], inst['sc_zhat_y'],
            inst['sc_zhat_z'])
    inst['unit_fa_x'], inst['unit_fa_y'], inst[
        'unit_fa_z'] = OMMBV.project_ecef_vector_onto_basis(
            inst['unit_fa_ecef_x'], inst['unit_fa_ecef_y'],
            inst['unit_fa_ecef_z'], inst['sc_xhat_x'], inst['sc_xhat_y'],
            inst['sc_xhat_z'], inst['sc_yhat_x'], inst['sc_yhat_y'],
            inst['sc_yhat_z'], inst['sc_zhat_x'], inst['sc_zhat_y'],
            inst['sc_zhat_z'])
    inst['unit_mer_x'], inst['unit_mer_y'], inst[
        'unit_mer_z'] = OMMBV.project_ecef_vector_onto_basis(
            inst['unit_mer_ecef_x'], inst['unit_mer_ecef_y'],
            inst['unit_mer_ecef_z'], inst['sc_xhat_x'], inst['sc_xhat_y'],
            inst['sc_xhat_z'], inst['sc_yhat_x'], inst['sc_yhat_y'],
            inst['sc_yhat_z'], inst['sc_zhat_x'], inst['sc_zhat_y'],
            inst['sc_zhat_z'])

    inst.meta['unit_zon_x'] = {
        'long_name':
        'Zonal direction along IVM-x',
        'desc':
        'Unit vector for the zonal geomagnetic direction.',
        'label':
        'Zonal Unit Vector: IVM-X component',
        'axis':
        'Zonal Unit Vector: IVM-X component',
        'notes': ('The zonal vector is perpendicular to the '
                  'local magnetic field and the magnetic meridional plane. '
                  'The zonal vector maps to purely horizontal '
                  'at the magnetic equator, with positive values '
                  'pointed generally eastward. This vector '
                  'is expressed here in the IVM instrument frame.'
                  'The IVM-x direction points along the instrument '
                  'boresight, which is pointed into ram for '
                  'standard operations.'
                  'Calculated using the corresponding unit vector '
                  'in ECEF and the orientation '
                  'of the IVM also expressed in ECEF (sc_*hat_*).'),
        'scale':
        'linear',
        'units':
        '',
        'value_min':
        -1.,
        'value_max':
        1
    }
    inst.meta['unit_zon_y'] = {
        'long_name':
        'Zonal direction along IVM-y',
        'desc':
        'Unit vector for the zonal geomagnetic direction.',
        'label':
        'Zonal Unit Vector: IVM-Y component',
        'axis':
        'Zonal Unit Vector: IVM-Y component',
        'notes':
        ('The zonal vector is perpendicular to the '
         'local magnetic field and the magnetic meridional plane. '
         'The zonal vector maps to purely horizontal '
         'at the magnetic equator, with positive values '
         'pointed generally eastward. '
         'The unit vector is expressed here in the IVM coordinate system, '
         'where Y = Z x X, nominally southward when '
         'in standard pointing, X along ram. '
         'Calculated using the corresponding unit vector '
         'in ECEF and the orientation '
         'of the IVM also expressed in ECEF (sc_*hat_*).'),
        'scale':
        'linear',
        'units':
        '',
        'value_min':
        -1.,
        'value_max':
        1
    }
    inst.meta['unit_zon_z'] = {
        'long_name':
        'Zonal direction along IVM-z',
        'desc':
        'Unit vector for the zonal geomagnetic direction.',
        'label':
        'Zonal Unit Vector: IVM-Z component',
        'axis':
        'Zonal Unit Vector: IVM-Z component',
        'notes': ('The zonal vector is perpendicular to the '
                  'local magnetic field and the magnetic meridional plane. '
                  'The zonal vector maps to purely horizontal '
                  'at the magnetic equator, with positive values '
                  'pointed generally eastward. This vector '
                  'is expressed here in the IVM instrument frame.'
                  'The IVM-Z direction points towards nadir '
                  'when IVM-X is pointed into ram for '
                  'standard operations.'
                  'Calculated using the corresponding unit vector '
                  'in ECEF and the orientation '
                  'of the IVM also expressed in ECEF (sc_*hat_*).'),
        'scale':
        'linear',
        'units':
        '',
        'value_min':
        -1.,
        'value_max':
        1
    }

    inst.meta['unit_fa_x'] = {
        'long_name':
        'Field-aligned direction along IVM-x',
        'desc':
        'Unit vector for the geomagnetic field line direction.',
        'label':
        'Field Aligned Unit Vector: IVM-X component',
        'axis':
        'Field Aligned Unit Vector: IVM-X component',
        'notes': ('The field-aligned vector points along the '
                  'geomagnetic field, with positive values '
                  'along the field direction, and is '
                  'expressed here in the IVM instrument frame. '
                  'The IVM-x direction points along the instrument '
                  'boresight, which is pointed into ram for '
                  'standard operations.'
                  'Calculated using the corresponding unit vector '
                  'in ECEF and the orientation '
                  'of the IVM also expressed in ECEF (sc_*hat_*).'),
        'scale':
        'linear',
        'units':
        '',
        'value_min':
        -1.,
        'value_max':
        1
    }
    inst.meta['unit_fa_y'] = {
        'long_name':
        'Field-aligned direction along IVM-y',
        'desc':
        'Unit vector for the geomagnetic field line direction.',
        'label':
        'Field Aligned Unit Vector: IVM-Y component',
        'axis':
        'Field Aligned Unit Vector: IVM-Y component',
        'notes':
        ('The field-aligned vector points along the '
         'geomagnetic field, with positive values '
         'along the field direction. '
         'The unit vector is expressed here in the IVM coordinate system, '
         'where Y = Z x X, nominally southward when '
         'in standard pointing, X along ram. '
         'Calculated using the corresponding unit vector '
         'in ECEF and the orientation '
         'of the IVM also expressed in ECEF (sc_*hat_*).'),
        'scale':
        'linear',
        'units':
        '',
        'value_min':
        -1.,
        'value_max':
        1
    }
    inst.meta['unit_fa_z'] = {
        'long_name':
        'Field-aligned direction along IVM-z',
        'desc':
        'Unit vector for the geomagnetic field line direction.',
        'label':
        'Field Aligned Unit Vector: IVM-Z component',
        'axis':
        'Field Aligned Unit Vector: IVM-Z component',
        'notes': ('The field-aligned vector points along the '
                  'geomagnetic field, with positive values '
                  'along the field direction, and is '
                  'expressed here in the IVM instrument frame. '
                  'The IVM-Z direction points towards nadir '
                  'when IVM-X is pointed into ram for '
                  'standard operations.'
                  'Calculated using the corresponding unit vector '
                  'in ECEF and the orientation '
                  'of the IVM also expressed in ECEF (sc_*hat_*).'),
        'scale':
        'linear',
        'units':
        '',
        'value_min':
        -1.,
        'value_max':
        1
    }

    inst.meta['unit_mer_x'] = {
        'long_name':
        'Meridional direction along IVM-x',
        'desc':
        'Unit vector for the geomagnetic meridional direction.',
        'label':
        'Meridional Unit Vector: IVM-X component',
        'axis':
        'Meridional Unit Vector: IVM-X component',
        'notes':
        ('The meridional unit vector is perpendicular to the geomagnetic field '
         'and maps along magnetic field lines to vertical '
         'at the magnetic equator, where positive is up. '
         'The unit vector is expressed here in the IVM coordinate system, '
         'where x is along the IVM boresight, nominally along ram when '
         'in standard pointing. '
         'Calculated using the corresponding unit vector in ECEF and the orientation '
         'of the IVM also expressed in ECEF (sc_*hat_*).'),
        'scale':
        'linear',
        'units':
        '',
        'value_min':
        -1.,
        'value_max':
        1
    }
    inst.meta['unit_mer_y'] = {
        'long_name':
        'Meridional direction along IVM-y',
        'desc':
        'Unit vector for the geomagnetic meridional direction.',
        'label':
        'Meridional Unit Vector: IVM-Y component',
        'axis':
        'Meridional Unit Vector: IVM-Y component',
        'notes':
        ('The meridional unit vector is perpendicular to the geomagnetic field '
         'and maps along magnetic field lines to vertical '
         'at the magnetic equator, where positive is up. '
         'The unit vector is expressed here in the IVM coordinate system, '
         'where Y = Z x X, nominally southward when '
         'in standard pointing, X along ram. '
         'Calculated using the corresponding unit vector in ECEF and the orientation '
         'of the IVM also expressed in ECEF (sc_*hat_*).'),
        'scale':
        'linear',
        'units':
        '',
        'value_min':
        -1.,
        'value_max':
        1
    }
    inst.meta['unit_mer_z'] = {
        'long_name':
        'Meridional direction along IVM-z',
        'desc':
        'Unit vector for the geomagnetic meridional direction.',
        'label':
        'Meridional Unit Vector: IVM-Z component',
        'axis':
        'Meridional Unit Vector: IVM-Z component',
        'notes':
        ('The meridional unit vector is perpendicular to the geomagnetic field '
         'and maps along magnetic field lines to vertical '
         'at the magnetic equator, where positive is up. '
         'The unit vector is expressed here in the IVM coordinate system, '
         'where Z is nadir pointing (towards Earth), '
         'when in standard pointing, X along ram. '
         'Calculated using the corresponding unit vector in ECEF and the orientation '
         'of the IVM also expressed in ECEF (sc_*hat_*).'),
        'scale':
        'linear',
        'units':
        '',
        'value_min':
        -1.,
        'value_max':
        1
    }

    return
Exemple #21
0
def add_mag_drift_unit_vectors_ecef(inst,
                                    lat_label='latitude',
                                    long_label='longitude',
                                    alt_label='altitude',
                                    **kwargs):
    """Adds unit vectors expressing the ion drift coordinate system
    organized by the geomagnetic field. Unit vectors are expressed
    in ECEF coordinates.

    Parameters
    ----------
    inst : pysat.Instrument
        Instrument object that will get unit vectors
    **kwargs
        Passed along to calculate_mag_drift_unit_vectors_ecef

    Returns
    -------
    None
        unit vectors are added to the passed Instrument object with a naming
        scheme:
            'unit_zon_ecef_*' : unit zonal vector, component along ECEF-(X,Y,or Z)
            'unit_fa_ecef_*' : unit field-aligned vector, component along ECEF-(X,Y,or Z)
            'unit_mer_ecef_*' : unit meridional vector, component along ECEF-(X,Y,or Z)

    """

    # add unit vectors for magnetic drifts in ecef coordinates
    zvx, zvy, zvz, bx, by, bz, mx, my, mz = OMMBV.calculate_mag_drift_unit_vectors_ecef(
        inst[lat_label], inst[long_label], inst[alt_label], inst.index,
        **kwargs)

    inst['unit_zon_ecef_x'] = zvx
    inst['unit_zon_ecef_y'] = zvy
    inst['unit_zon_ecef_z'] = zvz

    inst['unit_fa_ecef_x'] = bx
    inst['unit_fa_ecef_y'] = by
    inst['unit_fa_ecef_z'] = bz

    inst['unit_mer_ecef_x'] = mx
    inst['unit_mer_ecef_y'] = my
    inst['unit_mer_ecef_z'] = mz

    inst.meta['unit_zon_ecef_x'] = {
        'long_name':
        'Zonal unit vector along ECEF-x',
        'desc':
        'Zonal unit vector along ECEF-x',
        'label':
        'Zonal unit vector along ECEF-x',
        'notes': ('Magnetic zonal unit vector expressed using '
                  'Earth Centered Earth Fixed (ECEF) basis. '
                  'Vector system is calculated by determining the '
                  'vector direction that, '
                  'when mapped to the apex, is purely horizontal. '
                  'This component is along the ECEF-x direction.'),
        'axis':
        'Zonal unit vector along ECEF-x',
        'value_min':
        -1.,
        'value_max':
        1.,
    }
    inst.meta['unit_zon_ecef_y'] = {
        'long_name':
        'Zonal unit vector along ECEF-y',
        'desc':
        'Zonal unit vector along ECEF-y',
        'label':
        'Zonal unit vector along ECEF-y',
        'notes': ('Magnetic zonal unit vector expressed using '
                  'Earth Centered Earth Fixed (ECEF) basis. '
                  'Vector system is calculated by determining the '
                  'vector direction that, '
                  'when mapped to the apex, is purely horizontal. '
                  'This component is along the ECEF-y direction.'),
        'axis':
        'Zonal unit vector along ECEF-y',
        'value_min':
        -1.,
        'value_max':
        1.,
    }
    inst.meta['unit_zon_ecef_z'] = {
        'long_name':
        'Zonal unit vector along ECEF-z',
        'desc':
        'Zonal unit vector along ECEF-z',
        'label':
        'Zonal unit vector along ECEF-z',
        'notes': ('Magnetic zonal unit vector expressed using '
                  'Earth Centered Earth Fixed (ECEF) basis. '
                  'Vector system is calculated by determining the '
                  'vector direction that, '
                  'when mapped to the apex, is purely horizontal. '
                  'This component is along the ECEF-z direction.'),
        'axis':
        'Zonal unit vector along ECEF-z',
        'value_min':
        -1.,
        'value_max':
        1.,
    }

    inst.meta['unit_fa_ecef_x'] = {
        'long_name':
        'Field-aligned unit vector along ECEF-x',
        'desc':
        'Field-aligned unit vector along ECEF-x',
        'label':
        'Field-aligned unit vector along ECEF-x',
        'notes': ('Field-aligned unit vector expressed using '
                  'Earth Centered Earth Fixed (ECEF) basis. '
                  'This component is along the ECEF-x direction.'),
        'axis':
        'Field-aligned unit vector along ECEF-x',
        'value_min':
        -1.,
        'value_max':
        1.,
    }
    inst.meta['unit_fa_ecef_y'] = {
        'long_name':
        'Field-aligned unit vector along ECEF-y',
        'desc':
        'Field-aligned unit vector along ECEF-y',
        'label':
        'Field-aligned unit vector along ECEF-y',
        'notes': ('Field-aligned unit vector expressed using '
                  'Earth Centered Earth Fixed (ECEF) basis. '
                  'This component is along the ECEF-y direction.'),
        'axis':
        'Field-aligned unit vector along ECEF-y',
        'value_min':
        -1.,
        'value_max':
        1.,
    }
    inst.meta['unit_fa_ecef_z'] = {
        'long_name':
        'Field-aligned unit vector along ECEF-z',
        'desc':
        'Field-aligned unit vector along ECEF-z',
        'label':
        'Field-aligned unit vector along ECEF-z',
        'notes': ('Field-aligned unit vector expressed using '
                  'Earth Centered Earth Fixed (ECEF) basis. '
                  'This component is along the ECEF-z direction.'),
        'value_min':
        -1.,
        'value_max':
        1.,
    }

    inst.meta['unit_mer_ecef_x'] = {
        'long_name':
        'Meridional unit vector along ECEF-x',
        'desc':
        'Meridional unit vector along ECEF-x',
        'label':
        'Meridional unit vector along ECEF-x',
        'notes': ('Magnetic meridional unit vector expressed using '
                  'Earth Centered Earth Fixed (ECEF) basis. '
                  'Vector system is calculated by determining the '
                  'magnetic zonal vector direction that, '
                  'when mapped to the apex, is purely horizontal. '
                  'The meridional vector is perpendicular to the zonal '
                  'and field-aligned directions. '
                  'This component is along the ECEF-x direction.'),
        'axis':
        'Meridional unit vector along ECEF-x',
        'value_min':
        -1.,
        'value_max':
        1.,
    }
    inst.meta['unit_mer_ecef_y'] = {
        'long_name':
        'Meridional unit vector along ECEF-y',
        'desc':
        'Meridional unit vector along ECEF-y',
        'label':
        'Meridional unit vector along ECEF-y',
        'notes': ('Magnetic meridional unit vector expressed using '
                  'Earth Centered Earth Fixed (ECEF) basis. '
                  'Vector system is calculated by determining the '
                  'magnetic zonal vector direction that, '
                  'when mapped to the apex, is purely horizontal. '
                  'The meridional vector is perpendicular to the zonal '
                  'and field-aligned directions. '
                  'This component is along the ECEF-y direction.'),
        'axis':
        'Meridional unit vector along ECEF-y',
        'value_min':
        -1.,
        'value_max':
        1.,
    }
    inst.meta['unit_mer_ecef_z'] = {
        'long_name':
        'Meridional unit vector along ECEF-z',
        'desc':
        'Meridional unit vector along ECEF-z',
        'label':
        'Meridional unit vector along ECEF-z',
        'notes': ('Magnetic meridional unit vector expressed using '
                  'Earth Centered Earth Fixed (ECEF) basis. '
                  'Vector system is calculated by determining the '
                  'magnetic zonal vector direction that, '
                  'when mapped to the apex, is purely horizontal. '
                  'The meridional vector is perpendicular to the zonal '
                  'and field-aligned directions. '
                  'This component is along the ECEF-z direction.'),
        'axis':
        'Meridional unit vector along ECEF-z',
        'value_min':
        -1.,
        'value_max':
        1.,
    }

    return
Exemple #22
0
def load(fnames, tag=None, inst_id=None, obs_long=0., obs_lat=0., obs_alt=0.,
         TLE1=None, TLE2=None, num_samples=None, cadence='1S'):
    """
    Returns data and metadata in the format required by pysat. Generates
    position of satellite in both geographic and ECEF co-ordinates.

    Routine is directly called by pysat and not the user.

    Parameters
    ----------
    fnames : list
        List of filenames
    tag : str or NoneType
        Identifies a particular subset of satellite data (accepts '')
        (default=None)
    inst_id : str or NoneType
        Instrument satellite ID (accepts '')
        (default=None)
    obs_long: float
        Longitude of the observer on the Earth's surface
        (default=0.)
    obs_lat: float
        Latitude of the observer on the Earth's surface
        (default=0.)
    obs_alt: float
        Altitude of the observer on the Earth's surface
        (default=0.)
    TLE1 : string or NoneType
        First string for Two Line Element. Must be in TLE format (default=None)
    TLE2 : string or NoneType
        Second string for Two Line Element. Must be in TLE format (default=None)
    num_samples : int or NoneType
        Number of samples per day (default=None)
    cadence : str
        Uses pandas.frequency string formatting ('1S', etc)
        (default='1S')

    Returns
    -------
    data : pandas.DataFrame
        Object containing satellite data
    meta : pysat.Meta
        Object containing metadata such as column names and units

    Example
    -------
    ::

          TLE1='1 25544U 98067A   18135.61844383  .00002728  00000-0  48567-4 0  9998'
          TLE2='2 25544  51.6402 181.0633 0004018  88.8954  22.2246 15.54059185113452'
          inst = pysat.Instrument('pysat', 'ephem', TLE1=TLE1, TLE2=TLE2)
          inst.load(2018, 1)

    """

    # TLEs (Two Line Elements for ISS)
    # format of TLEs is fixed and available from wikipedia...
    # lines encode list of orbital elements of an Earth-orbiting object
    # for a given point in time
    line1 = ''.join(('1 25544U 98067A   18135.61844383  .00002728  00000-0  ',
                     '48567-4 0  9998'))
    line2 = ''.join(('2 25544  51.6402 181.0633 0004018  88.8954  22.2246 ',
                     '15.54059185113452'))

    # Use ISS defaults if not provided by user
    if TLE1 is not None:
        line1 = TLE1
    if TLE2 is not None:
        line2 = TLE2

    if num_samples is None:
        num_samples = 100

    # Extract list of times from filenames and inst_id
    times, index, dates = ps_meth.generate_times(fnames, num_samples,
                                                 freq=cadence)

    # The observer's (ground station) position on the Earth surface
    site = ephem.Observer()
    site.lon = str(obs_long)
    site.lat = str(obs_lat)
    site.elevation = obs_alt

    # The first parameter in readtle() is the satellite name
    sat = ephem.readtle('pysat', line1, line2)
    output_params = []
    for timestep in index:
        lp = {}
        site.date = timestep
        sat.compute(site)

        # Parameters relative to the ground station
        lp['obs_sat_az_angle'] = ephem.degrees(sat.az)
        lp['obs_sat_el_angle'] = ephem.degrees(sat.alt)

        # Total distance between transmitter and receiver
        lp['obs_sat_slant_range'] = sat.range

        # Satellite location (sub-latitude and sub-longitude)
        lp['glat'] = np.degrees(sat.sublat)
        lp['glong'] = np.degrees(sat.sublong)

        # Elevation of satellite in m, converted to km
        lp['alt'] = sat.elevation / 1000.0

        # Get ECEF position of satellite
        lp['x'], lp['y'], lp['z'] = OMMBV.geodetic_to_ecef(lp['glat'],
                                                           lp['glong'],
                                                           lp['alt'])
        output_params.append(lp)

    output = pds.DataFrame(output_params, index=index)
    # Modify input object to include calculated parameters
    # Put data into DataFrame
    data = pds.DataFrame({'glong': output['glong'],
                          'glat': output['glat'],
                          'alt': output['alt'],
                          'position_ecef_x': output['x'],
                          'position_ecef_y': output['y'],
                          'position_ecef_z': output['z'],
                          'obs_sat_az_angle': output['obs_sat_az_angle'],
                          'obs_sat_el_angle': output['obs_sat_el_angle'],
                          'obs_sat_slant_range':
                          output['obs_sat_slant_range']},
                         index=index)
    data.index.name = 'Epoch'

    return data, meta.copy()
Exemple #23
0
    def test_field_line_tracing_against_vitmo(self):
        """Compare model to http://omniweb.gsfc.nasa.gov/vitmo/cgm_vitmo.html"""

        # convert position to ECEF
        ecf_x, ecf_y, ecf_z = OMMBV.geocentric_to_ecef(omni['p_lat'],
                                                       omni['p_long'],
                                                       omni['p_alt'])
        trace_n = []
        trace_s = []
        date = datetime.datetime(2000, 1, 1)
        for x, y, z in zip(ecf_x, ecf_y, ecf_z):
            # trace north and south, take last points
            trace_n.append(
                OMMBV.field_line_trace(np.array([x, y, z]), date, 1., 0.,
                                      step_size=0.5, max_steps=1.E6)[-1, :])
            trace_s.append(
                OMMBV.field_line_trace(np.array([x, y, z]), date, -1., 0.,
                                      step_size=0.5, max_steps=1.E6)[-1, :])
        trace_n = pds.DataFrame(trace_n, columns=['x', 'y', 'z'])
        trace_n['lat'], trace_n['long'], trace_n['altitude'] = OMMBV.ecef_to_geocentric(trace_n['x'],
                                                                                        trace_n['y'],
                                                                                        trace_n['z'])
        trace_s = pds.DataFrame(trace_s, columns=['x', 'y', 'z'])
        trace_s['lat'], trace_s['long'], trace_s['altitude'] = OMMBV.ecef_to_geocentric(trace_s['x'],
                                                                                        trace_s['y'],
                                                                                        trace_s['z'])

        # ensure longitudes are all 0-360
        idx, = np.where(omni['n_long'] < 0)
        omni.ix[idx, 'n_long'] += 360.
        idx, = np.where(omni['s_long'] < 0)
        omni.ix[idx, 's_long'] += 360.

        idx, = np.where(trace_n['long'] < 0)
        trace_n.ix[idx, 'long'] += 360.
        idx, = np.where(trace_s['long'] < 0)
        trace_s.ix[idx, 'long'] += 360.

        # compute difference between OMNI and local calculation
        # there is a difference near 0 longitude, ignore this area
        diff_n_lat = (omni['n_lat'] - trace_n['lat'])[4:-4]
        diff_n_lon = (omni['n_long'] - trace_n['long'])[4:-4]
        diff_s_lat = (omni['s_lat'] - trace_s['lat'])[4:-4]
        diff_s_lon = (omni['s_long'] - trace_s['long'])[4:-4]

        try:
            f = plt.figure()
            plt.plot(omni['n_long'], omni['n_lat'], 'r.', label='omni')
            plt.plot(omni['s_long'], omni['s_lat'], 'r.', label='_omni')
            plt.plot(trace_n['long'], trace_n['lat'], 'b.', label='UTD')
            plt.plot(trace_s['long'], trace_s['lat'], 'b.', label='_UTD')
            plt.title('Comparison of Magnetic Footpoints for Field Lines through 20 Lat, 550 km')
            plt.xlabel('Geographic Longitude')
            plt.ylabel('Geographic Latitude')
            plt.legend(loc=0)
            plt.xlim((0, 360.))
            plt.savefig('magnetic_footpoint_comparison.pdf')
            print('Saving magnetic_footpoint_comparison.pdf')
        except:
            pass

        # better than 0.5 km accuracy expected for settings above
        assert np.all(np.nanstd(diff_n_lat) < .5)
        assert np.all(np.nanstd(diff_n_lon) < .5)
        assert np.all(np.nanstd(diff_s_lat) < .5)
        assert np.all(np.nanstd(diff_s_lon) < .5)
Exemple #24
0
    def test_ecef_geodetic_apex_diff_plots(self):
        """Characterize uncertainty of ECEF and Geodetic transformations"""
        import matplotlib.pyplot as plt
        # on_travis = os.environ.get('ONTRAVIS') == 'True'

        p_lats, p_longs, p_alts = gen_plot_grid_fixed_alt(550.)
        # data returned are the locations along each direction
        # the full range of points obtained by iterating over all
        # recasting alts into a more convenient form for later calculation
        p_alts = [p_alts[0]] * len(p_longs)
        # set the date
        date = datetime.datetime(2000, 1, 1)
        # memory for results
        apex_x = np.zeros((len(p_lats), len(p_longs) + 1))
        apex_y = np.zeros((len(p_lats), len(p_longs) + 1))
        apex_z = np.zeros((len(p_lats), len(p_longs) + 1))
        apex_alt = np.zeros((len(p_lats), len(p_longs) + 1))
        norm_alt = np.zeros((len(p_lats), len(p_longs) + 1))

        # set up multi
        if self.dc is not None:
            import itertools
            targets = itertools.cycle(dc.ids)
            pending = []
            for i, p_lat in enumerate(p_lats):
                print (i, p_lat)
                # iterate through target cyclicly and run commands
                dview.targets = next(targets)
                pending.append(dview.apply_async(OMMBV.geodetic_to_ecef, np.array([p_lat] * len(p_longs)), p_longs,
                                                 p_alts))
            for i, p_lat in enumerate(p_lats):
                print ('collecting ', i, p_lat)
                # collect output
                x, y, z = pending.pop(0).get()

                # iterate through target cyclicly and run commands
                dview.targets = next(targets)
                pending.append(dview.apply_async(OMMBV.python_ecef_to_geodetic, x, y, z))

            for i, p_lat in enumerate(p_lats):
                print ('collecting 2', i, p_lat)
                # collect output
                lat2, lon2, alt2 = pending.pop(0).get()

                # iterate through target cyclicly and run commands
                dview.targets = next(targets)
                pending.append(dview.apply_async(OMMBV.apex_location_info, np.array([p_lat] * len(p_longs)), p_longs,
                                                 p_alts, [date] * len(p_longs),
                                                 return_geodetic=True))

                pending.append(dview.apply_async(OMMBV.apex_location_info, lat2, lon2, alt2,
                                                 [date] * len(p_longs),
                                                 return_geodetic=True))

            for i, p_lat in enumerate(p_lats):
                print ('collecting 3', i, p_lat)
                x, y, z, _, _, h = pending.pop(0).get()
                x2, y2, z2, _, _, h2 = pending.pop(0).get()
                norm_alt[i, :-1] = np.abs(h)
                apex_x[i, :-1] = np.abs(x2 - x)
                apex_y[i, :-1] = np.abs(y2 - y)
                apex_z[i, :-1] = np.abs(z2 - z)
                apex_alt[i, :-1] = np.abs(h2 - h)


        else:
            # single processor case
            for i, p_lat in enumerate(p_lats):
                print (i, p_lat)
                x, y, z = OMMBV.geodetic_to_ecef([p_lat] * len(p_longs), p_longs, p_alts)
                lat2, lon2, alt2 = OMMBV.ecef_to_geodetic(x, y, z)
                x2, y2, z2 = OMMBV.geodetic_to_ecef(lat2, lon2, alt2)
                apex_x[i, :-1] = np.abs(x2 - x)
                apex_y[i, :-1] = np.abs(y2 - y)
                apex_z[i, :-1] = np.abs(z2 - z)

        # account for periodicity
        apex_x[:, -1] = apex_x[:, 0]
        apex_y[:, -1] = apex_y[:, 0]
        apex_z[:, -1] = apex_z[:, 0]
        apex_alt[:, -1] = apex_alt[:, 0]
        norm_alt[:, -1] = norm_alt[:, 0]

        ytickarr = np.array([0, 0.25, 0.5, 0.75, 1]) * (len(p_lats) - 1)
        xtickarr = np.array([0, 0.2, 0.4, 0.6, 0.8, 1]) * len(p_longs)
        ytickvals = ['-50', '-25', '0', '25', '50']

        try:
            fig = plt.figure()
            plt.imshow(np.log10(apex_x), origin='lower')
            plt.colorbar()
            plt.yticks(ytickarr, ytickvals)
            plt.xticks(xtickarr, ['0', '72', '144', '216', '288', '360'])
            plt.title('Log ECEF-Geodetic Apex Difference (ECEF-x km)')
            plt.xlabel('Geodetic Longitude (Degrees)')
            plt.ylabel('Geodetic Latitude (Degrees)')
            plt.savefig('ecef_geodetic_apex_diff_x.pdf')
            plt.close()

            fig = plt.figure()
            plt.imshow(np.log10(apex_y), origin='lower')
            plt.colorbar()
            plt.yticks(ytickarr, ytickvals)
            plt.xticks(xtickarr, ['0', '72', '144', '216', '288', '360'])
            plt.title('Log ECEF-Geodetic Apex Difference (ECEF-y km)')
            plt.xlabel('Geodetic Longitude (Degrees)')
            plt.ylabel('Geodetic Latitude (Degrees)')
            plt.savefig('ecef_geodetic_apex_diff_y.pdf')
            plt.close()

            fig = plt.figure()
            plt.imshow(np.log10(apex_z), origin='lower')
            plt.colorbar()
            plt.yticks(ytickarr, ytickvals)
            plt.xticks(xtickarr, ['0', '72', '144', '216', '288', '360'])
            plt.title('Log ECEF-Geodetic Apex Difference (ECEF-z km)')
            plt.xlabel('Geodetic Longitude (Degrees)')
            plt.ylabel('Geodetic Latitude (Degrees)')
            plt.savefig('ecef_geodetic_apex_diff_z.pdf')
            plt.close()

            fig = plt.figure()
            plt.imshow(np.log10(apex_alt), origin='lower')
            plt.colorbar()
            plt.yticks(ytickarr, ytickvals)
            plt.xticks(xtickarr, ['0', '72', '144', '216', '288', '360'])
            plt.title('Log ECEF-Geodetic Apex Altitude Difference (km)')
            plt.xlabel('Geodetic Longitude (Degrees)')
            plt.ylabel('Geodetic Latitude (Degrees)')
            plt.savefig('ecef_geodetic_apex_diff_h.pdf')
            plt.close()

            fig = plt.figure()
            plt.imshow(np.log10(apex_alt / norm_alt), origin='lower')
            plt.colorbar()
            plt.yticks(ytickarr, ytickvals)
            plt.xticks(xtickarr, ['0', '72', '144', '216', '288', '360'])
            plt.title('Log ECEF-Geodetic Apex Normalized Altitude Difference (km)')
            plt.xlabel('Geodetic Longitude (Degrees)')
            plt.ylabel('Geodetic Latitude (Degrees)')
            plt.savefig('ecef_geodetic_apex_norm_diff_h.pdf')
            plt.close()

        except:
            pass
Exemple #25
0
    def test_apex_info_accuracy(self):
        """Characterize performance of apex_location_info as fine_step_size varied"""
        lats, longs, alts = gen_trace_data_fixed_alt(550.)
        ecf_x, ecf_y, ecf_z = OMMBV.geodetic_to_ecef(lats,
                                                     longs,
                                                     alts)
        # step size to be tried
        fine_steps_goal = np.array([25.6, 12.8, 6.4, 3.2, 1.6, 0.8, 0.4, 0.2,
                                    0.1, 0.05, .025, .0125, .00625, .003125,
                                    .0015625, .00078125, .000390625, .0001953125,
                                    .0001953125 / 2., .0001953125 / 4., .0001953125 / 8.,
                                    .0001953125 / 16., .0001953125 / 32., .0001953125 / 64.,
                                    .0001953125 / 128., .0001953125 / 256., .0001953125 / 512.,
                                    .0001953125 / 1024., .0001953125 / 2048., .0001953125 / 4096.])

        date = datetime.datetime(2000, 1, 1)
        dx = []
        dy = []
        dz = []
        dh = []

        # set up multi
        if self.dc is not None:
            import itertools
            targets = itertools.cycle(dc.ids)
            pending = []
            for lat, lon, alt in zip(lats, longs, alts):
                for steps in fine_steps_goal:
                    # iterate through target cyclicly and run commands
                    dview.targets = next(targets)
                    pending.append(dview.apply_async(OMMBV.apex_location_info, [lat],
                                                     [lon], [alt], [date], fine_step_size=steps,
                                                     return_geodetic=True))
                out = []
                for steps in fine_steps_goal:
                    # collect output
                    x, y, z, _, _, apex_height = pending.pop(0).get()
                    pt = [x[0], y[0], z[0], apex_height[0]]
                    out.append(pt)

                final_pt = pds.DataFrame(out, columns=['x', 'y', 'z', 'h'])
                dx.append(np.abs(final_pt.loc[1:, 'x'].values - final_pt.loc[:, 'x'].values[:-1]))
                dy.append(np.abs(final_pt.loc[1:, 'y'].values - final_pt.loc[:, 'y'].values[:-1]))
                dz.append(np.abs(final_pt.loc[1:, 'z'].values - final_pt.loc[:, 'z'].values[:-1]))
                dh.append(np.abs(final_pt.loc[1:, 'h'].values - final_pt.loc[:, 'h'].values[:-1]))
        else:
            for lat, lon, alt in zip(lats, longs, alts):
                out = []
                for steps in fine_steps_goal:
                    x, y, z, _, _, apex_height = OMMBV.apex_location_info([lat], [lon], [alt], [date],
                                                                          fine_step_size=steps,
                                                                          return_geodetic=True)
                    pt = [x[0], y[0], z[0], apex_height[0]]
                    out.append(pt)

                final_pt = pds.DataFrame(out, columns=['x', 'y', 'z', 'h'])
                dx.append(np.abs(final_pt.loc[1:, 'x'].values - final_pt.loc[:, 'x'].values[:-1]))
                dy.append(np.abs(final_pt.loc[1:, 'y'].values - final_pt.loc[:, 'y'].values[:-1]))
                dz.append(np.abs(final_pt.loc[1:, 'z'].values - final_pt.loc[:, 'z'].values[:-1]))
                dh.append(np.abs(final_pt.loc[1:, 'h'].values - final_pt.loc[:, 'h'].values[:-1]))

        dx = pds.DataFrame(dx)
        dy = pds.DataFrame(dy)
        dz = pds.DataFrame(dz)
        dh = pds.DataFrame(dh)

        try:

            plt.figure()
            yerrx = np.nanstd(np.log10(dx), axis=0)
            yerry = np.nanstd(np.log10(dy), axis=0)
            yerrz = np.nanstd(np.log10(dz), axis=0)
            yerrh = np.nanstd(np.log10(dh), axis=0)

            plt.errorbar(np.log10(fine_steps_goal[1:]), np.log10(dx.mean(axis=0)),
                         yerr=yerrx,
                         label='x')
            plt.errorbar(np.log10(fine_steps_goal[1:]), np.log10(dy.mean(axis=0)),
                         yerr=yerry,
                         label='y')
            plt.errorbar(np.log10(fine_steps_goal[1:]), np.log10(dz.mean(axis=0)),
                         yerr=yerrz,
                         label='z')
            plt.errorbar(np.log10(fine_steps_goal[1:]), np.log10(dh.mean(axis=0)),
                         yerr=yerrh,
                         label='h')

            plt.xlabel('Log Step Size (km)')
            plt.ylabel('Change in Apex Position (km)')
            plt.title("Change in Field Apex Position vs Fine Step Size")
            plt.legend()
            plt.tight_layout()
            plt.savefig('apex_location_vs_step_size.pdf')
            plt.close()
        except:
            pass
Exemple #26
0
    def test_apex_fine_max_step_diff_plots(self):
        """Test apex location info for sensitivity to fine_steps parameters"""
        import matplotlib.pyplot as plt
        # on_travis = os.environ.get('ONTRAVIS') == 'True'

        p_lats, p_longs, p_alts = gen_plot_grid_fixed_alt(550.)
        # data returned are the locations along each direction
        # the full range of points obtained by iterating over all
        # recasting alts into a more convenient form for later calculation
        p_alts = [p_alts[0]] * len(p_longs)
        # set the date
        date = datetime.datetime(2000, 1, 1)
        # memory for results
        apex_lat = np.zeros((len(p_lats), len(p_longs) + 1))
        apex_lon = np.zeros((len(p_lats), len(p_longs) + 1))
        apex_alt = np.zeros((len(p_lats), len(p_longs) + 1))
        apex_z = np.zeros((len(p_lats), len(p_longs) + 1))
        norm_alt = np.zeros((len(p_lats), len(p_longs) + 1))

        # set up multi
        if self.dc is not None:
            import itertools
            targets = itertools.cycle(dc.ids)
            pending = []
            for i, p_lat in enumerate(p_lats):
                print (i, p_lat)
                # iterate through target cyclicly and run commands
                dview.targets = next(targets)
                pending.append(dview.apply_async(OMMBV.apex_location_info, [p_lat] * len(p_longs), p_longs,
                                                 p_alts, [date] * len(p_longs),
                                                 fine_max_steps=5,
                                                 return_geodetic=True))
                pending.append(dview.apply_async(OMMBV.apex_location_info, [p_lat] * len(p_longs), p_longs,
                                                 p_alts, [date] * len(p_longs),
                                                 fine_max_steps=10,
                                                 return_geodetic=True))
            for i, p_lat in enumerate(p_lats):
                print ('collecting ', i, p_lat)
                # collect output
                x, y, z, _, _, h = pending.pop(0).get()
                x2, y2, z2, _, _, h2 = pending.pop(0).get()
                apex_lat[i, :-1] = np.abs(x2 - x)
                apex_lon[i, :-1] = np.abs(y2 - y)
                apex_z[i, :-1] = np.abs(z2 - z)
                apex_alt[i, :-1] = np.abs(h2 - h)

        else:
            # single processor case
            for i, p_lat in enumerate(p_lats):
                print (i, p_lat)
                x, y, z, _, _, h = OMMBV.apex_location_info([p_lat] * len(p_longs), p_longs,
                                                            p_alts, [date] * len(p_longs),
                                                            fine_max_steps=5, return_geodetic=True)
                x2, y2, z2, _, _, h2 = OMMBV.apex_location_info([p_lat] * len(p_longs), p_longs,
                                                                p_alts, [date] * len(p_longs),
                                                                fine_max_steps=10, return_geodetic=True)

                norm_alt[i, :-1] = h
                apex_lat[i, :-1] = np.abs(x2 - x)
                apex_lon[i, :-1] = np.abs(y2 - y)
                apex_z[i, :-1] = np.abs(z2 - z)
                apex_alt[i, :-1] = np.abs(h2 - h)

        # account for periodicity
        apex_lat[:, -1] = apex_lat[:, 0]
        apex_lon[:, -1] = apex_lon[:, 0]
        apex_z[:, -1] = apex_z[:, 0]
        apex_alt[:, -1] = apex_alt[:, 0]
        norm_alt[:, -1] = norm_alt[:, 0]

        idx, idy, = np.where(apex_lat > 10.)
        print('Locations with large apex x (ECEF) location differences.', p_lats[idx], p_longs[idx])

        ytickarr = np.array([0, 0.25, 0.5, 0.75, 1]) * (len(p_lats) - 1)
        xtickarr = np.array([0, 0.2, 0.4, 0.6, 0.8, 1]) * len(p_longs)
        ytickvals = ['-50', '-25', '0', '25', '50']

        try:
            fig = plt.figure()
            plt.imshow(np.log10(apex_lat), origin='lower')
            plt.colorbar()
            plt.yticks(ytickarr, ytickvals)
            plt.xticks(xtickarr, ['0', '72', '144', '216', '288', '360'])
            plt.title('Log Apex Location Difference (ECEF-x km)')
            plt.xlabel('Geodetic Longitude (Degrees)')
            plt.ylabel('Geodetic Latitude (Degrees)')
            plt.savefig('apex_loc_max_steps_diff_x.pdf')
            plt.close()

            fig = plt.figure()
            plt.imshow(np.log10(apex_lon), origin='lower')
            plt.colorbar()
            plt.yticks(ytickarr, ytickvals)
            plt.xticks(xtickarr, ['0', '72', '144', '216', '288', '360'])
            plt.title('Log Apex Location Difference (ECEF-y km)')
            plt.xlabel('Geodetic Longitude (Degrees)')
            plt.ylabel('Geodetic Latitude (Degrees)')
            plt.savefig('apex_loc_max_steps_diff_y.pdf')
            plt.close()

            fig = plt.figure()
            plt.imshow(np.log10(apex_z), origin='lower')
            plt.colorbar()
            plt.yticks(ytickarr, ytickvals)
            plt.xticks(xtickarr, ['0', '72', '144', '216', '288', '360'])
            plt.title('Log Apex Location Difference (ECEF-z km)')
            plt.xlabel('Geodetic Longitude (Degrees)')
            plt.ylabel('Geodetic Latitude (Degrees)')
            plt.savefig('apex_loc_max_steps_diff_z.pdf')
            plt.close()

            fig = plt.figure()
            plt.imshow(np.log10(apex_alt / norm_alt), origin='lower')
            plt.colorbar()
            plt.yticks(ytickarr, ytickvals)
            plt.xticks(xtickarr, ['0', '72', '144', '216', '288', '360'])
            plt.title('Log Apex Altitude Normalized Difference (km)')
            plt.xlabel('Geodetic Longitude (Degrees)')
            plt.ylabel('Geodetic Latitude (Degrees)')
            plt.savefig('apex_norm_loc_max_steps_diff_h.pdf')
            plt.close()

            fig = plt.figure()
            plt.imshow(np.log10(apex_alt), origin='lower')
            plt.colorbar()
            plt.yticks(ytickarr, ytickvals)
            plt.xticks(xtickarr, ['0', '72', '144', '216', '288', '360'])
            plt.title('Log Apex Altitude Normalized Difference (km)')
            plt.xlabel('Geodetic Longitude (Degrees)')
            plt.ylabel('Geodetic Latitude (Degrees)')
            plt.savefig('apex_loc_max_steps_diff_h.pdf')
            plt.close()

        except:
            pass
Exemple #27
0
    def test_apex_plots(self):
        """Plot basic apex parameters"""
        import matplotlib.pyplot as plt

        p_lats, p_longs, p_alts = gen_plot_grid_fixed_alt(120.)
        # data returned are the locations along each direction
        # the full range of points obtained by iterating over all
        # recasting alts into a more convenient form for later calculation
        p_alts = [p_alts[0]] * len(p_longs)
        # set the date
        date = datetime.datetime(2000, 1, 1)
        # memory for results
        apex_lat = np.zeros((len(p_lats), len(p_longs) + 1))
        apex_lon = np.zeros((len(p_lats), len(p_longs) + 1))
        apex_alt = np.zeros((len(p_lats), len(p_longs) + 1))

        # set up multi
        if self.dc is not None:
            import itertools
            targets = itertools.cycle(dc.ids)
            pending = []
            for i, p_lat in enumerate(p_lats):
                print (i, p_lat)
                # iterate through target cyclicly and run commands
                dview.targets = next(targets)
                pending.append(dview.apply_async(OMMBV.apex_location_info, [p_lat] * len(p_longs), p_longs,
                                                 p_alts, [date] * len(p_longs),
                                                 return_geodetic=True))
            for i, p_lat in enumerate(p_lats):
                print ('collecting ', i, p_lat)
                # collect output
                x, y, z, olat, olon, oalt = pending.pop(0).get()
                apex_lat[i, :-1] = olat
                apex_lon[i, :-1] = olon
                apex_alt[i, :-1] = oalt

        else:
            # single processor case
            for i, p_lat in enumerate(p_lats):
                print (i, p_lat)
                x, y, z, olat, olon, oalt = OMMBV.apex_location_info([p_lat] * len(p_longs), p_longs,
                                                                     p_alts, [date] * len(p_longs),
                                                                     return_geodetic=True)
                apex_lat[i, :-1] = olat
                apex_lon[i, :-1] = olon
                apex_alt[i, :-1] = oalt

        # calculate difference between apex longitude and original longitude
        # values for apex long are -180 to 180, shift to 0 to 360
        # process degrees a bit to make the degree difference the most meaningful (close to 0)
        idx, idy, = np.where(apex_lon < 0.)
        apex_lon[idx, idy] += 360.
        idx, idy, = np.where(apex_lon >= 360.)
        apex_lon[idx, idy] -= 360.
        apex_lon[:, :-1] -= p_longs
        idx, idy, = np.where(apex_lon > 180.)
        apex_lon[idx, idy] -= 360.
        idx, idy, = np.where(apex_lon <= -180.)
        apex_lon[idx, idy] += 360.

        # account for periodicity
        apex_lat[:, -1] = apex_lat[:, 0]
        apex_lon[:, -1] = apex_lon[:, 0]
        apex_alt[:, -1] = apex_alt[:, 0]

        ytickarr = np.array([0, 0.25, 0.5, 0.75, 1]) * (len(p_lats) - 1)
        xtickarr = np.array([0, 0.2, 0.4, 0.6, 0.8, 1]) * len(p_longs)
        ytickvals = ['-25', '-12.5', '0', '12.5', '25']

        try:
            fig = plt.figure()
            plt.imshow(apex_lat, origin='lower')
            plt.colorbar()
            plt.yticks(ytickarr, ytickvals)
            plt.xticks(xtickarr, ['0', '72', '144', '216', '288', '360'])
            plt.title('Apex Latitude (Degrees) at 120 km')
            plt.xlabel('Geodetic Longitude (Degrees)')
            plt.ylabel('Geodetic Latitude (Degrees)')
            plt.savefig('apex_lat.pdf')
            plt.close()

            fig = plt.figure()
            plt.imshow(apex_lon, origin='lower')
            plt.colorbar()
            plt.yticks(ytickarr, ytickvals)
            plt.xticks(xtickarr, ['0', '72', '144', '216', '288', '360'])
            plt.title('Apex Longitude Difference (Degrees) at 120 km')
            plt.xlabel('Geodetic Longitude (Degrees)')
            plt.ylabel('Geodetic Latitude (Degrees)')
            plt.savefig('apex_lon.pdf')
            plt.close()

            fig = plt.figure()
            plt.imshow(np.log10(apex_alt), origin='lower')
            plt.colorbar()
            plt.yticks(ytickarr, ytickvals)
            plt.xticks(xtickarr, ['0', '72', '144', '216', '288', '360'])
            plt.title('Log Apex Altitude (km) at 120 km')
            plt.xlabel('Geodetic Longitude (Degrees)')
            plt.ylabel('Geodetic Latitude (Degrees)')
            plt.savefig('apex_alt.pdf')
            plt.close()
        except:
            pass
Exemple #28
0
    def test_step_along_mag_unit_vector_sensitivity(self,
                                                    direction,
                                                    tol=1.E-5):
        """Characterize apex location uncertainty of neighboring field lines.

        Parameters
        ----------
        direction : str
            Step along 'meridional' or 'zonal' directions.

        """

        # Create memory for method locations from method output
        x = np.zeros((len(self.lats), len(self.longs)))
        y = x.copy()
        z = x.copy()
        h = x.copy()

        # Second set of outputs
        x2 = np.zeros((len(self.lats), len(self.longs)))
        y2 = x2.copy()
        z2 = x2.copy()
        h2 = x.copy()

        dates = [self.date] * len(self.longs)

        for i, p_lat in enumerate(self.lats):
            (in_x, in_y,
             in_z) = OMMBV.trans.geodetic_to_ecef([p_lat] * len(self.longs),
                                                  self.longs, self.alts)

            (x[i, :], y[i, :],
             z[i, :]) = OMMBV.step_along_mag_unit_vector(in_x,
                                                         in_y,
                                                         in_z,
                                                         dates,
                                                         direction=direction,
                                                         num_steps=2,
                                                         step_size=1. / 2.)
            # Second run
            (x2[i, :], y2[i, :],
             z2[i, :]) = OMMBV.step_along_mag_unit_vector(in_x,
                                                          in_y,
                                                          in_z,
                                                          dates,
                                                          direction=direction,
                                                          num_steps=1,
                                                          step_size=1.)

        for i, p_lat in enumerate(self.lats):
            # Convert all locations to geodetic coordinates
            tlat, tlon, talt = OMMBV.trans.ecef_to_geodetic(
                x[i, :], y[i, :], z[i, :])

            # Get apex location
            (x[i, :], y[i, :], z[i, :], _, _,
             h[i, :]) = OMMBV.trace.apex_location_info(tlat,
                                                       tlon,
                                                       talt,
                                                       dates,
                                                       return_geodetic=True)

            # Repeat process for second set of positions.
            tlat, tlon, talt = OMMBV.trans.ecef_to_geodetic(
                x2[i, :], y2[i, :], z2[i, :])
            (x2[i, :], y2[i, :], z2[i, :], _, _,
             h2[i, :]) = OMMBV.trace.apex_location_info(tlat,
                                                        tlon,
                                                        talt,
                                                        dates,
                                                        return_geodetic=True)

        # Test within expected tolerance.
        for item1, item2 in zip([x, y, z, h], [x2, y2, z2, h2]):
            idx, idy, = np.where(np.abs(item1) >= 1.E-10)
            np.testing.assert_allclose(item1[idx, idy],
                                       item2[idx, idy],
                                       rtol=tol)
            idx, idy, = np.where(np.abs(item1) < 1.E-10)
            np.testing.assert_allclose(item1[idx, idy],
                                       item2[idx, idy],
                                       atol=tol)

        return
Exemple #29
0
    def test_apexpy_v_OMMBV(self):
        """Comparison with apexpy along magnetic equator"""
        # generate test values
        glongs = np.arange(99) * 3.6 - 180.
        glats = np.zeros(99) + 10.
        alts = np.zeros(99) + 450.
        # date of run
        date = self.inst.date
        # map to the magnetic equator
        ecef_x, ecef_y, ecef_z, eq_lat, eq_long, eq_z = OMMBV.apex_location_info(
            glats, glongs, alts, [date] * len(alts), return_geodetic=True)
        idx = np.argsort(eq_long)
        eq_long = eq_long[idx]
        eq_lat = eq_lat[idx]
        eq_z = eq_z[idx]
        # get apex basis vectors
        apex = apexpy.Apex(date=self.inst.date)
        apex_vecs = apex.basevectors_apex(eq_lat, eq_long, eq_z, coords='geo')
        apex_fa = apex_vecs[8]
        apex_mer = apex_vecs[7]
        apex_zon = apex_vecs[6]

        # normalize into unit vectors
        apex_mer[0, :], apex_mer[1, :], apex_mer[
            2, :] = OMMBV.normalize_vector(-apex_mer[0, :], -apex_mer[1, :],
                                           -apex_mer[2, :])

        apex_zon[0, :], apex_zon[1, :], apex_zon[
            2, :] = OMMBV.normalize_vector(apex_zon[0, :], apex_zon[1, :],
                                           apex_zon[2, :])
        apex_fa[0, :], apex_fa[1, :], apex_fa[2, :] = OMMBV.normalize_vector(
            apex_fa[0, :], apex_fa[1, :], apex_fa[2, :])

        # calculate mag unit vectors in ECEF coordinates
        out = OMMBV.calculate_mag_drift_unit_vectors_ecef(
            eq_lat, eq_long, eq_z, [self.inst.date] * len(eq_z))
        zx, zy, zz, fx, fy, fz, mx, my, mz = out

        # convert into north, east, and up system
        ze, zn, zu = OMMBV.ecef_to_enu_vector(zx, zy, zz, eq_lat, eq_long)
        fe, fn, fu = OMMBV.ecef_to_enu_vector(fx, fy, fz, eq_lat, eq_long)
        me, mn, mu = OMMBV.ecef_to_enu_vector(mx, my, mz, eq_lat, eq_long)

        # create inputs straight from IGRF
        igrf_n = []
        igrf_e = []
        igrf_u = []
        for lat, lon, alt in zip(eq_lat, eq_long, eq_z):
            out = OMMBV.igrf.igrf13syn(0, date.year, 1, alt,
                                       np.deg2rad(90 - lat), np.deg2rad(lon))
            out = np.array(out)
            # normalize
            out /= out[-1]
            igrf_n.append(out[0])
            igrf_e.append(out[1])
            igrf_u.append(-out[2])

        # dot product of zonal and field aligned
        dot_apex_zonal = apex_fa[0, :] * apex_zon[0, :] + apex_fa[
            1, :] * apex_zon[1, :] + apex_fa[2, :] * apex_zon[2, :]
        dot_apex_mer = apex_fa[0, :] * apex_mer[0, :] + apex_fa[
            1, :] * apex_mer[1, :] + apex_fa[2, :] * apex_mer[2, :]

        dotmagvect_zonal = ze * fe + zn * fn + zu * fu
        dotmagvect_mer = me * fe + mn * fn + mu * fu

        assert np.all(dotmagvect_mer < 1.E-8)
        assert np.all(dotmagvect_zonal < 1.E-8)

        try:
            plt.figure()
            plt.plot(eq_long,
                     np.log10(np.abs(dot_apex_zonal)),
                     label='apex_zonal')
            plt.plot(eq_long, np.log10(np.abs(dot_apex_mer)), label='apex_mer')
            plt.plot(eq_long,
                     np.log10(np.abs(dotmagvect_zonal)),
                     label='magv_zonal')
            plt.plot(eq_long,
                     np.log10(np.abs(dotmagvect_mer)),
                     label='magv_mer')
            plt.ylabel('Log Dot Product Magnitude')
            plt.xlabel('Geodetic Longitude (Degrees)')
            plt.legend()
            plt.savefig('dot_product.pdf')
            plt.close()

            plt.figure()
            plt.plot(eq_long, fe, label='fa_e', color='r')
            plt.plot(eq_long, fn, label='fa_n', color='k')
            plt.plot(eq_long, fu, label='fa_u', color='b')

            plt.plot(eq_long,
                     apex_fa[0, :],
                     label='apexpy_e',
                     color='r',
                     linestyle='dotted')
            plt.plot(eq_long,
                     apex_fa[1, :],
                     label='apexpy_n',
                     color='k',
                     linestyle='dotted')
            plt.plot(eq_long,
                     apex_fa[2, :],
                     label='apexpy_u',
                     color='b',
                     linestyle='dotted')

            plt.plot(eq_long,
                     igrf_e,
                     label='igrf_e',
                     color='r',
                     linestyle='-.')
            plt.plot(eq_long,
                     igrf_n,
                     label='igrf_n',
                     color='k',
                     linestyle='-.')
            plt.plot(eq_long,
                     igrf_u,
                     label='igrf_u',
                     color='b',
                     linestyle='-.')

            plt.legend()
            plt.xlabel('Longitude')
            plt.ylabel('Vector Component')
            plt.title('Field Aligned Vector')
            plt.savefig('comparison_field_aligned.pdf')
            plt.close()

            f = plt.figure()
            plt.plot(eq_long, ze, label='zonal_e', color='r')
            plt.plot(eq_long, zn, label='zonal_n', color='k')
            plt.plot(eq_long, zu, label='zonal_u', color='b')

            plt.plot(eq_long,
                     apex_zon[0, :],
                     label='apexpy_e',
                     color='r',
                     linestyle='dotted')
            plt.plot(eq_long,
                     apex_zon[1, :],
                     label='apexpy_n',
                     color='k',
                     linestyle='dotted')
            plt.plot(eq_long,
                     apex_zon[2, :],
                     label='apexpy_u',
                     color='b',
                     linestyle='dotted')

            plt.legend()
            plt.xlabel('Longitude')
            plt.ylabel('Vector Component')
            plt.title('Zonal Vector')
            plt.savefig('comparison_zonal.pdf')
            plt.close()

            f = plt.figure()
            plt.plot(eq_long, me, label='mer_e', color='r')
            plt.plot(eq_long, mn, label='mer_n', color='k')
            plt.plot(eq_long, mu, label='mer_u', color='b')

            plt.plot(eq_long,
                     apex_mer[0, :],
                     label='apexpy_e',
                     color='r',
                     linestyle='dotted')
            plt.plot(eq_long,
                     apex_mer[1, :],
                     label='apexpy_n',
                     color='k',
                     linestyle='dotted')
            plt.plot(eq_long,
                     apex_mer[2, :],
                     label='apexpy_u',
                     color='b',
                     linestyle='dotted')

            plt.legend()
            plt.xlabel('Longitude')
            plt.ylabel('Vector Component')
            plt.title('Meridional Vector')
            plt.savefig('comparison_meridional.pdf')
            plt.close()
        except:
            pass
Exemple #30
0
def add_ram_pointing_sc_attitude_vectors(inst):
    """
    Add attitude vectors for spacecraft assuming ram pointing.

    Presumes spacecraft is pointed along the velocity vector (x), z is
    generally nadir pointing (positive towards Earth), and y completes the
    right handed system (generally southward).

    Parameters
    ----------
    inst : pysat.Instrument
        Instrument object

    Returns
    -------
    None
        Modifies pysat.Instrument object in place to include S/C attitude
        unit vectors, expressed in ECEF basis. Vectors are named
        sc_(x,y,z)hat_ecef_(x,y,z).
        sc_xhat_ecef_x is the spacecraft unit vector along x (positive along
        velocity vector) reported in ECEF, ECEF x-component.

    Notes
    -----
        Expects velocity and position of spacecraft in Earth Centered
        Earth Fixed (ECEF) coordinates to be in the instrument object
        and named velocity_ecef_* (*=x,y,z) and position_ecef_* (*=x,y,z)

        Adds attitude vectors for spacecraft in the ECEF basis by calculating
        the scalar product of each attitude vector with each component of ECEF.

    """

    # Ram pointing is along velocity vector
    inst['sc_xhat_ecef_x'], inst['sc_xhat_ecef_y'], inst['sc_xhat_ecef_z'] = \
        OMMBV.normalize_vector(inst['velocity_ecef_x'],
                               inst['velocity_ecef_y'],
                               inst['velocity_ecef_z'])

    # Begin with z along Nadir (towards Earth)
    # if orbit isn't perfectly circular, then the s/c z vector won't
    # point exactly along nadir. However, nadir pointing is close enough
    # to the true z (in the orbital plane) that we can use it to get y,
    # and use x and y to get the real z
    inst['sc_zhat_ecef_x'], inst['sc_zhat_ecef_y'], inst['sc_zhat_ecef_z'] = \
        OMMBV.normalize_vector(-inst['position_ecef_x'],
                               -inst['position_ecef_y'],
                               -inst['position_ecef_z'])

    # get y vector assuming right hand rule
    # Z x X = Y
    inst['sc_yhat_ecef_x'], inst['sc_yhat_ecef_y'], inst['sc_yhat_ecef_z'] = \
        OMMBV.cross_product(inst['sc_zhat_ecef_x'],
                            inst['sc_zhat_ecef_y'],
                            inst['sc_zhat_ecef_z'],
                            inst['sc_xhat_ecef_x'],
                            inst['sc_xhat_ecef_y'],
                            inst['sc_xhat_ecef_z'])
    # Normalize since Xhat and Zhat from above may not be orthogonal
    inst['sc_yhat_ecef_x'], inst['sc_yhat_ecef_y'], inst['sc_yhat_ecef_z'] = \
        OMMBV.normalize_vector(inst['sc_yhat_ecef_x'],
                               inst['sc_yhat_ecef_y'],
                               inst['sc_yhat_ecef_z'])

    # Strictly, need to recalculate Zhat so that it is consistent with RHS
    # just created
    # Z = X x Y
    inst['sc_zhat_ecef_x'], inst['sc_zhat_ecef_y'], inst['sc_zhat_ecef_z'] = \
        OMMBV.cross_product(inst['sc_xhat_ecef_x'],
                            inst['sc_xhat_ecef_y'],
                            inst['sc_xhat_ecef_z'],
                            inst['sc_yhat_ecef_x'],
                            inst['sc_yhat_ecef_y'],
                            inst['sc_yhat_ecef_z'])

    # Adding metadata
    inst.meta['sc_xhat_ecef_x'] = {
        'units':
        '',
        'desc':
        ' '.join(('S/C attitude (x-direction, ram) unit vector,',
                  'expressed in ECEF basis, x-component'))
    }
    inst.meta['sc_xhat_ecef_y'] = {
        'units':
        '',
        'desc':
        ' '.join(('S/C attitude (x-direction, ram) unit vector,',
                  'expressed in ECEF basis, y-component'))
    }
    inst.meta['sc_xhat_ecef_z'] = {
        'units':
        '',
        'desc':
        ' '.join(('S/C attitude (x-direction, ram) unit vector,',
                  'expressed in ECEF basis, z-component'))
    }

    inst.meta['sc_zhat_ecef_x'] = {
        'units':
        '',
        'desc':
        ' '.join(('S/C attitude (z-direction, generally nadir) unit',
                  'vector, expressed in ECEF basis, x-component'))
    }
    inst.meta['sc_zhat_ecef_y'] = {
        'units':
        '',
        'desc':
        ' '.join(('S/C attitude (z-direction, generally nadir) unit',
                  'vector, expressed in ECEF basis, y-component'))
    }
    inst.meta['sc_zhat_ecef_z'] = {
        'units':
        '',
        'desc':
        ' '.join(('S/C attitude (z-direction, generally nadir) unit',
                  'vector, expressed in ECEF basis, z-component'))
    }

    inst.meta['sc_yhat_ecef_x'] = {
        'units':
        '',
        'desc':
        ' '.join(('S/C attitude (y-direction, generally south) unit',
                  'vector, expressed in ECEF basis, x-component'))
    }
    inst.meta['sc_yhat_ecef_y'] = {
        'units':
        '',
        'desc':
        ' '.join(('S/C attitude (y-direction, generally south) unit',
                  'vector, expressed in ECEF basis, y-component'))
    }
    inst.meta['sc_yhat_ecef_z'] = {
        'units':
        '',
        'desc':
        ' '.join(('S/C attitude (y-direction, generally south) unit',
                  'vector, expressed in ECEF basis, z-component'))
    }

    # check what magnitudes we get
    mag = np.sqrt(inst['sc_zhat_ecef_x']**2 + inst['sc_zhat_ecef_y']**2 +
                  inst['sc_zhat_ecef_z']**2)
    idx, = np.where((mag < .999999999) | (mag > 1.000000001))
    if len(idx) > 0:
        print(mag[idx])
        raise RuntimeError(' '.join(('Unit vector generation failure. Not',
                                     'sufficently orthogonal.')))

    return