Beispiel #1
0
    def test_string_to_frame(self):
        """
        string_to_frame tests
        """
        frame_ITRF_1 = orekit_utils.string_to_frame("ITRF")
        frame_ITRF_2 = FramesFactory.getITRF(IERSConventions.IERS_2010, True)
        self.assertTrue(frame_ITRF_1.equals(frame_ITRF_2))

        frame_EME2000_1 = orekit_utils.string_to_frame("EME")
        frame_EME2000_2 = FramesFactory.getEME2000()
        frame_EME2000_3 = orekit_utils.string_to_frame("J2000")
        self.assertTrue(frame_EME2000_1.equals(frame_EME2000_2) and
                            frame_EME2000_1.equals(frame_EME2000_3))

        frame_TEME_1 = orekit_utils.string_to_frame("TEME")
        frame_TEME_2 = FramesFactory.getTEME()
        self.assertTrue(frame_TEME_1.equals(frame_TEME_2))


        frame_Topo_1 = orekit_utils.string_to_frame("Topocentric", 5., 5., 5., "groundstation_1")
        earth = OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
                         Constants.WGS84_EARTH_FLATTENING,
                         FramesFactory.getITRF(IERSConventions.IERS_2010, True))
        location = GeodeticPoint(radians(5.), radians(5.), 5.)
        frame_Topo_2 = TopocentricFrame(earth, location, "groundstation_1")
        self.assertTrue(frame_Topo_1.getNadir().equals(frame_Topo_2.getNadir()))
Beispiel #2
0
def frame_to_string(frame):
    """
	Given a orekit frame object with the following defined options below, the fucntion returns the orekit
	associated frame string.
	Args:
		frame: (Frame) with the following options...
					ITRF (geocentric and rotates with earth for use
						for points near or on earth's surface--very accurate)
					Earth Centered Inertial (ECI) Frame
						the frame is fixed to the celetial grid and doesn't rotate
						with the earth
					ECI frame, but specifically used by NORAD TLEs
					very specific frame defined at an coordinate on or near
									  the surface of an object (here we define earth). Rotates
									  with body defined by ITRF frame (can think as an offset
									  of ITRF frame for things like ground stations)
	Returns:
		string OR -1 if undefined Frame
	"""
    if frame == FramesFactory.getITRF(IERSConventions.IERS_2010, True):
        return utils.ITRF
    elif frame == FramesFactory.getEME2000():
        return utils.EME
    elif frame == FramesFactory.getTEME():
        return utils.TEME
    else:
        return -1
Beispiel #3
0
def check_iot_in_range(propagator, grstn_latitude, grstn_longitude,
                       grstn_altitude, time):
    """
	Determines whether a satellite is above the horizon at a specific time
	in the reference frame of a ground station
	Args:
        propagator: orekit propagator object of overhead satellite
		grstn_latitude (float): (degrees) groudn station latitude
		grstn_longitude (float): (degrees) ground station longitude
		grstn_altitude (float): (meters) ground station altitude
		time (string): time at which the range check is to occur.
	"""
    ITRF = FramesFactory.getITRF(IERSConventions.IERS_2010, True)
    earth = OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
                             Constants.WGS84_EARTH_FLATTENING, ITRF)

    gs_location = GeodeticPoint(radians(grstn_latitude),
                                radians(grstn_longitude),
                                float(grstn_altitude))
    gs_frame = TopocentricFrame(earth, gs_location, "ground station")
    pv = propagator.getPVCoordinates(time, propagator.getFrame())
    elevation = degrees(
        gs_frame.getElevation(pv.getPosition(), propagator.getFrame(), time))

    if elevation > 0:
        return True

    return False
Beispiel #4
0
    def _get_frame(self, name):
        '''Uses a string to identify which coordinate frame to initialize from Orekit package.

        See `Orekit FramesFactory <https://www.orekit.org/static/apidocs/org/orekit/frames/FramesFactory.html>`_
        '''
        if self.logger_func is not None:
            self._logger_start('_get_frame')

        if name == 'EME':
            return FramesFactory.getEME2000()
        elif name == 'CIRF':
            return FramesFactory.getCIRF(IERSConventions.IERS_2010,
                                         not self.frame_tidal_effects)
        elif name == 'ITRF':
            return FramesFactory.getITRF(IERSConventions.IERS_2010,
                                         not self.frame_tidal_effects)
        elif name == 'TIRF':
            return FramesFactory.getTIRF(IERSConventions.IERS_2010,
                                         not self.frame_tidal_effects)
        elif name == 'ITRFEquinox':
            return FramesFactory.getITRFEquinox(IERSConventions.IERS_2010,
                                                not self.frame_tidal_effects)
        if name == 'TEME':
            return FramesFactory.getTEME()
        else:
            raise Exception('Frame "{}" not recognized'.format(name))

        if self.logger_func is not None:
            self._logger_record('_get_frame')
def get_station(longi, lat, name, planet=OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
                                                          Constants.WGS84_EARTH_FLATTENING,
                                                          FramesFactory.getITRF(IERSConventions.IERS_2010, True))):
    """
    Returns the wanted Topocentric Frame computed thanks to its coordinates 

    Parameters
    ----------
    longi : float
        longitude of the wanted Topocentric Frame.
    lat : float
        Latitude of the wanted Topocentric Frame.
    name : str
        Wanted name for the Topocentric Frame.
    planet : OnesAxisEllipsoid, optional
        Planet where the Topocentric Frame should be associated to. The default is our planet, the Earth.

    Returns
    -------
    station_frame : Topocentric Frame
        Wanted Topocentric Frame.

    """
    longitude = radians(longi)
    latitude = radians(lat)
    station = GeodeticPoint(latitude, longitude, 0.0)
    station_frame = TopocentricFrame(planet, station, name)
    return station_frame
Beispiel #6
0
 def test_frame_to_string(self):
     """
     frame_to_string test
     """
     EME = FramesFactory.getEME2000()
     ITRF = FramesFactory.getITRF(IERSConventions.IERS_2010, True)
     TEME = FramesFactory.getTEME()
     self.assertTrue(orekit_utils.frame_to_string(EME) == "EME")
     self.assertTrue(orekit_utils.frame_to_string(ITRF) == "ITRF")
     self.assertTrue(orekit_utils.frame_to_string(TEME) == "TEME")
