SGP4/SDP4

This package implements the interface to the SGP4/SDP4 propagator provided by SatelliteToolboxSgp4.jl.

Algorithm

The SGP4/SDP4 implementation was built using [1, 2, 3].

Initialization

We must initialize the SGP4/SDP4 propagator with a two-line element set (TLE) using the following function:

Propagators.init(Val(:SGP4), tle::TLE; kwargs...) -> OrbitPropagatorSgp4

which creates a SGP4/SDP4 propagator structure OrbitPropagatorSgp4 with the tle. The following keyword selects the gravitational constants for the propagation algorithm:

  • sgp4c::Sgp4Constants: SGP4 orbit propagator constants (see Sgp4Constants). (Default: SGP4C_WGS84)

The package [SatelliteToolboxSgp4.jl] contains some pre-built constants for this propagator:

SGP4/SDP4 Propagator ConstantsDescriptionType
SGP4C_WGS84Constants based on WGS-84Float64
SGP4C_WGS72Constants based on WGS-72Float64
Note

The type used in the propagation will be the same as used to define the constants in the structure sgp4c. The constants in another number type can be obtained with the converting constructor, e.g. Sgp4Constants{Float32}(SGP4C_WGS84).

Note

The package SatelliteToolboxTle.jl defines the type TLE, which is re-exported here. It contains some useful functionalities, such as TLE fetching from online services. For more information, refer to the package documentation.

julia> tle = tle"""
              AMAZONIA 1
              1 47699U 21015A   23083.68657856 -.00000044  10000-8  43000-4 0  9990
              2 47699  98.4304 162.1097 0001247 136.2017 223.9283 14.40814394108652"""TLE: AMAZONIA 1 (Epoch = 2023-03-24T16:28:40.388):
  ├─ Line 1
Satellite Number         : 47699
Classification           : U
International Designator : 21015A
Epoch Year               : 23
Epoch Day                : 83.68657856
ṅ/2                      : -4.4e-7 rev/day²
n̈/6                      : 1.0e-9 rev/day³
B*                       : 4.3e-5 1/ER
Ephemeris Type           : 0
Element Set Number       : 999
  └─ Line 2
       Inclination              : 98.4304°
       RA of the Ascending Node : 162.1097°
       Eccentricity             : 0.0001247
       Arg. of Perigee          : 136.2017°
       Mean Anomaly             : 223.9283°
       Mean Motion              : 14.40814394 rev/day
       Revolution Number        : 10865
julia> Propagators.init(Val(:SGP4), tle)OrbitPropagatorSgp4{Float64, Float64} (SGP4 Orbit Propagator): ├─ Mean Elements Epoch : 2.46003e6 (2023-03-24T16:28:40.388) Semi-Major Axis : 7133.946314 km Mean Motion : 14.40814394 rev/day Eccentricity : 0.0001247 Inclination : 98.4304° RA of Asc. Node : 162.1097° Arg. of Periapsis : 136.2017° Mean Anomaly : 223.9283° B* : 4.3e-5 1/ER ├─ Constants R₀ : 6378.137 km XKE : 0.07436685317 er^(3/2)/min J₂ : 0.001082629989 J₃ : -2.53215306e-6 J₄ : -1.61098761e-6 └─ Propagation Last Instant : 0.0 min

We also support initializing an SGP4/SDP4 propagator passing the TLE information in individual terms:

Propagators.init(Val(:SGP4), epoch::Number, n₀::Number, e₀::Number, i₀::Number, Ω₀::Number, ω₀::Number, M₀::Number, bstar::Number; kwargs...) -> OrbitPropagatorSgp4

where:

  • epoch::Number: Epoch of the orbital elements [Julian Day].
  • n₀::Number: SGP type "mean" mean motion at epoch [rad/s].
  • e₀::Number: "Mean" eccentricity at epoch.
  • i₀::Number: "Mean" inclination at epoch [rad].
  • Ω₀::Number: "Mean" longitude of the ascending node at epoch [rad].
  • ω₀::Number: "Mean" argument of perigee at epoch [rad].
  • M₀::Number: "Mean" mean anomaly at epoch [rad].
  • bstar::Number: Drag parameter (B*).

