Example #1
0
 def _from_periapsis(
     cls,
     semilatus_rectum_au,
     eccentricity,
     inclination_degrees,
     longitude_of_ascending_node_degrees,
     argument_of_perihelion_degrees,
     t_periapsis,
     gm_km3_s2,
     center=None,
     target=None,
 ):
     """Build a `KeplerOrbit` given its parameters and date of periapsis."""
     gm_au3_d2 = gm_km3_s2 * _CONVERT_GM
     pos, vel = ele_to_vec(
         semilatus_rectum_au,
         eccentricity,
         DEG2RAD * inclination_degrees,
         DEG2RAD * longitude_of_ascending_node_degrees,
         DEG2RAD * argument_of_perihelion_degrees,
         0.0,
         gm_au3_d2,
     )
     return cls(
         Distance(pos),
         Velocity(vel),
         t_periapsis,
         gm_au3_d2,
         center,
         target,
     )
Example #2
0
def check_orbit(p, e, i, Om, w, v, ts):
    """Checks that the given set of elements are calculated properly by
    elementslib.py

    Converts the given elements to state vectors using ele_to_vec, then uses
    those state vectors to create an OsculatingElements object, and then
    checks that the data in the OsculatingElements object matches the input
    elements.
    """
    length = 1
    for item in p, e, i, Om, w, v:
        if isinstance(item, (list, ndarray)):
            length = len(item)

    mu = 403503.2355022598
    pos_vec, vel_vec = ele_to_vec(p, e, i, Om, w, v, mu)
    time_tt = ts.utc(2018).tt
    time = ts.tt(jd=repeat(time_tt, pos_vec[0].size))
    elements = OsculatingElements(Distance(km=pos_vec),
                                  Velocity(km_per_s=vel_vec), time, mu)
    check_types(elements, length)
    compare(time, elements.time, 1e-9)
    compare(elements.semi_latus_rectum.km, p, 1e-9)
    compare(elements.eccentricity, e, 1e-14)
    compare(elements.inclination.radians, i, 1e-14, mod=True)
    compare(elements.longitude_of_ascending_node.radians, Om, 1e-14, mod=True)
    compare(elements.argument_of_periapsis.radians, w, 1e-14, mod=True)
    compare(elements.true_anomaly.radians, v, 1e-14, mod=True)
Example #3
0
def midpoint(p1, p2):
    '''The midpoint between two celestial coordinates.

    Given a point as a tuple of either ``(Ra, Dec)`` or ``(Ra, Dec, Dist)``.
    RA is of Skyfield type Angle, with ``preference='hours'``. Dec is of Skyfield
    type Angle. Distance is Skyfield type Distance, in AU units.

    If dist is included, the returned tuple is (Mid_RA, Mid_Dec, Mid_Dist).
    Otherwise, it is (Mid_RA, Mid_Dec).

    You can alternatively use ``center`` for multiple points.'''

    if (not isinstance(p1, tuple) or not isinstance(p2, tuple)):
        raise ValueError('Can only pass two tuples to this function.')
        return None

    mean_ra = (p1[0].radians + p2[0].radians) / 2.0
    mean_dec = (p1[1].radians + p2[1].radians) / 2.0
    if (len(p1) == 3 and len(p2) == 3):
        mean_dist = (p1[2].AU + p2[2].AU) / 2.0
        return (Angle(radians=mean_ra, preference='hours'),
                Angle(radians=mean_dec, signed=True), Distance(AU=mean_dist))
    else:
        return (Angle(radians=mean_ra,
                      preference='hours'), Angle(radians=mean_dec,
                                                 signed=True))
