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
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
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()))
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
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)
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)
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
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
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)
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()
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)
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 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
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
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),
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
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