asteRisk
Rafael Ayala, Daniel Ayala, Lara Sellés Vidal
March 21, 2021
Unlike positional information of planes and other aircrafts, satellite positions is not readily available for any timepoint along its orbit. Instead, orbital state vectors at discrete and relatively scarce (compared to, for example, the large information density available for aircrafts) timepoints and generated from multiple observations are generated by entities with access to such information, such as the United States Space Surveillance Network. Unclassified information can be obtained through resources such as Space-Track and CelesTrak, most commonly in the form of TLE (Two/Three Line Elements).
However, due to the periodicity of the trajectories (orbits) followed by satellites, if the state vector of an object orbiting Earth is known at a given time, it is possible to predict their state vectors at future or past times. In order to accurately perform such predictions, known as orbit propagation, it is necessary to use complex models that take into consideration not only the gravitational attraction of Earth, but also atmospheric drag and secular and periodic perturbations of the Moon and the Sun, among other effects.
The most widely applied models are the SGP4 and SDP4 models, whose first implementations were introduced in FORTRAN IV in 1988. asteRisk aims to constitute the basis for astrodynamics analysis in R. To that extent, it provides a native R implementation of the SGP4 and SDP4 models and utilities to parse and read TLE files and to convert coordinates between different reference frames.
Before installing asteRisk, make sure you have the latest version of R installed. To install asteRisk, start R and enter:
install.packages("asteRisk")
Once installed, the package can be loaded as shown below:
library(asteRisk)
TLE (Two-/Three- Line Element) is the standard format for representing orbital state vectors. In short, TLEs have 2 lines that contain the orbital parameters that characterize the state of a satellite at a given time, known as epoch. An additional initial line can be present to indicate the name of the satellite. A detailed description of the TLE format can be found here.
asteRisk provides the utilities to read TLE files (function readTLE()), and to directly parse character vectors containing the lines of a TLE as strings (function parseTLElines()). The resulting lists contain the NORAD catalog number of the satellite, the classification level of the information, its international designator, launch year, launch number, launch piece, the date and time of the state vector, the orbital parameters (angular speed, eccentricity, inclination, argument of the perigee, longitude of the ascending node, anomaly) and the drag coefficient of the satellite.
A file with a set of 29 TLE, provided in Revisiting Space Track Report #3 and typically used to benchmark implementations of the SGP4/SDP4 propagators, is distributed with asteRisk, in file named testTLE.txt:
# Read the provided file with 29 benchmark TLE, which contains objects with a
# variety of orbital parameters
test_TLEs <- readTLE(paste0(path.package("asteRisk"), "/testTLE.txt"))
# TLE number 17 contains a state vectors for Italsat 2
test_TLEs[[17]]
## $NORADcatalogNumber
## [1] "24208"
##
## $classificationLevel
## [1] "unclassified"
##
## $internationalDesignator
## [1] "1996-044A"
##
## $launchYear
## [1] 1996
##
## $launchNumber
## [1] "044"
##
## $launchPiece
## [1] "A"
##
## $dateTime
## [1] "2006-06-26 0:58:29.3433600001845"
##
## $elementNumber
## [1] 160
##
## $inclination
## [1] 3.8536
##
## $ascension
## [1] 80.0121
##
## $eccentricity
## [1] 0.002664
##
## $perigeeArgument
## [1] 311.0977
##
## $meanAnomaly
## [1] 48.3
##
## $meanMotion
## [1] 1.007781
##
## $meanMotionDerivative
## [1] -1.88e-06
##
## $meanMotionSecondDerivative
## [1] 0
##
## $Bstar
## [1] 1e-04
##
## $ephemerisType
## [1] "Distributed data (SGP4/SDP4)"
##
## $epochRevolutionNumber
## [1] 3611
##
## $objectName
## [1] "# ITALSAT 2 # 24h resonant GEO, inclination > 3 deg"
# It is also possible to directly parse a character vector with 2 or 3 elements,
# where each element is a string representing a line of the TLE, to obtain the
# same result:
italsat2_lines <- c("ITALSAT 2", "1 24208U 96044A 06177.04061740 -.00000094 00000-0 10000-3 0 1600",
"2 24208 3.8536 80.0121 0026640 311.0977 48.3000 1.00778054 36119")
italsat2_TLE <- parseTLElines(italsat2_lines)
italsat2_TLE
## $NORADcatalogNumber
## [1] "24208"
##
## $classificationLevel
## [1] "unclassified"
##
## $internationalDesignator
## [1] "1996-044A"
##
## $launchYear
## [1] 1996
##
## $launchNumber
## [1] "044"
##
## $launchPiece
## [1] "A"
##
## $dateTime
## [1] "2006-06-26 0:58:29.3433600001845"
##
## $elementNumber
## [1] 160
##
## $inclination
## [1] 3.8536
##
## $ascension
## [1] 80.0121
##
## $eccentricity
## [1] 0.002664
##
## $perigeeArgument
## [1] 311.0977
##
## $meanAnomaly
## [1] 48.3
##
## $meanMotion
## [1] 1.007781
##
## $meanMotionDerivative
## [1] -1.88e-06
##
## $meanMotionSecondDerivative
## [1] 0
##
## $Bstar
## [1] 1e-04
##
## $ephemerisType
## [1] "Distributed data (SGP4/SDP4)"
##
## $epochRevolutionNumber
## [1] 3611
##
## $objectName
## [1] "ITALSAT 2"
test_TLEs[[17]]$inclination == italsat2_TLE$inclination
## [1] TRUE
The two orbit propagators currently available in asteRisk are the SGP4 and SDP4 models. They allow the calculation of the position and velocity of the satellite at different times, both before and after the time corresponding to the known state vector (referred to as “epoch”). Kepler’s equation is solved through fixed-point integration. It should be noted that the SGP4 model can only accurately propagate the orbit of objects near Earth (with an orbital period shorter than 225 minutes, corresponding approximately to an altitude lower than 5877.5 km).
For propagation of objects in deep space (with orbital periods larger than 225 minutes, corresponding to altitudes higher than 5877.5 km), the SDP4 model should be used, which contains additions to take into account the secular and periodic perturbations of the Moon and the Sun on the orbit of the satellite. It also considers Earth resonance effects on 24-hour geostationary and 12-hour Molniya orbits.
However, it should be noted that SDP4 employs only simplified drag equations, and lacks corrections for low-perigee orbits. Therefore, it is recommended to apply the standard SGP4 model for satellites that are not in deep space.
asteRisk provides three functions to apply the SGP4/SDP4 propagators. The sgdp4() function automatically determines if the satellite is in deep space or near Earth, and applies the appropriate model. The application of either SGP4 or SDP4 can be forced with the sgp4() and sdp4() functions respectively. However, it is not recommended to apply SGP4 to objects in deep space or SDP4 to objects near Earth. In the following example, we calculate and visualize the trajectory of a satellite with a high-eccentricity Molniya orbit:
# Element 11 of the set of test TLE contains an orbital state vector for
# satellite MOLNIYA 1-83, launched from the USSR in 1992 and decayed in 2007
molniya <- test_TLEs[[11]]
1/molniya$meanMotion
## [1] 0.496845
# From the inverse of the mean motion, we can see that the orbital period is
# approximately half a day, in accordance with a Molniya orbit Let´s use the SDP4
# model to calculate the position and velocity of the satellite for a full
# orbital period every 10 minutes. It is important to provide the mean motion in
# radians/min, the inclination, anomaly, argument of perigee and longitude of the
# ascending node in radians, and the target time as an increment in minutes for
# the epoch time
targetTimes <- seq(0, 720, by = 10)
results_position_matrix <- matrix(nrow = length(targetTimes), ncol = 3)
results_velocity_matrix <- matrix(nrow = length(targetTimes), ncol = 3)
for (i in 1:length(targetTimes)) {
new_result <- sgdp4(n0 = molniya$meanMotion * ((2 * pi)/(1440)), e0 = molniya$eccentricity,
i0 = molniya$inclination * pi/180, M0 = molniya$meanAnomaly * pi/180, omega0 = molniya$perigeeArgument *
pi/180, OMEGA0 = molniya$ascension * pi/180, Bstar = molniya$Bstar, initialDateTime = molniya$dateTime,
targetTime = targetTimes[i])
results_position_matrix[i, ] <- new_result[[1]]
results_velocity_matrix[i, ] <- new_result[[2]]
}
last_molniya_propagation <- new_result
results_position_matrix = cbind(results_position_matrix, targetTimes)
colnames(results_position_matrix) <- c("x", "y", "z", "time")
# Let´s verify that the SDP4 algorithm was automatically chosen
last_molniya_propagation$algorithm
## [1] "sdp4"
# We can visualize the resulting trajectory using a plotly animation to confirm
# that indeed a full revolution was completed and that the orbit is highly
# eccentric.
library(plotly)
library(lazyeval)
library(dplyr)
# In order to create the animation, we must first define a function to create the
# accumulated dataframe required for the animation
accumulate_by <- function(dat, var) {
var <- f_eval(var, dat)
lvls <- plotly:::getLevels(var)
dats <- lapply(seq_along(lvls), function(x) {
cbind(dat[var %in% lvls[seq(1, x)], ], frame = lvls[[x]])
})
bind_rows(dats)
}
accumulated_df <- accumulate_by(as.data.frame(results_position_matrix), ~time)
orbit_animation <- plot_ly(accumulated_df, x = ~x, y = ~y, z = ~z, type = "scatter3d",
mode = "marker", opacity = 0.8, line = list(width = 6, color = ~time, reverscale = FALSE),
frame = ~frame)
orbit_animation <- animation_opts(orbit_animation, frame = 50)
orbit_animation <- layout(orbit_animation, scene = list(xaxis = list(range = c(min(results_position_matrix[,
1]), max(results_position_matrix[, 1]))), yaxis = list(range = c(min(results_position_matrix[,
2]), max(results_position_matrix[, 2]))), zaxis = list(range = c(min(results_position_matrix[,
3]), max(results_position_matrix[, 3])))))
orbit_animation
The positions and velocities calculated with the SGP4 and SDP4 models are in the TEME (True Equator, Mean Equinox) frame of reference, which is an Earth-centered inertial coordinate frame, where the origin is placed at the center of mass of Earth and the coordinate frame is fixed with respect to the stars (and therefore not fixed with respect to the Earth surface in its rotation).
asteRisk provides the TEMEtoECEF() function, which converts positions and velocities in TEME to the ECEF (Earth Centered, Earth Fixed) frame of reference. In the ECEF frame, the origin is also placed at the center of mass of Earth, but the frame rotates with respect to the stars to remain fixed with respect to the Earth surface as it rotates.
Additionally, the TEMEtoLATLON() and ECEFtoLATLON() functions convert Cartesian coordinates to geodetic latitude, longitude and altitude values, from the TEME and ECEF frames respectively. This can be useful, for example, to visualize the ground track followed by a satellite.
# Let us convert the last propagation previously calculated for the MOLNIYA 1-83
# satellite into the ECEF frame. In order to do so, it is required to provide a
# date-time string indicating the time for the newly calculated position and
# velocity. Since this was 720 minutes after the epoch for the original state
# vector, we can just add 12 hours to it
molniya$dateTime
## [1] "2006-06-25 0:33:42.8348159988855"
new_dateTime <- "2006-06-25 12:33:43"
ecef_coordinates <- TEMEtoECEF(last_molniya_propagation$position, last_molniya_propagation$velocity,
new_dateTime)
# Let us now convert the previously calculated set of TEME coordinates to
# geodetic latitude and longitude
geodetic_matrix <- matrix(nrow = nrow(results_position_matrix), ncol = 3)
for (i in 1:nrow(geodetic_matrix)) {
new_dateTime <- as.character(as.POSIXct(molniya$dateTime, tz = "UTC") + 60 *
targetTimes[i])
new_geodetic <- TEMEtoLATLON(results_position_matrix[i, 1:3], new_dateTime)
geodetic_matrix[i, ] <- new_geodetic
}
colnames(geodetic_matrix) <- c("latitude", "longitude", "altitude")
# We can then visualize the ground track of the satellite
library(ggmap)
ggmap(get_map(c(left = -180, right = 180, bottom = -80, top = 80))) + geom_segment(data = as.data.frame(geodetic_matrix),
aes(x = longitude, y = latitude, xend = c(tail(longitude, n = -1), NA), yend = c(tail(latitude,
n = -1), NA)), na.rm = TRUE) + geom_point(data = as.data.frame(geodetic_matrix),
aes(x = longitude, y = latitude), color = "blue", size = 0.3, alpha = 0.8)