Ground Track

We can obtain the ground track of a satellite using the function:

ground_track(orbp::OrbitPropagator; kwargs...) -> Vector{NTuple{2, Float64}}

It computes the satellite ground track using the orbit propagator orbp. It returns a vector of NTuple{2, Float64} where the first element is the latitude [rad] and the second is the longitude [rad] of each point in the ground track.

The following keywords are available:

  • add_nans::Bool: If true, we add NaN if there is a discontinuity in the ground track to improve plotting. (Default: true)

  • duration::Number: Duration of the analysis. (Default: 86400)

  • initial_time::Number: Initial time regarding the orbit propagator orbp epoch [s]. (Default: 0)

  • f_eci_to_ecef::Function: Function to convert the orbit propagator position represented in the Earth-centered inertial (ECI) reference frame to the Earth-centered, Earth-fixed (ECEF) reference frame. The signature must be

    f_eci_to_ecef(r_i::AbstractVector, jd::Number) -> AbstractVector

    and it must return the position vector r_i represented in the ECEF at the instant jd [Julian Day]. By default, we use TEME as the ECI and PEF as the ECEF. (Default: _ground_track_default_eci_to_ecef)

  • step::Union{Nothing, Number}: Step for the computation. If nothing, we will roughly compute the step to approximate 1° in the mean anomaly. (Default: nothing)

  • track_types::Symbol: A symbol describing what kind of track types we must add to the output vector. It can be :ascending for only ascending passages, :descending for only descending passages, or :all for both. (Default: :all)

Ground Track Inclination

Many analyses require the ground track inclination. For example, if we are designing a remote sensing mission with an optical payload, we must know how much two images overlap. This information can only be computed using spherical trigonometry and the ground track inclination instead of the orbital one.

The ground track inclination $i_{gt}$, shown in the following figure, is a composition of the orbit inclination, the Earth's angular speed, and the RAAN time derivative. We can compute it using:

\[i_{gt} = \tan^{-1}\left(\frac{ \omega_s \sin i }{ \omega_s \cos i - \omega_e + \dot{\Omega} }\right)\ ,\]

where $i$ is the orbital inclination, $\omega_s$ is the satellite angular speed at Equator, $\omega_e$ is the Earth angular speed, and $\Omega$ is the RAAN.

Ground Track Inclination

Formally, we should use the satellite instantaneous angular speed at the Equator in $\omega_s$. However, given the perturbations caused by the Earth's gravitational potential, this speed is not simple to compute. The calculation would required to implement an orbit propagator. Thus, we simplify it by assuming that the orbit eccentricity is small. This assumption is reasonable given the missions that would benefit from the computation of the ground track inclination. In this case, we approximate $\omega_s$ as the mean satellite angular speed.

The function:

ground_track_inclination(a::Number, e::Number, i::Number; kwargs...) -> T
ground_track_inclination(orb::Orbit{Tepoch, T}); kwargs...) where {Tepoch <: Number, T <: Number} -> T

computes the ground track inclination at the Equator [rad] in an orbit with semi-major axis a [m], eccentricity e [ ], and inclination i [rad]. The orbit can also be specified by orb (see Orbit).

Note

The output type T in the first signature is obtained by promoting the inputs to a float type.

The following keywords are available:

  • perturbation::Symbol: Symbol to select the perturbation terms that will be used. It can be :J0, :J2, or :J4. (Default: :J2)
  • m0::Number: Standard gravitational parameter for Earth [m³ / s²]. (Default: GM_EARTH)
  • J2::Number: J₂ perturbation term. (Default: EGM_2008_J2)
  • J4::Number: J₄ perturbation term. (Default: EGM_2008_J4)
  • R0::Number: Earth's equatorial radius [m]. (Default: EARTH_EQUATORIAL_RADIUS)
  • we::Number: Earth's angular speed [rad / s]. (Default: EARTH_ANGULAR_SPEED)

Examples

We will compute the descending ground tracks of the Amazonia-1 mission for five days. The first thing we need to do is define the orbit:

julia> jd₀ = date_to_jd(2021, 1, 1)2.4592155e6
julia> orb = KeplerianElements( jd₀, 7130.982e3, 0.001111, 98.405 |> deg2rad, ltdn_to_raan(10.5, jd₀), π / 2, 0 )KeplerianElements{Float64, Float64}: Epoch : 2.45922e6 (2021-01-01T00:00:00) Semi-major axis : 7130.98 km Eccentricity : 0.001111 Inclination : 98.405 ° RAAN : 78.4021 ° Arg. of Perigee : 90.0 ° True Anomaly : 0.0 °

The next step is to define the desired propagator:

julia> orbp = Propagators.init(Val(:J2), orb)OrbitPropagatorJ2{Float64, Float64}:
   Propagator name : J2 Orbit Propagator
  Propagator epoch : 2021-01-01T00:00:00
  Last propagation : 2021-01-01T00:00:00

Now, we can use the function ground_track to obtain the satellite ground track considering only the descending passages:

julia> gt = ground_track(orbp; duration = 5 * 86400, track_types = :descending)13069-element Vector{Tuple{Float64, Float64}}:
 (1.4249660489382514, -1.9629142342294301)
 (1.4239401788695614, -2.083242314527422)
 (1.4209046654853266, -2.2003522483295006)
 (1.4159776024387374, -2.311522262637157)
 (1.409331946731526, -2.4148462532664547)
 (1.4011694236152394, -2.509306269805821)
 (1.3916971289754156, -2.5946490797008246)
 (1.3811109723095272, -2.6711712548167506)
 (1.3695864566113856, -2.7395018851996817)
 (1.3572752195159548, -2.800429478686105)
 ⋮
 (-1.3696124211656855, 2.1732322226073304)
 (-1.381086271483457, 2.105195658702011)
 (-1.39163034068695, 2.029040761805442)
 (-1.4010715305058756, 1.9441454031386136)
 (-1.4092166385020077, 1.850212787307041)
 (-1.415861314669152, 1.7474838581667644)
 (-1.420806159442775, 1.6369482222353913)
 (-1.4238795249095673, 1.5204676340474153)
 (-1.424963071388477, 1.400710314672049)

Finally, we can extract the latitude and longitude of each point in the ground track using:

julia> gt_lat = first.(gt)13069-element Vector{Float64}:
  1.4249660489382514
  1.4239401788695614
  1.4209046654853266
  1.4159776024387374
  1.409331946731526
  1.4011694236152394
  1.3916971289754156
  1.3811109723095272
  1.3695864566113856
  1.3572752195159548
  ⋮
 -1.3696124211656855
 -1.381086271483457
 -1.39163034068695
 -1.4010715305058756
 -1.4092166385020077
 -1.415861314669152
 -1.420806159442775
 -1.4238795249095673
 -1.424963071388477
julia> gt_lon = last.(gt)13069-element Vector{Float64}: -1.9629142342294301 -2.083242314527422 -2.2003522483295006 -2.311522262637157 -2.4148462532664547 -2.509306269805821 -2.5946490797008246 -2.6711712548167506 -2.7395018851996817 -2.800429478686105 ⋮ 2.1732322226073304 2.105195658702011 2.029040761805442 1.9441454031386136 1.850212787307041 1.7474838581667644 1.6369482222353913 1.5204676340474153 1.400710314672049

If we use GeoMakie.jl to plot, we obtain:

Amazonia-1 descending ground track

Finally, the ground track inclination is:

julia> ground_track_inclination(orb) |> rad2deg102.29560077110351

Plotting

If the user loads the package GeoMakie.jl together with a Makie.jl back end, an extension is loaded and adds the possibility to plot the ground track. In this case, the following functions are available:

plot_ground_track(gt::Vector{NTuple{2, T}}; kwargs...) where T<:Number -> Figure, Axis

It plots the ground track gt computed using the function ground_track. It returns the objects Figure and Axis used to plot the data. For more information, please, refer to Makie.jl documentation.

Note

This function plots the countries' borders in the created figure using the file with the country polygons fetched with the function fetch_country_polygons. Hence, if this files does not exist, the algorithm tries to download it.

All kwargs... are passed to the function plot_world_map.

plot_ground_track!(ax:Axis, gt::Vector{NTuple{2, Number}}) -> Nothing

It plots in the Makie.jl axis ax the ground track gt computed using the function ground_track.

The user can use this function to plot the ground track on top of an existing figure.

Example

The code:

julia> using GeoMakie, CairoMakie
julia> jd₀ = date_to_jd(2021, 1, 1)2.4592155e6
julia> orb = KeplerianElements( jd₀, 7130.982e3, 0.001111, 98.405 |> deg2rad, ltdn_to_raan(10.5, jd₀), π / 2, 0 )KeplerianElements{Float64, Float64}: Epoch : 2.45922e6 (2021-01-01T00:00:00) Semi-major axis : 7130.98 km Eccentricity : 0.001111 Inclination : 98.405 ° RAAN : 78.4021 ° Arg. of Perigee : 90.0 ° True Anomaly : 0.0 °
julia> orbp = Propagators.init(Val(:J2), orb)OrbitPropagatorJ2{Float64, Float64}: Propagator name : J2 Orbit Propagator Propagator epoch : 2021-01-01T00:00:00 Last propagation : 2021-01-01T00:00:00
julia> gt = ground_track(orbp; duration = 5 * 86400, track_types = :ascending)13069-element Vector{Tuple{Float64, Float64}}: (-1.424964140739, 0.960539608952033) (-1.4239477798328477, 0.8407347528233482) (-1.4209390984013148, 0.7241056888391548) (-1.4160541797985997, 0.6133430888155192) (-1.4094632285996702, 0.5103377847257986) (-1.4013650457262612, 0.4161073325039118) (-1.3919640562420157, 0.33091467581565814) (-1.3814539672330939, 0.2544776504600747) (-1.3700085911562516, 0.18618222612097848) (-1.3577783323962722, 0.12525335886774872) ⋮ (1.3699837816565492, -1.188487585068758) (1.381479606988557, -1.2570788572953417) (1.3920315039566236, -1.3338851836503087) (1.4014632452203108, -1.4195273964288588) (1.4095784122544457, -1.5142871300265797) (1.4161698413635262, -1.6178887073593322) (1.421036430395973, -1.7292857250638352) (1.4240067086872223, -1.846542611589315) (1.4249648934601344, -1.9669150896210252)
julia> fig, ax = plot_ground_track(gt)(Scene (1450px, 800px): 0 Plots 1 Child Scene: └ Scene (1450px, 800px), Axis (2 plots))
julia> save("ground_track.png", fig)CairoMakie.Screen{IMAGE}

produces the following figure:

Ground track