Example #4
0
    def from_true_anomaly(cls, p, e, i, Om, w, v,
                          epoch,
                          mu_km_s=None,
                          mu_au_d=None,
                          center=None,
                          target=None,
                          center_name=None,
                          target_name=None,
        ):
        """ Creates a `KeplerOrbit` object from elements using true anomaly

        Parameters
        ----------
        p : Distance
            Semi-Latus Rectum
        e : float
             Eccentricity
        i : Angle
            Inclination
        Om : Angle
            Longitude of Ascending Node
        w : Angle
            Argument of periapsis
        v : Angle
            True anomaly
        epoch : Time
            Time corresponding to `position` and `velocity`
        mu_km_s : float
            Value of mu (G * M) in km^3/s^2
        mu_au_d : float
            Value of mu (G * M) in au^3/d^2
        center : int
            NAIF ID of the primary body, 399 for geocentric orbits, 10 for
            heliocentric orbits
        target : int
            NAIF ID of the secondary body
        """
        if (mu_km_s and mu_au_d) or (not mu_km_s and not mu_au_d):
            raise ValueError('Either mu_km_s or mu_au_d should be used, but not both')

        if mu_au_d:
            mu_km_s = mu_au_d * AU_KM**3 / DAY_S**2

        position, velocity = ele_to_vec(p.km,
                                        e,
                                        i.radians,
                                        Om.radians,
                                        w.radians,
                                        v.radians,
                                        mu_km_s,
        )
        return cls(Distance(km=position),
                   Velocity(km_per_s=velocity),
                   epoch,
                   mu_km_s,
                   center=center,
                   target=target,
                   center_name=center_name,
                   target_name=target_name,
        )
Example #5
0
 def _from_comet_dataframe(cls, df, ts):
     # TODO: rewrite this once skyfield.mpc._mpc_comets() goes live.
     mu_km_s = GM_dict[10]
     mu_au_d = mu_km_s / (AU_KM**3) * (DAY_S**2)
     e = df.e
     a = df.Perihelion_dist / (1 - e)
     p = a * (1 - e**2)
     n = sqrt(mu_au_d / a**3)
     peri_day = ts.tt(df.Year_of_perihelion, 0, df.Day_of_perihelion)
     epoch = ts.tt(df.Epoch_year, df.Epoch_month, df.Epoch_day)
     M = n * (epoch - peri_day)
     return cls.from_mean_anomaly(
         p=Distance(au=p),
         e=e,
         i=Angle(degrees=df.i),
         Om=Angle(degrees=df.Node),
         w=Angle(degrees=df.Peri),
         M=Angle(radians=M),
         epoch=epoch,
         mu_km_s=mu_km_s,
         center=10,
         # TODO: infer target SPK-ID from info in dataframe
         center_name='SUN',
         target_name=df.Designation_and_name,
     )
Example #6
0
def test_against_horizons():
    # See the following files in the Skyfield repository:
    #
    # horizons/ceres-orbital-elements
    # horizons/ceres-position

    ts = load.timescale()
    t = ts.tdb_jd(2458886.500000000)

    a = 2.768873850275102E+00 # A
    e = 7.705857791518426E-02 # EC
    p_au = a * (1 - e*e)   # Wikipedia

    k = KeplerOrbit._from_mean_anomaly(
        semilatus_rectum_au=p_au,
        eccentricity=e,
        inclination_degrees=2.718528770987308E+01,
        longitude_of_ascending_node_degrees=2.336112629072238E+01,
        argument_of_perihelion_degrees=1.328964361683606E+02,
        mean_anomaly_degrees=1.382501360489816E+02,
        epoch=t,
        gm_km3_s2=GM_SUN,
        center=None,
        target=None,
    )
    r, v = k._at(t)[:2]
    sun_au = [
        -0.004105894975783999, 0.006739680703224941, 0.002956344702049446,
    ]
    horizons_au = [
        1.334875927366032E+00, -2.239607658161781E+00, -1.328895183461897E+00,
    ]
    epsilon = Distance(m=0.001).au
    assert abs(r + sun_au - horizons_au).max() < epsilon
def check_orbit(p,
                e,
                i,
                Om,
                w,
                v,
                p_eps=None,
                e_eps=None,
                i_eps=None,
                Om_eps=None,
                w_eps=None,
                v_eps=None):
    pos0, vel0 = ele_to_vec(p, e, i, Om, w, v, mu)

    pos1, vel1 = propagate(pos0, vel0, 0, times, mu)
    ele = OsculatingElements(Distance(km=pos1), Velocity(km_per_s=vel1),
                             dummy_time, mu)

    if p_eps: compare(p, ele.semi_latus_rectum.km, p_eps)
    if e_eps: compare(e, ele.eccentricity, e_eps)
    if i_eps: compare(i, ele.inclination.radians, i_eps, mod=True)
    if Om_eps:
        compare(Om, ele.longitude_of_ascending_node.radians, Om_eps, mod=True)
    if w_eps: compare(w, ele.argument_of_periapsis.radians, w_eps, mod=True)
    if v_eps: compare(v, ele.true_anomaly.radians, v_eps, mod=True)