Beispiel #7
0
def string_to_frame(frame_name,
                    latitude=0.,
                    longitude=0.,
                    altitude=0.,
                    name=""):
    """
	Given a string with the following defined options below, the fucntion returns the orekit
	associated frame object.
	Args:
		frame_name: (string) with the following options...
					"ITRF" ... returns the ITRF (geocentric and rotates with earth for use
								for points near or on earth's surface--very accurate)
					"EME2000"/"J2000" ... Earth Centered Inertial (ECI) Frame
								  the frame is fixed to the celetial grid and doesn't rotate
								  with the earth
					"TEME" ... another ECI frame, but specifically used by NORAD TLEs
					"Topocentric" ... very specific frame defined at an coordinate on or near
									  the surface of an object (here we define earth). Rotates
									  with body defined by ITRF frame (can think as an offset
									  of ITRF frame for things like ground stations)
		latitude, longitude: (float in degrees) for defining the topocentric frame origin--not
							 required otherwise
		altitude: (float in meters) for defining the topocentric frame origin--not required otherwise
		name: (string) for defining the topocentric frame origin label--not required otherwise
	Returns:
		orekit frame object OR -1 if undefined string
	"""
    if (frame_name == utils.ITRF):
        return FramesFactory.getITRF(IERSConventions.IERS_2010, True)
    elif ((frame_name == utils.EME) or (frame_name == "J2000")):
        return FramesFactory.getEME2000()
    elif (frame_name == utils.TEME):
        return FramesFactory.getTEME()
    elif (frame_name == utils.TOPOCENTRIC):
        earth = OneAxisEllipsoid(
            Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
            Constants.WGS84_EARTH_FLATTENING,
            FramesFactory.getITRF(IERSConventions.IERS_2010, True))
        location = GeodeticPoint(radians(latitude), radians(longitude),
                                 float(altitude))
        return TopocentricFrame(earth, location, name)
    else:
        return -1
Beispiel #8
0
def _pose_absolute_cartesian(ns_spacecraft, init_coords, init_epoch):
    pos = init_coords["init_coord"]
    init_state = Cartesian()

    # Coordinates given in J2000 Frame
    # ----------------------------------------------------------------------------------------
    if init_coords["coord_frame"] == "J2000":
        init_state.R = np.array(
            [float(pos["x"]),
             float(pos["y"]),
             float(pos["z"])])

        init_state.V = np.array(
            [float(pos["v_x"]),
             float(pos["v_y"]),
             float(pos["v_z"])])
    # ----------------------------------------------------------------------------------------

    # Coordinates given in ITRF Frame
    # ----------------------------------------------------------------------------------------
    elif init_coords["coord_frame"] == "ITRF":
        inertialFrame = FramesFactory.getEME2000()
        init_frame = FramesFactory.getITRF(
            IERS.IERS_2010, False)  # False -> don't ignore tidal effects
        p = Vector3D(
            float(1e3),  # convert to [m]
            Vector3D(float(pos["x"]), float(pos["y"]), float(pos["z"])))
        v = Vector3D(
            float(1e3),  # convert to [m/s]
            Vector3D(float(pos["v_x"]), float(pos["v_y"]), float(pos["v_z"])))

        ITRF2EME = init_frame.getTransformTo(inertialFrame,
                                             to_orekit_date(init_epoch))
        pv_EME = ITRF2EME.transformPVCoordinates(PVCoordinates(p, v))

        init_state.R = np.array([
            pv_EME.position.x, pv_EME.position.y, pv_EME.position.z
        ]) / 1e3  # convert to [km]
        init_state.V = np.array([
            pv_EME.velocity.x, pv_EME.velocity.y, pv_EME.velocity.z
        ]) / 1e3  # convert to [km/s]

    else:
        raise ValueError(
            "[" + ns_spacecraft + " ]: " +
            "Conversion from coordinate frame " + init_coords["coord_frame"] +
            " not implemented. Please provided coordinates in a different reference frame."
        )
    # ----------------------------------------------------------------------------------------

    return init_state
def when_is_iss_visible(longi, lat, station_name):
    """
    Returns some parameters of the ISS at its maximum elevation.

    Parameters
    ----------
    longi : float
        Longitude of the place where you stand on Earth.
    lat : float
        Latitude of the place where you stand on Earth.
    station_name : str
        Name of this place.

    Returns
    -------
    extrap_date : AbsoluteDate
        Date and time of the event.
    elevation : float
        Maximum elevation angle in radians.
    current_x : float
        X coordinate in the Topocentric Frame.
    current_y : TYPE
        Y coordinate in the Topocentric Frame.

    """
    iss_tle = get_iss_tle()
    itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, True)
    earth = OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
                             Constants.WGS84_EARTH_FLATTENING, itrf)
    station_frame = get_station(longi, lat, station_name, planet=earth)
    propagator = TLEPropagator.selectExtrapolator(iss_tle)

    current_x = 100001
    current_y = 100001
    current_ele = 0
    extrap_date = iss_tle.getDate()
    inertial_frame = FramesFactory.getEME2000()
    while not is_iss_visible(degrees(current_ele)):
        pv = propagator.getPVCoordinates(extrap_date, inertial_frame)
        pos_tmp = pv.getPosition()
        inertial2station_frame = inertial_frame.getTransformTo(station_frame, extrap_date)
        current_pos_station_frame = inertial2station_frame.transformPosition(pos_tmp)
        current_x = current_pos_station_frame.getX()
        current_y = current_pos_station_frame.getY()
        current_ele = station_frame.getElevation(current_pos_station_frame,
                                                 station_frame, extrap_date)
        extrap_date = extrap_date.shiftedBy(10.0)
    elevation = station_frame.getElevation(pos_tmp, inertial_frame,
                                           extrap_date)
    return extrap_date, elevation, current_x, current_y
