astrox-python 0.1.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.
- astrox/__init__.py +49 -0
- astrox/_http.py +504 -0
- astrox/access.py +173 -0
- astrox/components/__init__.py +202 -0
- astrox/components/_axes.py +508 -0
- astrox/components/_common.py +141 -0
- astrox/components/_constraints.py +138 -0
- astrox/components/_objects.py +167 -0
- astrox/components/_positions.py +673 -0
- astrox/components/_rotations.py +115 -0
- astrox/components/_sensors.py +134 -0
- astrox/components/_vgt.py +337 -0
- astrox/coverage/__init__.py +41 -0
- astrox/coverage/_core.py +552 -0
- astrox/coverage/_fom.py +125 -0
- astrox/coverage/coverage_time.py +83 -0
- astrox/coverage/number_of_assets.py +160 -0
- astrox/coverage/response_time.py +160 -0
- astrox/coverage/revisit_time.py +160 -0
- astrox/coverage/simple_coverage.py +152 -0
- astrox/exceptions.py +92 -0
- astrox/lighting.py +121 -0
- astrox/orbits.py +499 -0
- astrox/propagator.py +1072 -0
- astrox/rocket.py +82 -0
- astrox_python-0.1.0.dist-info/METADATA +82 -0
- astrox_python-0.1.0.dist-info/RECORD +29 -0
- astrox_python-0.1.0.dist-info/WHEEL +4 -0
- astrox_python-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,673 @@
|
|
|
1
|
+
"""Position-source component value objects."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Mapping, Sequence
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Any, TypeAlias
|
|
8
|
+
|
|
9
|
+
from astrox.orbits import CartesianState, KeplerianElements
|
|
10
|
+
from astrox.propagator import HpopConfig
|
|
11
|
+
|
|
12
|
+
from ._common import (
|
|
13
|
+
_cartesian_state_to_wire,
|
|
14
|
+
_hpop_config_to_wire,
|
|
15
|
+
_include_if_supplied,
|
|
16
|
+
_number_sequence_to_list,
|
|
17
|
+
_orbit_elements_to_wire,
|
|
18
|
+
_tle_lines_to_list,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
@dataclass(frozen=True, kw_only=True)
|
|
22
|
+
class SitePosition:
|
|
23
|
+
"""Fixed geodetic site position."""
|
|
24
|
+
|
|
25
|
+
longitude_deg: float
|
|
26
|
+
latitude_deg: float
|
|
27
|
+
height_m: float
|
|
28
|
+
central_body: str | None = None
|
|
29
|
+
clamp_to_ground: bool | None = None
|
|
30
|
+
height_above_ground_m: float | None = None
|
|
31
|
+
|
|
32
|
+
def to_wire(self) -> dict[str, Any]:
|
|
33
|
+
"""Lower to a typed ASTROX generic entity-position fragment."""
|
|
34
|
+
return {"$type": "SitePosition", **self.to_site_wire()}
|
|
35
|
+
|
|
36
|
+
def to_site_wire(self) -> dict[str, Any]:
|
|
37
|
+
"""Lower to ASTROX site-only position shape."""
|
|
38
|
+
payload: dict[str, Any] = {
|
|
39
|
+
"cartographicDegrees": [
|
|
40
|
+
self.longitude_deg,
|
|
41
|
+
self.latitude_deg,
|
|
42
|
+
self.height_m,
|
|
43
|
+
],
|
|
44
|
+
}
|
|
45
|
+
_include_if_supplied(payload, "CentralBody", self.central_body)
|
|
46
|
+
_include_if_supplied(payload, "clampToGround", self.clamp_to_ground)
|
|
47
|
+
_include_if_supplied(payload, "HeightAboveGround", self.height_above_ground_m)
|
|
48
|
+
return payload
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass(frozen=True, kw_only=True)
|
|
52
|
+
class CzmlPosition:
|
|
53
|
+
"""CZML-like sampled position source."""
|
|
54
|
+
|
|
55
|
+
epoch: str
|
|
56
|
+
central_body: str | None = None
|
|
57
|
+
interpolation_algorithm: str | None = None
|
|
58
|
+
interpolation_degree: int | None = None
|
|
59
|
+
reference_frame: str | None = None
|
|
60
|
+
interval: str | None = None
|
|
61
|
+
cartesian: tuple[float, ...] | None = None
|
|
62
|
+
cartesian_velocity: tuple[float, ...] | None = None
|
|
63
|
+
|
|
64
|
+
def to_wire(self) -> dict[str, Any]:
|
|
65
|
+
"""Lower to a typed ASTROX generic entity-position fragment."""
|
|
66
|
+
return {"$type": "CzmlPosition", **self.to_czml_wire()}
|
|
67
|
+
|
|
68
|
+
def to_czml_wire(self) -> dict[str, Any]:
|
|
69
|
+
"""Lower to an ASTROX CZML position-data fragment."""
|
|
70
|
+
payload: dict[str, Any] = {"epoch": self.epoch}
|
|
71
|
+
_include_if_supplied(payload, "CentralBody", self.central_body)
|
|
72
|
+
_include_if_supplied(
|
|
73
|
+
payload,
|
|
74
|
+
"interpolationAlgorithm",
|
|
75
|
+
self.interpolation_algorithm,
|
|
76
|
+
)
|
|
77
|
+
_include_if_supplied(payload, "interpolationDegree", self.interpolation_degree)
|
|
78
|
+
_include_if_supplied(payload, "referenceFrame", self.reference_frame)
|
|
79
|
+
_include_if_supplied(payload, "interval", self.interval)
|
|
80
|
+
if self.cartesian is not None:
|
|
81
|
+
payload["cartesian"] = list(self.cartesian)
|
|
82
|
+
if self.cartesian_velocity is not None:
|
|
83
|
+
payload["cartesianVelocity"] = list(self.cartesian_velocity)
|
|
84
|
+
return payload
|
|
85
|
+
|
|
86
|
+
@classmethod
|
|
87
|
+
def from_czml_wire(cls, payload: dict[str, Any]) -> CzmlPosition:
|
|
88
|
+
"""Build from an ASTROX CZML position-data payload."""
|
|
89
|
+
cartesian = payload.get("cartesian")
|
|
90
|
+
cartesian_velocity = payload.get("cartesianVelocity")
|
|
91
|
+
return cls(
|
|
92
|
+
epoch=payload["epoch"],
|
|
93
|
+
central_body=payload.get("CentralBody"),
|
|
94
|
+
interpolation_algorithm=payload.get("interpolationAlgorithm"),
|
|
95
|
+
interpolation_degree=payload.get("interpolationDegree"),
|
|
96
|
+
reference_frame=payload.get("referenceFrame"),
|
|
97
|
+
interval=payload.get("interval"),
|
|
98
|
+
cartesian=None if cartesian is None else tuple(cartesian),
|
|
99
|
+
cartesian_velocity=None
|
|
100
|
+
if cartesian_velocity is None
|
|
101
|
+
else tuple(cartesian_velocity),
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass(frozen=True, kw_only=True)
|
|
106
|
+
class CzmlPositionSTM(CzmlPosition):
|
|
107
|
+
"""CZML position sample augmented with STM-like orientation and translation."""
|
|
108
|
+
|
|
109
|
+
unit_quaternion: tuple[float, ...]
|
|
110
|
+
cartesian_translation: tuple[float, ...] | None = None
|
|
111
|
+
|
|
112
|
+
@classmethod
|
|
113
|
+
def from_czml_wire(cls, payload: dict[str, Any]) -> CzmlPositionSTM:
|
|
114
|
+
"""Build from the ASTROX CzmlPositionSTM payload."""
|
|
115
|
+
cartesian_velocity = payload.get("cartesianVelocity")
|
|
116
|
+
cartesian_translation = payload.get("cartesianTranslation")
|
|
117
|
+
return cls(
|
|
118
|
+
epoch=payload["epoch"],
|
|
119
|
+
central_body=payload.get("CentralBody"),
|
|
120
|
+
interpolation_algorithm=payload.get("interpolationAlgorithm"),
|
|
121
|
+
interpolation_degree=payload.get("interpolationDegree"),
|
|
122
|
+
reference_frame=payload["referenceFrame"],
|
|
123
|
+
interval=payload.get("interval"),
|
|
124
|
+
cartesian=tuple(payload["cartesian"]),
|
|
125
|
+
cartesian_velocity=None
|
|
126
|
+
if cartesian_velocity is None
|
|
127
|
+
else tuple(cartesian_velocity),
|
|
128
|
+
unit_quaternion=tuple(payload["unitQuaternion"]),
|
|
129
|
+
cartesian_translation=None
|
|
130
|
+
if cartesian_translation is None
|
|
131
|
+
else tuple(cartesian_translation),
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@dataclass(frozen=True, kw_only=True)
|
|
136
|
+
class CzmlPositions:
|
|
137
|
+
"""Composite CZML-like sampled position source."""
|
|
138
|
+
|
|
139
|
+
positions: tuple[CzmlPosition, ...]
|
|
140
|
+
central_body: str | None = None
|
|
141
|
+
|
|
142
|
+
def to_wire(self) -> dict[str, Any]:
|
|
143
|
+
"""Lower to a typed ASTROX generic entity-position fragment."""
|
|
144
|
+
payload: dict[str, Any] = {
|
|
145
|
+
"$type": "CzmlPositions",
|
|
146
|
+
"CzmlPositions": [position.to_czml_wire() for position in self.positions],
|
|
147
|
+
}
|
|
148
|
+
_include_if_supplied(payload, "CentralBody", self.central_body)
|
|
149
|
+
return payload
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@dataclass(frozen=True, kw_only=True)
|
|
153
|
+
class CentralBodyPosition:
|
|
154
|
+
"""Central-body position source."""
|
|
155
|
+
|
|
156
|
+
name: str
|
|
157
|
+
|
|
158
|
+
def to_wire(self) -> dict[str, Any]:
|
|
159
|
+
"""Lower to a typed ASTROX generic entity-position fragment."""
|
|
160
|
+
return {"$type": "CentralBody", "Name": self.name}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@dataclass(frozen=True, kw_only=True)
|
|
164
|
+
class J2Position:
|
|
165
|
+
"""J2-propagated Keplerian position source."""
|
|
166
|
+
|
|
167
|
+
orbit_epoch: str
|
|
168
|
+
orbit: KeplerianElements
|
|
169
|
+
start: str | None = None
|
|
170
|
+
stop: str | None = None
|
|
171
|
+
step_s: float | None = None
|
|
172
|
+
central_body: str | None = None
|
|
173
|
+
gravitational_parameter_m3_s2: float | None = None
|
|
174
|
+
coord_system: str | None = None
|
|
175
|
+
j2_normalized_value: float | None = None
|
|
176
|
+
ref_distance_m: float | None = None
|
|
177
|
+
|
|
178
|
+
def to_wire(self) -> dict[str, Any]:
|
|
179
|
+
"""Lower to a typed ASTROX generic entity-position fragment."""
|
|
180
|
+
payload: dict[str, Any] = {
|
|
181
|
+
"$type": "J2",
|
|
182
|
+
"OrbitEpoch": self.orbit_epoch,
|
|
183
|
+
"CoordType": "Classical",
|
|
184
|
+
"OrbitalElements": _orbit_elements_to_wire(self.orbit, parameter="orbit"),
|
|
185
|
+
}
|
|
186
|
+
_include_if_supplied(payload, "Start", self.start)
|
|
187
|
+
_include_if_supplied(payload, "Stop", self.stop)
|
|
188
|
+
_include_if_supplied(payload, "Step", self.step_s)
|
|
189
|
+
_include_if_supplied(payload, "CentralBody", self.central_body)
|
|
190
|
+
_include_if_supplied(
|
|
191
|
+
payload,
|
|
192
|
+
"GravitationalParameter",
|
|
193
|
+
self.gravitational_parameter_m3_s2,
|
|
194
|
+
)
|
|
195
|
+
_include_if_supplied(payload, "CoordSystem", self.coord_system)
|
|
196
|
+
_include_if_supplied(payload, "J2NormalizedValue", self.j2_normalized_value)
|
|
197
|
+
_include_if_supplied(payload, "RefDistance", self.ref_distance_m)
|
|
198
|
+
return payload
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
@dataclass(frozen=True, kw_only=True)
|
|
202
|
+
class TwoBodyPosition:
|
|
203
|
+
"""Two-body propagated Keplerian position source."""
|
|
204
|
+
|
|
205
|
+
orbit_epoch: str
|
|
206
|
+
orbit: KeplerianElements
|
|
207
|
+
start: str | None = None
|
|
208
|
+
stop: str | None = None
|
|
209
|
+
step_s: float | None = None
|
|
210
|
+
central_body: str | None = None
|
|
211
|
+
gravitational_parameter_m3_s2: float | None = None
|
|
212
|
+
coord_system: str | None = None
|
|
213
|
+
|
|
214
|
+
def to_wire(self) -> dict[str, Any]:
|
|
215
|
+
"""Lower to a typed ASTROX generic entity-position fragment."""
|
|
216
|
+
payload: dict[str, Any] = {
|
|
217
|
+
"$type": "TwoBody",
|
|
218
|
+
"OrbitEpoch": self.orbit_epoch,
|
|
219
|
+
"CoordType": "Classical",
|
|
220
|
+
"OrbitalElements": _orbit_elements_to_wire(self.orbit, parameter="orbit"),
|
|
221
|
+
}
|
|
222
|
+
_include_if_supplied(payload, "Start", self.start)
|
|
223
|
+
_include_if_supplied(payload, "Stop", self.stop)
|
|
224
|
+
_include_if_supplied(payload, "Step", self.step_s)
|
|
225
|
+
_include_if_supplied(payload, "CentralBody", self.central_body)
|
|
226
|
+
_include_if_supplied(
|
|
227
|
+
payload,
|
|
228
|
+
"GravitationalParameter",
|
|
229
|
+
self.gravitational_parameter_m3_s2,
|
|
230
|
+
)
|
|
231
|
+
_include_if_supplied(payload, "CoordSystem", self.coord_system)
|
|
232
|
+
return payload
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
@dataclass(frozen=True, kw_only=True)
|
|
236
|
+
class Sgp4Position:
|
|
237
|
+
"""SGP4 TLE position source."""
|
|
238
|
+
|
|
239
|
+
tle_lines: tuple[str, str]
|
|
240
|
+
start: str | None = None
|
|
241
|
+
stop: str | None = None
|
|
242
|
+
step_s: float | None = None
|
|
243
|
+
satellite_number: str | None = None
|
|
244
|
+
|
|
245
|
+
def to_wire(self) -> dict[str, Any]:
|
|
246
|
+
"""Lower to a typed ASTROX generic entity-position fragment."""
|
|
247
|
+
payload: dict[str, Any] = {
|
|
248
|
+
"$type": "SGP4",
|
|
249
|
+
"TLEs": list(self.tle_lines),
|
|
250
|
+
}
|
|
251
|
+
_include_if_supplied(payload, "Start", self.start)
|
|
252
|
+
_include_if_supplied(payload, "Stop", self.stop)
|
|
253
|
+
_include_if_supplied(payload, "Step", self.step_s)
|
|
254
|
+
_include_if_supplied(payload, "SatelliteNumber", self.satellite_number)
|
|
255
|
+
return payload
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
@dataclass(frozen=True, kw_only=True)
|
|
259
|
+
class HpopPosition:
|
|
260
|
+
"""HPOP-propagated position source."""
|
|
261
|
+
|
|
262
|
+
start: str
|
|
263
|
+
stop: str
|
|
264
|
+
orbit_epoch: str
|
|
265
|
+
orbital_elements: tuple[float, ...]
|
|
266
|
+
coord_type: str
|
|
267
|
+
config: HpopConfig | Mapping[str, Any] | None = None
|
|
268
|
+
coord_epoch: str | None = None
|
|
269
|
+
coord_system: str | None = None
|
|
270
|
+
gravitational_parameter_m3_s2: float | None = None
|
|
271
|
+
coefficient_of_drag: float | None = None
|
|
272
|
+
area_mass_ratio_drag_m2_kg: float | None = None
|
|
273
|
+
coefficient_of_srp: float | None = None
|
|
274
|
+
area_mass_ratio_srp_m2_kg: float | None = None
|
|
275
|
+
|
|
276
|
+
def to_wire(self) -> dict[str, Any]:
|
|
277
|
+
"""Lower to a typed ASTROX generic entity-position fragment."""
|
|
278
|
+
payload: dict[str, Any] = {
|
|
279
|
+
"$type": "HPOP",
|
|
280
|
+
"Start": self.start,
|
|
281
|
+
"Stop": self.stop,
|
|
282
|
+
"OrbitEpoch": self.orbit_epoch,
|
|
283
|
+
"CoordType": self.coord_type,
|
|
284
|
+
"OrbitalElements": list(self.orbital_elements),
|
|
285
|
+
}
|
|
286
|
+
_include_if_supplied(payload, "CoordEpoch", self.coord_epoch)
|
|
287
|
+
_include_if_supplied(payload, "CoordSystem", self.coord_system)
|
|
288
|
+
_include_if_supplied(
|
|
289
|
+
payload,
|
|
290
|
+
"GravitationalParameter",
|
|
291
|
+
self.gravitational_parameter_m3_s2,
|
|
292
|
+
)
|
|
293
|
+
_include_if_supplied(payload, "CoefficientOfDrag", self.coefficient_of_drag)
|
|
294
|
+
_include_if_supplied(
|
|
295
|
+
payload,
|
|
296
|
+
"AreaMassRatioDrag",
|
|
297
|
+
self.area_mass_ratio_drag_m2_kg,
|
|
298
|
+
)
|
|
299
|
+
_include_if_supplied(payload, "CoefficientOfSRP", self.coefficient_of_srp)
|
|
300
|
+
_include_if_supplied(
|
|
301
|
+
payload,
|
|
302
|
+
"AreaMassRatioSRP",
|
|
303
|
+
self.area_mass_ratio_srp_m2_kg,
|
|
304
|
+
)
|
|
305
|
+
if self.config is not None:
|
|
306
|
+
payload["HpopPropagator"] = _hpop_config_to_wire(
|
|
307
|
+
self.config,
|
|
308
|
+
parameter="config",
|
|
309
|
+
)
|
|
310
|
+
return payload
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
@dataclass(frozen=True, kw_only=True)
|
|
314
|
+
class SimpleAscentPosition:
|
|
315
|
+
"""Simple ascent position source."""
|
|
316
|
+
|
|
317
|
+
start: str
|
|
318
|
+
stop: str
|
|
319
|
+
launch_latitude_deg: float
|
|
320
|
+
launch_longitude_deg: float
|
|
321
|
+
launch_altitude_m: float
|
|
322
|
+
burnout_velocity_m_s: float
|
|
323
|
+
burnout_latitude_deg: float
|
|
324
|
+
burnout_longitude_deg: float
|
|
325
|
+
burnout_altitude_m: float
|
|
326
|
+
step_s: float | None = None
|
|
327
|
+
central_body: str | None = None
|
|
328
|
+
|
|
329
|
+
def to_wire(self) -> dict[str, Any]:
|
|
330
|
+
"""Lower to a typed ASTROX generic entity-position fragment."""
|
|
331
|
+
payload: dict[str, Any] = {
|
|
332
|
+
"$type": "SimpleAscent",
|
|
333
|
+
"Start": self.start,
|
|
334
|
+
"Stop": self.stop,
|
|
335
|
+
"LaunchLatitude": self.launch_latitude_deg,
|
|
336
|
+
"LaunchLongitude": self.launch_longitude_deg,
|
|
337
|
+
"LaunchAltitude": self.launch_altitude_m,
|
|
338
|
+
"BurnoutVelocity": self.burnout_velocity_m_s,
|
|
339
|
+
"BurnoutLatitude": self.burnout_latitude_deg,
|
|
340
|
+
"BurnoutLongitude": self.burnout_longitude_deg,
|
|
341
|
+
"BurnoutAltitude": self.burnout_altitude_m,
|
|
342
|
+
}
|
|
343
|
+
_include_if_supplied(payload, "Step", self.step_s)
|
|
344
|
+
_include_if_supplied(payload, "CentralBody", self.central_body)
|
|
345
|
+
return payload
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
@dataclass(frozen=True, kw_only=True)
|
|
349
|
+
class BallisticPosition:
|
|
350
|
+
"""Ballistic flight position source."""
|
|
351
|
+
|
|
352
|
+
start: str
|
|
353
|
+
ballistic_type: str
|
|
354
|
+
ballistic_type_value: float
|
|
355
|
+
step_s: float | None = None
|
|
356
|
+
central_body: str | None = None
|
|
357
|
+
gravitational_parameter_m3_s2: float | None = None
|
|
358
|
+
launch_latitude_deg: float | None = None
|
|
359
|
+
launch_longitude_deg: float | None = None
|
|
360
|
+
launch_altitude_m: float | None = None
|
|
361
|
+
impact_latitude_deg: float | None = None
|
|
362
|
+
impact_longitude_deg: float | None = None
|
|
363
|
+
impact_altitude_m: float | None = None
|
|
364
|
+
|
|
365
|
+
def to_wire(self) -> dict[str, Any]:
|
|
366
|
+
"""Lower to a typed ASTROX generic entity-position fragment."""
|
|
367
|
+
payload: dict[str, Any] = {
|
|
368
|
+
"$type": "Ballistic",
|
|
369
|
+
"Start": self.start,
|
|
370
|
+
"BallisticType": self.ballistic_type,
|
|
371
|
+
"BallisticTypeValue": self.ballistic_type_value,
|
|
372
|
+
}
|
|
373
|
+
_include_if_supplied(payload, "Step", self.step_s)
|
|
374
|
+
_include_if_supplied(payload, "CentralBody", self.central_body)
|
|
375
|
+
_include_if_supplied(
|
|
376
|
+
payload,
|
|
377
|
+
"GravitationalParameter",
|
|
378
|
+
self.gravitational_parameter_m3_s2,
|
|
379
|
+
)
|
|
380
|
+
_include_if_supplied(payload, "LaunchLatitude", self.launch_latitude_deg)
|
|
381
|
+
_include_if_supplied(payload, "LaunchLongitude", self.launch_longitude_deg)
|
|
382
|
+
_include_if_supplied(payload, "LaunchAltitude", self.launch_altitude_m)
|
|
383
|
+
_include_if_supplied(payload, "ImpactLatitude", self.impact_latitude_deg)
|
|
384
|
+
_include_if_supplied(payload, "ImpactLongitude", self.impact_longitude_deg)
|
|
385
|
+
_include_if_supplied(payload, "ImpactAltitude", self.impact_altitude_m)
|
|
386
|
+
return payload
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
EntityPosition: TypeAlias = (
|
|
390
|
+
SitePosition
|
|
391
|
+
| CzmlPosition
|
|
392
|
+
| CzmlPositions
|
|
393
|
+
| CentralBodyPosition
|
|
394
|
+
| J2Position
|
|
395
|
+
| TwoBodyPosition
|
|
396
|
+
| Sgp4Position
|
|
397
|
+
| HpopPosition
|
|
398
|
+
| SimpleAscentPosition
|
|
399
|
+
| BallisticPosition
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
_POSITION_TYPES = (
|
|
404
|
+
SitePosition,
|
|
405
|
+
CzmlPosition,
|
|
406
|
+
CzmlPositions,
|
|
407
|
+
CentralBodyPosition,
|
|
408
|
+
J2Position,
|
|
409
|
+
TwoBodyPosition,
|
|
410
|
+
Sgp4Position,
|
|
411
|
+
HpopPosition,
|
|
412
|
+
SimpleAscentPosition,
|
|
413
|
+
BallisticPosition,
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def site_position(
|
|
418
|
+
*,
|
|
419
|
+
longitude_deg: float,
|
|
420
|
+
latitude_deg: float,
|
|
421
|
+
height_m: float,
|
|
422
|
+
central_body: str | None = None,
|
|
423
|
+
clamp_to_ground: bool | None = None,
|
|
424
|
+
height_above_ground_m: float | None = None,
|
|
425
|
+
) -> SitePosition:
|
|
426
|
+
"""Create a fixed geodetic site position."""
|
|
427
|
+
return SitePosition(
|
|
428
|
+
longitude_deg=longitude_deg,
|
|
429
|
+
latitude_deg=latitude_deg,
|
|
430
|
+
height_m=height_m,
|
|
431
|
+
central_body=central_body,
|
|
432
|
+
clamp_to_ground=clamp_to_ground,
|
|
433
|
+
height_above_ground_m=height_above_ground_m,
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
def czml_position(
|
|
438
|
+
*,
|
|
439
|
+
epoch: str,
|
|
440
|
+
central_body: str | None = None,
|
|
441
|
+
interpolation_algorithm: str | None = None,
|
|
442
|
+
interpolation_degree: int | None = None,
|
|
443
|
+
reference_frame: str | None = None,
|
|
444
|
+
interval: str | None = None,
|
|
445
|
+
cartesian: Sequence[float] | None = None,
|
|
446
|
+
cartesian_velocity: Sequence[float] | None = None,
|
|
447
|
+
) -> CzmlPosition:
|
|
448
|
+
"""Create a CZML-like sampled position source."""
|
|
449
|
+
return CzmlPosition(
|
|
450
|
+
epoch=epoch,
|
|
451
|
+
central_body=central_body,
|
|
452
|
+
interpolation_algorithm=interpolation_algorithm,
|
|
453
|
+
interpolation_degree=interpolation_degree,
|
|
454
|
+
reference_frame=reference_frame,
|
|
455
|
+
interval=interval,
|
|
456
|
+
cartesian=tuple(_number_sequence_to_list(cartesian, parameter="cartesian"))
|
|
457
|
+
if cartesian is not None
|
|
458
|
+
else None,
|
|
459
|
+
cartesian_velocity=tuple(
|
|
460
|
+
_number_sequence_to_list(cartesian_velocity, parameter="cartesian_velocity")
|
|
461
|
+
)
|
|
462
|
+
if cartesian_velocity is not None
|
|
463
|
+
else None,
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def czml_positions(
|
|
468
|
+
positions: Sequence[CzmlPosition],
|
|
469
|
+
*,
|
|
470
|
+
central_body: str | None = None,
|
|
471
|
+
) -> CzmlPositions:
|
|
472
|
+
"""Create a composite CZML-like sampled position source."""
|
|
473
|
+
if isinstance(positions, (str, bytes)) or not isinstance(positions, Sequence):
|
|
474
|
+
raise TypeError("positions must be a sequence of CzmlPosition values")
|
|
475
|
+
items = tuple(positions)
|
|
476
|
+
if not all(isinstance(position, CzmlPosition) for position in items):
|
|
477
|
+
raise TypeError("positions must be a sequence of CzmlPosition values")
|
|
478
|
+
return CzmlPositions(positions=items, central_body=central_body)
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
def central_body_position(name: str) -> CentralBodyPosition:
|
|
482
|
+
"""Create a central-body position source."""
|
|
483
|
+
return CentralBodyPosition(name=name)
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
def j2_position(
|
|
487
|
+
*,
|
|
488
|
+
orbit_epoch: str,
|
|
489
|
+
orbit: KeplerianElements,
|
|
490
|
+
start: str | None = None,
|
|
491
|
+
stop: str | None = None,
|
|
492
|
+
step_s: float | None = None,
|
|
493
|
+
central_body: str | None = None,
|
|
494
|
+
gravitational_parameter_m3_s2: float | None = None,
|
|
495
|
+
coord_system: str | None = None,
|
|
496
|
+
j2_normalized_value: float | None = None,
|
|
497
|
+
ref_distance_m: float | None = None,
|
|
498
|
+
) -> J2Position:
|
|
499
|
+
"""Create a J2-propagated Keplerian position source."""
|
|
500
|
+
_orbit_elements_to_wire(orbit, parameter="orbit")
|
|
501
|
+
return J2Position(
|
|
502
|
+
orbit_epoch=orbit_epoch,
|
|
503
|
+
orbit=orbit,
|
|
504
|
+
start=start,
|
|
505
|
+
stop=stop,
|
|
506
|
+
step_s=step_s,
|
|
507
|
+
central_body=central_body,
|
|
508
|
+
gravitational_parameter_m3_s2=gravitational_parameter_m3_s2,
|
|
509
|
+
coord_system=coord_system,
|
|
510
|
+
j2_normalized_value=j2_normalized_value,
|
|
511
|
+
ref_distance_m=ref_distance_m,
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
def two_body_position(
|
|
516
|
+
*,
|
|
517
|
+
orbit_epoch: str,
|
|
518
|
+
orbit: KeplerianElements,
|
|
519
|
+
start: str | None = None,
|
|
520
|
+
stop: str | None = None,
|
|
521
|
+
step_s: float | None = None,
|
|
522
|
+
central_body: str | None = None,
|
|
523
|
+
gravitational_parameter_m3_s2: float | None = None,
|
|
524
|
+
coord_system: str | None = None,
|
|
525
|
+
) -> TwoBodyPosition:
|
|
526
|
+
"""Create a two-body propagated Keplerian position source."""
|
|
527
|
+
_orbit_elements_to_wire(orbit, parameter="orbit")
|
|
528
|
+
return TwoBodyPosition(
|
|
529
|
+
orbit_epoch=orbit_epoch,
|
|
530
|
+
orbit=orbit,
|
|
531
|
+
start=start,
|
|
532
|
+
stop=stop,
|
|
533
|
+
step_s=step_s,
|
|
534
|
+
central_body=central_body,
|
|
535
|
+
gravitational_parameter_m3_s2=gravitational_parameter_m3_s2,
|
|
536
|
+
coord_system=coord_system,
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
def sgp4_position(
|
|
541
|
+
*,
|
|
542
|
+
tle_lines: tuple[str, str] | list[str],
|
|
543
|
+
start: str | None = None,
|
|
544
|
+
stop: str | None = None,
|
|
545
|
+
step_s: float | None = None,
|
|
546
|
+
satellite_number: str | None = None,
|
|
547
|
+
) -> Sgp4Position:
|
|
548
|
+
"""Create an SGP4 TLE position source."""
|
|
549
|
+
return Sgp4Position(
|
|
550
|
+
tle_lines=tuple(_tle_lines_to_list(tle_lines)),
|
|
551
|
+
start=start,
|
|
552
|
+
stop=stop,
|
|
553
|
+
step_s=step_s,
|
|
554
|
+
satellite_number=satellite_number,
|
|
555
|
+
)
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
def hpop_position(
|
|
559
|
+
*,
|
|
560
|
+
start: str,
|
|
561
|
+
stop: str,
|
|
562
|
+
orbit_epoch: str,
|
|
563
|
+
orbit: KeplerianElements | None = None,
|
|
564
|
+
state: CartesianState | None = None,
|
|
565
|
+
config: HpopConfig | Mapping[str, Any] | None = None,
|
|
566
|
+
coord_epoch: str | None = None,
|
|
567
|
+
coord_system: str | None = None,
|
|
568
|
+
gravitational_parameter_m3_s2: float | None = None,
|
|
569
|
+
coefficient_of_drag: float | None = None,
|
|
570
|
+
area_mass_ratio_drag_m2_kg: float | None = None,
|
|
571
|
+
coefficient_of_srp: float | None = None,
|
|
572
|
+
area_mass_ratio_srp_m2_kg: float | None = None,
|
|
573
|
+
) -> HpopPosition:
|
|
574
|
+
"""Create an HPOP-propagated position source."""
|
|
575
|
+
if (orbit is None) == (state is None):
|
|
576
|
+
raise ValueError("exactly one of orbit or state must be provided")
|
|
577
|
+
if orbit is not None:
|
|
578
|
+
coord_type = "Classical"
|
|
579
|
+
orbital_elements = tuple(_orbit_elements_to_wire(orbit, parameter="orbit"))
|
|
580
|
+
else:
|
|
581
|
+
coord_type = "Cartesian"
|
|
582
|
+
orbital_elements = tuple(_cartesian_state_to_wire(state, parameter="state"))
|
|
583
|
+
if config is not None:
|
|
584
|
+
_hpop_config_to_wire(config, parameter="config")
|
|
585
|
+
return HpopPosition(
|
|
586
|
+
start=start,
|
|
587
|
+
stop=stop,
|
|
588
|
+
orbit_epoch=orbit_epoch,
|
|
589
|
+
orbital_elements=orbital_elements,
|
|
590
|
+
coord_type=coord_type,
|
|
591
|
+
config=config,
|
|
592
|
+
coord_epoch=coord_epoch,
|
|
593
|
+
coord_system=coord_system,
|
|
594
|
+
gravitational_parameter_m3_s2=gravitational_parameter_m3_s2,
|
|
595
|
+
coefficient_of_drag=coefficient_of_drag,
|
|
596
|
+
area_mass_ratio_drag_m2_kg=area_mass_ratio_drag_m2_kg,
|
|
597
|
+
coefficient_of_srp=coefficient_of_srp,
|
|
598
|
+
area_mass_ratio_srp_m2_kg=area_mass_ratio_srp_m2_kg,
|
|
599
|
+
)
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
def simple_ascent_position(
|
|
603
|
+
*,
|
|
604
|
+
start: str,
|
|
605
|
+
stop: str,
|
|
606
|
+
launch_latitude_deg: float,
|
|
607
|
+
launch_longitude_deg: float,
|
|
608
|
+
launch_altitude_m: float,
|
|
609
|
+
burnout_velocity_m_s: float,
|
|
610
|
+
burnout_latitude_deg: float,
|
|
611
|
+
burnout_longitude_deg: float,
|
|
612
|
+
burnout_altitude_m: float,
|
|
613
|
+
step_s: float | None = None,
|
|
614
|
+
central_body: str | None = None,
|
|
615
|
+
) -> SimpleAscentPosition:
|
|
616
|
+
"""Create a simple ascent position source."""
|
|
617
|
+
return SimpleAscentPosition(
|
|
618
|
+
start=start,
|
|
619
|
+
stop=stop,
|
|
620
|
+
launch_latitude_deg=launch_latitude_deg,
|
|
621
|
+
launch_longitude_deg=launch_longitude_deg,
|
|
622
|
+
launch_altitude_m=launch_altitude_m,
|
|
623
|
+
burnout_velocity_m_s=burnout_velocity_m_s,
|
|
624
|
+
burnout_latitude_deg=burnout_latitude_deg,
|
|
625
|
+
burnout_longitude_deg=burnout_longitude_deg,
|
|
626
|
+
burnout_altitude_m=burnout_altitude_m,
|
|
627
|
+
step_s=step_s,
|
|
628
|
+
central_body=central_body,
|
|
629
|
+
)
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
def ballistic_position(
|
|
633
|
+
*,
|
|
634
|
+
start: str,
|
|
635
|
+
ballistic_type: str,
|
|
636
|
+
ballistic_type_value: float,
|
|
637
|
+
step_s: float | None = None,
|
|
638
|
+
central_body: str | None = None,
|
|
639
|
+
gravitational_parameter_m3_s2: float | None = None,
|
|
640
|
+
launch_latitude_deg: float | None = None,
|
|
641
|
+
launch_longitude_deg: float | None = None,
|
|
642
|
+
launch_altitude_m: float | None = None,
|
|
643
|
+
impact_latitude_deg: float | None = None,
|
|
644
|
+
impact_longitude_deg: float | None = None,
|
|
645
|
+
impact_altitude_m: float | None = None,
|
|
646
|
+
) -> BallisticPosition:
|
|
647
|
+
"""Create a ballistic flight position source."""
|
|
648
|
+
return BallisticPosition(
|
|
649
|
+
start=start,
|
|
650
|
+
ballistic_type=ballistic_type,
|
|
651
|
+
ballistic_type_value=ballistic_type_value,
|
|
652
|
+
step_s=step_s,
|
|
653
|
+
central_body=central_body,
|
|
654
|
+
gravitational_parameter_m3_s2=gravitational_parameter_m3_s2,
|
|
655
|
+
launch_latitude_deg=launch_latitude_deg,
|
|
656
|
+
launch_longitude_deg=launch_longitude_deg,
|
|
657
|
+
launch_altitude_m=launch_altitude_m,
|
|
658
|
+
impact_latitude_deg=impact_latitude_deg,
|
|
659
|
+
impact_longitude_deg=impact_longitude_deg,
|
|
660
|
+
impact_altitude_m=impact_altitude_m,
|
|
661
|
+
)
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
def _position_to_wire(position: EntityPosition) -> dict[str, Any]:
|
|
665
|
+
if not isinstance(position, _POSITION_TYPES):
|
|
666
|
+
raise TypeError("position must be an astrox.components position value")
|
|
667
|
+
return position.to_wire()
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
def _site_position_to_wire(position: SitePosition) -> dict[str, Any]:
|
|
671
|
+
if not isinstance(position, SitePosition):
|
|
672
|
+
raise TypeError("site_position must be an astrox.components.SitePosition value")
|
|
673
|
+
return position.to_site_wire()
|