Example #8
0
    def from_mpcorb_dataframe(cls, df, ts):
        if 'Number' in df:
            target = int(df.Number.strip('()')) + 2000000
        else:
            target = None

        if 'Name' not in df or df.Name == nan:
            target_name = df.Principal_desig
        else:
            target_name = df.Name

        p = df.a * (1 - df.e**2)
        return cls.from_mean_anomaly(
            p=Distance(au=p),
            e=df.e,
            i=Angle(degrees=df.i),
            Om=Angle(degrees=df.Node),
            w=Angle(degrees=df.Peri),
            M=Angle(degrees=df.M),
            epoch=ts.tdb_jd(df.Epoch),
            mu_km_s=GM_dict[10] + GM_dict.get(target, 0),
            center=10,
            target=target,
            center_name='SUN',
            target_name=target_name,
        )
Example #9
0
def test_stringifying_vector_distance():
    a = array([1.23, 4.56])
    s = str(Distance(au=a))
    if '[1' in s:
        # Python 3.5, says Travis CI.  No idea.
        assert s == '[1.23 4.56] au'
    else:
        # Every other version of Python.
        assert s == '[ 1.23  4.56] au'
Example #10
0
def test_helpful_exceptions():
    distance = Distance(1.234)
    expect = '''\
to use this Distance, ask for its value in a particular unit:

    distance.au
    distance.km
    distance.m'''

    with assert_raises(UnpackingError) as a:
        x, y, z = distance
    assert str(a.exception) == expect

    with assert_raises(UnpackingError) as a:
        distance[0]
    assert str(a.exception) == expect

    velocity = Velocity(1.234)
    expect = '''\
to use this Velocity, ask for its value in a particular unit:

    velocity.au_per_d
    velocity.km_per_s
    velocity.m_per_s'''

    with assert_raises(UnpackingError) as a:
        x, y, z = velocity
    assert str(a.exception) == expect

    with assert_raises(UnpackingError) as a:
        velocity[0]
    assert str(a.exception) == expect

    angle = Angle(radians=1.234)
    expect = '''\
to use this Angle, ask for its value in a particular unit:

    angle.degrees
    angle.hours
    angle.radians'''

    with assert_raises(UnpackingError) as a:
        x, y, z = angle
    assert str(a.exception) == expect

    with assert_raises(UnpackingError) as a:
        angle[0]
    assert str(a.exception) == expect
Example #11
0
    def from_dataframe(cls, df, ts):
        # https://minorplanetcenter.net/iau/info/PackedDates.html

        # if 'Number' in df:
        #     target = int(df.Number.strip('()')) + 2000000
        # else:

        target = None
        name = 'foo'

        # if 'Name' not in df or df.Name == nan:
        #     target_name = df.Principal_desig
        # else:
        #target_name = df.Name

        p = df.semimajor_axis_au.values * (1.0 - df.eccentricity.values**2.0)

        # TODO: rework the epoch conversion using arrays, if possible, as in
        # https://stackoverflow.com/questions/49503173/

        def n(c):
            return ord(c) - 48 if c.isdigit() else ord(c) - 55

        def d(s):
            year = 100 * n(s[0]) + int(s[1:3])
            month = n(s[3])
            day = n(s[4])
            return julian_day(year, month, day)

        epoch_jd = [d(s) for s in df.epoch_packed.values]
        t = ts.tt_jd(epoch_jd)

        # TODO: vectorize

        return cls.from_mean_anomaly(
            p=Distance(au=p[0]),
            e=df.eccentricity.values[0],
            i=Angle(degrees=df.inclination_degrees.values[0]),
            Om=Angle(degrees=df.longitude_of_ascending_node_degrees.values[0]),
            w=Angle(degrees=df.argument_of_perihelion_degrees.values[0]),
            M=Angle(degrees=df.mean_anomaly_degrees.values[0]),
            epoch=t[0],
            mu_km_s=GM_dict[10] + GM_dict.get(target, 0),
            center=10,
            target=target,
            center_name='SUN',
            target_name=name,
        )
