propagate_many#

cysgp4.propagate_many(mjds, tles, observers=None, do_eci_pos=True, do_eci_vel=True, do_geo=True, do_topo=True, do_obs_pos=False, do_sat_azel=False, do_sat_rotmat=False, sat_frame='zxy', on_error='raise', method='dwarner')[source]#

Calculate positions of many satellites at a various times at once.

This is an array interface to the sgp4 calculations, which allows to perform calculations for different satellite TLEs, observers and times in a parallelized manner. numpy broadcasting rules apply.

With the Boolean parameters, do_eci_pos, do_eci_vel, do_geo, do_topo, do_obs_pos, and do_sat_azel the user can decide, which position frames are returned in the output dictionary.

Satellite are defined via TLEs (see PyTle). The propagate_many function works with a single (scalar) PyTle object or a list/array of them. The same is true for the mjds and optional observers parameters, which must be a scalar (a double and PyObserver instance, respectively) or a list/array of them.

As in most use cases, a large number of positions is probably queried, the returned values do not use the PyEci or PyCoordTopocentric classes, but pack everything in arrays. This is not only faster, but makes it easier to further process the results.

For parallelization, OpenMP is utilized. To change the number of CPU cores that are used, one can either set the environment variable OMP_NUM_THREADS or use set_num_threads.

Parameters:
mjdsndarray, list, or scalar of float

Modified Julian Date.

tlesndarray, list, or scalar of PyTle

TLE instance of the satellite of interest.

observersndarray, list, or scalar of PyObserver or None (default: None)

Observer instance. If None then the observer location is set to (0 deg, 0 deg, 0 km).

do_eci_posBoolean, optional (default: True)

Whether to include ECI cartesian positions in the result.

do_eci_velBoolean, optional (default: True)

Whether to include ECI cartesian velocity in the result.

do_geoBoolean, optional (default: True)

Whether to include geographic/geodetic positions in the result.

do_topoBoolean, optional (default: True)

Whether to include topocentric positions in the result.

do_obs_posBoolean, optional (default: True)

Whether to include the observer ECI position in the results.

do_sat_azelBoolean, optional (default: True)

Whether to include the observer position as seen by the satellite (distance/azimuth/elevation) in the results.

do_sat_rotmatBoolean, optional (default: False)

Whether to include the rotation matrix that converts the (moving and rotated) satellite frame (in cartesian) into cartesian ECI-aligned coordinates in the results. This can be useful for cases where the user needs to transform additional vectors between both frames (and is not only interested the observer position in the satellite frame as returned by do_sat_azel).

sat_frame‘zxy’ or ‘xyz’, optional (default: ‘zxy’)

How the moving satellite frame is defined. Two options are implemented, ‘zxy’ and ‘xyz’. If ‘zxy’ is chosen, the moving satellite frame is constructed such that the z axis is aligned with the satellite motion vector. The y axis is lies perpendicularly to the plane defined by the motion vector and the ECI zero point (aka the Earth centre). The resulting x axis, which is orthogonal to the y and z axes, is then approximately pointing towards nadir. Alternatively, if the frame is set as xyz, the x axis is the motion vector, y has the same meaning (but points into the opposite direction) and z is approximately pointing towards the nadir. The definition of the output polar angles is different for the two reference frames, see Returns.

on_errorstr, optional (either ‘raise’ or ‘coerce_to_nan’, default: ‘raise’)

If the underlying SGP C++ library throws an error (which often happens if one works with times that are strongly deviating from the TLE epoch), a Python RuntimeError is usually thrown. For batch processing, this is not always desirable. If on_error = 'coerce_to_nan' then C++ errors will be suppressed and the resulting position vectors will be converted to NaN-values instead.

Note: this option is only relevant for method='dwarner'!

methodstr, optional (either ‘dwarner’ or ‘vallado’)

This option can be used to change from the default SGP C++ library (by Daniel Warner) to the Vallado model. For the latter, the external python-sgp4 library is used. ‘vallado’ may be slower due to the additional boiler-plate code and differences in the model itself and implementation details.