Beispiel #10
0
    def __init__(self,
                 lat=48.58,
                 longi=7.75,
                 alt=142.0,
                 loc="Strasbourg Frame",
                 time_zone=1.0,
                 gnomon_length=1.0):
        """
        Initiate the Sundial instance. The default is a sundial located in Strasbourg.

        Parameters
        ----------
        lat : float, optional
            Latitude of the murial sundial. The default is 48.58 (Strasbourg).
        longi : float, optional
            Longitude of the mural sundial. The default is 7.75 (Strasbourg).
        alt : float, optional
            Altitude of the mural sundial. The default is 142.0 (Strasbourg).
        loc : str, optional
            Name of the place where the murial sundial will stand. The default is "Strasbourg".
        time_zone : int, optional
            Time zone of the place where the murial sundial will stand . The default is 1 (Strasbourg).
        gnomon_length : float, optional
            Length of the gnomon of the murial Sundial. The default is 1.0.

        Returns
        -------
        None.

        """
        self.latitude = radians(lat)
        self.longitude = radians(longi)
        self.altitude = alt
        # utc = TimeScalesFactory.getUTC()
        # date = AbsoluteDate(year, month, 1, 0, 0, 0.0, utc)
        # self.date = date
        self.location = loc
        self.time_zone = time_zone
        # self.summer_time = summer_time
        self.gnomon_length = gnomon_length
        self.station_geo_point = GeodeticPoint(self.latitude, self.longitude,
                                               self.altitude)
        itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, True)
        earth = OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
                                 Constants.WGS84_EARTH_FLATTENING, itrf)
        self.station_frame = TopocentricFrame(earth, self.station_geo_point,
                                              self.location)
        self.pv_sun = CelestialBodyFactory.getSun()
        self.pv_sun = PVCoordinatesProvider.cast_(self.pv_sun)
Beispiel #11
0
def nadir_pointing_law(parameters):
    """
    Returns a nadir pointing attitude law (points to ground point directly below) for the earth as the
    celestial body orbited
    Args:
        parameters: dictionary containing at least...
                    parameters["frame"] = (str) "EME", "J2000", or "TEME" only
    Returns:
        AttidueProvider: attitude law that tells the satellite to point at nadir
    """
    ITRF = FramesFactory.getITRF(IERSConventions.IERS_2010, True)
    earth = OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
                             Constants.WGS84_EARTH_FLATTENING, ITRF)
    frame = string_to_frame(parameters["frame"])
    return NadirPointing(frame, earth)
Beispiel #12
0
def propagate(tle_line1, tle_line2, duration, step, maneuvers=[]):
    tle = create_tle(tle_line1, tle_line2)
    tle_propagator = TLEPropagator.selectExtrapolator(tle)
    propagator_with_maneuvers = AdapterPropagator(tle_propagator)

    # TODO works only for chronological maneuvers, because of the "propagate" to get the state
    for maneuver in maneuvers:
        date, frame, deltaV, isp = maneuver
        propagator_with_maneuvers.addEffect(
            SmallManeuverAnalyticalModel(
                propagator_with_maneuvers.propagate(
                    datetime_to_absolutedate(date)), frame, deltaV, isp))

    ITRF = FramesFactory.getITRF(IERSConventions.IERS_2010, True)
    earth = OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
                             Constants.WGS84_EARTH_FLATTENING, ITRF)

    time = [tle.getDate().shiftedBy(float(dt)) \
            for dt in np.arange(0, duration, step)]
    orbits = [
        KeplerianOrbit(propagator_with_maneuvers.propagate(date).getOrbit())
        for date in time
    ]

    subpoints = [
        earth.transform(
            propagator_with_maneuvers.propagate(date).getPVCoordinates(),
            tle_propagator.getFrame(), date) for date in time
    ]
    return pd.DataFrame({
        'time':
        [absolutedate_to_datetime(orbit.getDate()) for orbit in orbits],
        'a': [orbit.getA() for orbit in orbits],
        'e': [orbit.getE() for orbit in orbits],
        'i': [orbit.getI() for orbit in orbits],
        'pom': [orbit.getPerigeeArgument() for orbit in orbits],
        'RAAN': [orbit.getRightAscensionOfAscendingNode() for orbit in orbits],
        'v': [orbit.getTrueAnomaly() for orbit in orbits],
        'ex': [orbit.getEquinoctialEx() for orbit in orbits],
        'ey': [orbit.getEquinoctialEy() for orbit in orbits],
        'hx': [orbit.getHx() for orbit in orbits],
        'hy': [orbit.getHy() for orbit in orbits],
        'latitude':
        [np.degrees(gp.getLatitude().getReal()) for gp in subpoints],
        'longitude':
        [np.degrees(gp.getLongitude().getReal()) for gp in subpoints]
    })
Beispiel #13
0
def find_sat_distance(prop1, prop2, time):
    """
	distance between two propagators at a given time
	Inputs: prop1/prop2 (Two orekit propagator objects), time (orekit absolute time object--utc)
	Output: Distance (meters)
	"""
    ITRF = FramesFactory.getITRF(IERSConventions.IERS_2010, True)
    earth = OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
                             Constants.WGS84_EARTH_FLATTENING, ITRF)

    pv_1 = prop1.getPVCoordinates(time, prop1.getFrame())
    pv_2 = prop2.getPVCoordinates(time, prop2.getFrame())

    p_1 = pv_1.getPosition()
    p_2 = pv_2.getPosition()

    return abs(Vector3D.distance(p_1, p_2))
def when_is_iss_visible_bis(longi, lat, station_name):
    iss_tle = get_iss_tle()
    itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, True)
    earth = OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
                             Constants.WGS84_EARTH_FLATTENING, itrf)
    station_frame = get_station(longi, lat, earth, station_name)
    propagator = TLEPropagator.selectExtrapolator(iss_tle)
    ele_detector = ElevationDetector(60.0, 0.001, station_frame).withConstantElevation(radians(5.0)).withHandler(ContinueOnEvent())
    logger = EventsLogger()
    logged_detector = logger.monitorDetector(ele_detector)
    # propagator = Propagator.cast_(propagator)
    propagator.addEventDetector(logged_detector)
    initial_date = iss_tle.getDate()
    propagator.propagate(initial_date, initial_date.shiftedBy(3600.0*0.5)) ##20 days here
    date_ele_max = logger.getLoggedEvents().get(0).getState().getDate()
    pos_max_j2000 = logger.getLoggedEvents().get(0).getState().getPVCoordinates().getPosition()
    ele_max = station_frame.getElevation(pos_max_j2000,
                                         FramesFactory.getEME2000(), date_ele_max)
    return date_ele_max, ele_max
Beispiel #15
0
def ground_pointing_law(parameters):
    """
	Given a longitude, lattitude, and altitude it returns a ground pointing attitude law
	Args:
		parameters: dictionary containing at least...
                    parameters["frame"] = (str) "EME2000", "J2000", or "TEME" only
                    parameters["latitude"] = (float -- radians) latitude
                    parameters["longitude"] = (float -- radians) longitude
                    parameters["altitude"] = (float -- meters) altitude above sea level
	Returns:
		AttidueProvider: attitude law that tells the satellite to point at a specific point on the ground
	"""
    ITRF = FramesFactory.getITRF(IERSConventions.IERS_2010, True)
    earth = OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
                             Constants.WGS84_EARTH_FLATTENING, ITRF)
    point = GeodeticPoint(float(parameters["latitude"]),
                          float(parameters["longitude"]),
                          float(parameters["altitude"]))
    frame = string_to_frame(parameters["frame"])
    return TargetPointing(frame, point, earth)