def test_against_horizons():
    # See the following files in the Skyfield repository:
    #
    # horizons/ceres-orbital-elements
    # horizons/ceres-position

    ts = load.timescale(builtin=True)
    t = ts.tdb_jd(2458886.500000000)

    a = 2.768873850275102E+00  # A
    e = 7.705857791518426E-02  # EC
    p_au = a * (1 - e**2)  # Wikipedia

    k = KeplerOrbit.from_mean_anomaly(
        p=Distance(au=p_au),  # see above
        e=e,
        i=Angle(degrees=2.718528770987308E+01),
        Om=Angle(degrees=2.336112629072238E+01),
        w=Angle(degrees=1.328964361683606E+02),
        M=Angle(degrees=1.382501360489816E+02),
        epoch=t,  #?
        mu_km_s=None,
        mu_au_d=2.9591220828559093E-04,
        center=None,
        target=None,
        center_name=None,
        target_name=None,
    )
    r, v = k._at(t)[:2]
    sun_au = [
        -0.004105894975783999,
        0.006739680703224941,
        0.002956344702049446,
    ]
    horizons_au = [
        1.334875927366032E+00,
        -2.239607658161781E+00,
        -1.328895183461897E+00,
    ]
    assert max(abs(r + sun_au - horizons_au)) < 2e-15
Example #13
0
def geoDistance(
    a: GeographicPosition,
    b: GeographicPosition,
    geoid: Optional[Geoid] = None,
) -> Distance:
    """Compute the surface distance between two geographic positions.

    TODO: Add this to the Geoid class instead.
    """
    geoid = geoid or wgs84

    # We use Lambert's formula. See
    # https://en.wikipedia.org/wiki/Geographical_distance#Lambert's_formula_for_long_lines
    omf = 1.0 - (1.0 / geoid.inverse_flattening)

    # Compute reduced latitudes
    beta1 = numpy.arctan(omf * numpy.tan(a.latitude.radians))
    beta2 = numpy.arctan(omf * numpy.tan(b.latitude.radians))

    # The (spherical) central angle between the two points
    sigma = numpy.arccos(
        numpy.sin(beta1) * numpy.sin(beta2) +
        numpy.cos(beta1) * numpy.cos(beta2) *
        numpy.cos(b.longitude.radians - a.longitude.radians))

    # And now Lambert's equation.
    sinSigma = numpy.sin(sigma)

    P = (beta2 + beta1) / 2
    Q = (beta2 - beta1) / 2

    xBase = numpy.sin(P) * numpy.cos(Q) / numpy.cos(sigma / 2)
    yBase = numpy.cos(P) * numpy.sin(Q) / numpy.sin(sigma / 2)

    X = (sigma - sinSigma) * xBase * xBase
    Y = (sigma + sinSigma) * yBase * yBase

    arc = sigma - (X + Y) / (2 * geoid.inverse_flattening)

    return Distance.from_au(geoid.radius.au * arc)
Example #14
0
def test_converting_from_m_to_km():
    distance = Distance(m=1234.0)
    assert abs(distance.km - 1.234) < 1e-15
Example #15
0
def test_converting_from_km_to_m():
    distance = Distance(km=1.234)
    assert abs(distance.m - 1234.0) < 1e-15
Example #16
0
def test_constructors_accept_plain_lists():
    Distance(au=[1, 2, 3])
    Distance(km=[1, 2, 3])
    Distance(m=[1, 2, 3])
    Velocity(au_per_d=[1, 2, 3])
    Velocity(km_per_s=[1, 2, 3])
Example #17
0
def test_converting_distance_with_astropy():
    distance = Distance(au=1.234)
    value1 = distance.km
    value2 = distance.to(u.km)
    epsilon = 0.02  # definitions of AU seem to disagree slightly
    assert abs(value1 - value2.value) < epsilon
