Decay Analysis

The decay analysis estimates how the orbit of a satellite evolves under perturbations until it reenters the atmosphere. This information is paramount for mission design since it provides the expected orbital lifetime, which is required, for example, by space debris mitigation regulations.

We can perform the decay analysis of a satellite using the function:

decay_analysis(orb::KeplerianElements; kwargs...) -> DataFrame

It computes the orbital decay analysis of a satellite with initial mean elements orb represented in the TOD reference frame, propagating the mean orbital elements with averaged perturbations until the mean perigee altitude reaches terminate_altitude or the propagation time reaches tf.

By default, the input elements are treated as mean elements with respect to the averaged dynamics, following the same convention of semi-analytical tools such as STELA. Osculating elements, obtained, for example, from an instantaneous state vector, can be used by setting the keyword input_type to :osculating, in which case they are converted to mean elements before the propagation.

The model averages the following perturbations over one orbit: Earth gravity zonal harmonics (including a closed-form J₂² correction), third-body attraction of the Sun and the Moon, atmospheric drag using a configurable atmospheric model (NRLMSISE-00 by default), and solar radiation pressure gated by the Earth shadow.

Warning

This function only works after loading the package OrdinaryDiffEqAdamsBashforthMoulton.jl, which provides the default solver VCABM. Loading OrdinaryDiffEq.jl v6 also works because it depends on that package, but OrdinaryDiffEq.jl v7 or newer does not. In this case, the package OrdinaryDiffEqAdamsBashforthMoulton.jl must be loaded explicitly.