Returns:
resultdict

Resulting positions for each requested frame:

  • eci_pos : ndarray of float

    Satellites ECI cartesian positions. Last dimension has length 3, one for each of x, y, and z.

  • eci_vel : ndarray of float

    Satellites ECI cartesian velicities. Last dimension has length 3, one for each of v_x, v_y, and v_z.

  • geo : ndarray of float

    Satellites Geodetic positions. Last dimension has length 3, one for each of lon, lat, and alt.

  • topo : ndarray of float

    Satellites Topocentric positions. Last dimension has length 4, one for each of az, el, dist, and dist_rate.

  • obs_pos : ndarray of float

    Observer positions in ECI frame (Cartesian). Last dimension has length 3, one for each of x, y, and z.

  • sat_azel : ndarray of float

    If sat_frame is ‘zxy’, z lies in the direction of motion, y perpendicular to the z-axis and the Earth center, x is pointing approximately towards nadir, see also sat_frame parameter description. The Observer positions in the (co-moving) satellite frame are given as azimuth, elevation in the specified reference frame, and distance (az, el, dist). az is the angle between the projection of the vector towards the Observer onto the xy-plane and the x-axis. -180 deg < az < 180 deg. el is the angle between the normal vector and the xy-plane. -90 deg < el < 90 deg.

    If sat_frame is ‘xyz’, x lies in the direction of motion, y is perpendicular to z and the Earth center, z is pointing approximately towards nadir, see also sat_frame parameter description. The Observer positions in the (moving) satellite frame are given as azimuth and polar angle in the specified reference frame, and distance (az, theta, dist). az is the angle between the projection of the vector towards the observer onto the xy-plane and the x-axis. -180 deg < az < 180 deg. theta is the angle between the normal vector and the z-axis. -90 deg < theta < 90 deg.

  • sat_rotmat : ndarray of float

    Rotation matrices which would transform a vector defined in the (moving and rotated) satellite frames (in cartesian) to the cartesian ECI-aligned basis frame. It is noted that the origin of this ECI-aligned frame is still at the satellite center.

    This can be useful for cases where the user needs to transform additional vectors between both frames (and is not only interested the observer position in the satellite frame as returned by do_sat_azel).

    Likewise, the inverse of these rotation matrices (aka the transposed) can be used to rotate any vector from ECI-aligned satellite basis frame to the satellite frame.

In all cases the first dimensions are determined by the (broadcasted) shape of the inputs mjd, tles, and observers.

Examples

The following demonstrates how to use the propagate_many function:

>>> import numpy as np
>>> from cysgp4 import PyTle, PyObserver, propagate_many
>>> from cysgp4 import get_example_tles, tles_from_text

>>> tle_text = get_example_tles()
>>> tles = np.array(
...     tles_from_text(tle_text)
...     )[np.newaxis, np.newaxis, :20]  # use first 20 TLEs
>>> observers = np.array([
...     PyObserver(6.88375, 50.525, 0.366),
...     PyObserver(16.88375, 50.525, 0.366),
...     ])[np.newaxis, :, np.newaxis]
>>> mjds = np.linspace(
...     58805.5, 58806.5, 1000  # 1000 time steps
...     )[:, np.newaxis, np.newaxis]

>>> result = propagate_many(mjds, tles, observers)
>>> print(sorted(result.keys()))
['eci_pos', 'eci_vel', 'geo', 'topo']

>>> # shapes are as follows
>>> print(np.broadcast(mjds, tles, observers).shape)
(1000, 2, 20)
>>> print(result['eci_pos'].shape, result['topo'].shape)
(1000, 2, 20, 3) (1000, 2, 20, 4)

>>> result = propagate_many(
...     mjds, tles, observers,
...     do_eci_pos=False, do_eci_vel=False, do_geo=False, do_topo=True
...     )
>>> print(sorted(result.keys()))
['topo']