The keywords kwargs... are the same as in the first version of this function.

Finally, the propagator can be initialized with an Orbit Mean-Elements Message (OMM) whose mean element theory is SGP4:

Propagators.init(Val(:SGP4), omm::OrbitMeanElementsMessage; kwargs...) -> OrbitPropagatorSgp4

The mean motion is obtained from the field MEAN_MOTION or, if it is absent, from the semi-major axis and the gravitational coefficient, and the drag term is set to 0 if the field BSTAR is absent. The keywords kwargs... are the same as in the first version of this function.

Note

The package SatelliteToolboxOrbitDataMessages.jl defines the type OrbitMeanElementsMessage and the functions to read, parse, and fetch the messages, and it is re-exported here.

julia> omm = parse_omm(
           """
           <?xml version="1.0" encoding="utf-8"?>
           <ndm><omm id="CCSDS_OMM_VERS" version="3.0">
           <header><CREATION_DATE>2025-12-30T23:36:37</CREATION_DATE><ORIGINATOR>18 SPCS</ORIGINATOR></header>
           <body><segment>
           <metadata><OBJECT_NAME>AMAZONIA 1</OBJECT_NAME><OBJECT_ID>2021-015A</OBJECT_ID><CENTER_NAME>EARTH</CENTER_NAME><REF_FRAME>TEME</REF_FRAME><TIME_SYSTEM>UTC</TIME_SYSTEM><MEAN_ELEMENT_THEORY>SGP4</MEAN_ELEMENT_THEORY></metadata>
           <data>
           <meanElements><EPOCH>2025-12-30T18:12:04.533984</EPOCH><MEAN_MOTION>14.40772474</MEAN_MOTION><ECCENTRICITY>0.00011240</ECCENTRICITY><INCLINATION>98.3721</INCLINATION><RA_OF_ASC_NODE>75.0877</RA_OF_ASC_NODE><ARG_OF_PERICENTER>97.3772</ARG_OF_PERICENTER><MEAN_ANOMALY>262.7545</MEAN_ANOMALY></meanElements>
           <tleParameters><EPHEMERIS_TYPE>0</EPHEMERIS_TYPE><CLASSIFICATION_TYPE>U</CLASSIFICATION_TYPE><NORAD_CAT_ID>47699</NORAD_CAT_ID><ELEMENT_SET_NO>999</ELEMENT_SET_NO><REV_AT_EPOCH>25439</REV_AT_EPOCH><BSTAR>0.00015330000000</BSTAR><MEAN_MOTION_DOT>0.00000447</MEAN_MOTION_DOT><MEAN_MOTION_DDOT>0.0000000000000</MEAN_MOTION_DDOT></tleParameters>
           </data>
           </segment></body>
           </omm></ndm>
           """
       )OrbitMeanElementsMessage:
  ├─ Header
Creation Date : 2025-12-30T23:36:37
Originator    : 18 SPCS
  ├─ Metadata
Object Name         : AMAZONIA 1
Object ID           : 2021-015A
Center Name         : EARTH
Ref. Frame          : TEME
Time System         : UTC
Mean Element Theory : SGP4
  └─ Data
       ├─ Mean Keplerian Elements
Epoch              : 2025-12-30T18:12:04.533984
Mean Motion        : 14.40772474 rev/day
Eccentricity       : 0.0001124
Inclination        : 98.3721°
RA of Asc. Node    : 75.0877°
Arg. of Pericenter : 97.3772°
Mean Anomaly       : 262.7545°
       └─ TLE Related Parameters
            Ephemeris Type      : 0
            Classification Type : U
            NORAD Cat ID        : 47699
            Element Set Number  : 999
            Rev at Epoch        : 25439
            B*                  : 0.0001533 1/ER
            ∂(Mean Motion)/∂t   : 4.47e-6 rev/day²
            ∂²(Mean Motion)/∂t² : 0.0 rev/day³