The following keywords are available:

  • satellite_mass::Number: Satellite mass [kg]. This keyword is required.
  • satellite_mean_area::Number: Mean cross-sectional area [m²] used for both the atmospheric drag and the solar radiation pressure. This keyword is required.
  • atmospheric_model::Any: Callable object (a function or a callable structure) that returns the atmospheric density [kg/m³] at a given location and time considering a set of space indices. It must have the signature (jd_utc::Number, lat::Number, lon::Number, alt::Number, space_indices::NamedTuple) -> Number where jd_utc is the Julian date in UTC, lat, lon, and alt are the geodetic latitude [rad], longitude [rad], and altitude [m] of the point where the density is evaluated, and space_indices is the named tuple with the space indices at that instant provided by the keyword space_indices. If it is nothing, the system uses an internal wrapper for the NRLMSISE-00 model provided by AtmosphericModels.jl, which requires the fields f107 (daily 10.7 cm solar flux) [sfu], f107_avg (81-day average of the 10.7 cm solar flux) [sfu], and ap (daily geomagnetic index) [-] in the named tuple. The macros @decay_analysis__jacchia77, @decay_analysis__jacchia77_stela, and @decay_analysis__jr1971 provide keyword sets that select the Jacchia 1977 (report and STELA variants) and the Jacchia-Roberts 1971 models instead (see Using the Jacchia Models). (Default: nothing)
  • atmospheric_model_name::Union{Nothing, String}: Name of the atmospheric model recorded in the metadata Atmospheric Model of the output DataFrame and shown, for example, by plot_decay_analysis. If it is nothing, the name is derived from the keyword atmospheric_model: "NRLMSISE-00" for the default model and "Custom (<name>)" for user-provided callables. (Default: nothing)
  • gravity_model::Union{AbstractGravityModel, Nothing}: Gravity model used to compute the Earth gravitational perturbation. If it is nothing, the system fetches and loads the EGM96 model. (Default: nothing)
  • input_type::Symbol: How the input elements orb are interpreted. If it is :mean, they are treated as mean elements with respect to the averaged dynamics. If it is :osculating, they are treated as osculating elements and converted to mean elements before the propagation. Any other symbol raises an ArgumentError. (Default: :mean)
  • num_sampling_points_per_orbit::Int: Number of sampling points used to average the perturbations over one orbit. (Default: 17)
  • abstol::Number: Absolute tolerance of the numerical integration. (Default: 1e-6)
  • C_d::Number: Drag coefficient [-]. (Default: 2.2)
  • C_r::Number: Solar radiation pressure coefficient [-]. (Default: 1.25)
  • distance_unit::Symbol: Unit of the altitude columns in the output DataFrame. It can be :m for meters or :km for kilometers. (Default: :km)
  • space_indices::Any: Space indices required by the atmospheric model. It can be a constant NamedTuple used for all instants or a callable object of time (in Julian days) that returns the named tuple with the space indices at that instant: (jd_utc::Number) -> NamedTuple. If it is nothing, the system provides the named tuple (f107 = ..., f107_avg = ..., ap = ...) required by the default atmospheric model using the data in SpaceIndices.jl: the observed daily F10.7 of the previous day (space index F10obs), as prescribed by the NRLMSISE-00 documentation, the observed centered 81-day average F10.7 (space index F10obs_avg_center81), and the observed daily geomagnetic index (space index Ap_daily). Outside the observed timespans, the F10.7 values fall back to the predicted observed F10.7 (space index F10obs_predicted), which is a harmonic model fitted to the observed data that captures the mean solar cycle behavior, and the geomagnetic index falls back to Ap = 9, as in STELA. In this case, the required space index sets are initialized automatically, downloading the data files on first use. Notice that the F10.7 prediction is intended for long-term analyses and must not be used as a short-term forecast of the solar activity. (Default: nothing)
  • reltol::Number: Relative tolerance of the numerical integration. (Default: 1e-6)
  • return_solution::Bool: If true, the function also returns the raw solution of the numerical integration (see SciMLBase.ODESolution), whose state vector is the equinoctial orbital elements [a, ψ, e_x, e_y, i_x, i_y]. (Default: false)
  • solver: Solver from the OrdinaryDiffEq.jl ecosystem used for the numerical integration. Notice that the user must load the package that provides the selected solver (for example, Tsit5 requires OrdinaryDiffEqTsit5.jl or OrdinaryDiffEq.jl). (Default: VCABM())
  • terminate_altitude::Number: Mean perigee altitude [m] that terminates the analysis. (Default: 120e3)
  • tf::Number: Maximum propagation time [s] after the orbit epoch. (Default: 30 * 365.25 * 86400, or 30 years)
  • time_unit::Symbol: Unit of the column time in the output DataFrame. It can be :s for seconds, :m for minutes, :h for hours, :d for days, or :y for Julian years (365.25 days). (Default: :y)
  • verbose::Bool: If true, a progress interface is shown in stderr during the numerical integration. In interactive terminals, a live panel shows a progress bar, the current mean perigee and apogee altitudes, the elapsed model time, and the elapsed wall time; at the end, the panel is left on the screen with the final state and a summary line is printed below it. Otherwise, plain progress lines are printed at every 5%, followed by the summary line. The progress fraction is the maximum between the time fraction and the perigee descent fraction, so it reaches 100% at either termination condition. Enabling the interface does not change the analysis result. (Default: false)

The function returns a DataFrame with the mean orbital element evolution during the decay with the columns:

  • date: Date and time of each point [UTC] encoded using DateTime.
  • time: Elapsed time of each point since the beginning of the analysis [time_unit].
  • space_indices: Named tuple with the space indices used by the dynamics at each point. This column has no unit metadata since its fields have heterogeneous units. The default source provides the fields f107 [sfu], f107_avg [sfu], and ap [-].
  • mean_elements: Mean Keplerian elements encoded using KeplerianElements [SI], where the epoch is the point date [UTC].
  • apogee_altitude: Mean apogee altitude [distance_unit].
  • perigee_altitude: Mean perigee altitude [distance_unit].

The unit of each column is stored in the DataFrame using metadata. The DataFrame also stores the table-level metadata Satellite Mass [kg], Satellite Mean Area [m²], Terminate Altitude [m], Drag Coefficient [-], SRP Coefficient [-], Atmospheric Model, and Space Indices Source, which are used, for example, by the function plot_decay_analysis.

