-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathsimple.py
286 lines (245 loc) · 10.7 KB
/
simple.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# flake8: noqa: E501
"""
Functions to simplify certain aspects of SSAPy.
For when you want a quick answer and do not need want to use high fidelity defaults.
"""
from .utils import get_times, points_on_circle
from . import Orbit, rv
from .accel import AccelKepler, AccelSolRad, AccelEarthRad, AccelDrag
from .body import get_body
from .gravity import AccelHarmonic, AccelThirdBody
from .propagator import RK78Propagator
from astropy.time import Time
import numpy as np
from typing import Union, List, Tuple
def keplerian_prop(integration_timestep: float = 10) -> RK78Propagator:
"""
Create and return an RK78Propagator for a Keplerian orbit.
This propagator uses only the Keplerian acceleration for a two-body problem.
Parameters:
----------
integration_timestep : float, optional
The time step for the RK78Propagator integration (default is 10 seconds).
Returns:
-------
RK78Propagator
An instance of RK78Propagator configured with Keplerian acceleration.
"""
return RK78Propagator(AccelKepler(), h=integration_timestep)
accel_3_cache = None
def threebody_prop(integration_timestep: float = 10) -> RK78Propagator:
"""
Create and return an RK78Propagator with a set of accelerations for a three-body problem.
The three bodies considered are Earth (Keplerian effect), Moon, and the Earth itself.
Parameters:
----------
integration_timestep : float, optional
The time step for the RK78Propagator integration (default is 10 seconds).
Returns:
-------
RK78Propagator
An instance of RK78Propagator configured with the three-body accelerations.
"""
global accel_3_cache
if accel_3_cache is None:
accel_3_cache = AccelKepler() + AccelThirdBody(get_body("moon"))
return RK78Propagator(accel_3_cache, h=integration_timestep)
accel_4_cache = None
def fourbody_prop(integration_timestep: float = 10) -> RK78Propagator:
"""
Create and return an RK78Propagator with a set of accelerations for a four-body problem.
The four bodies considered are Earth (Keplerian effect), Moon, Sun, and the Earth itself.
Parameters:
----------
integration_timestep : float, optional
The time step for the RK78Propagator integration (default is 10 seconds).
Returns:
-------
RK78Propagator
An instance of RK78Propagator configured with the four-body accelerations.
"""
global accel_4_cache
if accel_4_cache is None:
accel_4_cache = AccelKepler() + AccelThirdBody(get_body("moon")) + AccelThirdBody(get_body("Sun"))
return RK78Propagator(accel_4_cache, h=integration_timestep)
accel_best_cache = None
def best_prop(integration_timestep=10, kwargs=dict(mass=250, area=.022, CD=2.3, CR=1.3)):
"""
Create and return an RK78Propagator with a comprehensive set of accelerations.
Parameters:
----------
integration_timestep : float, optional
The time step for the RK78Propagator integration (default is 10 seconds).
kwargs : dict, optional
Dictionary of parameters for non-conservative forces (mass, area, CD, CR).
If not provided, defaults are used.
Returns:
-------
RK78Propagator
An instance of RK78Propagator configured with cached accelerations.
"""
global accel_best_cache
if accel_best_cache is None:
aEarth = AccelKepler() + AccelHarmonic(get_body("Earth", model="EGM2008"), 140, 140)
aMoon = AccelThirdBody(get_body("moon")) + AccelHarmonic(get_body("moon"), 20, 20)
aSun = AccelThirdBody(get_body("Sun"))
aMercury = AccelThirdBody(get_body("Mercury"))
aVenus = AccelThirdBody(get_body("Venus"))
aMars = AccelThirdBody(get_body("Mars"))
aJupiter = AccelThirdBody(get_body("Jupiter"))
aSaturn = AccelThirdBody(get_body("Saturn"))
aUranus = AccelThirdBody(get_body("Uranus"))
aNeptune = AccelThirdBody(get_body("Neptune"))
nonConservative = AccelSolRad(**kwargs) + AccelEarthRad(**kwargs) + AccelDrag(**kwargs)
planets = aMercury + aVenus + aMars + aJupiter + aSaturn + aUranus + aNeptune
accel_best_cache = aEarth + aMoon + aSun + planets + nonConservative
return RK78Propagator(accel_best_cache, h=integration_timestep)
def ssapy_kwargs(mass=250, area=0.022, CD=2.3, CR=1.3):
"""
Generate a dictionary of default parameters for a space object used in simulations.
Parameters:
----------
mass : float, optional
Mass of the object in kilograms (default is 250 kg).
area : float, optional
Cross-sectional area of the object in square meters (default is 0.022 m^2).
CD : float, optional
Drag coefficient of the object (default is 2.3).
CR : float, optional
Radiation pressure coefficient of the object (default is 1.3).
Returns:
-------
dict
A dictionary containing the parameters for the space object.
"""
# Asteroid parameters
kwargs = dict(
mass=mass, # [kg]
area=area, # [m^2]
CD=CD, # Drag coefficient
CR=CR, # Radiation pressure coefficient
)
return kwargs
def ssapy_prop(integration_timestep=60, propkw=ssapy_kwargs()):
"""
Setup and return an RK78 propagator with specified accelerations and radiation pressure effects.
Parameters:
----------
integration_timestep : int
Time step for the numerical integration (in seconds).
propkw : dict, optional
Keyword arguments for radiation pressure accelerations. If None, default arguments are used.
Returns:
-------
RK78Propagator
An RK78 propagator configured with the specified accelerations and time step.
"""
# Accelerations - pass a body object or string of body name.
moon = get_body("moon")
sun = get_body("Sun")
Mercury = get_body("Mercury")
Venus = get_body("Venus")
Earth = get_body("Earth", model="EGM2008")
Mars = get_body("Mars")
Jupiter = get_body("Jupiter")
Saturn = get_body("Saturn")
Uranus = get_body("Uranus")
Neptune = get_body("Neptune")
aEarth = AccelKepler() + AccelHarmonic(Earth, 140, 140)
aSun = AccelThirdBody(sun)
aMoon = AccelThirdBody(moon) + AccelHarmonic(moon, 20, 20)
aSolRad = AccelSolRad(**propkw)
aEarthRad = AccelEarthRad(**propkw)
accel = aEarth + aMoon + aSun + aSolRad + aEarthRad
# Build propagator
prop = RK78Propagator(accel, h=integration_timestep)
return prop
# Uses the current best propagator and acceleration models in ssapy
def ssapy_orbit(orbit=None, a=None, e=0, i=0, pa=0, raan=0, ta=0, r=None, v=None, duration=(30, 'day'), freq=(1, 'hr'), t0=Time("2025-01-01", scale='utc'), t=None, prop=ssapy_prop()):
"""
Compute the orbit of a spacecraft given either Keplerian elements or position and velocity vectors.
Parameters:
- orbit (Orbit, optional): An Orbit object if you already have an orbit defined.
- a (float, optional): Semi-major axis of the orbit in meters.
- e (float, optional): Eccentricity of the orbit (default is 0, i.e., circular orbit).
- i (float, optional): Inclination of the orbit in degrees.
- pa (float, optional): Argument of perigee in degrees.
- raan (float, optional): Right ascension of the ascending node in degrees.
- ta (float, optional): True anomaly in degrees.
- r (array-like, optional): Position vector in meters.
- v (array-like, optional): Velocity vector in meters per second.
- duration (tuple, optional): Duration of the simulation as a tuple (value, unit), where unit is 'day', 'hour', etc. Default is 30 days.
- freq (tuple, optional): Frequency of the output as a tuple (value, unit), where unit is 'day', 'hour', etc. Default is 1 hour.
- t0 (str, optional): Start date of the simulation in 'YYYY-MM-DD' format. Default is "2025-01-01".
- t (array-like, optional): Specific times at which to compute the orbit. If None, times will be generated based on duration and frequency.
- prop (function, optional): A function to compute the perturbation effects. Default is `ssapy_prop()`.
Returns:
- r (array-like): Position vectors of the spacecraft at the specified times.
- v (array-like): Velocity vectors of the spacecraft at the specified times.
- t (array-like): Times at which the orbit was computed. Returned only if `t` was None.
Raises:
- ValueError: If neither Keplerian elements nor position and velocity vectors are provided.
- RuntimeError or ValueError: If an error occurs during computation.
"""
t0 = Time(t0, scale='utc')
if t is None:
time_is_None = True
t = get_times(duration=duration, freq=freq, t0=t0)
else:
t0 = t[0]
time_is_None = False
if orbit is not None:
pass
elif a is not None:
kElements = [a, e, i, pa, raan, ta]
orbit = Orbit.fromKeplerianElements(*kElements, t0)
elif r is not None and v is not None:
orbit = Orbit(r, v, t0)
else:
raise ValueError("Either Keplerian elements (a, e, i, pa, raan, ta) or position and velocity vectors (r, v) must be provided.")
try:
r, v = rv(orbit, t, prop)
if time_is_None:
return r, v, t
else:
return r, v
except (RuntimeError, ValueError) as err:
print(err)
return np.nan, np.nan, np.nan
def get_similar_orbits(r0, v0, rad=1e5, num_orbits=4, duration=(90, 'days'), freq=(1, 'hour'), start_date="2025-1-1", mass=250):
"""
Generate similar orbits by varying the initial position.
Parameters:
----------
r0 : array_like
Initial position vector of shape (3,).
v0 : array_like
Initial velocity vector of shape (3,).
rad : float
Radius of the circle around the initial position to generate similar orbits.
num_orbits : int
Number of similar orbits to generate.
duration : tuple
Duration of the orbit simulation.
freq : tuple
Frequency of output data.
start_date : str
Start date for the simulation.
mass : float
Mass of the satellite.
Returns:
-------
trajectories : ndarray
Stacked array of shape (3, n_times, num_orbits) containing the trajectories.
"""
r0 = np.reshape(r0, (1, 3))
v0 = np.reshape(v0, (1, 3))
print(r0, v0)
for idx, point in enumerate(points_on_circle(r0, v0, rad=rad, num_points=num_orbits)):
# Calculate entire satellite trajectory
r, v = ssapy_orbit(r=point, v=v0, duration=duration, freq=freq, start_date=start_date, integration_timestep=10, mass=mass, area=mass / 19000 + 0.01, CD=2.3, CR=1.3)
if idx == 0:
trajectories = np.concatenate((r0, v0), axis=1)[:len(r)]
rv = np.concatenate((r, v), axis=1)
trajectories = np.dstack((trajectories, rv))
return trajectories