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
Example #2
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
Example #3
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()))
Example #4
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]
    })
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
Example #6
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)
Example #7
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)
Example #8
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
Example #10
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
Example #11
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)
Example #12
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()))
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
yr_mo_day = (2012, 8, 20)
utc = TimeScalesFactory.getUTC()
Example #14
0
class PropagatorOrekit(PropagatorBase):
    '''Propagator class implementing the Orekit propagator.


    :ivar list solarsystem_perturbers: List of strings of names of objects in the solarsystem that should be used for third body perturbation calculations. All objects listed at `CelestialBodyFactory <https://www.orekit.org/static/apidocs/org/orekit/bodies/CelestialBodyFactory.html>`_ are available.
    :ivar str in_frame: String identifying the input frame to be used. All frames listed at `FramesFactory <https://www.orekit.org/static/apidocs/org/orekit/frames/FramesFactory.html>`_ are available.
    :ivar str out_frame: String identifying the output frame to be used. All frames listed at `FramesFactory <https://www.orekit.org/static/apidocs/org/orekit/frames/FramesFactory.html>`_ are available.
    :ivar bool drag_force: Should drag force be included in propagation.
    :ivar bool radiation_pressure: Should radiation pressure force be included in propagation.
    :ivar bool frame_tidal_effects: Should coordinate frames include Tidal effects.
    :ivar str integrator: String representing the numerical integrator from the Hipparchus package to use. Any integrator listed at `Hipparchus nonstiff ode <https://www.hipparchus.org/apidocs/org/hipparchus/ode/nonstiff/package-summary.html>`_ is available.
    :ivar float minStep: Minimum time step allowed in the numerical orbit propagation given in seconds.
    :ivar float maxStep: Maximum time step allowed in the numerical orbit propagation given in seconds.
    :ivar float position_tolerance: Position tolerance in numerical orbit propagation errors given in meters.
    :ivar str earth_gravity: Gravitation model to use for calculating central acceleration force. Currently avalible options are `'HolmesFeatherstone'` and `'Newtonian'`. See `gravity <https://www.orekit.org/static/apidocs/org/orekit/forces/gravity/package-summary.html>`_.
    :ivar tuple gravity_order: A tuple of two integers for describing the order of spherical harmonics used in the `HolmesFeatherstoneAttractionModel <https://www.orekit.org/static/apidocs/org/orekit/forces/gravity/HolmesFeatherstoneAttractionModel.html>`_ model.
    :ivar str atmosphere: Atmosphere model used to calculate atmospheric drag. Currently available options are `'DTM2000'`. See `atmospheres <https://www.orekit.org/static/apidocs/org/orekit/forces/drag/atmosphere/package-summary.html>`_.
    :ivar str solar_activity: The model used for calculating solar activity and thereby the influx of solar radiation. Used in the atmospheric drag force model. Currently available options are `'Marshall'` for the `MarshallSolarActivityFutureEstimation <https://www.orekit.org/static/apidocs/org/orekit/forces/drag/atmosphere/data/MarshallSolarActivityFutureEstimation.html>`_.
    :ivar str constants_source: Controls which source for Earth constants to use. Currently avalible options are `'WGS84'` and `'JPL-IAU'`. See `constants <https://www.orekit.org/static/apidocs/org/orekit/utils/Constants.html>`_.
    :ivar float mu: Standard gravitational constant for the Earth.  Definition depend on the :class:`propagator_orekit.PropagatorOrekit` constructor parameter :code:`constants_source`
    :ivar float R_earth: Radius of the Earth in m. Definition depend on the :class:`propagator_orekit.PropagatorOrekit` constructor parameter :code:`constants_source`
    :ivar float f_earth: Flattening of the Earth (i.e. :math:`\\frac{a-b}{a}` ). Definition depend on the :class:`propagator_orekit.PropagatorOrekit` constructor parameter :code:`constants_source`.
    :ivar float M_earth: Mass of the Earth in kg. Definition depend on the :class:`propagator_orekit.PropagatorOrekit` constructor parameter :code:`constants_source`
    :ivar org.orekit.frames.Frame inputFrame: The orekit frame instance for the input frame.
    :ivar org.orekit.frames.Frame outputFrame: The orekit frame instance for the output frame.
    :ivar org.orekit.frames.Frame inertialFrame: The orekit frame instance for the inertial frame. If inputFrame is pseudo innertial this is the same as inputFrame.
    :ivar org.orekit.bodies.OneAxisEllipsoid body: The model ellipsoid representing the Earth.
    :ivar dict _forces: Dictionary of forces to include in the numerical integration. Contains instances of children of :class:`org.orekit.forces.AbstractForceModel`.
    :ivar list _tolerances: Contains the absolute and relative tolerances calculated by the `tolerances <https://www.orekit.org/static/apidocs/org/orekit/propagation/numerical/NumericalPropagator.html#tolerances(double,org.orekit.orbits.Orbit,org.orekit.orbits.OrbitType)>`_ function.
    :ivar org.orekit.propagation.numerical.NumericalPropagator propagator: The numerical propagator instance.
    :ivar org.orekit.forces.drag.atmosphere.data.MarshallSolarActivityFutureEstimation.StrengthLevel SolarStrengthLevel: The strength of the solar activity. Options are 'AVRAGE', 'STRONG', 'WEAK'.

    The constructor creates a propagator instance with supplied options.

    :param list solarsystem_perturbers: List of strings of names of objects in the solarsystem that should be used for third body perturbation calculations. All objects listed at `CelestialBodyFactory <https://www.orekit.org/static/apidocs/org/orekit/bodies/CelestialBodyFactory.html>`_ are available.
    :param str in_frame: String identifying the input frame to be used. All frames listed at `FramesFactory <https://www.orekit.org/static/apidocs/org/orekit/frames/FramesFactory.html>`_ are available.
    :param str out_frame: String identifying the output frame to be used. All frames listed at `FramesFactory <https://www.orekit.org/static/apidocs/org/orekit/frames/FramesFactory.html>`_ are available.
    :param bool drag_force: Should drag force be included in propagation.
    :param bool radiation_pressure: Should radiation pressure force be included in propagation.
    :param bool frame_tidal_effects: Should coordinate frames include Tidal effects.
    :param str integrator: String representing the numerical integrator from the Hipparchus package to use. Any integrator listed at `Hipparchus nonstiff ode <https://www.hipparchus.org/apidocs/org/hipparchus/ode/nonstiff/package-summary.html>`_ is available.
    :param float min_step: Minimum time step allowed in the numerical orbit propagation given in seconds.
    :param float max_step: Maximum time step allowed in the numerical orbit propagation given in seconds.
    :param float position_tolerance: Position tolerance in numerical orbit propagation errors given in meters.
    :param str atmosphere: Atmosphere model used to calculate atmospheric drag. Currently available options are `'DTM2000'`. See `atmospheres <https://www.orekit.org/static/apidocs/org/orekit/forces/drag/atmosphere/package-summary.html>`_.
    :param str solar_activity: The model used for calculating solar activity and thereby the influx of solar radiation. Used in the atmospheric drag force model. Currently available options are `'Marshall'` for the `MarshallSolarActivityFutureEstimation <https://www.orekit.org/static/apidocs/org/orekit/forces/drag/atmosphere/data/MarshallSolarActivityFutureEstimation.html>`_.
    :param str constants_source: Controls which source for Earth constants to use. Currently avalible options are `'WGS84'` and `'JPL-IAU'`. See `constants <https://www.orekit.org/static/apidocs/org/orekit/utils/Constants.html>`_.
    :param str earth_gravity: Gravitation model to use for calculating central acceleration force. Currently avalible options are `'HolmesFeatherstone'` and `'Newtonian'`. See `gravity <https://www.orekit.org/static/apidocs/org/orekit/forces/gravity/package-summary.html>`_.
    :param tuple gravity_order: A tuple of two integers for describing the order of spherical harmonics used in the `HolmesFeatherstoneAttractionModel <https://www.orekit.org/static/apidocs/org/orekit/forces/gravity/HolmesFeatherstoneAttractionModel.html>`_ model.
    :param str solar_activity_strength: The strength of the solar activity. Options are 'AVRAGE', 'STRONG', 'WEAK'.
    '''
    class OrekitVariableStep(PythonOrekitStepHandler):
        '''Class for handling the steps.
        '''
        def set_params(self, t, start_date, states_pointer, outputFrame):
            self.t = t
            self.start_date = start_date
            self.states_pointer = states_pointer
            self.outputFrame = outputFrame

        def init(self, s0, t):
            pass

        def handleStep(self, interpolator, isLast):
            state1 = interpolator.getCurrentState()
            state0 = interpolator.getPreviousState()

            t0 = state0.getDate().durationFrom(self.start_date)
            t1 = state1.getDate().durationFrom(self.start_date)

            for ti, t in enumerate(self.t):

                if np.abs(t) >= np.abs(t0) and np.abs(t) <= np.abs(t1):
                    t_date = self.start_date.shiftedBy(float(t))

                    _state = interpolator.getInterpolatedState(t_date)

                    PVCoord = _state.getPVCoordinates(self.outputFrame)

                    x_tmp = PVCoord.getPosition()
                    v_tmp = PVCoord.getVelocity()

                    self.states_pointer[0, ti] = x_tmp.getX()
                    self.states_pointer[1, ti] = x_tmp.getY()
                    self.states_pointer[2, ti] = x_tmp.getZ()
                    self.states_pointer[3, ti] = v_tmp.getX()
                    self.states_pointer[4, ti] = v_tmp.getY()
                    self.states_pointer[5, ti] = v_tmp.getZ()

    def _logger_start(self, name):
        self.__log_tmp[name] = time.time()

    def _logger_record(self, name):
        dt = [time.time() - self.__log_tmp[name]]
        if name in self.__log:
            self.__log[name] += dt
        else:
            self.__log[name] = dt

    def print_logger(self):
        _max_len = 0
        for name in self.__log:
            if len(name) > _max_len:
                _max_len = len(name)
        _head = '{:<' + str(_max_len) + '} | {:<15} | {}'
        _row = '{:<' + str(_max_len) + '} | {:<15} | {:<15.6f}'
        self.logger_func(_head.format('Name', 'Count', 'Mean execution time'))
        for name, vec in self.__log.items():
            self.logger_func(_row.format(name, len(vec), np.mean(vec)))

    def get_logger(self):
        _ret = {}
        for name, vec in self.__log.items():
            _ret[name] = np.mean(vec)
        return _ret

    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__')

    def __str__(self):
        if self.logger_func is not None:
            self._logger_start('__str__')

        ret = ''
        ret += 'PropagatorOrekit instance @ {}:'.format(
            hash(self)) + '\n' + '-' * 25 + '\n'
        ret += '{:20s}: '.format('Integrator') + self.integrator + '\n'
        ret += '{:20s}: '.format('Minimum step') + str(
            self.minStep) + ' s' + '\n'
        ret += '{:20s}: '.format('Maximum step') + str(
            self.maxStep) + ' s' + '\n'
        ret += '{:20s}: '.format('Position Tolerance') + str(
            self.position_tolerance) + ' m' + '\n'
        if self._tolerances is not None:
            ret += '{:20s}: '.format('Absolute Tolerance') + str(
                JArray_double.cast_(self._tolerances[0])) + ' m' + '\n'
            ret += '{:20s}: '.format('Relative Tolerance') + str(
                JArray_double.cast_(self._tolerances[1])) + ' m' + '\n'
        ret += '\n'
        ret += '{:20s}: '.format('Input frame') + self.in_frame + '\n'
        ret += '{:20s}: '.format('Output frame') + self.out_frame + '\n'
        ret += '{:20s}: '.format('Gravity model') + self.earth_gravity + '\n'
        if self.earth_gravity == 'HolmesFeatherstone':
            ret += '{:20s} - Harmonic expansion order {}'.format(
                '', self.gravity_order) + '\n'
        ret += '{:20s}: '.format('Atmosphere model') + self.atmosphere + '\n'
        ret += '{:20s}: '.format('Solar model') + self.solar_activity + '\n'
        ret += '{:20s}: '.format('Constants') + self.constants_source + '\n'
        ret += 'Included forces:' + '\n'
        for key in self._forces:
            ret += ' - {}'.format(' '.join(key.split('_'))) + '\n'
        ret += 'Third body perturbations:' + '\n'
        for body in self.solarsystem_perturbers:
            ret += ' - {:}'.format(body) + '\n'

        if self.logger_func is not None:
            self._logger_record('__str__')

        return ret

    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 _construct_propagator(self, initialOrbit):
        '''
        Get the specified integrator from hipparchus package. List available at: `nonstiff ode <https://www.hipparchus.org/apidocs/org/hipparchus/ode/nonstiff/package-summary.html>`_

        Configure the integrator tolerances using the orbit.
        '''
        if self.logger_func is not None:
            self._logger_start('_construct_propagator')

        self._tolerances = NumericalPropagator.tolerances(
            self.position_tolerance, initialOrbit, initialOrbit.getType())

        integrator_constructor = getattr(
            org.hipparchus.ode.nonstiff,
            '{}Integrator'.format(self.integrator),
        )

        integrator = integrator_constructor(
            self.minStep,
            self.maxStep,
            JArray_double.cast_(self._tolerances[0]),
            JArray_double.cast_(self._tolerances[1]),
        )

        propagator = NumericalPropagator(integrator)
        propagator.setOrbitType(initialOrbit.getType())

        self.propagator = propagator

        if self.logger_func is not None:
            self._logger_record('_construct_propagator')

    def _set_forces(self, A, cd, cr):
        '''Using the spacecraft specific parameters, set the drag force and radiation pressure models.
        
        **See:**
            * `drag <https://www.orekit.org/static/apidocs/org/orekit/forces/drag/package-summary.html>`_
            * `radiation <https://www.orekit.org/static/apidocs/org/orekit/forces/radiation/package-summary.html>`_
        '''
        __params = [A, cd, cr]

        if self.logger_func is not None:
            self._logger_start('_set_forces')

        re_calc = True
        if self.__params is None:
            re_calc = True
        else:
            if not np.allclose(np.array(__params, dtype=np.float),
                               self.__params,
                               rtol=1e-3):
                re_calc = True

        if re_calc:
            self.__params = __params
            if self.drag_force:
                if self.solar_activity == 'Marshall':
                    msafe = org.orekit.forces.drag.atmosphere.data.MarshallSolarActivityFutureEstimation(
                        "(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\p{Digit}\\p{Digit}\\p{Digit}\\p{Digit}F10\\.(?:txt|TXT)",
                        self.SolarStrengthLevel,
                    )
                    manager = org.orekit.data.DataProvidersManager.getInstance(
                    )
                    manager.feed(msafe.getSupportedNames(), msafe)

                    if self.atmosphere == 'DTM2000':
                        atmosphere_instance = org.orekit.forces.drag.atmosphere.DTM2000(
                            msafe, CelestialBodyFactory.getSun(), self.body)
                    else:
                        raise Exception('Atmosphere model not recognized')

                    drag_model = org.orekit.forces.drag.DragForce(
                        atmosphere_instance,
                        org.orekit.forces.drag.IsotropicDrag(
                            float(A), float(cd)),
                    )

                    self._forces['drag_force'] = drag_model
                else:
                    raise Exception('Solar activity model not recognized')

            if self.radiation_pressure:
                radiation_pressure_model = org.orekit.forces.radiation.SolarRadiationPressure(
                    CelestialBodyFactory.getSun(),
                    self.body.getEquatorialRadius(),
                    org.orekit.forces.radiation.
                    IsotropicRadiationSingleCoefficient(float(A), float(cr)),
                )

                self._forces['radiation_pressure'] = radiation_pressure_model

            #self.propagator.removeForceModels()

            for force_name, force in self._forces.items():
                self.propagator.addForceModel(force)

        if self.logger_func is not None:
            self._logger_record('_set_forces')

    def get_orbit(self, t, a, e, inc, raan, aop, mu0, mjd0, **kwargs):
        '''
        **Implementation:**
    
        Units are in meters and degrees.

        Keyword arguments are:

            * float A: Area in m^2
            * float C_D: Drag coefficient
            * float C_R: Radiation pressure coefficient
            * float m: Mass of object in kg

        *NOTE:*
            * If the eccentricity is below 1e-10 the eccentricity will be set to 1e-10 to prevent Keplerian Jacobian becoming singular.
        

        The implementation first checks if the input frame is Pseudo inertial, if this is true this is used as the propagation frame. If not it is automatically converted to EME (ECI-J2000).

        Since there are forces that are dependent on the space-craft parameters, if these parameter has been changed since the last iteration the numerical integrator is re-initialized at every call of this method. The forces that can be initialized without spacecraft parameters (e.g. Earth gravitational field) are done at propagator construction.

        **Uses:**
            * :func:`propagator_base.PropagatorBase._make_numpy`
            * :func:`propagator_orekit.PropagatorOrekit._construct_propagator`
            * :func:`propagator_orekit.PropagatorOrekit._set_forces`
            * :func:`dpt_tools.kep2cart`
            * :func:`dpt_tools.cart2kep`
            * :func:`dpt_tools.true2mean`
            * :func:`dpt_tools.mean2true`
            
        
        See :func:`propagator_base.PropagatorBase.get_orbit`.
        '''

        if self.logger_func is not None:
            self._logger_start('get_orbit')

        if self.radiation_pressure:
            if 'C_R' not in kwargs:
                raise Exception(
                    'Radiation pressure force enabled but no coefficient "C_R" given'
                )
        else:
            kwargs['C_R'] = 1.0

        if self.drag_force:
            if 'C_D' not in kwargs:
                raise Exception(
                    'Drag force enabled but no drag coefficient "C_D" given')
        else:
            kwargs['C_D'] = 1.0

        t = self._make_numpy(t)

        initialDate = mjd2absdate(mjd0)

        if not self.inputFrame.isPseudoInertial():
            orb = np.array(
                [a, e, inc, aop, raan,
                 dpt.mean2true(mu0, e, radians=False)],
                dtype=np.float)
            cart = dpt.kep2cart(
                orb,
                m=kwargs['m'],
                M_cent=self.M_earth,
                radians=False,
            )

            pos = org.hipparchus.geometry.euclidean.threed.Vector3D(
                float(cart[0]), float(cart[1]), float(cart[2]))
            vel = org.hipparchus.geometry.euclidean.threed.Vector3D(
                float(cart[3]), float(cart[4]), float(cart[5]))
            PV_state = PVCoordinates(pos, vel)

            transform = self.inputFrame.getTransformTo(self.inertialFrame,
                                                       initialDate)
            new_PV = transform.transformPVCoordinates(PV_state)

            initialOrbit = CartesianOrbit(
                new_PV,
                self.inertialFrame,
                initialDate,
                self.mu + float(scipy.constants.G * kwargs['m']),
            )
        else:

            #this is to prevent Keplerian Jacobian to become singular
            use_equinoctial = False
            if e < dpt.e_lim:
                use_equinoctial = True
            if inc < dpt.i_lim:
                use_equinoctial = True

            if use_equinoctial:
                ex = e * np.cos(np.radians(aop) + np.radians(raan))
                ey = e * np.sin(np.radians(aop) + np.radians(raan))
                hx = np.tan(np.radians(inc) * 0.5) * np.cos(np.radians(raan))
                hy = np.tan(np.radians(inc) * 0.5) * np.sin(np.radians(raan))
                lv = np.radians(mu0) + np.radians(aop) + np.radians(raan)

                initialOrbit = EquinoctialOrbit(
                    float(a),
                    float(ex),
                    float(ey),
                    float(hx),
                    float(hy),
                    float(lv),
                    PositionAngle.MEAN,
                    self.inertialFrame,
                    initialDate,
                    self.mu + float(scipy.constants.G * kwargs['m']),
                )
            else:
                initialOrbit = KeplerianOrbit(
                    float(a),
                    float(e),
                    float(np.radians(inc)),
                    float(np.radians(aop)),
                    float(np.radians(raan)),
                    float(np.radians(mu0)),
                    PositionAngle.MEAN,
                    self.inertialFrame,
                    initialDate,
                    self.mu + float(scipy.constants.G * kwargs['m']),
                )

        self._construct_propagator(initialOrbit)
        self._set_forces(kwargs['A'], kwargs['C_D'], kwargs['C_R'])

        initialState = SpacecraftState(initialOrbit)

        self.propagator.setInitialState(initialState)

        tb_inds = t < 0.0
        t_back = t[tb_inds]

        tf_indst = t >= 0.0
        t_forward = t[tf_indst]

        if len(t_forward) == 1:
            if np.any(t_forward == 0.0):
                t_back = t
                t_forward = []
                tb_inds = t <= 0

        state = np.empty((6, len(t)), dtype=np.float)
        step_handler = PropagatorOrekit.OrekitVariableStep()

        if self.logger_func is not None:
            self._logger_start('propagation')

        if len(t_back) > 0:
            _t = t_back
            _t_order = np.argsort(np.abs(_t))
            _t_res = np.argsort(_t_order)
            _t = _t[_t_order]
            _state = np.empty((6, len(_t)), dtype=np.float)
            step_handler.set_params(_t, initialDate, _state, self.outputFrame)

            self.propagator.setMasterMode(step_handler)

            self.propagator.propagate(initialDate.shiftedBy(float(_t[-1])))

            #now _state is full and in the order of _t
            state[:, tb_inds] = _state[:, _t_res]

        if len(t_forward) > 0:
            _t = t_forward
            _t_order = np.argsort(np.abs(_t))
            _t_res = np.argsort(_t_order)
            _t = _t[_t_order]
            _state = np.empty((6, len(_t)), dtype=np.float)
            step_handler.set_params(_t, initialDate, _state, self.outputFrame)

            self.propagator.setMasterMode(step_handler)

            self.propagator.propagate(initialDate.shiftedBy(float(_t[-1])))

            #now _state is full and in the order of _t
            state[:, tf_indst] = _state[:, _t_res]

        if self.logger_func is not None:
            self._logger_record('propagation')

        if self.logger_func is not None:
            self._logger_record('get_orbit')

        return state

    def get_orbit_cart(self, t, x, y, z, vx, vy, vz, mjd0, **kwargs):
        '''
        **Implementation:**

        Converts Cartesian vector to Kepler elements and calls :func:`propagator_orekit.PropagatorOrekit.get_orbit`.

        All units are in m and m/s.

        **Uses:**
            * :func:`dpt_tools.cart2kep`
            * :func:`dpt_tools.true2mean`

        See :func:`propagator_base.PropagatorBase.get_orbit_cart`.
        '''
        cart = np.array([x, y, z, vx, vy, vz], dtype=np.float)
        orb = dpt.cart2kep(
            cart,
            m=kwargs['m'],
            M_cent=self.M_earth,
            radians=False,
        )

        a = orb[0]
        e = orb[1]
        inc = orb[2]
        aop = orb[3]
        raan = orb[4]
        mu0 = dpt.true2mean(orb[5], e, radians=False)

        return self.get_orbit(t, a, e, inc, raan, aop, mu0, mjd0, **kwargs)