Beispiel #16
0
    def test_ground_pointing_law(self):
        """
        ground_pointing_law test
        """
        parameters = {"latitude": 12.0, "altitude": 2343.0, "longitude": 12.0}
        parameters1 = {
            "eccentricity": 0.0008641,
            "semimajor_axis": 6801395.04,
            "inclination": 87.0,
            "perigee_argument": 10.0,
            "right_ascension_of_ascending_node": 10.0,
            "anomaly": radians(0.0),
            "anomaly_type": "TRUE",
            "orbit_update_date": '2021-12-02T00:00:00.000',
            "frame": "EME"
        }

        orbit_propagator_1 = analytical_propagator(parameters1)
        orbit_propagator_2 = analytical_propagator(parameters1)
        parameters["frame"] = orekit_utils.frame_to_string(
            orbit_propagator_1.getFrame())

        ITRF = FramesFactory.getITRF(IERSConventions.IERS_2010, True)
        target = GeodeticPoint(parameters["latitude"], parameters["longitude"],
                               parameters["altitude"])
        attitude_provider = ground_pointing_law(parameters)
        attitude_provider_1 = TargetPointing(
            FramesFactory.getEME2000(), target,
            OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
                             Constants.WGS84_EARTH_FLATTENING, ITRF))

        orbit_propagator_1.setAttitudeProvider(attitude_provider)
        orbit_propagator_2.setAttitudeProvider(attitude_provider_1)

        time_to_propagate = orekit_utils.absolute_time_converter_utc_string(
            '2022-05-02T00:00:00.000')
        state_1 = orbit_propagator_1.propagate(time_to_propagate)
        state_2 = orbit_propagator_2.propagate(time_to_propagate)

        self.assertTrue((state_1.getAttitude().getSpin().toString() ==
                         state_2.getAttitude().getSpin().toString()))
Beispiel #17
0
    def _get_frame(self, name):
        '''Uses a string to identify which coordinate frame to initialize from Orekit package.

        See `Orekit FramesFactory <https://www.orekit.org/static/apidocs/org/orekit/frames/FramesFactory.html>`_
        '''

        if self.profiler is not None:
            self.profiler.start('Orekit:get_frame')

        if name == 'EME':
            frame = FramesFactory.getEME2000()
        elif name == 'EME2000':
            frame = FramesFactory.getEME2000()
        elif name == 'GCRF':
            frame = FramesFactory.getGCRF()
        elif name == 'CIRF':
            frame = FramesFactory.getCIRF(
                IERSConventions.IERS_2010,
                not self.settings['frame_tidal_effects'])
        elif name == 'ITRF':
            frame = FramesFactory.getITRF(
                IERSConventions.IERS_2010,
                not self.settings['frame_tidal_effects'])
        elif name == 'TIRF':
            frame = FramesFactory.getTIRF(
                IERSConventions.IERS_2010,
                not self.settings['frame_tidal_effects'])
        elif name == 'ITRFEquinox':
            frame = FramesFactory.getITRFEquinox(
                IERSConventions.IERS_2010,
                not self.settings['frame_tidal_effects'])
        elif name == 'TEME':
            frame = FramesFactory.getTEME()
        else:
            raise Exception('Frame "{}" not recognized'.format(name))

        if self.profiler is not None:
            self.profiler.stop('Orekit:get_frame')

        return frame
# CSV
import csv



##############################################################################
##############################################################################
# Create the accessory objects and handle the observations
##############################################################################
##############################################################################

# Create the GCRF (ECI) and ITRF (ECEF) Frames
GCRF_Frame = FramesFactory.getGCRF()
J2000_Frame = FramesFactory.getEME2000()
ITRF_Frame = FramesFactory.getITRF(IERSConventions.IERS_2010, True)
earth = OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
                                       Constants.WGS84_EARTH_FLATTENING,
                                       ITRF_Frame)


# CREATE THE GROUND STATION
# First create the Topocentric Frame
station_coord = GeodeticPoint(radians(40), radians(-110), 2000.0)
station_frame = TopocentricFrame(earth, station_coord, 'MyGndStation')

# Now create the Ground station itself and the satellite object
GndStation = GroundStation(station_frame)
Sat = ObservableSatellite(1) # create the observable satellite object, name it 1 as default

