J2 Analytical Orbit Propagator
The J2 analytical orbit propagator considers only the $J_2$ perturbation term of the geopotential field to obtain a simple analytic solution for propagating the mean orbital elements. It has low precision but is extremely useful during mission design. For example, when designing a Sun-Synchronous orbit for a remote sensing mission, the satellite usually has a propulsion subsystem to maintain its orbit. Hence, the J2 propagator solution can be used as the nominal orbit through the mission lifetime to perform initial studies.
Algorithm
The algorithm implemented here is based on [1, 2]. This version takes into account only the secular effects in the orbit elements.
After neglecting all terms with higher order than J2, we obtain that only the right accession of the ascending node (RAAN), the argument of perigee, and mean motion are perturbed by the geopotential field with secular effects. Hence, the algorithm propagates the mean elements using the following equations:
\[\begin{aligned} p_0 &= a_0 \cdot (1 - e_0^2) \\ \bar{n} &= n_0 \cdot \left[1 + \frac{3}{4} \cdot J_2 \cdot \left(\frac{R_0}{p_0}\right)^2 \cdot \sqrt{1 - e_0^2} \cdot \left(2 - 3\sin{i_0}^2\right)\right] \Omega(t) &= \Omega_0 - \frac{3}{2} \cdot J_2 \cdot \left(\frac{R_0}{p_0}\right)^2 \cdot \bar{n} \cdot \cos{i_0} \cdot \left(t - t_0\right) \\ \omega(t) &= \omega_0 + \frac{3}{4} \cdot J_2 \cdot \left(\frac{R_0}{p_0}\right)^2 \cdot \bar{n} \cdot \left(4 - 5\sin{i_0}^2\right) \cdot \left(t - t_0\right) \\ M(t) &= M_0 + \bar{n} \cdot \left(t - t_0\right) \\ \end{aligned}\]
where the subscript $_0$ indicates the initial element, $t_0$ is the initial mean elements' epoch, $a$ is the mean semi-major axis, $n_0$ is mean motion, $\bar{n}$ is the perturbed mean motion, $e$ is the eccentricity, $p$ is the semi-latus rectum, $R_0$ is the Earth's equatorial radius, $\Omega$ is the RAAN, and $M$ is the mean anomaly.
Initialization
We can initialize the J2 analytical orbit propagator with the following function:
Propagators.init(Val(:J2), orb₀::KeplerianElements; kwargs...) -> OrbitPropagatorJ2which creates a J2 propagator structure OrbitPropagatorJ2 with the mean Keplerian elements orb₀.
The following keyword selects the gravitational constants for the propagation algorithm:
j2c::J2PropagatorConstants: J2 orbit propagator constants (seeJ2PropagatorConstants). (Default:J2C_EGM2008)
This package contains some pre-built propagation constants for this propagator:
| J2 Propagator Constant | Description | Type |
|---|---|---|
J2C_EGM2008 | EGM-2008 gravitational constants | Float64 |
J2C_EGM2008_F32 | EGM-2008 gravitational constants | Float32 |
J2C_EGM1996 | EGM-1996 gravitational constants | Float64 |
J2C_EGM1996_F32 | EGM-1996 gravitational constants | Float32 |
J2C_JGM02 | JGM-02 gravitational constants | Float64 |
J2C_JGM02_F32 | JGM-02 gravitational constants | Float32 |
J2C_JGM03 | JGM-03 gravitational constants | Float64 |
J2C_JGM03_F32 | JGM-03 gravitational constants | Float32 |
The type used in the propagation will be the same as used to define the constants in the structure j2c.
julia> orb = KeplerianElements( date_to_jd(2023, 1, 1, 0, 0, 0), 7190.982e3, 0.001111, 98.405 |> deg2rad, 100 |> deg2rad, 90 |> deg2rad, 19 |> deg2rad )KeplerianElements{TrueAnomaly, Float64, Float64}: Epoch : 2.45995e6 (2023-01-01T00:00:00) Semi-Major Axis : 7190.982 km Eccentricity : 0.001111 Inclination : 98.405° RA of Asc. Node : 100.0° Arg. of Periapsis : 90.0° True Anomaly : 19.0°julia> orbp = Propagators.init(Val(:J2), orb; j2c = J2C_JGM03)OrbitPropagatorJ2{Float64, Float64} (J2 Orbit Propagator): ├─ Mean Elements │ Epoch : 2.45995e6 (2023-01-01T00:00:00) │ Semi-Major Axis : 7190.982 km │ Eccentricity : 0.001111 │ Inclination : 98.405° │ RA of Asc. Node : 100.0° │ Arg. of Periapsis : 90.0° │ Mean Anomaly : 18.95858415° ├─ Secular Rates │ Mean Motion : 14.2285532 rev/day │ RAAN Rate : 0.9565453748 °/day │ Arg. of Periapsis Rate : -2.922500732 °/day ├─ Constants │ R₀ : 6378.1363 km │ μm : 0.001239447666 rad/s │ J₂ : 0.001082636023 └─ Propagation Last Instant : 0.0 s
Fitting Mean Elements
We can use the function:
Propagators.fit_mean_elements(::Val{:J2}, vjd::AbstractVector{Tjd}, vr_i::AbstractVector{Tv}, vv_i::AbstractVector{Tv}; kwargs...) -> KeplerianElements{MeanAnomaly, Float64, T}, SMatrix{6, 6, T}, NamedTupleto fit a set of mean Keplerian elements for the J2 orbit propagator using the osculating elements represented by a set of position vectors vr_i [m] and a set of velocity vectors vv_i [m / s] represented in an inertial reference frame at instants in the array vjd [Julian Day].
It returns the fitted Keplerian 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 version will allocate a new J2 propagator with the constants j2c. If the allocation must be avoided, use the function Propagators.fit_mean_elements! instead.
The following keywords are available to configure the fitting process:
j2c::J2PropagatorConstants{T}: J2 orbit propagator constants (seeJ2PropagatorConstants), whose number typeTis used in the fitting. (Default:J2C_EGM2008)atol::Number: Tolerance for the residual absolute value. If the residual is lower thanatolat 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 thanrtol, the computation loop stops. (Default: 2e-4)initial_guess::Union{Nothing, KeplerianElements}: Initial guess for the mean elements fitting process. If it isnothing, the algorithm will obtain an initial estimate from the osculating elements invr_iandvv_i. (Default:nothing)jacobian_method::AbstractJacobianMethod: Method used to compute the Jacobian matrix. It can beFiniteDiffJacobian()for finite differences orForwardDiffJacobian()for ForwardDiff.jl automatic differentiation. (Default:FiniteDiffJacobian())jacobian_perturbation::Number: Initial state perturbation to compute the finite-difference when calculating the Jacobian matrix. Only used withFiniteDiffJacobian(). (Default: 1e-3)jacobian_perturbation_tol::Number: Tolerance to accept the perturbation when calculating the Jacobian matrix. If the computed perturbation is lower thanjacobian_perturbation_tol, we increase it until its absolute value is higher thanjacobian_perturbation_tol. Only used withFiniteDiffJacobian(). (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 aDateTime[UTC]. (Default:vjd[end])verbose::Bool: Iftrue, the algorithm prints debugging information tostdout. (Default:true)weight_vector::AbstractVector: Vector with the measurements weights for the least-square algorithm. We assemble the weight matrixWas a diagonal matrix with the elements inweight_vectorat its diagonal. (Default:@SVector(ones(Bool, 6)))
julia> vr_i = [ [-6792.402703741442, 2192.6458461287293, 0.18851758695295118] .* 1000, [-1781.214419290065, 1619.7795321872854, 6707.771633846665] .* 1000, [ 5693.643675547716, -1192.342828671633, 4123.976025977494] .* 1000, [ 5291.613719530499, -2354.5417593130833, -4175.561367156414] .* 1000, [-2416.3705905186903, -268.74923235392623, -6715.411357310478] .* 1000, [-6795.043410709359, 2184.4414321930635, -0.4327055325971031] .* 1000, ];julia> vv_i = [ [0.3445760107690598, 1.0395135806993514, 7.393686131436984] .* 1000, [6.875680282038698, -1.864319399615942, 2.270603214569518] .* 1000, [3.8964090757666496, -2.1887896252945875, -5.9960180359219075] .* 1000, [-4.470258022565413, 0.5119576359985208, -5.9608372367141635] .* 1000, [-6.647358060413909, 2.495415251255861, 2.292118747543002] .* 1000, [0.3427096905434428, 1.040125572862349, 7.3936887585116855] .* 1000, ];julia> vjd = [ 2.46002818657856e6 2.460028200467449e6 2.460028214356338e6 2.4600282282452267e6 2.4600282421341157e6 2.4600282560230047e6 ];julia> orb, P, stats = Propagators.fit_mean_elements(Val(:J2), vjd, vr_i, vv_i)ACTION: Fitting the mean elements for the J2 propagator. Iteration Position RMSE Velocity RMSE Total RMSE RMSE Variation [km] [km / s] [ ] PROGRESS: 1 53.2569 0.0547793 53256.9 --- PROGRESS: 2 4.34511 0.00537699 4345.11 -91.8412 % PROGRESS: 3 4.34128 0.00540095 4341.29 -0.0879912 % PROGRESS: 4 4.3413 0.00540076 4341.31 0.000476015 % (KeplerianElements{MeanAnomaly, Float64, Float64}: Epoch = 2.46003e6 (2023-03-24T18:08:40.388), [0.16604846256633984 0.06643574147585313 … -3.855413713897692e-5 0.0001240322540765396; 0.0664357414761595 0.26633448227200807 … -1.794361209902454e-5 -1.956795680247049e-5; … ; -3.855413713714673e-5 -1.7943612101226995e-5 … 4.397198445854844e-7 -8.092704692944129e-8; 0.00012403225407634047 -1.9567956803186787e-5 … -8.092704693161908e-8 1.2451922438501393e-7], (converged = true, iterations = 4, position_rmse = 4341.303234180302, velocity_rmse = 5.400761345636881, total_rmse = 4341.3065935646))julia> orbKeplerianElements{MeanAnomaly, Float64, Float64}: Epoch : 2.46003e6 (2023-03-24T18:08:40.388) Semi-Major Axis : 7131.634013 km Eccentricity : 0.001142993013 Inclination : 98.43659393° RA of Asc. Node : 162.1773906° Arg. of Periapsis : 101.2857729° Mean Anomaly : 258.8172342°julia> stats(converged = true, iterations = 4, position_rmse = 4341.303234180302, velocity_rmse = 5.400761345636881, total_rmse = 4341.3065935646)
References
- [1] Vallado, D. A (2013). Fundamentals of Astrodynamics and Applications. 4th ed. Microcosm Press, Hawthorn, CA, USA.
- [2] Kozai, Y (1959). The Motion of a Close Earth Satellite. The Astronomical Journal, v. 64, no. 1274, pp. 367 – 377.