Example #18
0
for lon in range(-181, 181):
    longitudes[lon] = GSO_longitudes.count(lon)

fig, ax = plt.subplots()
ax.bar(longitudes.keys(), longitudes.values())
ax.set_title(u'Распределение геостационарных КА по долготе')
ax.set_xlabel(u'Долгота,°')
ax.set_ylabel(u'Кол-во спутников')
plt.savefig('Распределение геостационарных КА по долготе.png')

min_distances = []

for s1 in GSO_satellites:
    p1 = s1.at(t)
    min_dist = Distance(au=100).km
    for s2 in GSO_satellites:
        if s2 == s1:
            continue
        p2 = s2.at(t)
        dist = (p2 - p1).distance().km
        if dist < min_dist:
            min_dist = dist
    min_distances.append(min_dist)

fig, ax = plt.subplots()
hist(min_distances, bins='scott', density=True, alpha=0.5, ax=ax)
ax.set_title(u'Распределение КА по расстоянию между «ближайшими соседями»')
ax.set_xlabel(u'Расстояние, км')
ax.set_ylabel(u'Частота')
plt.savefig('Распределение КА по расстоянию.png')
Example #19
0
    def _from_mean_anomaly(
        cls,
        semilatus_rectum_au,
        eccentricity,
        inclination_degrees,
        longitude_of_ascending_node_degrees,
        argument_of_perihelion_degrees,
        mean_anomaly_degrees,
        epoch,
        gm_km3_s2,
        center=None,
        target=None,
    ):
        """ Creates a `KeplerOrbit` object from elements using mean anomaly

        Parameters
        ----------
        p : Distance
            Semi-Latus Rectum
        e : float
             Eccentricity
        i : Angle
            Inclination
        Om : Angle
            Longitude of Ascending Node
        w : Angle
            Argument of periapsis
        M : Angle
            Mean anomaly
        epoch : Time
            Time corresponding to `position` and `velocity`
        mu_km_s : float
            Value of mu (G * M) in km^3/s^2
        mu_au3_d2 : float
            Value of mu (G * M) in au^3/d^2
        center : int
            NAIF ID of the primary body, 399 for geocentric orbits, 10 for
            heliocentric orbits
        target : int
            NAIF ID of the secondary body
        """
        M = DEG2RAD * mean_anomaly_degrees
        gm_au3_d2 = gm_km3_s2 * _CONVERT_GM
        if eccentricity < 1.0:
            E = eccentric_anomaly(eccentricity, M)
            v = true_anomaly_closed(eccentricity, E)
        elif eccentricity > 1.0:
            E = eccentric_anomaly(eccentricity, M)
            v = true_anomaly_hyperbolic(eccentricity, E)
        else:
            v = true_anomaly_parabolic(semilatus_rectum_au, gm_au3_d2, M)

        pos, vel = ele_to_vec(
            semilatus_rectum_au,
            eccentricity,
            DEG2RAD * inclination_degrees,
            DEG2RAD * longitude_of_ascending_node_degrees,
            DEG2RAD * argument_of_perihelion_degrees,
            v,
            gm_au3_d2,
        )
        return cls(
            Distance(pos),
            Velocity(vel),
            epoch,
            gm_au3_d2,
            center,
            target,
        )