Example #15
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__')
Example #16
0
    def propagate(self, t, state0, epoch, **kwargs):
        '''
        **Implementation:**
    
        Units are in meters and degrees.

        Keyword arguments are:

            * float A: Area in m^2
            * float C_D: Drag coefficient
            * float C_R: Radiation pressure coefficient
            * float m: Mass of object in kg

        *NOTE:*
            * If the eccentricity is below 1e-10 the eccentricity will be set to 1e-10 to prevent Keplerian Jacobian becoming singular.
        

        The implementation first checks if the input frame is Pseudo inertial, if this is true this is used as the propagation frame. If not it is automatically converted to EME (ECI-J2000).

        Since there are forces that are dependent on the space-craft parameters, if these parameter has been changed since the last iteration the numerical integrator is re-initialized at every call of this method. The forces that can be initialized without spacecraft parameters (e.g. Earth gravitational field) are done at propagator construction.

        See :func:`propagator_base.PropagatorBase.get_orbit`.
        '''
        if self.profiler is not None:
            self.profiler.start('Orekit:propagate')

        if self.settings['in_frame'].startswith('Orekit-'):
            self.inputFrame = self._get_frame(
                self.settings['in_frame'].replace('Orekit-', ''))
            orekit_in_frame = True
        else:
            self.inputFrame = self._get_frame('GCRF')
            orekit_in_frame = False

        if self.settings['out_frame'].startswith('Orekit-'):
            self.outputFrame = self._get_frame(
                self.settings['out_frame'].replace('Orekit-', ''))
            orekit_out_frame = True
        else:
            self.outputFrame = self._get_frame('GCRF')
            orekit_out_frame = False

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

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

        if isinstance(state0, pyorb.Orbit):
            state0_cart = np.squeeze(state0.cartesian)
        else:
            state0_cart = state0

        if self.settings['radiation_pressure']:
            if 'C_R' not in kwargs:
                raise Exception(
                    'Radiation pressure force enabled but no coefficient "C_R" given'
                )
        else:
            kwargs['C_R'] = 1.0

        if self.settings['drag_force']:
            if 'C_D' not in kwargs:
                raise Exception(
                    'Drag force enabled but no drag coefficient "C_D" given')
        else:
            kwargs['C_D'] = 1.0

        if 'm' not in kwargs:
            kwargs['m'] = 0.0

        t, epoch = self.convert_time(t, epoch)
        times = epoch + t

        mjd0 = epoch.mjd
        t = t.value
        if not isinstance(t, np.ndarray):
            t = np.array([t])

        if self.logger is not None:
            self.logger.debug(f'Orekit:propagate:len(t) = {len(t)}')

        if not orekit_in_frame:
            state0_cart = frames.convert(
                epoch,
                state0_cart,
                in_frame=self.settings['in_frame'],
                out_frame='GCRS',
                profiler=self.profiler,
                logger=self.logger,
            )

        initialDate = mjd2absdate(mjd0, self.utc)

        pos = org.hipparchus.geometry.euclidean.threed.Vector3D(
            float(state0_cart[0]), float(state0_cart[1]),
            float(state0_cart[2]))
        vel = org.hipparchus.geometry.euclidean.threed.Vector3D(
            float(state0_cart[3]), float(state0_cart[4]),
            float(state0_cart[5]))
        PV_state = PVCoordinates(pos, vel)

        if not self.inputFrame.isPseudoInertial():
            transform = self.inputFrame.getTransformTo(self.inertialFrame,
                                                       initialDate)
            PV_state = transform.transformPVCoordinates(PV_state)

        initialOrbit = CartesianOrbit(
            PV_state,
            self.inertialFrame,
            initialDate,
            self.mu + float(scipy.constants.G * kwargs['m']),
        )

        self._construct_propagator(initialOrbit)
        self._set_forces(kwargs['A'], kwargs['C_D'], kwargs['C_R'])

        initialState = SpacecraftState(initialOrbit)

        self.propagator.setInitialState(initialState)

        tb_inds = t < 0.0
        t_back = t[tb_inds]

        tf_indst = t >= 0.0
        t_forward = t[tf_indst]

        if len(t_forward) == 1:
            if np.any(t_forward == 0.0):
                t_back = t
                t_forward = []
                tb_inds = t <= 0

        state = np.empty((6, len(t)), dtype=np.float)
        step_handler = Orekit.OrekitVariableStep()

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

        if self.settings['heartbeat']:
            __heartbeat = self.heartbeat
        else:
            __heartbeat = None

        if len(t_back) > 0:
            _t = t_back
            _t_order = np.argsort(np.abs(_t))
            _t_res = np.argsort(_t_order)
            _t = _t[_t_order]
            _state = np.empty((6, len(_t)), dtype=np.float)

            step_handler.set_params(_t,
                                    initialDate,
                                    _state,
                                    self.outputFrame,
                                    __heartbeat,
                                    profiler=self.profiler)

            self.propagator.setMasterMode(step_handler)

            self.propagator.propagate(initialDate.shiftedBy(float(_t[-1])))

            #now _state is full and in the order of _t
            state[:, tb_inds] = _state[:, _t_res]

        if len(t_forward) > 0:
            _t = t_forward
            _t_order = np.argsort(np.abs(_t))
            _t_res = np.argsort(_t_order)
            _t = _t[_t_order]
            _state = np.empty((6, len(_t)), dtype=np.float)
            step_handler.set_params(_t,
                                    initialDate,
                                    _state,
                                    self.outputFrame,
                                    __heartbeat,
                                    profiler=self.profiler)

            self.propagator.setMasterMode(step_handler)

            self.propagator.propagate(initialDate.shiftedBy(float(_t[-1])))

            #now _state is full and in the order of _t
            state[:, tf_indst] = _state[:, _t_res]

        if not orekit_out_frame:
            state = frames.convert(
                times,
                state,
                in_frame='GCRS',
                out_frame=self.settings['out_frame'],
                profiler=self.profiler,
                logger=self.logger,
            )

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

        if self.logger is not None:
            self.logger.debug(f'Orekit:propagate:completed')

        return state