# SET THE DAY OF THE OBSERVATIONS
Beispiel #19
0
    def __init__(
        self,
        in_frame='EME',
        out_frame='ITRF',
        frame_tidal_effects=False,
        integrator='DormandPrince853',
        min_step=0.001,
        max_step=120.0,
        position_tolerance=10.0,
        earth_gravity='HolmesFeatherstone',
        gravity_order=(10, 10),
        solarsystem_perturbers=['Moon', 'Sun'],
        drag_force=True,
        atmosphere='DTM2000',
        radiation_pressure=True,
        solar_activity='Marshall',
        constants_source='WGS84',
        solar_activity_strength='WEAK',
        logger_func=None,
    ):
        super(PropagatorOrekit, self).__init__()

        self.logger_func = logger_func
        self.__log = {}
        self.__log_tmp = {}
        if self.logger_func is not None:
            self._logger_start('__init__')

        self.solarsystem_perturbers = solarsystem_perturbers
        self.in_frame = in_frame
        self.frame_tidal_effects = frame_tidal_effects
        self.out_frame = out_frame
        self.drag_force = drag_force
        self.gravity_order = gravity_order
        self.atmosphere = atmosphere
        self.radiation_pressure = radiation_pressure
        self.solar_activity = solar_activity
        self.SolarStrengthLevel = getattr(
            org.orekit.forces.drag.atmosphere.data.
            MarshallSolarActivityFutureEstimation.StrengthLevel,
            solar_activity_strength)
        self.integrator = integrator
        self.minStep = min_step
        self.maxStep = max_step
        self.position_tolerance = position_tolerance
        self._tolerances = None

        self.constants_source = constants_source
        if constants_source == 'JPL-IAU':
            self.mu = Constants.JPL_SSD_EARTH_GM
            self.R_earth = Constants.IAU_2015_NOMINAL_EARTH_EQUATORIAL_RADIUS
            self.f_earth = (Constants.IAU_2015_NOMINAL_EARTH_EQUATORIAL_RADIUS
                            - Constants.IAU_2015_NOMINAL_EARTH_POLAR_RADIUS
                            ) / Constants.IAU_2015_NOMINAL_EARTH_POLAR_RADIUS
        else:
            self.mu = Constants.WGS84_EARTH_MU
            self.R_earth = Constants.WGS84_EARTH_EQUATORIAL_RADIUS
            self.f_earth = Constants.WGS84_EARTH_FLATTENING

        self.M_earth = self.mu / scipy.constants.G

        self.earth_gravity = earth_gravity

        self.__params = None

        self.inputFrame = self._get_frame(self.in_frame)
        self.outputFrame = self._get_frame(self.out_frame)

        if self.inputFrame.isPseudoInertial():
            self.inertialFrame = self.inputFrame
        else:
            self.inertialFrame = FramesFactory.getEME2000()

        self.body = OneAxisEllipsoid(self.R_earth, self.f_earth,
                                     self.outputFrame)

        self._forces = {}

        if self.radiation_pressure:
            self._forces['radiation_pressure'] = None
        if self.drag_force:
            self._forces['drag_force'] = None

        if self.earth_gravity == 'HolmesFeatherstone':
            provider = org.orekit.forces.gravity.potential.GravityFieldFactory.getNormalizedProvider(
                self.gravity_order[0], self.gravity_order[1])
            holmesFeatherstone = org.orekit.forces.gravity.HolmesFeatherstoneAttractionModel(
                FramesFactory.getITRF(IERSConventions.IERS_2010, True),
                provider,
            )
            self._forces['earth_gravity'] = holmesFeatherstone

        elif self.earth_gravity == 'Newtonian':
            Newtonian = org.orekit.forces.gravity.NewtonianAttraction(self.mu)
            self._forces['earth_gravity'] = Newtonian

        else:
            raise Exception(
                'Supplied Earth gravity model "{}" not recognized'.format(
                    self.earth_gravity))

        if self.solarsystem_perturbers is not None:
            for body in self.solarsystem_perturbers:
                body_template = getattr(CelestialBodyFactory,
                                        'get{}'.format(body))
                body_instance = body_template()
                perturbation = org.orekit.forces.gravity.ThirdBodyAttraction(
                    body_instance)

                self._forces['perturbation_{}'.format(body)] = perturbation
        if self.logger_func is not None:
            self._logger_record('__init__')
Beispiel #20
0
def field_of_view_detector(sat_propagator,
                           latitude,
                           longitude,
                           altitude,
                           start_time,
                           degree_fov,
                           duration=0,
                           stepsize=1):
    """
    Determines if a ground point will be within the field of view (defined as a
    circular feild of view of however many degrees from the sat) for a specific
    sat_propagator that should include an integrated attitude provider.
    Args:
        sat_propagator: orekit propagator object that should include at the least
                        an internal orbit and attitude law (this law should either
                        be ground pointing or nadir pointing)
        latitude, longitude, altitude: (floats in degrees and meters) coordinate point
                                       to be checked if within feild of view of camera
        start_time: orekit absolute time object (start time of checking)
        degree_fov: (float in degrees) the full degree field of view of the camera/instrument
        duration: (int in seconds) duration to check after start time, if not inputed
                  will default to zero, and function will return a boolean for the
                  state at only the start time
        stepsize: (int >= 1) size in seconds between each prediction (smallest step is 1 second)
    Returns:
        duration=0:
            bool value that tells if the ground point is in the feild of view at the
            start time
        else:
            array of structure [[time_start, end_time], ... ,[start_end, end_time]] that
            contains the entry/exit times of the feild of view prediction.
    """
    earth = OneAxisEllipsoid(
        Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
        Constants.WGS84_EARTH_FLATTENING,
        FramesFactory.getITRF(IERSConventions.IERS_2010, True))
    ground_target = GeodeticPoint(radians(float(latitude)),
                                  radians(float(longitude)), float(altitude))
    ground_target_frame = TopocentricFrame(earth, ground_target,
                                           "ground_target")
    circular_fov = CircularFieldOfView(Vector3D.PLUS_K,
                                       radians(float(degree_fov / 2)),
                                       radians(0.))
    fov_detector = FieldOfViewDetector(
        ground_target_frame, circular_fov).withHandler(ContinueOnEvent())
    elevation_detector = ElevationDetector(
        ground_target_frame).withConstantElevation(0.0).withHandler(
            ContinueOnEvent())

    if (duration <= 0):
        return (
            (fov_detector.g(sat_propagator.propagate(start_time)) < 0) and
            (elevation_detector.g(sat_propagator.propagate(start_time)) > 0))

    time_within_fov = []
    time_array = [
        start_time.shiftedBy(float(time))
        for time in np.arange(0, duration, stepsize)
    ]
    entry_empty = True
    entry_time = 0
    for time in time_array:
        within_fov = (
            (fov_detector.g(sat_propagator.propagate(time)) < 0)
            and (elevation_detector.g(sat_propagator.propagate(time)) > 0))
        if (entry_empty and within_fov):
            entry_time = time
            entry_empty = False
        elif ((not entry_empty) and (not within_fov)):
            time_within_fov.append([entry_time, time])
            entry_empty = True
            entry_time = 0
        elif ((not entry_empty) and (time == time_array[-1])):
            time_within_fov.append([entry_time, time])

    return time_within_fov
Beispiel #21
0
]

# Collect both API data in two dictionaries
OREKIT_BODIES_AND_FRAMES = dict(
    zip(BODIES_NAMES, zip(OREKIT_BODIES, OREKIT_FIXED_FRAMES)))
POLIASTRO_BODIES_AND_FRAMES = dict(
    zip(
        BODIES_NAMES,
        zip(POLIASTRO_BODIES, POLIASTRO_ICRS_FRAMES, POLIASTRO_FIXED_FRAMES),
    ))

# Macros for J2000 and ICRF frame from Orekit API
J2000_OREKIT = AbsoluteDate.J2000_EPOCH
ICRF_FRAME_OREKIT = FramesFactory.getICRF()
GCRF_FRAME_OREKIT = FramesFactory.getGCRF()
ITRF_FRAME_OREKIT = FramesFactory.getITRF(IERSConventions.IERS_2010, False)