julia> Propagators.init(Val(:SGP4), omm)OrbitPropagatorSgp4{Float64, Float64} (SGP4 Orbit Propagator): ├─ Mean Elements Epoch : 2.46104e6 (2025-12-30T18:12:04.534) Semi-Major Axis : 7134.084691 km Mean Motion : 14.40772474 rev/day Eccentricity : 0.0001124 Inclination : 98.3721° RA of Asc. Node : 75.0877° Arg. of Periapsis : 97.3772° Mean Anomaly : 262.7545° B* : 0.0001533 1/ER ├─ Constants R₀ : 6378.137 km XKE : 0.07436685317 er^(3/2)/min J₂ : 0.001082629989 J₃ : -2.53215306e-6 J₄ : -1.61098761e-6 └─ Propagation Last Instant : 0.0 min

Fitting Mean Elements

We can use the function:

Propagators.fit_mean_elements([sink::Type, ]::Val{:SGP4}, vjd::AbstractVector{Tjd}, vr_teme::AbstractVector{Tv}, vv_teme::AbstractVector{Tv}; kwargs...) -> sink, SMatrix{7, 7, T}, NamedTuple

to fit a set of SGP4 mean elements using the osculating elements represented by a set of position vectors vr_teme [m] and a set of velocity vectors vv_teme [m / s] represented in the True-Equator, Mean-Equinox reference frame (TEME) at instants in the array vjd [Julian Day, UTC]. The mean elements are returned as an object of type sink, which can be a TLE or an OrbitMeanElementsMessage. If sink is omitted, an OrbitMeanElementsMessage is returned.

It returns the fitted mean elements, the final covariance matrix of the least-square algorithm, and a NamedTuple with its statistics: converged, iterations, position_rmse [m], velocity_rmse [m / s], and total_rmse.

This algorithm was based on [4].

Note

This algorithm version will allocate a new SGP4 propagator with the constants sgp4c. If the allocation must be avoided, use the function Propagators.fit_mean_elements! instead.

The following keywords are available to configure the fitting process:

  • sgp4c::Sgp4Constants: SGP4 orbit propagator constants (see Sgp4Constants), whose number type T is used in the fitting. It is only available in the allocating function. (Default: SGP4C_WGS84)
  • atol::Number: Tolerance for the residual absolute value. If the residual is lower than atol at any iteration, the computation loop stops. (Default: 2e-4)
  • rtol::Number: Tolerance for the relative difference between the residuals. If the relative difference between the residuals in two consecutive iterations is lower than rtol, the computation loop stops. (Default: 2e-4)
  • estimate_bstar::Bool: If true, the algorithm will try to estimate the B* parameter. Otherwise, it will be set to 0 or to the value in the initial guess. (Default: true)
  • include_covariance::Bool: If true, the covariance of the mean position and velocity obtained by the least-square algorithm is stored in the covariance matrix section of the output, represented in the TEME reference frame. It is only used when sink is OrbitMeanElementsMessage. (Default: true)
  • initial_guess::Union{Nothing, AbstractVector, TLE, OrbitMeanElementsMessage}: Initial guess for the fitting process. If it is nothing, the algorithm will obtain an initial estimate from the osculating elements in vr_teme and vv_teme. (Default: nothing)
  • jacobian_method::AbstractJacobianMethod: Method used to compute the Jacobian matrix. It can be FiniteDiffJacobian() for finite differences or ForwardDiffJacobian() for ForwardDiff.jl automatic differentiation. (Default: FiniteDiffJacobian())
  • jacobian_perturbation::Number: Initial state perturbation to compute the finite-difference when calculating the Jacobian matrix. (Default: 1e-3)
  • jacobian_perturbation_tol::Number: Tolerance to accept the perturbation when calculating the Jacobian matrix. If the computed perturbation is lower than jacobian_perturbation_tol, we increase it until its absolute value is higher than jacobian_perturbation_tol. (Default: 1e-7)
  • max_iterations::Int: Maximum number of iterations allowed for the least-square fitting. (Default: 50)
  • mean_elements_epoch::Union{Number, DateTime}: Epoch of the fitted mean elements, represented by a Julian Day [UTC] or a DateTime [UTC]. (Default: vjd[end])
  • template::Union{Nothing, sink, NamedTuple}: Source of the metadata of the output. If it is an object of type sink, its metadata is copied. If it is a NamedTuple, its entries are passed as keywords to the constructor of sink on top of the default metadata, e.g. (; name = "AMAZONIA 1", satellite_number = 47699) for a TLE or (; object_name = "AMAZONIA 1", object_id = "2021-015A", norad_cat_id = 47699) for an OrbitMeanElementsMessage. (Default: nothing)
  • verbose::Bool: If true, the algorithm prints debugging information to stdout. (Default: true)
  • weight_vector::AbstractVector: Vector with the measurements weights for the least-square algorithm. We assemble the weight matrix W as a diagonal matrix with the elements in weight_vector at its diagonal. (Default: @SVector(ones(Bool, 6)))