Example #17
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
Example #18
0
VM = orekit.initVM()
setup_orekit_curdir("orekit-data.zip")

# Macro for conversion between degrees and radians
DEG_TO_RAD = np.pi / 180

# Collect orekit bodies and properties
Sun_orekit, RSun_orekit = CelestialBodyFactory.getSun(), Constants.SUN_RADIUS
# orekit requires that the attractor is of the ~OneAxisEllipsoid so its event
# detector can properly function. Therefore, the Earth is instantiated from this
# class although the flatteing factor is set to zero so it still becomes a
# perfect sphere
REarth_orekit = Constants.WGS84_EARTH_EQUATORIAL_RADIUS
Earth_orekit = OneAxisEllipsoid(
    Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
    float(0),
    FramesFactory.getITRF(IERSConventions.IERS_2010, True),
)

# The COE for the orbit to be defined
a, ecc, inc, raan, argp, nu = (6828137.0, 0.0073, 87.0, 20.0, 10.0, 0)

# Define the orkeit and poliastro orbits to be used for all the event validation
# cases in this script
epoch0_orekit = AbsoluteDate(2020, 1, 1, 0, 0, 00.000,
                             TimeScalesFactory.getUTC())
ss0_orekit = KeplerianOrbit(
    float(a),
    float(ecc),
    float(inc * DEG_TO_RAD),
    float(argp * DEG_TO_RAD),
Example #19
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
Example #20
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