# Some of tests are marked as XFAIL since Orekit implements the data from IAU
# WGCCRE 2009 report while poliastro uses IAU WGCCRE 2015 one


@pytest.mark.parametrize("r_vec", R_SET)
@pytest.mark.parametrize("v_vec", V_SET)
@pytest.mark.parametrize(
    "body_name",
    [
        "Sun",
        pytest.param(
            "Mercury", marks=pytest.mark.xfail
        ),  # poliastro WGCCRE 2015 report != orekit IAU 2009 report
        "Venus",
Beispiel #22
0
def get_ground_passes(propagator,
                      grstn_latitude,
                      grstn_longitude,
                      grstn_altitude,
                      start,
                      stop,
                      ploting_param=False):
    """
	Gets all passes for a specific satellite occuring during the given range. Pass is defined as 10° above the horizon,
	below this is unusable (for our purposes)
	Args:
		propagator ([any] OreKit orbit propagator): propagator, TLEPropagator or KeplerianPropagator for the
													satellite in question
		grstn_latitude (Float): latitude of the ground station in degrees
		grstn_longitude (Float): longitude of the ground station in degrees
		start (OreKit AbsoluteDate): the beginning of the desired time interval
		stop (OreKit AbsoluteDate): the end of the desired time interval
		plotting_param (Boolean): do not use, used by potential plotter
	Return value:
		A dictionary with {"start":[OreKit AbsoluteDate], "stop":[OreKit AbsoluteDate], "duration": (seconds) [float]}
		Alternatively, returns a queue of times in reference to the start time for ease of plotting ground passes.
	Notes:
		use absolutedate_to_datetime() to convert from AbsoluteDate
	TODO: add ElevationMask around ground station to deal with topography blocking communications
	"""

    ITRF = FramesFactory.getITRF(IERSConventions.IERS_2010, True)
    earth = OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
                             Constants.WGS84_EARTH_FLATTENING, ITRF)

    gs_location = GeodeticPoint(radians(grstn_latitude),
                                radians(grstn_longitude),
                                float(grstn_altitude))
    gs_frame = TopocentricFrame(earth, gs_location, "ground station")

    elevation_detector = ElevationDetector(gs_frame).withConstantElevation(
        0.0).withHandler(ContinueOnEvent())
    logger = EventsLogger()
    logged_detector = logger.monitorDetector(elevation_detector)

    propagator.addEventDetector(logged_detector)
    state = propagator.propagate(start, stop)

    events = logger.getLoggedEvents()
    pass_start_time = None
    result = []
    if not ploting_param:
        for event in logger.getLoggedEvents():
            if event.isIncreasing():
                pass_start_time = event.getState().getDate()
            else:
                stop_time = event.getState().getDate()
                result.append({
                    "start": pass_start_time,
                    "stop": stop_time,
                    "duration": stop_time.durationFrom(start) / 60
                })
                pass_start_time = None
    else:
        result = queue.Queue(0)
        for event in logger.getLoggedEvents():
            if event.isIncreasing():
                pass_start_time = event.getState().getDate()
            else:
                pass_stop_time = event.getState().getDate()
                result.put(pass_start_time.durationFrom(
                    start))  # start is the initial time of interest
                result.put(pass_stop_time.durationFrom(start))
                pass_start_time = None

    return result
Beispiel #23
0
def visible_above_horizon(propagator_main_sat,
                          propagator_tracked_sat,
                          start_time,
                          duration=0,
                          stepsize=1,
                          return_queue=False):
    """
	Based on two propagators, determines if the main_sat can "see" another tracked_sat determinant on
	if the tracked_sat is above the planet limb (horizon) from the perspective of the main_sat, see
	different return options below.
	Args:
		propagator_main_sat, propagator_tracked_sat: any type of analytical propagator (ex. TLEPropagator)
		start_time: Orekit absolute time object (start time of tracking)
		duration: (int) seconds, how long Orekit will predict if visible. IF ZERO: ignores and
				  returns boolean cooresponding to start time
		stepsize: (int >= 1) size in seconds between each prediction (smallest step is 1 second)
		return_queue: (boolean) if True, changes return type to queue of durations since start time
					  that includes {start time, end time, start time, ...} as queue (for mapping)
	Returns: (Dependent on inputs...)
		bool: booleen value if duration is not entered, kept zero, or negative (this corresponds to if
		the tracked sat is visible at the start time)
		time_IsVisible: array of structure [[time_start, end_time], ... ,[start_end, end_time]] that
						contains the entry/exit times of the above horizon prediction.
						or
						if return_queue = True, then returns a queue of duration since start_time for
						these dates (for mapping function)
	"""
    #setup detector
    ITRF = FramesFactory.getITRF(IERSConventions.IERS_2010, True)
    earth = OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
                             Constants.WGS84_EARTH_FLATTENING, ITRF)
    detector = InterSatDirectViewDetector(
        earth, propagator_tracked_sat).withHandler(ContinueOnEvent())
    #propogate for duration and create array of [time-stamp, bool] that tells if tracked_sat is visible to main_sat
    if (return_queue == True):
        time_IsVisible = queue.Queue(0)
    elif (duration > 0):
        time_IsVisible = []
    else:
        return (detector.g(propagator_main_sat.propagate(start_time)) > 0)
    time_array = [
        start_time.shiftedBy(float(time))
        for time in np.arange(0, duration, stepsize)
    ]
    entry_time = 0
    exit_time = 0
    for time in time_array:
        detector_value = detector.g(propagator_main_sat.propagate(time))
        #g here is the function that returns positive values if visible and negative if not visible
        if ((entry_time == 0) and (detector_value > 0)):
            entry_time = time
        elif ((entry_time != 0) and (detector_value <= 0)
              and (exit_time == 0)):
            exit_time = time
        elif ((entry_time != 0) and (exit_time != 0)):
            if (return_queue == True):
                time_IsVisible.put(exit_time.durationFrom(entry_time))
            else:
                time_IsVisible.append([entry_time, exit_time])
            entry_time = 0
            exit_time = 0
        elif ((entry_time != 0) and (exit_time == 0)
              and (time == time_array[-1])):
            if (return_queue == True):
                time_IsVisible.put(time.durationFrom(entry_time))
            else:
                time_IsVisible.append([entry_time, time])
    return time_IsVisible