Example #20
0
def write_PV_to_file(tle_obj_vec: list[dict],
                     t0: datetime,
                     tf: datetime,
                     timestep: float,
                     filepath: str = "sats-TLE-example.json") -> None:
    import json
    from collections import OrderedDict

    import numpy as np
    from sgp4.api import Satrec, SatrecArray, jday
    from skyfield.api import load
    from skyfield.sgp4lib import TEME
    from skyfield.units import Velocity, Distance
    from skyfield.framelib import ecliptic_J2000_frame
    from skyfield.positionlib import ICRF

    json_posveldata = OrderedDict()
    # all TLEs (w/ diff epochs) will be propagated to current time.

    epoch_error_flag = False
    time_vec = [t0]
    t = t0
    while t < tf:
        t += datetime.timedelta(seconds=timestep)
        time_vec.append(t)

    years, months, days, hours, minutes, seconds = zip(*[(t.year, t.month,
                                                          t.day, t.hour,
                                                          t.minute, t.second)
                                                         for t in time_vec])

    years, months, days, hours, minutes, seconds = np.array(years), np.array(
        months), np.array(days), np.array(hours), np.array(minutes), np.array(
            seconds)

    jds, frs = jday(years, months, days, hours, minutes, seconds)

    satrecs_vec = []
    for item in tle_obj_vec:
        sat_name = item["NORAD_CAT_ID"]
        epoch = datetime.datetime.fromisoformat(item["EPOCH"])

        if (tf > (epoch + datetime.timedelta(days=5))) or (
                tf < (epoch - datetime.timedelta(days=5))):
            # raise TypeError(f" Final propagation date  {tf} is more than 5 days away from latest TLE epoch ({epoch}), for satellite {sat_name}} ")
            epoch_error_flag = True

        sat = Satrec.twoline2rv(item["TLE_LINE1"], item["TLE_LINE2"])
        satrecs_vec.append(sat)
        json_posveldata[sat_name] = {"P": [], "V": []}

    satrecs = SatrecArray(satrecs_vec)

    errors, rs, vs = satrecs.sgp4(jds, frs)  # r,v in TEME frame

    ts = load.timescale()
    for idxsat, satdata in enumerate(json_posveldata.values()):

        for idxtime, (jdi, fri) in enumerate(zip(jds, frs)):

            # convert to J2000
            ttime = ts.ut1_jd(jdi + fri)

            rvicrf = ICRF.from_time_and_frame_vectors(
                t=ttime,
                frame=TEME,
                distance=Distance(km=rs[idxsat][idxtime]),
                velocity=Velocity(km_per_s=vs[idxsat][idxtime]))
            # rvj2000 = rvicrf.frame_xyz_and_velocity(ecliptic_J2000_frame)
            # pos_timestep_j2000, vel_timestep_j2000 = rvj2000[0].m, rvj2000[1].m_per_s

            pos_timestep_j2000, vel_timestep_j2000 = rvicrf.position.m, rvicrf.velocity.m_per_s

            satdata["P"].append(pos_timestep_j2000.tolist())
            satdata["V"].append(vel_timestep_j2000.tolist())

        # satdata["P"] = rs[idxsat].tolist() #TEME
        # satdata["V"] = vs[idxsat].tolist() #TEME

    if errors.any():
        print("SGP4 errors found.")  # check errror type.
    if epoch_error_flag:
        print(
            f"Warning: Results obtained might be inaccurate. Final propagation date  {tf} is more than 5 days away from latest TLE epoch ({epoch}), for satellite {sat_name} "
        )

    with open(filepath, "w") as file:
        json.dump(json_posveldata, file, indent=6)

    print(f"PV File written successfully at {filepath}.")
Example #21
0
def festoon_ephemeris(ephemeris):
    for name, radius_km in radii_km:
        name = name.lower().split()[-1]
        getattr(ephemeris, name).radius = Distance(km=radius_km)
def test_iterating_over_raw_measurement():
    distance = Distance(au=1.234)
    with assert_raises(UnpackingError) as a:
        x, y, z = distance
    assert str(a.exception) == '''\
        "Jupiter",
        "Saturn",
        "Uranus",
        "Neptune",
        "Pluto",
):
    planet = planet_name + " Barycenter"
    position = (planets[planet] - sun).at(t)

    # This is broken, see
    # https://github.com/skyfielders/python-skyfield/issues/655#issuecomment-960377889
    # osculating_elements_of(position, ecliptic_frame)

    # So we have to do things manually
    elems = OsculatingElements(
        Distance(np.einsum("mnr,nr->mr", ecliptic_frame,
                           position.position.au)),  # type: ignore
        Velocity(
            np.einsum("mnr,nr->mr", ecliptic_frame,
                      position.velocity.au_per_d)),  # type: ignore
        position.t,
        GM_dict[position.center] + GM_dict[position.target],
    )
    c_object = CelestialObject(position.target, planet_name, elems)
    c_object.glue()

position = (planets["Moon"] - planets["Earth"]).at(t)
elems = osculating_elements_of(position)
c_object = CelestialObject(position.target, "Moon", elems)
c_object.glue()