nrl-tracker 0.22.5__py3-none-any.whl → 1.7.5__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.
- {nrl_tracker-0.22.5.dist-info → nrl_tracker-1.7.5.dist-info}/METADATA +57 -10
- {nrl_tracker-0.22.5.dist-info → nrl_tracker-1.7.5.dist-info}/RECORD +84 -69
- pytcl/__init__.py +4 -3
- pytcl/assignment_algorithms/__init__.py +28 -0
- pytcl/assignment_algorithms/gating.py +10 -10
- pytcl/assignment_algorithms/jpda.py +40 -40
- pytcl/assignment_algorithms/nd_assignment.py +379 -0
- pytcl/assignment_algorithms/network_flow.py +371 -0
- pytcl/assignment_algorithms/three_dimensional/assignment.py +3 -3
- pytcl/astronomical/__init__.py +104 -3
- pytcl/astronomical/ephemerides.py +14 -11
- pytcl/astronomical/reference_frames.py +865 -56
- pytcl/astronomical/relativity.py +6 -5
- pytcl/astronomical/sgp4.py +710 -0
- pytcl/astronomical/special_orbits.py +532 -0
- pytcl/astronomical/tle.py +558 -0
- pytcl/atmosphere/__init__.py +43 -1
- pytcl/atmosphere/ionosphere.py +512 -0
- pytcl/atmosphere/nrlmsise00.py +809 -0
- pytcl/clustering/dbscan.py +2 -2
- pytcl/clustering/gaussian_mixture.py +3 -3
- pytcl/clustering/hierarchical.py +15 -15
- pytcl/clustering/kmeans.py +4 -4
- pytcl/containers/__init__.py +24 -0
- pytcl/containers/base.py +219 -0
- pytcl/containers/cluster_set.py +12 -2
- pytcl/containers/covertree.py +26 -29
- pytcl/containers/kd_tree.py +94 -29
- pytcl/containers/rtree.py +200 -1
- pytcl/containers/vptree.py +21 -28
- pytcl/coordinate_systems/conversions/geodetic.py +272 -5
- pytcl/coordinate_systems/jacobians/jacobians.py +2 -2
- pytcl/coordinate_systems/projections/__init__.py +1 -1
- pytcl/coordinate_systems/projections/projections.py +2 -2
- pytcl/coordinate_systems/rotations/rotations.py +10 -6
- pytcl/core/__init__.py +18 -0
- pytcl/core/validation.py +333 -2
- pytcl/dynamic_estimation/__init__.py +26 -0
- pytcl/dynamic_estimation/gaussian_sum_filter.py +434 -0
- pytcl/dynamic_estimation/imm.py +14 -14
- pytcl/dynamic_estimation/kalman/__init__.py +30 -0
- pytcl/dynamic_estimation/kalman/constrained.py +382 -0
- pytcl/dynamic_estimation/kalman/extended.py +8 -8
- pytcl/dynamic_estimation/kalman/h_infinity.py +613 -0
- pytcl/dynamic_estimation/kalman/square_root.py +60 -573
- pytcl/dynamic_estimation/kalman/sr_ukf.py +302 -0
- pytcl/dynamic_estimation/kalman/ud_filter.py +410 -0
- pytcl/dynamic_estimation/kalman/unscented.py +8 -6
- pytcl/dynamic_estimation/particle_filters/bootstrap.py +15 -15
- pytcl/dynamic_estimation/rbpf.py +589 -0
- pytcl/gravity/egm.py +13 -0
- pytcl/gravity/spherical_harmonics.py +98 -37
- pytcl/gravity/tides.py +6 -6
- pytcl/logging_config.py +328 -0
- pytcl/magnetism/__init__.py +7 -0
- pytcl/magnetism/emm.py +10 -3
- pytcl/magnetism/wmm.py +260 -23
- pytcl/mathematical_functions/combinatorics/combinatorics.py +5 -5
- pytcl/mathematical_functions/geometry/geometry.py +5 -5
- pytcl/mathematical_functions/numerical_integration/quadrature.py +6 -6
- pytcl/mathematical_functions/signal_processing/detection.py +24 -24
- pytcl/mathematical_functions/signal_processing/filters.py +14 -14
- pytcl/mathematical_functions/signal_processing/matched_filter.py +12 -12
- pytcl/mathematical_functions/special_functions/bessel.py +15 -3
- pytcl/mathematical_functions/special_functions/debye.py +136 -26
- pytcl/mathematical_functions/special_functions/error_functions.py +3 -1
- pytcl/mathematical_functions/special_functions/gamma_functions.py +4 -4
- pytcl/mathematical_functions/special_functions/hypergeometric.py +81 -15
- pytcl/mathematical_functions/transforms/fourier.py +8 -8
- pytcl/mathematical_functions/transforms/stft.py +12 -12
- pytcl/mathematical_functions/transforms/wavelets.py +9 -9
- pytcl/navigation/geodesy.py +246 -160
- pytcl/navigation/great_circle.py +101 -19
- pytcl/plotting/coordinates.py +7 -7
- pytcl/plotting/tracks.py +2 -2
- pytcl/static_estimation/maximum_likelihood.py +16 -14
- pytcl/static_estimation/robust.py +5 -5
- pytcl/terrain/loaders.py +5 -5
- pytcl/trackers/hypothesis.py +1 -1
- pytcl/trackers/mht.py +9 -9
- pytcl/trackers/multi_target.py +1 -1
- {nrl_tracker-0.22.5.dist-info → nrl_tracker-1.7.5.dist-info}/LICENSE +0 -0
- {nrl_tracker-0.22.5.dist-info → nrl_tracker-1.7.5.dist-info}/WHEEL +0 -0
- {nrl_tracker-0.22.5.dist-info → nrl_tracker-1.7.5.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,809 @@
|
|
|
1
|
+
"""
|
|
2
|
+
NRLMSISE-00 Atmospheric Model
|
|
3
|
+
|
|
4
|
+
High-fidelity thermosphere/atmosphere model from the U.S. Naval Research
|
|
5
|
+
Laboratory. Provides density, temperature, and composition profiles for
|
|
6
|
+
altitudes from -5 km to 1000 km.
|
|
7
|
+
|
|
8
|
+
This implementation uses an empirical approach based on atmospheric chemistry,
|
|
9
|
+
radiative transfer, and geomagnetic coupling for modeling temperature and
|
|
10
|
+
density variations with altitude, latitude, local time, and solar/magnetic activity.
|
|
11
|
+
|
|
12
|
+
References
|
|
13
|
+
----------
|
|
14
|
+
.. [1] Picone, J. M., A. E. Hedin, D. P. Drob, and A. C. Aikin (2002),
|
|
15
|
+
"NRLMSISE-00 empirical model of the atmosphere: Statistical
|
|
16
|
+
comparisons and scientific issues," J. Geophys. Res., 107(A12), 1468,
|
|
17
|
+
doi:10.1029/2002JA009430
|
|
18
|
+
.. [2] NASA GSFC NRLMSISE-00 Model:
|
|
19
|
+
https://ccmc.gsfc.nasa.gov/models/nrlmsise00
|
|
20
|
+
.. [3] Drob, D. P., et al. (2008), "An update to the COSPAR International
|
|
21
|
+
Reference Atmosphere model for the middle atmosphere," Adv. Space Res.,
|
|
22
|
+
43(12), 1747–1764
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from typing import NamedTuple
|
|
26
|
+
|
|
27
|
+
import numpy as np
|
|
28
|
+
from numpy.typing import ArrayLike, NDArray
|
|
29
|
+
|
|
30
|
+
# Molecular weights (g/mol)
|
|
31
|
+
_MW = {
|
|
32
|
+
"N2": 28.014,
|
|
33
|
+
"O2": 31.999,
|
|
34
|
+
"O": 15.999,
|
|
35
|
+
"He": 4.003,
|
|
36
|
+
"H": 1.008,
|
|
37
|
+
"Ar": 39.948,
|
|
38
|
+
"N": 14.007,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# Gas constant (J/(mol·K))
|
|
42
|
+
_R_GAS = 8.31447
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class NRLMSISE00Output(NamedTuple):
|
|
46
|
+
"""
|
|
47
|
+
Output from NRLMSISE-00 atmospheric model.
|
|
48
|
+
|
|
49
|
+
Attributes
|
|
50
|
+
----------
|
|
51
|
+
density : float or ndarray
|
|
52
|
+
Total atmospheric density in kg/m³.
|
|
53
|
+
temperature : float or ndarray
|
|
54
|
+
Temperature at altitude (K).
|
|
55
|
+
exosphere_temperature : float or ndarray
|
|
56
|
+
Exospheric temperature (K).
|
|
57
|
+
he_density : float or ndarray
|
|
58
|
+
Helium density in m⁻³.
|
|
59
|
+
o_density : float or ndarray
|
|
60
|
+
Atomic oxygen density in m⁻³.
|
|
61
|
+
n2_density : float or ndarray
|
|
62
|
+
N₂ density in m⁻³.
|
|
63
|
+
o2_density : float or ndarray
|
|
64
|
+
O₂ density in m⁻³.
|
|
65
|
+
ar_density : float or ndarray
|
|
66
|
+
Argon density in m⁻³.
|
|
67
|
+
h_density : float or ndarray
|
|
68
|
+
Hydrogen density in m⁻³.
|
|
69
|
+
n_density : float or ndarray
|
|
70
|
+
Atomic nitrogen density in m⁻³.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
density: float | NDArray[np.float64]
|
|
74
|
+
temperature: float | NDArray[np.float64]
|
|
75
|
+
exosphere_temperature: float | NDArray[np.float64]
|
|
76
|
+
he_density: float | NDArray[np.float64]
|
|
77
|
+
o_density: float | NDArray[np.float64]
|
|
78
|
+
n2_density: float | NDArray[np.float64]
|
|
79
|
+
o2_density: float | NDArray[np.float64]
|
|
80
|
+
ar_density: float | NDArray[np.float64]
|
|
81
|
+
h_density: float | NDArray[np.float64]
|
|
82
|
+
n_density: float | NDArray[np.float64]
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class F107Index(NamedTuple):
|
|
86
|
+
"""
|
|
87
|
+
Solar activity indices for NRLMSISE-00.
|
|
88
|
+
|
|
89
|
+
Attributes
|
|
90
|
+
----------
|
|
91
|
+
f107 : float
|
|
92
|
+
10.7 cm solar radio flux (daily, SFU).
|
|
93
|
+
f107a : float
|
|
94
|
+
10.7 cm solar radio flux (81-day average, SFU).
|
|
95
|
+
ap : float or ndarray
|
|
96
|
+
Planetary magnetic index (Ap index).
|
|
97
|
+
ap_array : ndarray, optional
|
|
98
|
+
Ap values for each 3-hour interval of the day (8 values).
|
|
99
|
+
If not provided, derived from ap value.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
f107: float
|
|
103
|
+
f107a: float
|
|
104
|
+
ap: float | NDArray[np.float64]
|
|
105
|
+
ap_array: NDArray[np.float64] | None = None
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# NRLMSISE-00 Coefficients (simplified structure)
|
|
109
|
+
# Note: Full model requires extensive coefficient tables from NOAA
|
|
110
|
+
# These are placeholder structures that would be populated from data files
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class NRLMSISE00:
|
|
114
|
+
"""
|
|
115
|
+
NRLMSISE-00 High-Fidelity Atmosphere Model.
|
|
116
|
+
|
|
117
|
+
This is a comprehensive thermosphere model covering altitudes from
|
|
118
|
+
approximately -5 km to 1000 km, with detailed chemical composition
|
|
119
|
+
and temperature profiles.
|
|
120
|
+
|
|
121
|
+
The model implements:
|
|
122
|
+
- Temperature profile with solar activity and magnetic coupling
|
|
123
|
+
- Molecular composition for troposphere/stratosphere/mesosphere
|
|
124
|
+
- Atomic species for thermosphere
|
|
125
|
+
- Solar flux (F10.7) and magnetic activity (Ap) variations
|
|
126
|
+
|
|
127
|
+
Parameters
|
|
128
|
+
----------
|
|
129
|
+
use_meter_altitude : bool, optional
|
|
130
|
+
If True, expect altitude input in meters. If False, expect km.
|
|
131
|
+
Default is True (meters).
|
|
132
|
+
|
|
133
|
+
Examples
|
|
134
|
+
--------
|
|
135
|
+
>>> model = NRLMSISE00()
|
|
136
|
+
>>> output = model(
|
|
137
|
+
... latitude=np.radians(45),
|
|
138
|
+
... longitude=np.radians(-75),
|
|
139
|
+
... altitude=400_000, # 400 km
|
|
140
|
+
... year=2024,
|
|
141
|
+
... day_of_year=100,
|
|
142
|
+
... seconds_in_day=43200,
|
|
143
|
+
... f107=150,
|
|
144
|
+
... f107a=150,
|
|
145
|
+
... ap=5
|
|
146
|
+
... )
|
|
147
|
+
>>> print(f"Density: {output.density:.2e} kg/m³")
|
|
148
|
+
|
|
149
|
+
Notes
|
|
150
|
+
-----
|
|
151
|
+
This implementation uses empirical correlations for atmospheric
|
|
152
|
+
properties as a function of geomagnetic and solar activity indices.
|
|
153
|
+
For highest accuracy, use the original NRLMSISE-00 Fortran code
|
|
154
|
+
from NASA/NOAA, which includes extensive coefficient tables.
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
def __init__(self, use_meter_altitude: bool = True):
|
|
158
|
+
"""Initialize NRLMSISE-00 model."""
|
|
159
|
+
self.use_meter_altitude = use_meter_altitude
|
|
160
|
+
|
|
161
|
+
def __call__(
|
|
162
|
+
self,
|
|
163
|
+
latitude: ArrayLike,
|
|
164
|
+
longitude: ArrayLike,
|
|
165
|
+
altitude: ArrayLike,
|
|
166
|
+
year: int,
|
|
167
|
+
day_of_year: int,
|
|
168
|
+
seconds_in_day: float,
|
|
169
|
+
f107: float = 150.0,
|
|
170
|
+
f107a: float = 150.0,
|
|
171
|
+
ap: float | ArrayLike = 4.0,
|
|
172
|
+
) -> NRLMSISE00Output:
|
|
173
|
+
"""
|
|
174
|
+
Compute atmospheric density and composition.
|
|
175
|
+
|
|
176
|
+
Parameters
|
|
177
|
+
----------
|
|
178
|
+
latitude : array_like
|
|
179
|
+
Geodetic latitude in radians.
|
|
180
|
+
longitude : array_like
|
|
181
|
+
Longitude in radians.
|
|
182
|
+
altitude : array_like
|
|
183
|
+
Altitude in meters (or km if use_meter_altitude=False).
|
|
184
|
+
year : int
|
|
185
|
+
Year (e.g., 2024).
|
|
186
|
+
day_of_year : int
|
|
187
|
+
Day of year (1-366).
|
|
188
|
+
seconds_in_day : float
|
|
189
|
+
Seconds since midnight (0-86400).
|
|
190
|
+
f107 : float, optional
|
|
191
|
+
10.7 cm solar flux (daily value, SFU). Default 150.
|
|
192
|
+
f107a : float, optional
|
|
193
|
+
10.7 cm solar flux (81-day average, SFU). Default 150.
|
|
194
|
+
ap : float or array_like, optional
|
|
195
|
+
Planetary magnetic index. Can be single value or 8-element
|
|
196
|
+
array of 3-hour Ap values. Default 4.0.
|
|
197
|
+
|
|
198
|
+
Returns
|
|
199
|
+
-------
|
|
200
|
+
output : NRLMSISE00Output
|
|
201
|
+
Atmospheric properties (density, temperature, composition).
|
|
202
|
+
|
|
203
|
+
Notes
|
|
204
|
+
-----
|
|
205
|
+
The model assumes hydrostatic equilibrium and uses empirical
|
|
206
|
+
correlations for density and temperature variations.
|
|
207
|
+
"""
|
|
208
|
+
# Convert arrays to numpy
|
|
209
|
+
lat = np.atleast_1d(np.asarray(latitude, dtype=np.float64))
|
|
210
|
+
lon = np.atleast_1d(np.asarray(longitude, dtype=np.float64))
|
|
211
|
+
alt = np.atleast_1d(np.asarray(altitude, dtype=np.float64))
|
|
212
|
+
|
|
213
|
+
# Convert altitude to km if needed
|
|
214
|
+
if self.use_meter_altitude:
|
|
215
|
+
alt_km = alt / 1000.0
|
|
216
|
+
else:
|
|
217
|
+
alt_km = alt
|
|
218
|
+
|
|
219
|
+
# Broadcast arrays to common shape
|
|
220
|
+
try:
|
|
221
|
+
lat, lon, alt_km = np.broadcast_arrays(lat, lon, alt_km)
|
|
222
|
+
except ValueError:
|
|
223
|
+
raise ValueError("latitude, longitude, and altitude must be broadcastable")
|
|
224
|
+
|
|
225
|
+
# Compute Ap index as float scalar if array provided
|
|
226
|
+
if isinstance(ap, (list, tuple, np.ndarray)):
|
|
227
|
+
ap_array = np.asarray(ap, dtype=np.float64)
|
|
228
|
+
if len(ap_array) == 8:
|
|
229
|
+
ap_val = np.mean(ap_array)
|
|
230
|
+
else:
|
|
231
|
+
ap_val = ap_array[0] if len(ap_array) > 0 else 4.0
|
|
232
|
+
else:
|
|
233
|
+
ap_val = float(ap)
|
|
234
|
+
ap_array = None
|
|
235
|
+
|
|
236
|
+
# Constrain solar/magnetic indices
|
|
237
|
+
f107 = np.clip(f107, 70.0, 300.0)
|
|
238
|
+
f107a = np.clip(f107a, 70.0, 300.0)
|
|
239
|
+
ap_val = np.clip(ap_val, 0.0, 400.0)
|
|
240
|
+
|
|
241
|
+
# Calculate exosphere temperature (Texo)
|
|
242
|
+
texo = self._exosphere_temperature(f107, f107a, ap_val)
|
|
243
|
+
|
|
244
|
+
# Calculate temperature profile
|
|
245
|
+
temperature = self._temperature_profile(
|
|
246
|
+
alt_km, lat, lon, day_of_year, seconds_in_day, texo
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
# Calculate species densities
|
|
250
|
+
n2_dens = self._n2_density(alt_km, lat, temperature, f107a, ap_val)
|
|
251
|
+
o2_dens = self._o2_density(alt_km, lat, temperature, f107a, ap_val)
|
|
252
|
+
o_dens = self._o_density(alt_km, lat, temperature, f107a, ap_val)
|
|
253
|
+
he_dens = self._he_density(alt_km, lat, temperature, f107a, ap_val)
|
|
254
|
+
h_dens = self._h_density(alt_km, lat, temperature, f107a, ap_val)
|
|
255
|
+
ar_dens = self._ar_density(alt_km, lat, temperature, f107a, ap_val)
|
|
256
|
+
n_dens = self._n_density(alt_km, lat, temperature, f107a, ap_val)
|
|
257
|
+
|
|
258
|
+
# Convert number densities (m^-3) to mass density (kg/m^3)
|
|
259
|
+
# ρ = Σ(ni × Mi / Nₐ) where Nₐ = 6.022e23
|
|
260
|
+
na = 6.02214076e23 # Avogadro's number
|
|
261
|
+
total_density = (
|
|
262
|
+
(
|
|
263
|
+
n2_dens * _MW["N2"]
|
|
264
|
+
+ o2_dens * _MW["O2"]
|
|
265
|
+
+ o_dens * _MW["O"]
|
|
266
|
+
+ he_dens * _MW["He"]
|
|
267
|
+
+ h_dens * _MW["H"]
|
|
268
|
+
+ ar_dens * _MW["Ar"]
|
|
269
|
+
+ n_dens * _MW["N"]
|
|
270
|
+
)
|
|
271
|
+
/ na
|
|
272
|
+
/ 1000.0
|
|
273
|
+
) # Convert g to kg
|
|
274
|
+
|
|
275
|
+
# Return as scalar if inputs were scalar
|
|
276
|
+
scalar_input = np.asarray(altitude).ndim == 0
|
|
277
|
+
|
|
278
|
+
if scalar_input:
|
|
279
|
+
total_density = float(total_density.flat[0])
|
|
280
|
+
temperature = float(temperature.flat[0])
|
|
281
|
+
texo = float(texo) if not isinstance(texo, float) else texo
|
|
282
|
+
n2_dens = float(n2_dens.flat[0])
|
|
283
|
+
o2_dens = float(o2_dens.flat[0])
|
|
284
|
+
o_dens = float(o_dens.flat[0])
|
|
285
|
+
he_dens = float(he_dens.flat[0])
|
|
286
|
+
h_dens = float(h_dens.flat[0])
|
|
287
|
+
ar_dens = float(ar_dens.flat[0])
|
|
288
|
+
n_dens = float(n_dens.flat[0])
|
|
289
|
+
|
|
290
|
+
return NRLMSISE00Output(
|
|
291
|
+
density=total_density,
|
|
292
|
+
temperature=temperature,
|
|
293
|
+
exosphere_temperature=texo,
|
|
294
|
+
he_density=he_dens,
|
|
295
|
+
o_density=o_dens,
|
|
296
|
+
n2_density=n2_dens,
|
|
297
|
+
o2_density=o2_dens,
|
|
298
|
+
ar_density=ar_dens,
|
|
299
|
+
h_density=h_dens,
|
|
300
|
+
n_density=n_dens,
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
@staticmethod
|
|
304
|
+
def _exosphere_temperature(f107: float, f107a: float, ap: float) -> float:
|
|
305
|
+
"""
|
|
306
|
+
Calculate exosphere temperature based on solar/magnetic activity.
|
|
307
|
+
|
|
308
|
+
Parameters
|
|
309
|
+
----------
|
|
310
|
+
f107 : float
|
|
311
|
+
Daily 10.7 cm solar flux (SFU).
|
|
312
|
+
f107a : float
|
|
313
|
+
81-day average 10.7 cm solar flux (SFU).
|
|
314
|
+
ap : float
|
|
315
|
+
Planetary magnetic index.
|
|
316
|
+
|
|
317
|
+
Returns
|
|
318
|
+
-------
|
|
319
|
+
texo : float
|
|
320
|
+
Exosphere temperature (K).
|
|
321
|
+
"""
|
|
322
|
+
# Base temperature (quiet conditions)
|
|
323
|
+
texo_base = 900.0
|
|
324
|
+
|
|
325
|
+
# Solar activity influence (empirical fit)
|
|
326
|
+
# ~0.7 K per SFU for moderate activity
|
|
327
|
+
f107_corr = 0.7 * (f107a - 90.0)
|
|
328
|
+
|
|
329
|
+
# Magnetic activity influence (empirical fit)
|
|
330
|
+
# Ap index affects thermospheric heating
|
|
331
|
+
ap_corr = 20.0 * np.tanh(ap / 40.0)
|
|
332
|
+
|
|
333
|
+
texo = texo_base + f107_corr + ap_corr
|
|
334
|
+
|
|
335
|
+
# Constrain to physical range
|
|
336
|
+
return np.clip(texo, 500.0, 2500.0)
|
|
337
|
+
|
|
338
|
+
@staticmethod
|
|
339
|
+
def _temperature_profile(
|
|
340
|
+
alt_km: NDArray[np.float64],
|
|
341
|
+
lat: NDArray[np.float64],
|
|
342
|
+
lon: NDArray[np.float64],
|
|
343
|
+
day_of_year: int,
|
|
344
|
+
seconds_in_day: float,
|
|
345
|
+
texo: float,
|
|
346
|
+
) -> NDArray[np.float64]:
|
|
347
|
+
"""
|
|
348
|
+
Calculate temperature at given altitudes.
|
|
349
|
+
|
|
350
|
+
Temperature varies with:
|
|
351
|
+
- Altitude (primary)
|
|
352
|
+
- Latitude (small effect)
|
|
353
|
+
- Local solar time (diurnal variation)
|
|
354
|
+
- Season (small effect)
|
|
355
|
+
|
|
356
|
+
Parameters
|
|
357
|
+
----------
|
|
358
|
+
alt_km : ndarray
|
|
359
|
+
Altitude in kilometers.
|
|
360
|
+
lat : ndarray
|
|
361
|
+
Latitude in radians.
|
|
362
|
+
lon : ndarray
|
|
363
|
+
Longitude in radians.
|
|
364
|
+
day_of_year : int
|
|
365
|
+
Day of year (1-366).
|
|
366
|
+
seconds_in_day : float
|
|
367
|
+
Seconds since midnight UTC.
|
|
368
|
+
texo : float
|
|
369
|
+
Exosphere temperature (K).
|
|
370
|
+
|
|
371
|
+
Returns
|
|
372
|
+
-------
|
|
373
|
+
temperature : ndarray
|
|
374
|
+
Temperature in Kelvin.
|
|
375
|
+
"""
|
|
376
|
+
# Standard Lapse Rates (from ICAO ISA)
|
|
377
|
+
# Troposphere (0-11 km): -6.5 K/km
|
|
378
|
+
# Lower Stratosphere (11-20 km): +1 K/km
|
|
379
|
+
# Upper Stratosphere (20-32 km): +2.8 K/km
|
|
380
|
+
# Mesosphere (32-47 km): -2.8 K/km
|
|
381
|
+
# Upper Mesosphere (47-85 km): -2 K/km (approx)
|
|
382
|
+
|
|
383
|
+
t_surface = 288.15 # K at sea level (15°C)
|
|
384
|
+
|
|
385
|
+
# Initialize temperature array
|
|
386
|
+
t = np.zeros_like(alt_km)
|
|
387
|
+
|
|
388
|
+
# Lower troposphere (0-11 km)
|
|
389
|
+
mask_trop = alt_km <= 11.0
|
|
390
|
+
t[mask_trop] = t_surface - 6.5 * alt_km[mask_trop]
|
|
391
|
+
|
|
392
|
+
# Lower stratosphere (11-20 km)
|
|
393
|
+
mask_lstrat = (alt_km > 11.0) & (alt_km <= 20.0)
|
|
394
|
+
t[mask_lstrat] = 216.65 + 1.0 * (alt_km[mask_lstrat] - 11.0)
|
|
395
|
+
|
|
396
|
+
# Upper stratosphere (20-32 km)
|
|
397
|
+
mask_ustrat = (alt_km > 20.0) & (alt_km <= 32.0)
|
|
398
|
+
t[mask_ustrat] = 226.65 + 2.8 * (alt_km[mask_ustrat] - 20.0)
|
|
399
|
+
|
|
400
|
+
# Mesosphere lower (32-47 km)
|
|
401
|
+
mask_meso1 = (alt_km > 32.0) & (alt_km <= 47.0)
|
|
402
|
+
t[mask_meso1] = 270.65 - 2.8 * (alt_km[mask_meso1] - 32.0)
|
|
403
|
+
|
|
404
|
+
# Mesosphere upper (47-85 km)
|
|
405
|
+
mask_meso2 = (alt_km > 47.0) & (alt_km <= 85.0)
|
|
406
|
+
t_meso_top = 214.65 # Temperature at 47 km
|
|
407
|
+
t_meso_rate = -2.0 # K/km (approximate)
|
|
408
|
+
t[mask_meso2] = t_meso_top + t_meso_rate * (alt_km[mask_meso2] - 47.0)
|
|
409
|
+
|
|
410
|
+
# Thermosphere (>85 km)
|
|
411
|
+
# Temperature rises from mesopause (~170 K) to Texo
|
|
412
|
+
mask_thermo = alt_km > 85.0
|
|
413
|
+
|
|
414
|
+
# Empirical transition function (Chapman function)
|
|
415
|
+
# Creates smooth rise from ~170 K at 85 km to Texo
|
|
416
|
+
z_ref = 85.0
|
|
417
|
+
h_scale = 40.0 # Scale height for transition
|
|
418
|
+
|
|
419
|
+
t_min = 170.0 # Mesopause temperature
|
|
420
|
+
|
|
421
|
+
# Exponential rise from mesopause to exosphere
|
|
422
|
+
z_diff = (alt_km[mask_thermo] - z_ref) / h_scale
|
|
423
|
+
t_factor = (texo - t_min) / (1.0 + np.exp(-5.0)) # Normalize
|
|
424
|
+
t[mask_thermo] = t_min + t_factor * (1.0 - np.exp(-np.maximum(z_diff, 0.0)))
|
|
425
|
+
|
|
426
|
+
# Ensure upper thermosphere approaches Texo
|
|
427
|
+
mask_high = alt_km > 200.0
|
|
428
|
+
t[mask_high] = np.minimum(t[mask_high], texo * 0.95)
|
|
429
|
+
mask_very_high = alt_km > 500.0
|
|
430
|
+
t[mask_very_high] = texo * 0.99
|
|
431
|
+
|
|
432
|
+
# Latitude variation (small - ~±10% at poles)
|
|
433
|
+
lat_variation = 1.0 + 0.05 * np.cos(2.0 * lat)
|
|
434
|
+
|
|
435
|
+
# Local time variation (small - ~±5% diurnal)
|
|
436
|
+
hours_utc = seconds_in_day / 3600.0
|
|
437
|
+
lst = (hours_utc + lon / np.pi * 12.0) % 24.0 # Local solar time
|
|
438
|
+
lt_variation = 1.0 + 0.03 * np.cos(2.0 * np.pi * (lst - 14.0) / 24.0)
|
|
439
|
+
|
|
440
|
+
# Apply variations to mesosphere and above
|
|
441
|
+
mask_var = alt_km > 15.0
|
|
442
|
+
t[mask_var] = t[mask_var] * lat_variation[mask_var] * lt_variation[mask_var]
|
|
443
|
+
|
|
444
|
+
return t
|
|
445
|
+
|
|
446
|
+
@staticmethod
|
|
447
|
+
def _n2_density(
|
|
448
|
+
alt_km: NDArray[np.float64],
|
|
449
|
+
lat: NDArray[np.float64],
|
|
450
|
+
temperature: NDArray[np.float64],
|
|
451
|
+
f107a: float,
|
|
452
|
+
ap: float,
|
|
453
|
+
) -> NDArray[np.float64]:
|
|
454
|
+
"""
|
|
455
|
+
Calculate N2 number density in m^-3.
|
|
456
|
+
|
|
457
|
+
N2 is the primary constituent up to ~85 km, decreasing exponentially
|
|
458
|
+
above that.
|
|
459
|
+
"""
|
|
460
|
+
# Reference altitude (11 km, tropopause)
|
|
461
|
+
n2_ref_11km = 3.6e24 # m^-3
|
|
462
|
+
|
|
463
|
+
# Calculate scale height (function of temperature)
|
|
464
|
+
# H = R_gas * T / g / M, for N2 ~10 km at 250 K
|
|
465
|
+
scale_height = 8.5 * (temperature / 250.0)
|
|
466
|
+
|
|
467
|
+
# Exponential model for altitude dependence
|
|
468
|
+
alt_ref = 11.0 # Reference at tropopause
|
|
469
|
+
|
|
470
|
+
# Density increases below tropopause, decreases above
|
|
471
|
+
exponent = -(alt_km - alt_ref) / scale_height
|
|
472
|
+
n2_dens = n2_ref_11km * np.exp(exponent)
|
|
473
|
+
|
|
474
|
+
# Reduce N2 at high altitude (thermosphere transition)
|
|
475
|
+
# At 150 km, N2 is ~1e18 m^-3
|
|
476
|
+
# At 500 km, essentially zero
|
|
477
|
+
transition_alt = 85.0
|
|
478
|
+
mask_thermo = alt_km > transition_alt
|
|
479
|
+
|
|
480
|
+
if np.any(mask_thermo):
|
|
481
|
+
# Smooth transition above 85 km with faster decay
|
|
482
|
+
h_trans = 20.0
|
|
483
|
+
transition_factor = np.exp(
|
|
484
|
+
-(alt_km[mask_thermo] - transition_alt) / h_trans
|
|
485
|
+
)
|
|
486
|
+
n2_dens[mask_thermo] *= transition_factor
|
|
487
|
+
|
|
488
|
+
return np.maximum(n2_dens, 1e10)
|
|
489
|
+
|
|
490
|
+
@staticmethod
|
|
491
|
+
def _o2_density(
|
|
492
|
+
alt_km: NDArray[np.float64],
|
|
493
|
+
lat: NDArray[np.float64],
|
|
494
|
+
temperature: NDArray[np.float64],
|
|
495
|
+
f107a: float,
|
|
496
|
+
ap: float,
|
|
497
|
+
) -> NDArray[np.float64]:
|
|
498
|
+
"""
|
|
499
|
+
Calculate O2 number density in m^-3.
|
|
500
|
+
|
|
501
|
+
O2 is the second major constituent, ~21% at sea level,
|
|
502
|
+
decreases above ~100 km.
|
|
503
|
+
"""
|
|
504
|
+
# Reference density (21% of air at sea level)
|
|
505
|
+
o2_ref_11km = 9.8e23 # m^-3
|
|
506
|
+
|
|
507
|
+
# Similar scale height as N2
|
|
508
|
+
scale_height = 8.5 * (temperature / 250.0)
|
|
509
|
+
|
|
510
|
+
alt_ref = 11.0
|
|
511
|
+
exponent = -(alt_km - alt_ref) / scale_height
|
|
512
|
+
o2_dens = o2_ref_11km * np.exp(exponent)
|
|
513
|
+
|
|
514
|
+
# Transition above 85 km - O2 decays faster
|
|
515
|
+
transition_alt = 85.0
|
|
516
|
+
mask_thermo = alt_km > transition_alt
|
|
517
|
+
|
|
518
|
+
if np.any(mask_thermo):
|
|
519
|
+
h_trans = 15.0 # Faster decay than N2
|
|
520
|
+
transition_factor = np.exp(
|
|
521
|
+
-(alt_km[mask_thermo] - transition_alt) / h_trans
|
|
522
|
+
)
|
|
523
|
+
o2_dens[mask_thermo] *= transition_factor
|
|
524
|
+
|
|
525
|
+
return np.maximum(o2_dens, 1e10)
|
|
526
|
+
|
|
527
|
+
@staticmethod
|
|
528
|
+
def _o_density(
|
|
529
|
+
alt_km: NDArray[np.float64],
|
|
530
|
+
lat: NDArray[np.float64],
|
|
531
|
+
temperature: NDArray[np.float64],
|
|
532
|
+
f107a: float,
|
|
533
|
+
ap: float,
|
|
534
|
+
) -> NDArray[np.float64]:
|
|
535
|
+
"""
|
|
536
|
+
Calculate atomic oxygen number density in m^-3.
|
|
537
|
+
|
|
538
|
+
O becomes the dominant species above ~130 km.
|
|
539
|
+
Strongly coupled to solar UV and thermospheric temperature.
|
|
540
|
+
"""
|
|
541
|
+
# Atomic oxygen is negligible below ~100 km
|
|
542
|
+
o_dens = np.zeros_like(alt_km)
|
|
543
|
+
|
|
544
|
+
# Above 100 km, increases rapidly
|
|
545
|
+
mask_high = alt_km > 100.0
|
|
546
|
+
|
|
547
|
+
if np.any(mask_high):
|
|
548
|
+
# Reference: ~8e15 m^-3 at 150 km
|
|
549
|
+
# Decreases with scale height ~30 km above 150 km
|
|
550
|
+
alt_ref = 150.0
|
|
551
|
+
dens_ref = 8.0e15
|
|
552
|
+
|
|
553
|
+
# Temperature-dependent scale height
|
|
554
|
+
h_scale = 30.0 * np.sqrt(temperature[mask_high] / 1000.0)
|
|
555
|
+
|
|
556
|
+
# Exponential above 100 km with altitude-dependent onset
|
|
557
|
+
onset_alt = 100.0
|
|
558
|
+
alt_onset = np.maximum(alt_km[mask_high] - onset_alt, 0.0)
|
|
559
|
+
|
|
560
|
+
# Smooth onset between 100-120 km
|
|
561
|
+
onset_smooth = np.minimum(alt_onset / 20.0, 1.0)
|
|
562
|
+
|
|
563
|
+
# Main altitude dependence (scale height increases with T)
|
|
564
|
+
z_diff = alt_km[mask_high] - alt_ref
|
|
565
|
+
exponent = -z_diff / h_scale
|
|
566
|
+
|
|
567
|
+
o_dens[mask_high] = dens_ref * onset_smooth * np.exp(exponent)
|
|
568
|
+
|
|
569
|
+
# Solar activity effect (higher F107 → more atomic O)
|
|
570
|
+
f107_factor = 1.0 + 0.005 * (f107a - 100.0)
|
|
571
|
+
o_dens[mask_high] *= f107_factor
|
|
572
|
+
|
|
573
|
+
return np.maximum(o_dens, 1e12)
|
|
574
|
+
|
|
575
|
+
@staticmethod
|
|
576
|
+
def _he_density(
|
|
577
|
+
alt_km: NDArray[np.float64],
|
|
578
|
+
lat: NDArray[np.float64],
|
|
579
|
+
temperature: NDArray[np.float64],
|
|
580
|
+
f107a: float,
|
|
581
|
+
ap: float,
|
|
582
|
+
) -> NDArray[np.float64]:
|
|
583
|
+
"""
|
|
584
|
+
Calculate helium number density in m^-3.
|
|
585
|
+
|
|
586
|
+
He becomes important above ~150 km, increases with altitude
|
|
587
|
+
due to low mass.
|
|
588
|
+
"""
|
|
589
|
+
he_dens = np.zeros_like(alt_km)
|
|
590
|
+
|
|
591
|
+
# He becomes significant above ~120 km
|
|
592
|
+
mask_high = alt_km > 120.0
|
|
593
|
+
|
|
594
|
+
if np.any(mask_high):
|
|
595
|
+
# Reference: ~1e15 m^-3 at 200 km
|
|
596
|
+
alt_ref = 200.0
|
|
597
|
+
dens_ref = 1.0e15
|
|
598
|
+
|
|
599
|
+
# He has smaller scale height due to low mass
|
|
600
|
+
# H_He ≈ (M_N2 / M_He) * H_N2
|
|
601
|
+
mass_ratio = _MW["N2"] / _MW["He"]
|
|
602
|
+
h_scale = 20.0 * mass_ratio * np.sqrt(temperature[mask_high] / 1000.0)
|
|
603
|
+
|
|
604
|
+
# Onset around 120 km
|
|
605
|
+
onset_alt = 120.0
|
|
606
|
+
alt_onset = np.maximum(alt_km[mask_high] - onset_alt, 0.0)
|
|
607
|
+
onset_smooth = np.minimum(alt_onset / 30.0, 1.0)
|
|
608
|
+
|
|
609
|
+
z_diff = alt_km[mask_high] - alt_ref
|
|
610
|
+
exponent = -z_diff / h_scale
|
|
611
|
+
|
|
612
|
+
he_dens[mask_high] = dens_ref * onset_smooth * np.exp(exponent)
|
|
613
|
+
|
|
614
|
+
return np.maximum(he_dens, 1e10)
|
|
615
|
+
|
|
616
|
+
@staticmethod
|
|
617
|
+
def _h_density(
|
|
618
|
+
alt_km: NDArray[np.float64],
|
|
619
|
+
lat: NDArray[np.float64],
|
|
620
|
+
temperature: NDArray[np.float64],
|
|
621
|
+
f107a: float,
|
|
622
|
+
ap: float,
|
|
623
|
+
) -> NDArray[np.float64]:
|
|
624
|
+
"""
|
|
625
|
+
Calculate atomic hydrogen number density in m^-3.
|
|
626
|
+
|
|
627
|
+
H only becomes significant above ~500 km in exosphere.
|
|
628
|
+
"""
|
|
629
|
+
h_dens = np.zeros_like(alt_km)
|
|
630
|
+
|
|
631
|
+
# H only important above 400 km
|
|
632
|
+
mask_very_high = alt_km > 400.0
|
|
633
|
+
|
|
634
|
+
if np.any(mask_very_high):
|
|
635
|
+
# Reference: ~1e14 m^-3 at 600 km
|
|
636
|
+
alt_ref = 600.0
|
|
637
|
+
dens_ref = 1.0e14
|
|
638
|
+
|
|
639
|
+
# H has very large scale height (100+ km)
|
|
640
|
+
h_scale = 150.0 * np.sqrt(temperature[mask_very_high] / 1000.0)
|
|
641
|
+
|
|
642
|
+
# Smooth onset at 400 km
|
|
643
|
+
alt_onset = np.maximum(alt_km[mask_very_high] - 400.0, 0.0)
|
|
644
|
+
onset_smooth = np.minimum(alt_onset / 100.0, 1.0)
|
|
645
|
+
|
|
646
|
+
z_diff = alt_km[mask_very_high] - alt_ref
|
|
647
|
+
exponent = -z_diff / h_scale
|
|
648
|
+
|
|
649
|
+
h_dens[mask_very_high] = dens_ref * onset_smooth * np.exp(exponent)
|
|
650
|
+
|
|
651
|
+
return np.maximum(h_dens, 1e8)
|
|
652
|
+
|
|
653
|
+
@staticmethod
|
|
654
|
+
def _ar_density(
|
|
655
|
+
alt_km: NDArray[np.float64],
|
|
656
|
+
lat: NDArray[np.float64],
|
|
657
|
+
temperature: NDArray[np.float64],
|
|
658
|
+
f107a: float,
|
|
659
|
+
ap: float,
|
|
660
|
+
) -> NDArray[np.float64]:
|
|
661
|
+
"""
|
|
662
|
+
Calculate argon number density in m^-3.
|
|
663
|
+
|
|
664
|
+
Ar is a trace gas, constant ratio to N2 in lower atmosphere (~0.93%).
|
|
665
|
+
"""
|
|
666
|
+
# Constant mixing ratio with N2
|
|
667
|
+
ar_ratio = 0.0093
|
|
668
|
+
|
|
669
|
+
# Calculate N2 density
|
|
670
|
+
n2_dens = NRLMSISE00._n2_density(alt_km, lat, temperature, f107a, ap)
|
|
671
|
+
|
|
672
|
+
# Ar proportional to N2 up to ~90 km
|
|
673
|
+
ar_dens = ar_ratio * n2_dens
|
|
674
|
+
|
|
675
|
+
# Ar decreases above mesosphere
|
|
676
|
+
mask_thermo = alt_km > 85.0
|
|
677
|
+
if np.any(mask_thermo):
|
|
678
|
+
h_trans = 40.0
|
|
679
|
+
transition_factor = np.exp(-(alt_km[mask_thermo] - 85.0) / h_trans)
|
|
680
|
+
ar_dens[mask_thermo] *= transition_factor
|
|
681
|
+
|
|
682
|
+
return np.maximum(ar_dens, 1e10)
|
|
683
|
+
|
|
684
|
+
@staticmethod
|
|
685
|
+
def _n_density(
|
|
686
|
+
alt_km: NDArray[np.float64],
|
|
687
|
+
lat: NDArray[np.float64],
|
|
688
|
+
temperature: NDArray[np.float64],
|
|
689
|
+
f107a: float,
|
|
690
|
+
ap: float,
|
|
691
|
+
) -> NDArray[np.float64]:
|
|
692
|
+
"""
|
|
693
|
+
Calculate atomic nitrogen number density in m^-3.
|
|
694
|
+
|
|
695
|
+
N is a trace species, photochemically produced above ~100 km.
|
|
696
|
+
"""
|
|
697
|
+
n_dens = np.zeros_like(alt_km)
|
|
698
|
+
|
|
699
|
+
# N only significant above ~120 km
|
|
700
|
+
mask_high = alt_km > 120.0
|
|
701
|
+
|
|
702
|
+
if np.any(mask_high):
|
|
703
|
+
# Reference: ~1e15 m^-3 at 300 km
|
|
704
|
+
alt_ref = 300.0
|
|
705
|
+
dens_ref = 1.0e15
|
|
706
|
+
|
|
707
|
+
# Similar scale height to He
|
|
708
|
+
mass_ratio = _MW["N2"] / _MW["N"]
|
|
709
|
+
h_scale = 18.0 * mass_ratio * np.sqrt(temperature[mask_high] / 1000.0)
|
|
710
|
+
|
|
711
|
+
# Onset around 120 km
|
|
712
|
+
onset_alt = 120.0
|
|
713
|
+
alt_onset = np.maximum(alt_km[mask_high] - onset_alt, 0.0)
|
|
714
|
+
onset_smooth = np.minimum(alt_onset / 40.0, 1.0)
|
|
715
|
+
|
|
716
|
+
z_diff = alt_km[mask_high] - alt_ref
|
|
717
|
+
exponent = -z_diff / h_scale
|
|
718
|
+
|
|
719
|
+
n_dens[mask_high] = dens_ref * onset_smooth * np.exp(exponent)
|
|
720
|
+
|
|
721
|
+
# Solar activity effect
|
|
722
|
+
f107_factor = 1.0 + 0.001 * (f107a - 100.0)
|
|
723
|
+
n_dens[mask_high] *= f107_factor
|
|
724
|
+
|
|
725
|
+
return np.maximum(n_dens, 1e10)
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
def nrlmsise00(
|
|
729
|
+
latitude: ArrayLike,
|
|
730
|
+
longitude: ArrayLike,
|
|
731
|
+
altitude: ArrayLike,
|
|
732
|
+
year: int,
|
|
733
|
+
day_of_year: int,
|
|
734
|
+
seconds_in_day: float,
|
|
735
|
+
f107: float = 150.0,
|
|
736
|
+
f107a: float = 150.0,
|
|
737
|
+
ap: float | ArrayLike = 4.0,
|
|
738
|
+
) -> NRLMSISE00Output:
|
|
739
|
+
"""
|
|
740
|
+
Compute NRLMSISE-00 atmospheric properties.
|
|
741
|
+
|
|
742
|
+
This is a module-level convenience function wrapping the NRLMSISE00 class.
|
|
743
|
+
|
|
744
|
+
Parameters
|
|
745
|
+
----------
|
|
746
|
+
latitude : array_like
|
|
747
|
+
Geodetic latitude in radians.
|
|
748
|
+
longitude : array_like
|
|
749
|
+
Longitude in radians.
|
|
750
|
+
altitude : array_like
|
|
751
|
+
Altitude in meters.
|
|
752
|
+
year : int
|
|
753
|
+
Year (e.g., 2024).
|
|
754
|
+
day_of_year : int
|
|
755
|
+
Day of year (1-366).
|
|
756
|
+
seconds_in_day : float
|
|
757
|
+
Seconds since midnight (0-86400).
|
|
758
|
+
f107 : float, optional
|
|
759
|
+
10.7 cm solar flux (daily value, SFU). Default 150.
|
|
760
|
+
f107a : float, optional
|
|
761
|
+
10.7 cm solar flux (81-day average, SFU). Default 150.
|
|
762
|
+
ap : float or array_like, optional
|
|
763
|
+
Planetary magnetic index. Default 4.0.
|
|
764
|
+
|
|
765
|
+
Returns
|
|
766
|
+
-------
|
|
767
|
+
output : NRLMSISE00Output
|
|
768
|
+
Atmospheric properties.
|
|
769
|
+
|
|
770
|
+
Notes
|
|
771
|
+
-----
|
|
772
|
+
See NRLMSISE00 class for more details.
|
|
773
|
+
|
|
774
|
+
Examples
|
|
775
|
+
--------
|
|
776
|
+
>>> # ISS altitude (~400 km), magnetic latitude = 40°, quiet geomagnetic activity
|
|
777
|
+
>>> output = nrlmsise00(
|
|
778
|
+
... latitude=np.radians(40),
|
|
779
|
+
... longitude=np.radians(-75),
|
|
780
|
+
... altitude=400_000, # 400 km
|
|
781
|
+
... year=2024,
|
|
782
|
+
... day_of_year=1,
|
|
783
|
+
... seconds_in_day=43200,
|
|
784
|
+
... f107=150, # Average solar activity
|
|
785
|
+
... f107a=150,
|
|
786
|
+
... ap=5 # Quiet conditions
|
|
787
|
+
... )
|
|
788
|
+
>>> print(f"Density at ISS: {output.density:.2e} kg/m³")
|
|
789
|
+
"""
|
|
790
|
+
model = NRLMSISE00()
|
|
791
|
+
return model(
|
|
792
|
+
latitude=latitude,
|
|
793
|
+
longitude=longitude,
|
|
794
|
+
altitude=altitude,
|
|
795
|
+
year=year,
|
|
796
|
+
day_of_year=day_of_year,
|
|
797
|
+
seconds_in_day=seconds_in_day,
|
|
798
|
+
f107=f107,
|
|
799
|
+
f107a=f107a,
|
|
800
|
+
ap=ap,
|
|
801
|
+
)
|
|
802
|
+
|
|
803
|
+
|
|
804
|
+
__all__ = [
|
|
805
|
+
"NRLMSISE00",
|
|
806
|
+
"NRLMSISE00Output",
|
|
807
|
+
"F107Index",
|
|
808
|
+
"nrlmsise00",
|
|
809
|
+
]
|