Beispiel #24
0
def main():

    a = 24396159.0  # semi major axis (m)
    e = 0.720  # eccentricity
    i = radians(10.0)  # inclination
    omega = radians(50.0)  # perigee argument
    raan = radians(150)  # right ascension of ascending node
    lM = 0.0  # mean anomaly

    # Set inertial frame
    inertialFrame = FramesFactory.getEME2000()

    # Initial date in UTC time scale
    utc = TimeScalesFactory.getUTC()
    initial_date = AbsoluteDate(2004, 1, 1, 23, 30, 00.000, utc)

    # Setup orbit propagator
    # gravitation coefficient
    mu = 3.986004415e+14

    # Orbit construction as Keplerian
    initialOrbit = KeplerianOrbit(a, e, i, omega, raan, lM, PositionAngle.MEAN,
                                  inertialFrame, initial_date, mu)

    initial_state = SpacecraftState(initialOrbit)

    # use a numerical propogator
    min_step = 0.001
    max_step = 1000.0
    position_tolerance = 10.0

    propagation_type = OrbitType.KEPLERIAN
    tolerances = NumericalPropagator.tolerances(position_tolerance,
                                                initialOrbit, propagation_type)

    integrator = DormandPrince853Integrator(min_step, max_step, 1e-5, 1e-10)

    propagator = NumericalPropagator(integrator)
    propagator.setOrbitType(propagation_type)

    # force model gravity field
    provider = GravityFieldFactory.getNormalizedProvider(10, 10)
    holmesFeatherstone = HolmesFeatherstoneAttractionModel(
        FramesFactory.getITRF(IERSConventions.IERS_2010, True), provider)

    # SRP
    # ssc = IsotropicRadiationSingleCoefficient(100.0, 0.8)  # Spacecraft surface area (m^2), C_r absorbtion
    # srp = SolarRadiationPressure(CelestialBodyFactory.getSun(), a, ssc)  # sun, semi-major Earth, spacecraft sensitivity

    propagator.addForceModel(holmesFeatherstone)
    # propagator.addForceModel(ThirdBodyAttraction(CelestialBodyFactory.getSun()))
    # propagator.addForceModel(ThirdBodyAttraction(CelestialBodyFactory.getMoon()))
    # propagator.addForceModel(srp)

    propagator.setMasterMode(60.0, TutorialStepHandler())

    propagator.setInitialState(initial_state)

    # propagator.setEphemerisMode()

    finalstate = propagator.propagate(initial_date.shiftedBy(
        10. * 24 * 60**2))  # TIme shift in seconds
Beispiel #25
0
    def __init__(self, orekit_data, settings=None, **kwargs):

        super(Orekit, self).__init__(settings=settings, **kwargs)

        if self.logger is not None:
            self.logger.debug(f'sorts.propagator.Orekit:init')
        if self.profiler is not None:
            self.profiler.start('Orekit:init')

        setup_orekit_curdir(filename=orekit_data)

        if self.logger is not None:
            self.logger.debug(f'Orekit:init:orekit-data = {orekit_data}')

        self.utc = TimeScalesFactory.getUTC()

        self.__settings = dict()
        self.__settings['SolarStrengthLevel'] = getattr(
            org.orekit.forces.drag.atmosphere.data.
            MarshallSolarActivityFutureEstimation.StrengthLevel,
            self.settings['solar_activity_strength'])
        self._tolerances = None

        if self.settings['constants_source'] == 'JPL-IAU':
            self.mu = Constants.JPL_SSD_EARTH_GM
            self.R_earth = Constants.IAU_2015_NOMINAL_EARTH_EQUATORIAL_RADIUS
            self.f_earth = (Constants.IAU_2015_NOMINAL_EARTH_EQUATORIAL_RADIUS
                            - Constants.IAU_2015_NOMINAL_EARTH_POLAR_RADIUS
                            ) / Constants.IAU_2015_NOMINAL_EARTH_POLAR_RADIUS
        else:
            self.mu = Constants.WGS84_EARTH_MU
            self.R_earth = Constants.WGS84_EARTH_EQUATORIAL_RADIUS
            self.f_earth = Constants.WGS84_EARTH_FLATTENING

        self.M_earth = self.mu / scipy.constants.G

        self.__params = None

        self._forces = {}

        if self.settings['radiation_pressure']:
            self._forces['radiation_pressure'] = None
        if self.settings['drag_force']:
            self._forces['drag_force'] = None

        if self.settings['earth_gravity'] == 'HolmesFeatherstone':
            provider = org.orekit.forces.gravity.potential.GravityFieldFactory.getNormalizedProvider(
                self.settings['gravity_order'][0],
                self.settings['gravity_order'][1])
            holmesFeatherstone = org.orekit.forces.gravity.HolmesFeatherstoneAttractionModel(
                FramesFactory.getITRF(IERSConventions.IERS_2010, True),
                provider,
            )
            self._forces['earth_gravity'] = holmesFeatherstone

        elif self.settings['earth_gravity'] == 'Newtonian':
            Newtonian = org.orekit.forces.gravity.NewtonianAttraction(self.mu)
            self._forces['earth_gravity'] = Newtonian

        else:
            raise Exception(
                'Supplied Earth gravity model "{}" not recognized'.format(
                    self.settings['earth_gravity']))

        if self.settings['solarsystem_perturbers'] is not None:
            for body in self.settings['solarsystem_perturbers']:
                body_template = getattr(CelestialBodyFactory,
                                        'get{}'.format(body))
                body_instance = body_template()
                perturbation = org.orekit.forces.gravity.ThirdBodyAttraction(
                    body_instance)

                self._forces['perturbation_{}'.format(body)] = perturbation

        if self.logger is not None:
            for key in self._forces:
                if self._forces[key] is not None:
                    self.logger.debug(
                        f'Orekit:init:_forces:{key} = {type(self._forces[key])}'
                    )
                else:
                    self.logger.debug(f'Orekit:init:_forces:{key} = None')

        if self.profiler is not None:
            self.profiler.stop('Orekit:init')