julia> vr_teme = [
           [-6792.402703741442, 2192.6458461287293, 0.18851758695295118] .* 1000,
           [-6357.88873265975, 2391.9476768911686, 2181.838771262736] .* 1000
       ];
julia> vv_teme = [ [0.3445760107690598, 1.0395135806993514, 7.393686131436984] .* 1000, [2.5285015912807003, 0.27812476784300005, 7.030323100703928] .* 1000 ];
julia> vjd = [ 2.46002818657856e6, 2.460028190050782e6 ];
julia> omm, P, stats = Propagators.fit_mean_elements(Val(:SGP4), vjd, vr_teme, vv_teme; estimate_bstar = false)ACTION: Fitting the mean elements. Iteration Position RMSE Velocity RMSE Total RMSE RMSE Variation [km] [km / s] [ ] PROGRESS: 1 13.589 0.00473939 13.589 --- PROGRESS: 2 0.000902827 4.59551e-06 0.000902839 -99.9934 % PROGRESS: 3 5.80328e-09 4.38304e-07 4.38343e-07 -99.9514 % (OMM: UNDEFINED [UNDEFINED] (Epoch = 2023-03-24T16:33:40.388), [1.0858656073289727 -0.024687650690061822 … -0.07518401717497411 -5701.149921125949; -0.02468765069515278 1.0179970390752602 … 0.022969178921837436 1742.0303943064114; … ; -0.07518401717443697 0.022969178922001787 … 0.06514639076610358 4945.076064431256; -5701.149921093163 1742.0303943214715 … 4945.076064438029 3.754926862323227e8], (converged = true, iterations = 3, position_rmse = 5.803278389782464e-6, velocity_rmse = 0.00043830421111141945, total_rmse = 0.00043834262799558185))
julia> ommOrbitMeanElementsMessage: ├─ Header Creation Date : 2026-09-09T23:39:29.477 Originator : SatelliteToolboxSgp4.jl ├─ Metadata Object Name : UNDEFINED Object ID : UNDEFINED Center Name : EARTH Ref. Frame : TEME Time System : UTC Mean Element Theory : SGP4 └─ Data ├─ Mean Keplerian Elements Epoch : 2023-03-24T16:33:40.388 Mean Motion : 14.408141572426263 rev/day Eccentricity : 0.00012463364072265955 Inclination : 98.4303999965365° RA of Asc. Node : 162.11312239004525° Arg. of Pericenter : 136.15040637093372° Mean Anomaly : 241.9793402770317° ├─ TLE Related Parameters Ephemeris Type : 0 Classification Type : U NORAD Cat ID : 9999 Element Set Number : 0 Rev at Epoch : 0 B* : 0.0 1/ER ∂(Mean Motion)/∂t : 0.0 rev/day² ∂²(Mean Motion)/∂t² : 0.0 rev/day³ └─ Covariance Matrix Ref. Frame : TEME CX_X : 1.0858656073289727 km² CY_X : -0.02468765069515278 km² CY_Y : 1.0179970390752602 km² CZ_X : 0.05023262240842293 km² CZ_Y : -0.018268381835362873 km² CZ_Z : 1.029392839278491 km² CX_DOT_X : 0.029137264243658333 km²/s CX_DOT_Y : -0.007924415502066634 km²/s CX_DOT_Z : 0.016557057339731274 km²/s CX_DOT_X_DOT : 0.007622981124452393 km²/s² CY_DOT_X : -0.019971517901669327 km²/s CY_DOT_Y : 0.00936499755647425 km²/s CY_DOT_Z : -0.012882030446835025 km²/s CY_DOT_X_DOT : -0.005889598825445197 km²/s² CY_DOT_Y_DOT : 0.0045854587718331015 km²/s² CZ_DOT_X : -0.07518401717443697 km²/s CZ_DOT_Y : 0.022969178922001787 km²/s CZ_DOT_Z : -0.045496198074863034 km²/s CZ_DOT_X_DOT : -0.022248080294371354 km²/s² CZ_DOT_Y_DOT : 0.017239367163023475 km²/s² CZ_DOT_Z_DOT : 0.06514639076610358 km²/s²
julia> stats(converged = true, iterations = 3, position_rmse = 5.803278389782464e-6, velocity_rmse = 0.00043830421111141945, total_rmse = 0.00043834262799558185)
julia> tle, P, stats = Propagators.fit_mean_elements(TLE, Val(:SGP4), vjd, vr_teme, vv_teme; estimate_bstar = false)ACTION: Fitting the mean elements. Iteration Position RMSE Velocity RMSE Total RMSE RMSE Variation [km] [km / s] [ ] PROGRESS: 1 13.589 0.00473939 13.589 --- PROGRESS: 2 0.000902827 4.59551e-06 0.000902839 -99.9934 % PROGRESS: 3 5.80328e-09 4.38304e-07 4.38343e-07 -99.9514 % (TLE: UNDEFINED [9999] (Epoch = 2023-03-24T16:33:40.388), [1.0858656073289727 -0.024687650690061822 … -0.07518401717497411 -5701.149921125949; -0.02468765069515278 1.0179970390752602 … 0.022969178921837436 1742.0303943064114; … ; -0.07518401717443697 0.022969178922001787 … 0.06514639076610358 4945.076064431256; -5701.149921093163 1742.0303943214715 … 4945.076064438029 3.754926862323227e8], (converged = true, iterations = 3, position_rmse = 5.803278389782464e-6, velocity_rmse = 0.00043830421111141945, total_rmse = 0.00043834262799558185))
julia> tleTLE: UNDEFINED (Epoch = 2023-03-24T16:33:40.388): ├─ Line 1 Satellite Number : 9999 Classification : U International Designator : 999999 Epoch Year : 23 Epoch Day : 83.69005078703704 ṅ/2 : 0.0 rev/day² n̈/6 : 0.0 rev/day³ B* : 0.0 1/ER Ephemeris Type : 0 Element Set Number : 0 └─ Line 2 Inclination : 98.4303999965365° RA of the Ascending Node : 162.11312239004525° Eccentricity : 0.00012463364072265955 Arg. of Perigee : 136.15040637093372° Mean Anomaly : 241.9793402770317° Mean Motion : 14.408141572426263 rev/day Revolution Number : 0

References

  • [1] Hoots, F. R., Roehrich, R. L (1980). Models for Propagation of NORAD Elements Set. Spacetrack Report No. 3.
  • [2] Vallado, D. A., Crawford, P., Hujsak, R., Kelso, T. S (2006). Revisiting Spacetrack Report #3: Rev1. AIAA.
  • [3] SGP4 Source code of STRF, which the C code was converted by Paul. S. Crawford and Andrew R. Brooks.
  • [4] Vallado, D. A., Crawford, P (2008). SGP4 Orbit Determination. AIAA.