The satellite lifetime can be obtained from the last row of the returned DataFrame: if the perigee altitude reached terminate_altitude before tf, the last date is the decay epoch estimation.

Examples

We will estimate the orbital lifetime of a 100 kg satellite with a mean cross-sectional area of 1 m² in a Sun-synchronous orbit with an altitude of 300 km. The first thing we need to do is define the orbit:

julia> jd₀ = date_to_jd(2024, 1, 1)2.4603105e6
julia> orb = KeplerianElements( jd₀, EARTH_EQUATORIAL_RADIUS + 300e3, 0.001, 98.0 |> deg2rad, ltdn_to_raan(10.5, jd₀), 90 |> deg2rad, 0 )KeplerianElements{Float64, Float64}: Epoch : 2.46031e6 (2024-01-01T00:00:00) Semi-major axis : 6678.14 km Eccentricity : 0.001 Inclination : 98.0 ° RAAN : 77.684 ° Arg. of Perigee : 90.0 ° True Anomaly : 0.0 °

Now, we can use the function decay_analysis to obtain the orbit evolution until the reentry. Notice that we only need to provide the satellite mass and mean area: the space indices default to the observed and predicted values provided by SpaceIndices.jl, and the system fetches the EGM96 gravity model automatically:

julia> df = decay_analysis(orb; satellite_mass = 100.0, satellite_mean_area = 1.0)40×6 DataFrame
 Row  date                     time         space_indices                          DateTime                 Float64      NamedTuple…                       ⋯