Beispiel #26
0
def parseStationData(stationFile, stationEccFile, epoch):
    """
    Parses the two files containing the coordinates of laser ranging ground stations
    :param stationFile: str, path to the station coordinates file
    :param stationEccFile: str, path to the station eccentricities file
    :param epoch: datetime object. used to compute the station position based on the velocity data
    :return: a pandas DataFrame containing:
        - index: str, 8-digit id of the ground station
        - columns:
            - CODE: int, 4-digit station code (including possibly several receivers)
            - PT: str, usually A
            - Latitude: float, station latitude in degrees
            - Longitude: float, station longitude in degrees
            - Altitude: float, station altitude above WGS84 reference sea level in meters
            - OrekitGroundStation: Orekit GroundStation object
    """
    from org.orekit.utils import IERSConventions
    from org.orekit.frames import FramesFactory
    itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, True)
    from org.orekit.models.earth import ReferenceEllipsoid
    wgs84ellipsoid = ReferenceEllipsoid.getWgs84(itrf)

    from org.hipparchus.geometry.euclidean.threed import Vector3D
    from org.orekit.frames import TopocentricFrame
    from org.orekit.estimation.measurements import GroundStation
    from numpy import rad2deg
    from orekit.pyhelpers import datetime_to_absolutedate

    import pandas as pd
    stationData = pd.DataFrame(columns=[
        'CODE', 'PT', 'Latitude', 'Longitude', 'Altitude',
        'OrekitGroundStation'
    ])
    stationxyz = pd.DataFrame(columns=[
        'CODE', 'PT', 'TYPE', 'SOLN', 'REF_EPOCH', 'UNIT', 'S',
        'ESTIMATED_VALUE', 'STD_DEV'
    ])

    # First run on the file to initialize the ground stations using the approximate lat/lon/alt data
    with open(stationFile) as f:
        line = ''
        while not line.startswith('+SITE/ID'):
            line = f.readline()
        line = f.readline()  # Skipping +SITE/ID
        line = f.readline()  # Skipping column header

        while not line.startswith('-SITE/ID'):
            stationCode = int(line[1:5])
            pt = line[7]
            l = line[44:]
            #lon_deg = float(l[0:3]) + float(l[3:6]) / 60.0 + float(l[6:11]) / 60.0 / 60.0
            #lat_deg = float(l[12:15]) + float(l[15:18]) / 60.0 + float(l[18:23]) / 60.0 / 60.0
            #alt_m = float(l[24:31])
            station_id = l[36:44]

            #geodeticPoint = GeodeticPoint(float(deg2rad(lat_deg)), float(deg2rad(lon_deg)), alt_m)
            #topocentricFrame = TopocentricFrame(wgs84ellipsoid, geodeticPoint, str(station_id))
            #groundStation = GroundStation(topocentricFrame)

            #stationData.loc[station_id] = [stationCode, pt, lat_deg, lon_deg, alt_m, groundStation]
            stationData.loc[station_id, ['CODE', 'PT']] = [
                stationCode, pt
            ]  # Only filling the station code and id
            line = f.readline()

        # Parsing accurate ground station position from XYZ
        while not line.startswith('+SOLUTION/ESTIMATE'):
            line = f.readline()

        line = f.readline()  # Skipping +SOLUTION/ESTIMATE
        line = f.readline()  # Skipping column header

        while not line.startswith('-SOLUTION/ESTIMATE'):
            index = int(line[1:6])
            lineType = line[7:11]
            code = int(line[14:18])
            pt = line[20:21]
            soln = int(line[22:26])
            refEpochStr = line[27:39]
            refEpoch = epochStringToDatetime(refEpochStr)
            unit = line[40:44]
            s = int(line[45:46])
            estimatedValue = float(line[47:68])
            stdDev = float(line[69:80])

            stationxyz.loc[index] = [
                code, pt, lineType, soln, refEpoch, unit, s, estimatedValue,
                stdDev
            ]
            line = f.readline()

    pivotTable = stationxyz.pivot_table(index=['CODE', 'PT'],
                                        columns=['TYPE'],
                                        values=['ESTIMATED_VALUE', 'STD_DEV'])
    stationxyz.set_index(['CODE', 'PT'], inplace=True)

    # Reading the eccentricities data
    stationEcc = pd.DataFrame(columns=[
        'SITE', 'PT', 'SOLN', 'T', 'DATA_START', 'DATA_END', 'XYZ', 'X', 'Y',
        'Z'
    ])

    with open(stationEccFile) as f:
        line = ''
        while not line.startswith('+SITE/ECCENTRICITY'):
            line = f.readline()
        line = f.readline()  # skipping +SITE/ECCENTRICITY
        line = f.readline()  # skipping table header

        while not line.startswith('-SITE/ECCENTRICITY'):
            site = int(line[0:5])
            pt = line[7:8]
            soln = int(line[9:13])
            t = line[14:15]
            dataStartStr = line[16:28]
            dataStart = epochStringToDatetime(dataStartStr)
            dataEndStr = line[29:41]
            dataEnd = epochStringToDatetime(dataEndStr)
            xyz = line[42:45]
            x = float(line[46:54])
            y = float(line[55:63])
            z = float(line[64:72])
            stationId = line[80:88]

            stationEcc.loc[stationId] = [
                site, pt, soln, t, dataStart, dataEnd, xyz, x, y, z
            ]

            line = f.readline()

    stationEcc.index.name = 'CDP-SOD'

    # A loop is needed here to create the Orekit objects
    for stationId, staData in stationData.iterrows():
        indexTuple = (staData['CODE'], staData['PT'])
        refEpoch = stationxyz.loc[indexTuple, 'REF_EPOCH'][0]
        yearsSinceEpoch = (epoch - refEpoch).days / 365.25

        pv = pivotTable.loc[indexTuple]['ESTIMATED_VALUE']
        x = float(pv['STAX'] +  # Station coordinates
                  pv['VELX'] * yearsSinceEpoch +  # Station displacements
                  stationEcc.loc[stationId]['X'])  # Station eccentricities
        y = float(pv['STAY'] + pv['VELY'] * yearsSinceEpoch +
                  stationEcc.loc[stationId]['Y'])
        z = float(pv['STAZ'] + pv['VELZ'] * yearsSinceEpoch +
                  stationEcc.loc[stationId]['Z'])
        station_xyz_m = Vector3D(x, y, z)
        geodeticPoint = wgs84ellipsoid.transform(
            station_xyz_m, itrf, datetime_to_absolutedate(epoch))
        lon_deg = rad2deg(geodeticPoint.getLongitude())
        lat_deg = rad2deg(geodeticPoint.getLatitude())
        alt_m = geodeticPoint.getAltitude()
        topocentricFrame = TopocentricFrame(wgs84ellipsoid, geodeticPoint,
                                            str(indexTuple[0]))
        groundStation = GroundStation(topocentricFrame)
        stationData.loc[stationId] = [
            staData['CODE'], staData['PT'], lat_deg, lon_deg, alt_m,
            groundStation
        ]

    return stationData