ssapy-toolkit 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ssapy_toolkit/Accelerations/__init__.py +18 -0
- ssapy_toolkit/Accelerations/accel_add.py +26 -0
- ssapy_toolkit/Accelerations/accel_circularize.py +88 -0
- ssapy_toolkit/Accelerations/accel_deltav.py +50 -0
- ssapy_toolkit/Accelerations/accel_deltav_vector.py +39 -0
- ssapy_toolkit/Accelerations/accel_earth_harmonics.py +172 -0
- ssapy_toolkit/Accelerations/accel_equitorial.py +41 -0
- ssapy_toolkit/Accelerations/accel_inclination.py +40 -0
- ssapy_toolkit/Accelerations/accel_moon.py +46 -0
- ssapy_toolkit/Accelerations/accel_plane.py +44 -0
- ssapy_toolkit/Accelerations/accel_point_earth.py +9 -0
- ssapy_toolkit/Accelerations/accel_radial.py +27 -0
- ssapy_toolkit/Accelerations/accel_sun.py +38 -0
- ssapy_toolkit/Accelerations/accel_uniform_earth.py +31 -0
- ssapy_toolkit/Accelerations/accel_velocity.py +25 -0
- ssapy_toolkit/Compute/__init__.py +18 -0
- ssapy_toolkit/Compute/calculate_errors.py +22 -0
- ssapy_toolkit/Compute/fft.py +43 -0
- ssapy_toolkit/Compute/find_bounding_cube.py +24 -0
- ssapy_toolkit/Compute/generate_sphere_of_vectors.py +102 -0
- ssapy_toolkit/Compute/lambertian_magnitude.py +250 -0
- ssapy_toolkit/Compute/lyapunov_exponent.py +190 -0
- ssapy_toolkit/Compute/mengos.py +31 -0
- ssapy_toolkit/Compute/proper_motions.py +107 -0
- ssapy_toolkit/Compute/sampling.py +537 -0
- ssapy_toolkit/Compute/segment_intersection.py +46 -0
- ssapy_toolkit/Coordinates/__init__.py +18 -0
- ssapy_toolkit/Coordinates/cartesian_to_cylindrical.py +20 -0
- ssapy_toolkit/Coordinates/cartesian_to_spherical.py +22 -0
- ssapy_toolkit/Coordinates/earth_trojan_sim.py +59 -0
- ssapy_toolkit/Coordinates/equitorial_and_ecliptic.py +167 -0
- ssapy_toolkit/Coordinates/gcrf_to_itrf.py +75 -0
- ssapy_toolkit/Coordinates/gcrf_to_llh.py +55 -0
- ssapy_toolkit/Coordinates/gcrf_to_lonlat.py +25 -0
- ssapy_toolkit/Coordinates/gcrf_to_lunar.py +76 -0
- ssapy_toolkit/Coordinates/gcrf_to_ntw.py +23 -0
- ssapy_toolkit/Coordinates/itrf_to_gcrf.py +67 -0
- ssapy_toolkit/Coordinates/j2000_to_gcrf.py +80 -0
- ssapy_toolkit/Coordinates/llh_to_gcrf.py +45 -0
- ssapy_toolkit/Coordinates/local_and_equitorial.py +130 -0
- ssapy_toolkit/Coordinates/lon_lat_bbox.py +47 -0
- ssapy_toolkit/Coordinates/lonlat_perigee.py +70 -0
- ssapy_toolkit/Coordinates/lunar_position.py +35 -0
- ssapy_toolkit/Coordinates/ntw_to_gcrf.py +35 -0
- ssapy_toolkit/Coordinates/on_sky_distance.py +23 -0
- ssapy_toolkit/Coordinates/sky_angles.py +155 -0
- ssapy_toolkit/Coordinates/surface_rv.py +60 -0
- ssapy_toolkit/Coordinates/unit_conversions.py +137 -0
- ssapy_toolkit/Coordinates/v_from_r.py +51 -0
- ssapy_toolkit/IO/__init__.py +18 -0
- ssapy_toolkit/IO/converter_json_hdf5.py +219 -0
- ssapy_toolkit/IO/csv_utils.py +340 -0
- ssapy_toolkit/IO/dict_to_from_hdf5.py +315 -0
- ssapy_toolkit/IO/get_memory.py +15 -0
- ssapy_toolkit/IO/guess_delimiter.py +24 -0
- ssapy_toolkit/IO/hdf5_to_csv.py +170 -0
- ssapy_toolkit/IO/hdf5_utils.py +207 -0
- ssapy_toolkit/IO/html_to_gif.py +158 -0
- ssapy_toolkit/IO/io_utils.py +184 -0
- ssapy_toolkit/IO/json_utils.py +99 -0
- ssapy_toolkit/IO/listfiles.py +56 -0
- ssapy_toolkit/IO/pickle_utils.py +13 -0
- ssapy_toolkit/IO/pprint_utils.py +447 -0
- ssapy_toolkit/IO/read_3le.py +351 -0
- ssapy_toolkit/IO/read_3le_by_bit.py +231 -0
- ssapy_toolkit/IO/tle_iter_pairs.py +41 -0
- ssapy_toolkit/IO/tle_prop_to_time.py +67 -0
- ssapy_toolkit/IO/workspace_io.py +306 -0
- ssapy_toolkit/IO/xml_utils.py +265 -0
- ssapy_toolkit/IO/yu_logging.py +68 -0
- ssapy_toolkit/IO/yuchaching.py +344 -0
- ssapy_toolkit/IO/yudata.py +89 -0
- ssapy_toolkit/Integrators/__init__.py +18 -0
- ssapy_toolkit/Integrators/fuel.py +70 -0
- ssapy_toolkit/Integrators/gravity_turn.py +29 -0
- ssapy_toolkit/Integrators/int_utils.py +126 -0
- ssapy_toolkit/Integrators/leap_frog.py +148 -0
- ssapy_toolkit/Integrators/quick_int.py +182 -0
- ssapy_toolkit/Integrators/rk4.py +69 -0
- ssapy_toolkit/Orbital_Mechanics/__init__.py +18 -0
- ssapy_toolkit/Orbital_Mechanics/all_orbit_quanities.py +165 -0
- ssapy_toolkit/Orbital_Mechanics/burn_to_deltav.py +80 -0
- ssapy_toolkit/Orbital_Mechanics/calculate_finite_burn_acceleration.py +38 -0
- ssapy_toolkit/Orbital_Mechanics/deltav_to_burn.py +82 -0
- ssapy_toolkit/Orbital_Mechanics/ellipse_fit.py +734 -0
- ssapy_toolkit/Orbital_Mechanics/ellipse_from_rv.py +177 -0
- ssapy_toolkit/Orbital_Mechanics/ellipse_inject.py +9 -0
- ssapy_toolkit/Orbital_Mechanics/equally_spaced_ta.py +135 -0
- ssapy_toolkit/Orbital_Mechanics/gamma_and_heading.py +230 -0
- ssapy_toolkit/Orbital_Mechanics/keplerian.py +1118 -0
- ssapy_toolkit/Orbital_Mechanics/lagrange_points.py +215 -0
- ssapy_toolkit/Orbital_Mechanics/launch_pads.py +83 -0
- ssapy_toolkit/Orbital_Mechanics/misc.py +173 -0
- ssapy_toolkit/Orbital_Mechanics/orbital_accel_model_comparisons.py +724 -0
- ssapy_toolkit/Orbital_Mechanics/orbital_comparison_stats.py +1000 -0
- ssapy_toolkit/Orbital_Mechanics/rv_to_ellipse.py +154 -0
- ssapy_toolkit/Orbital_Mechanics/synthetic_orbit_population.py +209 -0
- ssapy_toolkit/Orbital_Mechanics/transfer_coplanar.py +144 -0
- ssapy_toolkit/Orbital_Mechanics/transfer_coplanar_continuous.py +120 -0
- ssapy_toolkit/Orbital_Mechanics/transfer_hohmann.py +250 -0
- ssapy_toolkit/Orbital_Mechanics/transfer_inclination_continuous.py +169 -0
- ssapy_toolkit/Orbital_Mechanics/transfer_lambertian.py +216 -0
- ssapy_toolkit/Orbital_Mechanics/transfer_rendezvous.py +141 -0
- ssapy_toolkit/Orbital_Mechanics/transfer_shooter.py +240 -0
- ssapy_toolkit/Orbital_Mechanics/transfer_shooter_continuous.py +0 -0
- ssapy_toolkit/Orbital_Mechanics/transfer_velocity_and_inclination_continuous.py +184 -0
- ssapy_toolkit/Orbital_Mechanics/transfer_velocity_continuous.py +212 -0
- ssapy_toolkit/Plots/__init__.py +18 -0
- ssapy_toolkit/Plots/break_plot_lines.py +115 -0
- ssapy_toolkit/Plots/build_dashboard.py +74 -0
- ssapy_toolkit/Plots/cislunar_plot.py +213 -0
- ssapy_toolkit/Plots/cislunar_plot_3d.py +172 -0
- ssapy_toolkit/Plots/cislunar_plot_xy.py +199 -0
- ssapy_toolkit/Plots/divergence_gif.py +215 -0
- ssapy_toolkit/Plots/divergence_plot.py +107 -0
- ssapy_toolkit/Plots/figpath.py +87 -0
- ssapy_toolkit/Plots/gifify.py +307 -0
- ssapy_toolkit/Plots/globe_plot.py +277 -0
- ssapy_toolkit/Plots/groundtrack_dashboard.py +264 -0
- ssapy_toolkit/Plots/groundtrack_dashboard_gamma_heading.py +247 -0
- ssapy_toolkit/Plots/groundtrack_plot.py +352 -0
- ssapy_toolkit/Plots/groundtrack_video.py +292 -0
- ssapy_toolkit/Plots/misc_plotting.py +371 -0
- ssapy_toolkit/Plots/orbit_plot.py +139 -0
- ssapy_toolkit/Plots/orbit_plot_rv.py +126 -0
- ssapy_toolkit/Plots/orbit_plot_xy.py +223 -0
- ssapy_toolkit/Plots/orbit_plot_xyxz.py +249 -0
- ssapy_toolkit/Plots/plotutils.py +653 -0
- ssapy_toolkit/Plots/rendezvous_plot.py +98 -0
- ssapy_toolkit/Plots/set_axes_equal.py +32 -0
- ssapy_toolkit/Plots/tracking_plot.py +179 -0
- ssapy_toolkit/Plots/transfer_plot.py +147 -0
- ssapy_toolkit/Plots/write_gifs.py +222 -0
- ssapy_toolkit/Plots/write_videos.py +117 -0
- ssapy_toolkit/Rockets/__init__.py +18 -0
- ssapy_toolkit/Rockets/fuel.py +162 -0
- ssapy_toolkit/Rockets/rescale_burn.py +59 -0
- ssapy_toolkit/SSAPy_wrappers/__init__.py +18 -0
- ssapy_toolkit/SSAPy_wrappers/accel_ladder.py +80 -0
- ssapy_toolkit/SSAPy_wrappers/sat_kwargs.py +9 -0
- ssapy_toolkit/SSAPy_wrappers/ssapy_orbits.py +246 -0
- ssapy_toolkit/SSAPy_wrappers/ssapy_props.py +141 -0
- ssapy_toolkit/Time_Functions/__init__.py +18 -0
- ssapy_toolkit/Time_Functions/absolute_times_to_relative.py +68 -0
- ssapy_toolkit/Time_Functions/convert_dd_and_dms.py +55 -0
- ssapy_toolkit/Time_Functions/convert_dd_and_hms.py +65 -0
- ssapy_toolkit/Time_Functions/convert_gps_to_TT.py +17 -0
- ssapy_toolkit/Time_Functions/convert_to_gps.py +32 -0
- ssapy_toolkit/Time_Functions/get_times.py +163 -0
- ssapy_toolkit/Time_Functions/now_time.py +15 -0
- ssapy_toolkit/Time_Functions/relative_times_to_absolute.py +72 -0
- ssapy_toolkit/Yastropy/__init__.py +18 -0
- ssapy_toolkit/Yastropy/astropy_gcrf_to_llh.py +50 -0
- ssapy_toolkit/Yastropy/astropy_llh_to_gcrf.py +42 -0
- ssapy_toolkit/Yastropy/astropy_surface_rv.py +39 -0
- ssapy_toolkit/Yastropy/astropy_test.py +43 -0
- ssapy_toolkit/__init__.py +43 -0
- ssapy_toolkit/asteroids.py +168 -0
- ssapy_toolkit/constants.py +123 -0
- ssapy_toolkit/engines.py +195 -0
- ssapy_toolkit/hpc.py +213 -0
- ssapy_toolkit/launch_pads.py +215 -0
- ssapy_toolkit/orbit_initializer.py +72 -0
- ssapy_toolkit/utils.py +662 -0
- ssapy_toolkit/vectors.py +241 -0
- ssapy_toolkit-1.0.0.dist-info/METADATA +182 -0
- ssapy_toolkit-1.0.0.dist-info/RECORD +170 -0
- ssapy_toolkit-1.0.0.dist-info/WHEEL +5 -0
- ssapy_toolkit-1.0.0.dist-info/licenses/LICENSE +28 -0
- ssapy_toolkit-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Acceleration models and utilities for orbit propagation."""
|
|
2
|
+
|
|
3
|
+
import importlib
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
# Get the directory of the current file
|
|
7
|
+
sub_dir = os.path.dirname(__file__)
|
|
8
|
+
|
|
9
|
+
# Loop through all Python files in this directory and import their functions
|
|
10
|
+
for filename in os.listdir(sub_dir):
|
|
11
|
+
if filename.endswith(".py") and filename != "__init__.py":
|
|
12
|
+
module_name = f"{__name__}.{filename[:-3]}"
|
|
13
|
+
module = importlib.import_module(module_name)
|
|
14
|
+
|
|
15
|
+
# Add all attributes of the module to the package namespace
|
|
16
|
+
for attr in dir(module):
|
|
17
|
+
if not attr.startswith("_"): # Avoid internal attributes
|
|
18
|
+
globals()[attr] = getattr(module, attr)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def accel_add(*accel_funcs):
|
|
5
|
+
"""
|
|
6
|
+
Returns a function that sums multiple acceleration functions.
|
|
7
|
+
|
|
8
|
+
Parameters
|
|
9
|
+
----------
|
|
10
|
+
accel_funcs : list of functions
|
|
11
|
+
Each must take (r) or (r, t) depending on your usage.
|
|
12
|
+
|
|
13
|
+
Returns
|
|
14
|
+
-------
|
|
15
|
+
combined : function
|
|
16
|
+
A function that evaluates and sums all input accelerations.
|
|
17
|
+
"""
|
|
18
|
+
def combined(r, t=None):
|
|
19
|
+
total = np.zeros(3)
|
|
20
|
+
for f in accel_funcs:
|
|
21
|
+
try:
|
|
22
|
+
total += f(r, t)
|
|
23
|
+
except TypeError:
|
|
24
|
+
total += f(r)
|
|
25
|
+
return total
|
|
26
|
+
return combined
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# ssapy_toolkit/Accelerations/accel_circularize.py
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from ..constants import EARTH_MU, EARTH_RADIUS # [56]
|
|
5
|
+
|
|
6
|
+
_orbit_achieved = False # global variable to track circularization
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def reset_orbit_status():
|
|
10
|
+
"""Reset the internal latch so circularization thrust can be used again."""
|
|
11
|
+
global _orbit_achieved
|
|
12
|
+
_orbit_achieved = False
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def accel_to_circular(r, v, t=None, *, thrust, tol=10.0, min_altitude=100e3):
|
|
16
|
+
"""
|
|
17
|
+
Acceleration command that steers (r, v) toward a circular orbit at radius |r|.
|
|
18
|
+
|
|
19
|
+
Designed to be passed into leapfrog(..., accels=[...]) where leapfrog may call
|
|
20
|
+
accelerations as f(r,v,t) (t is accepted but not required here).
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
r : array_like, shape (3,)
|
|
25
|
+
Position vector (m).
|
|
26
|
+
v : array_like, shape (3,)
|
|
27
|
+
Velocity vector (m/s).
|
|
28
|
+
t : optional
|
|
29
|
+
Time (ignored; present for integrator compatibility).
|
|
30
|
+
thrust : float
|
|
31
|
+
Magnitude of available acceleration (m/s^2).
|
|
32
|
+
tol : float, optional
|
|
33
|
+
Convergence tolerance on ||v_target - v|| (m/s). Default 10.
|
|
34
|
+
min_altitude : float, optional
|
|
35
|
+
Minimum altitude above Earth's surface to allow control (m). Default 100 km.
|
|
36
|
+
|
|
37
|
+
Returns
|
|
38
|
+
-------
|
|
39
|
+
a : ndarray, shape (3,)
|
|
40
|
+
Acceleration command (m/s^2), or zeros if converged/disabled.
|
|
41
|
+
"""
|
|
42
|
+
global _orbit_achieved
|
|
43
|
+
|
|
44
|
+
if _orbit_achieved:
|
|
45
|
+
return np.zeros(3, dtype=float)
|
|
46
|
+
|
|
47
|
+
r = np.asarray(r, dtype=float).reshape(3)
|
|
48
|
+
v = np.asarray(v, dtype=float).reshape(3)
|
|
49
|
+
|
|
50
|
+
r_norm = np.linalg.norm(r)
|
|
51
|
+
if r_norm == 0.0 or thrust == 0.0:
|
|
52
|
+
return np.zeros(3, dtype=float)
|
|
53
|
+
|
|
54
|
+
if r_norm < EARTH_RADIUS + float(min_altitude):
|
|
55
|
+
return np.zeros(3, dtype=float)
|
|
56
|
+
|
|
57
|
+
# Unit radial direction
|
|
58
|
+
r_hat = r / r_norm
|
|
59
|
+
|
|
60
|
+
# Tangential velocity component
|
|
61
|
+
v_r = float(np.dot(v, r_hat))
|
|
62
|
+
v_t_vec = v - v_r * r_hat
|
|
63
|
+
v_t = np.linalg.norm(v_t_vec)
|
|
64
|
+
|
|
65
|
+
if v_t > 0.0:
|
|
66
|
+
t_hat = v_t_vec / v_t
|
|
67
|
+
else:
|
|
68
|
+
# Fallback if velocity is purely radial: pick a consistent tangential direction
|
|
69
|
+
t_hat = np.cross(np.array([0.0, 0.0, 1.0]), r_hat)
|
|
70
|
+
n = np.linalg.norm(t_hat)
|
|
71
|
+
if n == 0.0:
|
|
72
|
+
return np.zeros(3, dtype=float)
|
|
73
|
+
t_hat /= n
|
|
74
|
+
|
|
75
|
+
# Circular speed at current radius
|
|
76
|
+
v_circ = np.sqrt(EARTH_MU / r_norm)
|
|
77
|
+
|
|
78
|
+
# Desired circular velocity vector (purely tangential)
|
|
79
|
+
v_target = v_circ * t_hat
|
|
80
|
+
|
|
81
|
+
dv = v_target - v
|
|
82
|
+
dv_norm = np.linalg.norm(dv)
|
|
83
|
+
|
|
84
|
+
if dv_norm <= float(tol) or dv_norm == 0.0:
|
|
85
|
+
_orbit_achieved = True
|
|
86
|
+
return np.zeros(3, dtype=float)
|
|
87
|
+
|
|
88
|
+
return float(thrust) * (dv / dv_norm)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
def accel_deltav(dv: float, thrust_accel: float, center_idx: int, dt: float = 1.0) -> dict:
|
|
4
|
+
"""
|
|
5
|
+
Build a Δv-based along-track accel profile centered at a given step.
|
|
6
|
+
|
|
7
|
+
Parameters
|
|
8
|
+
----------
|
|
9
|
+
dv : float
|
|
10
|
+
Signed total Δv to deliver (m/s). (Only its sign matters.)
|
|
11
|
+
thrust_accel : float
|
|
12
|
+
Available constant acceleration magnitude (m/s²). Must be > 0.
|
|
13
|
+
center_idx : int
|
|
14
|
+
Index in your time array where the burn is centered.
|
|
15
|
+
dt : float, optional
|
|
16
|
+
Uniform timestep between your indices (s). Defaults to 1 s.
|
|
17
|
+
|
|
18
|
+
Returns
|
|
19
|
+
-------
|
|
20
|
+
profile : dict
|
|
21
|
+
A dict with keys:
|
|
22
|
+
- 'thrust' : signed acceleration (m/s²)
|
|
23
|
+
- 'start' : start index (int)
|
|
24
|
+
- 'end' : end index (int)
|
|
25
|
+
which you can pass directly to `leapfrog(..., velocity=profile)`.
|
|
26
|
+
"""
|
|
27
|
+
if thrust_accel <= 0:
|
|
28
|
+
raise ValueError("`thrust_accel` must be positive")
|
|
29
|
+
if dt <= 0:
|
|
30
|
+
raise ValueError("`dt` must be positive")
|
|
31
|
+
|
|
32
|
+
# total burn time (s) required for |Δv|
|
|
33
|
+
burn_time = abs(dv) / thrust_accel
|
|
34
|
+
|
|
35
|
+
# number of steps (round to nearest int, minimum 1)
|
|
36
|
+
steps = max(1, int(round(burn_time / dt)))
|
|
37
|
+
|
|
38
|
+
# center the burn on center_idx
|
|
39
|
+
half = steps // 2
|
|
40
|
+
start = center_idx - half
|
|
41
|
+
end = start + steps # ensures end - start == steps
|
|
42
|
+
|
|
43
|
+
# signed acceleration to match dv's sign
|
|
44
|
+
signed_accel = thrust_accel * np.sign(dv)
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
"thrust": signed_accel,
|
|
48
|
+
"start": start,
|
|
49
|
+
"end": end,
|
|
50
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def accel_deltav_vector(dv, duration):
|
|
5
|
+
"""
|
|
6
|
+
Compute the constant acceleration vector needed to achieve a given Δv in a set time.
|
|
7
|
+
|
|
8
|
+
Parameters
|
|
9
|
+
----------
|
|
10
|
+
dv : array_like, shape (3,)
|
|
11
|
+
Desired change in velocity vector (m/s).
|
|
12
|
+
duration : float
|
|
13
|
+
Time over which to apply the acceleration (s).
|
|
14
|
+
|
|
15
|
+
Returns
|
|
16
|
+
-------
|
|
17
|
+
thrust : float
|
|
18
|
+
Magnitude of the required acceleration (m/s²).
|
|
19
|
+
direction : ndarray, shape (3,)
|
|
20
|
+
Unit vector indicating acceleration direction.
|
|
21
|
+
a_vec : ndarray, shape (3,)
|
|
22
|
+
Acceleration vector (m/s²) that, when applied constantly over `duration`, yields `dv`.
|
|
23
|
+
|
|
24
|
+
Raises
|
|
25
|
+
------
|
|
26
|
+
ValueError
|
|
27
|
+
If `duration` is zero or negative.
|
|
28
|
+
"""
|
|
29
|
+
dv = np.asarray(dv, dtype=float)
|
|
30
|
+
if duration <= 0:
|
|
31
|
+
raise ValueError("Duration must be positive and non-zero.")
|
|
32
|
+
dv_norm = np.linalg.norm(dv)
|
|
33
|
+
if dv_norm == 0:
|
|
34
|
+
return 0.0, np.zeros(3), np.zeros(3)
|
|
35
|
+
|
|
36
|
+
thrust = dv_norm / duration
|
|
37
|
+
direction = dv / dv_norm
|
|
38
|
+
a_vec = thrust * direction
|
|
39
|
+
return thrust, direction, a_vec
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# ssapy_toolkit/Accelerations/accel_earth_harmonics.py
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from ..constants import EARTH_MU, EARTH_RADIUS
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _check_r_safe(r2: float) -> None:
|
|
8
|
+
"""Raise error if r is below Earth's surface."""
|
|
9
|
+
r_mag = float(np.sqrt(r2))
|
|
10
|
+
if r_mag < EARTH_RADIUS:
|
|
11
|
+
raise ValueError(f"r magnitude ({r_mag:.2f} m) is below Earth's surface.")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def accel_J2(r: np.ndarray) -> np.ndarray:
|
|
15
|
+
J2 = 1.08262668e-3
|
|
16
|
+
mu = EARTH_MU
|
|
17
|
+
Re = EARTH_RADIUS
|
|
18
|
+
|
|
19
|
+
r = np.asarray(r, dtype=float).reshape(3)
|
|
20
|
+
r2 = float(np.dot(r, r))
|
|
21
|
+
_check_r_safe(r2)
|
|
22
|
+
r_mag = np.sqrt(r2)
|
|
23
|
+
|
|
24
|
+
inv_r5 = 1.0 / (r2 * r_mag**3) # = 1/r^5
|
|
25
|
+
z = r[2]
|
|
26
|
+
u = z / r_mag
|
|
27
|
+
k2 = 5.0 * u**2 - 1.0
|
|
28
|
+
|
|
29
|
+
factor2 = 1.5 * J2 * mu * Re**2 * inv_r5
|
|
30
|
+
return factor2 * np.array([r[0] * k2, r[1] * k2, r[2] * (5.0 * u**2 - 3.0)])
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def accel_J3(r: np.ndarray) -> np.ndarray:
|
|
34
|
+
J3 = -2.5324105e-6
|
|
35
|
+
mu = EARTH_MU
|
|
36
|
+
Re = EARTH_RADIUS
|
|
37
|
+
|
|
38
|
+
r = np.asarray(r, dtype=float).reshape(3)
|
|
39
|
+
r2 = float(np.dot(r, r))
|
|
40
|
+
_check_r_safe(r2)
|
|
41
|
+
r_mag = np.sqrt(r2)
|
|
42
|
+
|
|
43
|
+
inv_r6 = 1.0 / (r2**3) # = 1/r^6
|
|
44
|
+
z = r[2]
|
|
45
|
+
u = z / r_mag
|
|
46
|
+
|
|
47
|
+
P3 = 0.5 * (5.0 * u**3 - 3.0 * u)
|
|
48
|
+
dP3 = 0.5 * (15.0 * u**2 - 3.0)
|
|
49
|
+
factor3 = -mu * J3 * Re**3 * inv_r6
|
|
50
|
+
return factor3 * (4.0 * P3 * r - dP3 * r * u * np.array([0.0, 0.0, 1.0]))
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def accel_J4(r: np.ndarray) -> np.ndarray:
|
|
54
|
+
J4 = -1.6198976e-6
|
|
55
|
+
mu = EARTH_MU
|
|
56
|
+
Re = EARTH_RADIUS
|
|
57
|
+
|
|
58
|
+
r = np.asarray(r, dtype=float).reshape(3)
|
|
59
|
+
r2 = float(np.dot(r, r))
|
|
60
|
+
_check_r_safe(r2)
|
|
61
|
+
r_mag = np.sqrt(r2)
|
|
62
|
+
|
|
63
|
+
inv_r7 = 1.0 / (r2**2 * r_mag**3) # = 1/r^7
|
|
64
|
+
z = r[2]
|
|
65
|
+
u = z / r_mag
|
|
66
|
+
|
|
67
|
+
P4 = (1.0 / 8.0) * (35.0 * u**4 - 30.0 * u**2 + 3.0)
|
|
68
|
+
dP4 = (1.0 / 8.0) * (140.0 * u**3 - 60.0 * u)
|
|
69
|
+
factor4 = -mu * J4 * Re**4 * inv_r7
|
|
70
|
+
return factor4 * (5.0 * P4 * r - dP4 * r * u * np.array([0.0, 0.0, 1.0]))
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def accel_J5(r: np.ndarray) -> np.ndarray:
|
|
74
|
+
J5 = -2.272960828e-7
|
|
75
|
+
mu = EARTH_MU
|
|
76
|
+
Re = EARTH_RADIUS
|
|
77
|
+
|
|
78
|
+
r = np.asarray(r, dtype=float).reshape(3)
|
|
79
|
+
r2 = float(np.dot(r, r))
|
|
80
|
+
_check_r_safe(r2)
|
|
81
|
+
r_mag = np.sqrt(r2)
|
|
82
|
+
|
|
83
|
+
inv_r9 = 1.0 / (r2**4 * r_mag) # = 1/r^9
|
|
84
|
+
z = r[2]
|
|
85
|
+
u = z / r_mag
|
|
86
|
+
|
|
87
|
+
P5 = (1.0 / 8.0) * (63.0 * u**5 - 70.0 * u**3 + 15.0 * u)
|
|
88
|
+
dP5 = (1.0 / 8.0) * (315.0 * u**4 - 210.0 * u**2 + 15.0)
|
|
89
|
+
factor5 = -mu * J5 * Re**5 * inv_r9
|
|
90
|
+
return factor5 * (6.0 * P5 * r - dP5 * r * u * np.array([0.0, 0.0, 1.0]))
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def accel_J6(r: np.ndarray) -> np.ndarray:
|
|
94
|
+
J6 = 5.406812391e-7
|
|
95
|
+
mu = EARTH_MU
|
|
96
|
+
Re = EARTH_RADIUS
|
|
97
|
+
|
|
98
|
+
r = np.asarray(r, dtype=float).reshape(3)
|
|
99
|
+
r2 = float(np.dot(r, r))
|
|
100
|
+
_check_r_safe(r2)
|
|
101
|
+
r_mag = np.sqrt(r2)
|
|
102
|
+
|
|
103
|
+
inv_r10 = 1.0 / (r2**4 * r_mag**2) # = 1/r^10
|
|
104
|
+
z = r[2]
|
|
105
|
+
u = z / r_mag
|
|
106
|
+
|
|
107
|
+
P6 = (1.0 / 16.0) * (231.0 * u**6 - 315.0 * u**4 + 105.0 * u**2 - 5.0)
|
|
108
|
+
dP6 = (1.0 / 16.0) * (1386.0 * u**5 - 1260.0 * u**3 + 210.0 * u)
|
|
109
|
+
factor6 = -mu * J6 * Re**6 * inv_r10
|
|
110
|
+
return factor6 * (7.0 * P6 * r - dP6 * r * u * np.array([0.0, 0.0, 1.0]))
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def accel_J7(r: np.ndarray) -> np.ndarray:
|
|
114
|
+
J7 = -3.529000898e-7
|
|
115
|
+
mu = EARTH_MU
|
|
116
|
+
Re = EARTH_RADIUS
|
|
117
|
+
|
|
118
|
+
r = np.asarray(r, dtype=float).reshape(3)
|
|
119
|
+
r2 = float(np.dot(r, r))
|
|
120
|
+
_check_r_safe(r2)
|
|
121
|
+
r_mag = np.sqrt(r2)
|
|
122
|
+
|
|
123
|
+
inv_r11 = 1.0 / (r2**4 * r_mag**3) # = 1/r^11
|
|
124
|
+
z = r[2]
|
|
125
|
+
u = z / r_mag
|
|
126
|
+
|
|
127
|
+
P7 = (1.0 / 16.0) * (429.0 * u**7 - 693.0 * u**5 + 315.0 * u**3 - 35.0 * u)
|
|
128
|
+
dP7 = (1.0 / 16.0) * (3003.0 * u**6 - 3465.0 * u**4 + 945.0 * u**2 - 35.0)
|
|
129
|
+
factor7 = -mu * J7 * Re**7 * inv_r11
|
|
130
|
+
return factor7 * (8.0 * P7 * r - dP7 * r * u * np.array([0.0, 0.0, 1.0]))
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def accel_J8(r: np.ndarray) -> np.ndarray:
|
|
134
|
+
J8 = 2.532000898e-7
|
|
135
|
+
mu = EARTH_MU
|
|
136
|
+
Re = EARTH_RADIUS
|
|
137
|
+
|
|
138
|
+
r = np.asarray(r, dtype=float).reshape(3)
|
|
139
|
+
r2 = float(np.dot(r, r))
|
|
140
|
+
_check_r_safe(r2)
|
|
141
|
+
r_mag = np.sqrt(r2)
|
|
142
|
+
|
|
143
|
+
inv_r12 = 1.0 / (r2**5 * r_mag**2) # = 1/r^12
|
|
144
|
+
z = r[2]
|
|
145
|
+
u = z / r_mag
|
|
146
|
+
|
|
147
|
+
P8 = (1.0 / 128.0) * (6435.0 * u**8 - 12012.0 * u**6 + 6930.0 * u**4 - 1260.0 * u**2 + 35.0)
|
|
148
|
+
dP8 = (1.0 / 128.0) * (51480.0 * u**7 - 72072.0 * u**5 + 27720.0 * u**3 - 2520.0 * u)
|
|
149
|
+
factor8 = -mu * J8 * Re**8 * inv_r12
|
|
150
|
+
return factor8 * (9.0 * P8 * r - dP8 * r * u * np.array([0.0, 0.0, 1.0]))
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def accel_earth_harmonics(r: np.ndarray) -> np.ndarray:
|
|
154
|
+
"""
|
|
155
|
+
Central gravity + J2..J8 zonal harmonics.
|
|
156
|
+
"""
|
|
157
|
+
r = np.asarray(r, dtype=float).reshape(3)
|
|
158
|
+
r2 = float(np.dot(r, r))
|
|
159
|
+
_check_r_safe(r2)
|
|
160
|
+
r_mag = np.sqrt(r2)
|
|
161
|
+
|
|
162
|
+
a_central = -EARTH_MU * r / (r_mag**3) # [59]
|
|
163
|
+
return (
|
|
164
|
+
a_central
|
|
165
|
+
+ accel_J2(r)
|
|
166
|
+
+ accel_J3(r)
|
|
167
|
+
+ accel_J4(r)
|
|
168
|
+
+ accel_J5(r)
|
|
169
|
+
+ accel_J6(r)
|
|
170
|
+
+ accel_J7(r)
|
|
171
|
+
+ accel_J8(r)
|
|
172
|
+
)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def accel_equatorial(r, v, magnitude):
|
|
5
|
+
"""
|
|
6
|
+
Acceleration vector always in the equatorial (east/west) direction,
|
|
7
|
+
i.e. perpendicular to the Earth's spin axis and radial direction,
|
|
8
|
+
positive -> counter-clockwise (eastward), negative -> clockwise.
|
|
9
|
+
|
|
10
|
+
Parameters
|
|
11
|
+
----------
|
|
12
|
+
r : array_like, shape (3,)
|
|
13
|
+
Position (radial) vector in meters.
|
|
14
|
+
v : array_like, shape (3,)
|
|
15
|
+
(Unused; kept for signature consistency.)
|
|
16
|
+
magnitude : float
|
|
17
|
+
Desired magnitude of the equatorial-plane acceleration in m/s^2.
|
|
18
|
+
|
|
19
|
+
Returns
|
|
20
|
+
-------
|
|
21
|
+
a : ndarray, shape (3,)
|
|
22
|
+
Equatorial-plane acceleration vector in m/s^2.
|
|
23
|
+
"""
|
|
24
|
+
r = np.asarray(r, dtype=float).reshape(3)
|
|
25
|
+
|
|
26
|
+
if magnitude == 0:
|
|
27
|
+
return np.zeros(3, dtype=float)
|
|
28
|
+
|
|
29
|
+
norm_r = np.linalg.norm(r)
|
|
30
|
+
if norm_r == 0:
|
|
31
|
+
return np.zeros(3, dtype=float)
|
|
32
|
+
|
|
33
|
+
r_hat = r / norm_r
|
|
34
|
+
z_hat = np.array([0.0, 0.0, 1.0], dtype=float)
|
|
35
|
+
|
|
36
|
+
equ_dir = np.cross(z_hat, r_hat)
|
|
37
|
+
norm_eq = np.linalg.norm(equ_dir)
|
|
38
|
+
if norm_eq == 0:
|
|
39
|
+
return np.zeros(3, dtype=float)
|
|
40
|
+
|
|
41
|
+
return float(magnitude) * equ_dir / norm_eq
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# ssapy_toolkit/Accelerations/accel_inclination.py
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def accel_inclination(r, v, magnitude):
|
|
7
|
+
"""
|
|
8
|
+
Acceleration vector always in the local north/south direction,
|
|
9
|
+
i.e. perpendicular to the radial direction but aimed toward
|
|
10
|
+
the Earth's North pole (or South if magnitude < 0).
|
|
11
|
+
|
|
12
|
+
Parameters
|
|
13
|
+
----------
|
|
14
|
+
r : array_like, shape (3,)
|
|
15
|
+
Position (radial) vector in meters.
|
|
16
|
+
v : array_like, shape (3,)
|
|
17
|
+
(Unused; kept for signature consistency.)
|
|
18
|
+
magnitude : float
|
|
19
|
+
Desired magnitude of the inclination-change acceleration in m/s^2.
|
|
20
|
+
Positive -> northward; negative -> southward.
|
|
21
|
+
|
|
22
|
+
Returns
|
|
23
|
+
-------
|
|
24
|
+
a : ndarray, shape (3,)
|
|
25
|
+
Inclination-change acceleration vector in m/s^2.
|
|
26
|
+
"""
|
|
27
|
+
r = np.asarray(r, dtype=float).reshape(3)
|
|
28
|
+
norm_r = np.linalg.norm(r)
|
|
29
|
+
if norm_r == 0.0 or magnitude == 0.0:
|
|
30
|
+
return np.zeros(3, dtype=float)
|
|
31
|
+
|
|
32
|
+
r_hat = r / norm_r
|
|
33
|
+
z_hat = np.array([0.0, 0.0, 1.0], dtype=float)
|
|
34
|
+
|
|
35
|
+
north_dir = z_hat - np.dot(z_hat, r_hat) * r_hat
|
|
36
|
+
norm_nd = np.linalg.norm(north_dir)
|
|
37
|
+
if norm_nd == 0.0:
|
|
38
|
+
return np.zeros(3, dtype=float)
|
|
39
|
+
|
|
40
|
+
return float(magnitude) * north_dir / norm_nd
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# ssapy_toolkit/Accelerations/accel_moon.py
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from astropy.time import Time
|
|
5
|
+
from astropy import units as u
|
|
6
|
+
from astropy.coordinates import get_body, GCRS, solar_system_ephemeris
|
|
7
|
+
|
|
8
|
+
from ..constants import MOON_MU
|
|
9
|
+
from ..Time_Functions import to_gps
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def accel_point_moon(r: np.ndarray, time) -> np.ndarray:
|
|
13
|
+
"""
|
|
14
|
+
Point-mass lunar gravity in GCRF.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
r : array_like, shape (3,)
|
|
19
|
+
Satellite position vector in GCRF (m), Earth-centered.
|
|
20
|
+
time : float | datetime-like | astropy.time.Time | array_like
|
|
21
|
+
Time corresponding to the state (anything supported by to_gps()).
|
|
22
|
+
|
|
23
|
+
Returns
|
|
24
|
+
-------
|
|
25
|
+
a_moon : ndarray, shape (3,)
|
|
26
|
+
Acceleration from the Moon (m/s^2).
|
|
27
|
+
"""
|
|
28
|
+
r = np.asarray(r, dtype=float).reshape(3)
|
|
29
|
+
|
|
30
|
+
# 1) Convert time to GPS seconds since 1980-01-06
|
|
31
|
+
time_gps = to_gps(time) # [176]
|
|
32
|
+
t = Time(time_gps, format="gps", scale="utc")
|
|
33
|
+
|
|
34
|
+
# 2) Get Moon position in GCRS at this time using JPL ephemeris
|
|
35
|
+
with solar_system_ephemeris.set("jpl"):
|
|
36
|
+
moon_gcrs = get_body("moon", t).transform_to(GCRS(obstime=t))
|
|
37
|
+
|
|
38
|
+
r_moon = moon_gcrs.cartesian.xyz.to(u.m).value # (3,)
|
|
39
|
+
|
|
40
|
+
# 3) Compute delta and gravitational acceleration
|
|
41
|
+
delta = r_moon - r
|
|
42
|
+
d = np.linalg.norm(delta)
|
|
43
|
+
if d == 0.0:
|
|
44
|
+
return np.zeros(3, dtype=float)
|
|
45
|
+
|
|
46
|
+
return MOON_MU * delta / d**3
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# ssapy_toolkit/Accelerations/accel_plane.py
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def accel_plane(r, v, magnitude):
|
|
7
|
+
"""
|
|
8
|
+
Tangential acceleration in the orbital plane, perpendicular to r̂.
|
|
9
|
+
|
|
10
|
+
Parameters
|
|
11
|
+
----------
|
|
12
|
+
r : array_like, shape (3,)
|
|
13
|
+
Position vector in meters.
|
|
14
|
+
v : array_like, shape (3,)
|
|
15
|
+
Velocity vector in meters per second.
|
|
16
|
+
magnitude : float
|
|
17
|
+
Desired acceleration magnitude in m/s^2.
|
|
18
|
+
Positive → along the direction of motion;
|
|
19
|
+
negative → opposite direction.
|
|
20
|
+
|
|
21
|
+
Returns
|
|
22
|
+
-------
|
|
23
|
+
a : ndarray, shape (3,)
|
|
24
|
+
Tangential acceleration vector in m/s^2.
|
|
25
|
+
"""
|
|
26
|
+
r = np.asarray(r, dtype=float).reshape(3)
|
|
27
|
+
v = np.asarray(v, dtype=float).reshape(3)
|
|
28
|
+
|
|
29
|
+
if magnitude == 0:
|
|
30
|
+
return np.zeros(3, dtype=float)
|
|
31
|
+
|
|
32
|
+
norm_r = np.linalg.norm(r)
|
|
33
|
+
if norm_r == 0:
|
|
34
|
+
return np.zeros(3, dtype=float)
|
|
35
|
+
|
|
36
|
+
r_hat = r / norm_r
|
|
37
|
+
|
|
38
|
+
# Remove radial component of v to get purely tangential direction
|
|
39
|
+
tangential = v - np.dot(v, r_hat) * r_hat
|
|
40
|
+
norm_tan = np.linalg.norm(tangential)
|
|
41
|
+
if norm_tan == 0:
|
|
42
|
+
return np.zeros(3, dtype=float)
|
|
43
|
+
|
|
44
|
+
return float(magnitude) * tangential / norm_tan
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def accel_radial(r, magnitude):
|
|
5
|
+
"""
|
|
6
|
+
Acceleration vector pointing radially outward from Earth's center
|
|
7
|
+
with user-specified magnitude.
|
|
8
|
+
|
|
9
|
+
Parameters
|
|
10
|
+
----------
|
|
11
|
+
r : array_like, shape (3,)
|
|
12
|
+
Position vector in meters.
|
|
13
|
+
magnitude : float
|
|
14
|
+
Acceleration magnitude in m/s^2.
|
|
15
|
+
|
|
16
|
+
Returns
|
|
17
|
+
-------
|
|
18
|
+
a : ndarray, shape (3,)
|
|
19
|
+
Acceleration vector in m/s^2.
|
|
20
|
+
|
|
21
|
+
Author: Travis Yeager
|
|
22
|
+
"""
|
|
23
|
+
r = np.asarray(r)
|
|
24
|
+
norm_r = np.linalg.norm(r)
|
|
25
|
+
if norm_r == 0.0:
|
|
26
|
+
return np.zeros(3)
|
|
27
|
+
return magnitude * r / norm_r
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from astropy.time import Time
|
|
3
|
+
from astropy import units as u
|
|
4
|
+
from astropy.coordinates import get_body, GCRS, solar_system_ephemeris
|
|
5
|
+
|
|
6
|
+
from ..constants import SUN_MU
|
|
7
|
+
from ..Time_Functions import to_gps
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def accel_point_sun(r: np.ndarray, time) -> np.ndarray:
|
|
11
|
+
"""
|
|
12
|
+
Point-mass solar gravity in GCRF, given raw_time (same input as leapfrog).
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
r : array_like, shape (3,)
|
|
17
|
+
Satellite position vector in GCRF (m), Earth-centered.
|
|
18
|
+
time : float or datetime-like or array_like
|
|
19
|
+
The time array element from leapfrog (same type you passed into leapfrog).
|
|
20
|
+
|
|
21
|
+
Returns
|
|
22
|
+
-------
|
|
23
|
+
a_sun : ndarray, shape (3,)
|
|
24
|
+
Acceleration from the Sun (m/s²).
|
|
25
|
+
"""
|
|
26
|
+
# 1) Convert time to GPS seconds
|
|
27
|
+
time_gps = to_gps(time)
|
|
28
|
+
t = Time(time_gps, format="gps", scale="utc")
|
|
29
|
+
|
|
30
|
+
# 2) Get Sun position in GCRS at this time using JPL ephemeris
|
|
31
|
+
with solar_system_ephemeris.set('jpl'):
|
|
32
|
+
sun_gcrs = get_body("sun", t).transform_to(GCRS(obstime=t))
|
|
33
|
+
|
|
34
|
+
r_sun = sun_gcrs.cartesian.xyz.to(u.m).value
|
|
35
|
+
|
|
36
|
+
# 3) Compute delta and gravitational acceleration
|
|
37
|
+
delta = r_sun - r
|
|
38
|
+
return SUN_MU * delta / np.linalg.norm(delta) ** 3
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# ssapy_toolkit/Accelerations/accel_uniform_earth.py
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from ..constants import EARTH_RADIUS, EARTH_MU
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def accel_uniform_earth(r):
|
|
8
|
+
"""
|
|
9
|
+
Piecewise gravity model:
|
|
10
|
+
- Outside Earth: point-mass gravity ~ -mu r / |r|^3
|
|
11
|
+
- Inside Earth (uniform-density sphere): linear gravity ~ -mu r / R^3
|
|
12
|
+
|
|
13
|
+
Parameters
|
|
14
|
+
----------
|
|
15
|
+
r : array_like, shape (3,)
|
|
16
|
+
Position vector (m).
|
|
17
|
+
|
|
18
|
+
Returns
|
|
19
|
+
-------
|
|
20
|
+
a : ndarray, shape (3,)
|
|
21
|
+
Acceleration (m/s^2).
|
|
22
|
+
"""
|
|
23
|
+
x, y, z = r
|
|
24
|
+
r_mag = np.sqrt(x**2 + y**2 + z**2)
|
|
25
|
+
|
|
26
|
+
if r_mag < EARTH_RADIUS:
|
|
27
|
+
factor = -EARTH_MU / (EARTH_RADIUS**3)
|
|
28
|
+
else:
|
|
29
|
+
factor = -EARTH_MU / (r_mag**3)
|
|
30
|
+
|
|
31
|
+
return np.array([factor * x, factor * y, factor * z])
|