─────┼──────────────────────────────────────────────────────────────────────────
   1 │ 2024-01-01T00:00:00      0.0          (f107 = 139.7, f107_avg = 162.5,… ⋯
   2 │ 2024-01-01T00:46:44.486  8.88688e-5   (f107 = 139.7, f107_avg = 162.5,…
   3 │ 2024-01-01T01:30:14.701  0.000171582  (f107 = 139.7, f107_avg = 162.5,…
   4 │ 2024-01-01T02:40:18.546  0.000304793  (f107 = 139.7, f107_avg = 162.5,…
   5 │ 2024-01-01T04:22:10.075  0.000498456  (f107 = 139.7, f107_avg = 162.5,… ⋯
   6 │ 2024-01-01T16:51:27.455  0.00192307   (f107 = 146.2, f107_avg = 162.8,…
   7 │ 2024-01-02T07:18:07.712  0.00357086   (f107 = 146.2, f107_avg = 162.8,…
   8 │ 2024-01-02T20:18:07.944  0.00505387   (f107 = 135.7, f107_avg = 162.7,…
  ⋮  │            ⋮                  ⋮                       ⋮                 ⋱
  34 │ 2024-01-22T21:26:13.137  0.0599403    (f107 = 178.5, f107_avg = 166.3,… ⋯
  35 │ 2024-01-23T04:10:09.120  0.0607083    (f107 = 178.5, f107_avg = 166.3,…
  36 │ 2024-01-23T09:24:30.384  0.061306     (f107 = 196.1, f107_avg = 166.4,…
  37 │ 2024-01-23T12:15:26.545  0.061631     (f107 = 196.1, f107_avg = 166.4,…
  38 │ 2024-01-23T13:48:40.685  0.0618083    (f107 = 196.1, f107_avg = 166.4,… ⋯
  39 │ 2024-01-23T14:29:17.948  0.0618855    (f107 = 196.1, f107_avg = 166.4,…
  40 │ 2024-01-23T14:29:17.948  0.0618855    (f107 = 196.1, f107_avg = 166.4,…
                                                   3 columns and 25 rows omitted

The estimated decay epoch is the date of the last point:

julia> df[end, :date]2024-01-23T14:29:17.948

We can also provide constant space indices, which is useful, for example, to analyze worst-case scenarios with high solar activity:

julia> df_high = decay_analysis(
           orb;
           satellite_mass = 100.0,
           satellite_mean_area = 1.0,
           space_indices = (f107 = 250.0, f107_avg = 250.0, ap = 9.0)
       )31×6 DataFrame
 Row  date                     time         space_indices                          DateTime                 Float64      NamedTuple…                       ⋯
─────┼──────────────────────────────────────────────────────────────────────────
   1 │ 2024-01-01T00:00:00      0.0          (f107 = 250.0, f107_avg = 250.0,… ⋯
   2 │ 2024-01-01T00:28:21.041  5.39027e-5   (f107 = 250.0, f107_avg = 250.0,…
   3 │ 2024-01-01T00:54:49.429  0.000104236  (f107 = 250.0, f107_avg = 250.0,…
   4 │ 2024-01-01T01:37:00.670  0.000184446  (f107 = 250.0, f107_avg = 250.0,…
   5 │ 2024-01-01T02:37:30.147  0.000299457  (f107 = 250.0, f107_avg = 250.0,… ⋯
   6 │ 2024-01-01T12:42:24.922  0.00144957   (f107 = 250.0, f107_avg = 250.0,…
   7 │ 2024-01-01T21:46:50.218  0.00248467   (f107 = 250.0, f107_avg = 250.0,…
   8 │ 2024-01-02T08:00:08.309  0.00365073   (f107 = 250.0, f107_avg = 250.0,…
  ⋮  │            ⋮                  ⋮                       ⋮                 ⋱
  25 │ 2024-01-13T06:36:12.492  0.0336075    (f107 = 250.0, f107_avg = 250.0,… ⋯
  26 │ 2024-01-13T12:58:02.976  0.0343335    (f107 = 250.0, f107_avg = 250.0,…
  27 │ 2024-01-13T16:27:41.344  0.0347321    (f107 = 250.0, f107_avg = 250.0,…
  28 │ 2024-01-13T18:26:31.069  0.034958     (f107 = 250.0, f107_avg = 250.0,…
  29 │ 2024-01-13T19:26:54.780  0.0350728    (f107 = 250.0, f107_avg = 250.0,… ⋯
  30 │ 2024-01-13T19:33:55.519  0.0350862    (f107 = 250.0, f107_avg = 250.0,…
  31 │ 2024-01-13T19:33:55.519  0.0350862    (f107 = 250.0, f107_avg = 250.0,…
                                                   3 columns and 16 rows omitted
julia> df_high[end, :date]2024-01-13T19:33:55.519

Using the Jacchia Models

The macros @decay_analysis__jacchia77, @decay_analysis__jacchia77_stela, and @decay_analysis__jr1971 provide keyword sets that select the Jacchia 1977 (report and STELA variants) and the Jacchia-Roberts 1971 atmospheric models provided by AtmosphericModels.jl instead of the default NRLMSISE-00. Each macro expands to the keywords atmospheric_model, atmospheric_model_name, and space_indices, hence it must be used in the keyword section of the call:

julia> df_j77 = decay_analysis(
           orb;
           satellite_mass = 100.0,
           satellite_mean_area = 1.0,
           @decay_analysis__jacchia77
       )40×6 DataFrame
 Row  date                     time         space_indices                          DateTime                 Float64      NamedTuple…                       ⋯
─────┼──────────────────────────────────────────────────────────────────────────
   1 │ 2024-01-01T00:00:00      0.0          (f107 = 141.4, f107_avg = 157.6,… ⋯
   2 │ 2024-01-01T00:52:30.482  9.98328e-5   (f107 = 141.4, f107_avg = 157.6,…
   3 │ 2024-01-01T01:41:23.539  0.000192776  (f107 = 141.4, f107_avg = 157.6,…
   4 │ 2024-01-01T03:00:18.400  0.000342814  (f107 = 141.4, f107_avg = 157.6,…
   5 │ 2024-01-01T04:55:20.390  0.000561525  (f107 = 141.4, f107_avg = 157.6,… ⋯
   6 │ 2024-01-01T21:49:50.179  0.00249037   (f107 = 131.2, f107_avg = 157.9,…
   7 │ 2024-01-02T10:22:04.143  0.00392058   (f107 = 137.4, f107_avg = 157.8,…
   8 │ 2024-01-02T21:56:19.247  0.00524055   (f107 = 137.4, f107_avg = 157.8,…
  ⋮  │            ⋮                  ⋮                       ⋮                 ⋱
  34 │ 2024-01-25T01:43:33.140  0.0659053    (f107 = 166.6, f107_avg = 161.5,… ⋯
  35 │ 2024-01-25T05:16:42.588  0.0663106    (f107 = 166.6, f107_avg = 161.5,…
  36 │ 2024-01-25T07:48:53.245  0.0665999    (f107 = 166.6, f107_avg = 161.5,…
  37 │ 2024-01-25T09:06:21.574  0.0667472    (f107 = 155.6, f107_avg = 161.5,…
  38 │ 2024-01-25T09:48:09.520  0.0668267    (f107 = 155.6, f107_avg = 161.5,… ⋯
  39 │ 2024-01-25T10:02:39.253  0.0668542    (f107 = 155.6, f107_avg = 161.5,…
  40 │ 2024-01-25T10:02:39.253  0.0668542    (f107 = 155.6, f107_avg = 161.5,…
                                                   3 columns and 25 rows omitted
julia> df_j77[end, :date]2024-01-25T10:02:39.253

The Jacchia models consume the space indices f107 (daily 10.7 cm solar flux) [sfu], f107_avg (81-day average of the 10.7 cm solar flux) [sfu], and kp (daily geomagnetic index Kp) [-]. The Jacchia models were derived using the F10.7 flux adjusted to 1 AU, unlike NRLMSISE-00, which uses the observed flux at the actual Earth-Sun distance. Hence, the default space indices source selected by the macros provides the adjusted F10.7 values and the observed Kp (space indices F10adj, F10adj_avg_center81, and Kp_daily), falling back to the predicted adjusted F10.7 (space index F10adj_predicted) and to Kp = 7 / 3 (equivalent to Ap = 9, as in STELA) outside the available timespans. Keywords passed after the macro override the ones it provides, so we can, for example, use the Jacchia 1977 model with constant space indices:

julia> df_j77_high = decay_analysis(
           orb;
           satellite_mass = 100.0,
           satellite_mean_area = 1.0,
           @decay_analysis__jacchia77,
           space_indices = (f107 = 250.0, f107_avg = 250.0, kp = 3.0)
       )25×6 DataFrame
 Row  date                     time         space_indices                          DateTime                 Float64      NamedTuple…                       ⋯
─────┼──────────────────────────────────────────────────────────────────────────
   1 │ 2024-01-01T00:00:00      0.0          (f107 = 250.0, f107_avg = 250.0,… ⋯
   2 │ 2024-01-01T00:32:05.790  6.10246e-5   (f107 = 250.0, f107_avg = 250.0,…
   3 │ 2024-01-01T01:02:01.534  0.000117928  (f107 = 250.0, f107_avg = 250.0,…
   4 │ 2024-01-01T01:49:50.816  0.00020885   (f107 = 250.0, f107_avg = 250.0,…
   5 │ 2024-01-01T02:58:40.230  0.000339704  (f107 = 250.0, f107_avg = 250.0,… ⋯
   6 │ 2024-01-01T13:53:44.324  0.00158518   (f107 = 250.0, f107_avg = 250.0,…
   7 │ 2024-01-01T23:43:18.009  0.0027061    (f107 = 250.0, f107_avg = 250.0,…
   8 │ 2024-01-02T10:40:46.349  0.00395614   (f107 = 250.0, f107_avg = 250.0,…
  ⋮  │            ⋮                  ⋮                       ⋮                 ⋱
  19 │ 2024-01-15T06:54:42.020  0.0391184    (f107 = 250.0, f107_avg = 250.0,… ⋯
  20 │ 2024-01-15T15:14:37.783  0.0400689    (f107 = 250.0, f107_avg = 250.0,…
  21 │ 2024-01-15T19:23:55.058  0.0405429    (f107 = 250.0, f107_avg = 250.0,…
  22 │ 2024-01-15T21:40:21.958  0.0408023    (f107 = 250.0, f107_avg = 250.0,…
  23 │ 2024-01-15T22:48:53.964  0.0409326    (f107 = 250.0, f107_avg = 250.0,… ⋯
  24 │ 2024-01-15T23:08:19.314  0.0409695    (f107 = 250.0, f107_avg = 250.0,…
  25 │ 2024-01-15T23:08:19.314  0.0409695    (f107 = 250.0, f107_avg = 250.0,…
                                                   3 columns and 10 rows omitted
julia> df_j77_high[end, :date]2024-01-15T23:08:19.314
Note

The Jacchia 1977 model does not have a closed-form solution, so its equations are numerically integrated at every density evaluation, making the analysis considerably slower than with the default NRLMSISE-00 model. The Jacchia-Roberts 1971 model, selected by @decay_analysis__jr1971, is a closed-form analytic fit of the Jacchia model family with speed comparable to NRLMSISE-00:

julia> df_jr71 = decay_analysis(
           orb;
           satellite_mass = 100.0,
           satellite_mean_area = 1.0,
           @decay_analysis__jr1971
       )43×6 DataFrame
 Row  date                     time         space_indices                          DateTime                 Float64      NamedTuple…                       ⋯
─────┼──────────────────────────────────────────────────────────────────────────
   1 │ 2024-01-01T00:00:00      0.0          (f107 = 141.4, f107_avg = 157.6,… ⋯
   2 │ 2024-01-01T00:49:15.160  9.36434e-5   (f107 = 141.4, f107_avg = 157.6,…
   3 │ 2024-01-01T01:35:05.788  0.000180806  (f107 = 141.4, f107_avg = 157.6,…
   4 │ 2024-01-01T02:49:00.420  0.000321331  (f107 = 141.4, f107_avg = 157.6,…
   5 │ 2024-01-01T04:36:35.624  0.000525884  (f107 = 141.4, f107_avg = 157.6,… ⋯
   6 │ 2024-01-01T22:32:27.666  0.00257141   (f107 = 131.2, f107_avg = 157.9,…
   7 │ 2024-01-02T14:40:44.504  0.00441239   (f107 = 137.4, f107_avg = 157.8,…
   8 │ 2024-01-03T06:29:14.777  0.00621577   (f107 = 137.4, f107_avg = 157.8,…
  ⋮  │            ⋮                  ⋮                       ⋮                 ⋱
  37 │ 2024-01-24T22:38:19.881  0.0655531    (f107 = 166.6, f107_avg = 161.5,… ⋯
  38 │ 2024-01-25T01:45:38.450  0.0659093    (f107 = 166.6, f107_avg = 161.5,…
  39 │ 2024-01-25T03:29:38.392  0.066107     (f107 = 166.6, f107_avg = 161.5,…
  40 │ 2024-01-25T04:31:11.095  0.066224     (f107 = 166.6, f107_avg = 161.5,…
  41 │ 2024-01-25T05:04:24.586  0.0662872    (f107 = 166.6, f107_avg = 161.5,… ⋯
  42 │ 2024-01-25T05:09:38.546  0.0662971    (f107 = 166.6, f107_avg = 161.5,…
  43 │ 2024-01-25T05:09:38.546  0.0662971    (f107 = 166.6, f107_avg = 161.5,…
                                                   3 columns and 28 rows omitted
julia> df_jr71[end, :date]2024-01-25T05:09:38.546

The macro @decay_analysis__jacchia77_stela selects the STELA variant of the Jacchia 1977 model, which replicates the simplified assembly used by the CNES tools STELA and PATRIUS. This variant produces total densities a few percent higher on average than the report formulation, allowing the reproduction of decay analyses performed with those tools: the decay time of a 500 km sun-synchronous satellite computed by STELA is reproduced within about 1 %, whereas the report formulation yields a decay time about 7 % longer:

julia> df_j77_stela = decay_analysis(
           orb;
           satellite_mass = 100.0,
           satellite_mean_area = 1.0,
           @decay_analysis__jacchia77_stela
       )41×6 DataFrame
 Row  date                     time         space_indices                          DateTime                 Float64      NamedTuple…                       ⋯
─────┼──────────────────────────────────────────────────────────────────────────
   1 │ 2024-01-01T00:00:00      0.0          (f107 = 141.4, f107_avg = 157.6,… ⋯
   2 │ 2024-01-01T00:50:01.642  9.51163e-5   (f107 = 141.4, f107_avg = 157.6,…
   3 │ 2024-01-01T01:36:35.641  0.000183653  (f107 = 141.4, f107_avg = 157.6,…
   4 │ 2024-01-01T02:51:41.559  0.000326437  (f107 = 141.4, f107_avg = 157.6,…
   5 │ 2024-01-01T04:41:03.195  0.000534362  (f107 = 141.4, f107_avg = 157.6,… ⋯
   6 │ 2024-01-01T18:34:03.541  0.00211814   (f107 = 131.2, f107_avg = 157.9,…
   7 │ 2024-01-02T07:03:45.852  0.00354355   (f107 = 131.2, f107_avg = 157.9,…
   8 │ 2024-01-02T19:09:27.508  0.0049233    (f107 = 137.4, f107_avg = 157.8,…
  ⋮  │            ⋮                  ⋮                       ⋮                 ⋱
  35 │ 2024-01-24T16:04:19.135  0.064804     (f107 = 166.6, f107_avg = 161.5,… ⋯
  36 │ 2024-01-24T21:40:49.597  0.0654438    (f107 = 166.6, f107_avg = 161.5,…
  37 │ 2024-01-25T00:37:45.126  0.0657802    (f107 = 166.6, f107_avg = 161.5,…
  38 │ 2024-01-25T02:24:33.518  0.0659833    (f107 = 166.6, f107_avg = 161.5,…
  39 │ 2024-01-25T03:26:51.048  0.0661017    (f107 = 166.6, f107_avg = 161.5,… ⋯
  40 │ 2024-01-25T03:56:57.774  0.066159     (f107 = 166.6, f107_avg = 161.5,…
  41 │ 2024-01-25T03:56:57.774  0.066159     (f107 = 166.6, f107_avg = 161.5,…
                                                   3 columns and 26 rows omitted
julia> df_j77_stela[end, :date]2024-01-25T03:56:57.774

Plotting

If the user loads the package Makie.jl, an extension is loaded and adds the possibility to plot the decay analysis using the function plot_decay_analysis. The figure shows the evolution of the mean apogee and perigee altitudes, a dashed line marking the terminate altitude, and an annotation marking the reentry. The keyword show_dates adds the absolute dates [UTC] to the figure: the analysis timespan in the subtitle and the estimated reentry date in the information panel and in the reentry annotation. The information panel shows the satellite mass, the mean area, the time to reenter, and a card with the analysis assumptions (atmospheric model and drag and SRP coefficients), all resolved from the DataFrame metadata. The keyword show_f107 also plots the daily and the 81-day average 10.7 cm solar flux indices used by the dynamics using a twin y-axis. The values are extracted from the column space_indices using the keywords f107_getter and f107_avg_getter, whose defaults match the named tuple provided by the default space indices source; passing nothing to a getter omits the related curve:

using CairoMakie


fig, ax = plot_decay_analysis(
    df;
    mission_name = "My Mission",
    show_dates   = true,
    show_f107    = true
)

fig
Example block output

To export the figure in high resolution for reports, use:

save("decay_analysis.png", fig; px_per_unit = 2)