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,115 @@
|
|
|
1
|
+
"""Rotation component value objects."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Any, TypeAlias
|
|
7
|
+
|
|
8
|
+
from ._common import _real_number, _string
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True, kw_only=True)
|
|
11
|
+
class AzElRotation:
|
|
12
|
+
"""Azimuth/elevation rotation fragment."""
|
|
13
|
+
|
|
14
|
+
azimuth_deg: float
|
|
15
|
+
elevation_deg: float
|
|
16
|
+
|
|
17
|
+
def to_wire(self) -> dict[str, Any]:
|
|
18
|
+
"""Lower to an ASTROX AzEl orientation fragment."""
|
|
19
|
+
return {
|
|
20
|
+
"$type": "AzEl",
|
|
21
|
+
"Azimuth": self.azimuth_deg,
|
|
22
|
+
"Elevation": self.elevation_deg,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True, kw_only=True)
|
|
27
|
+
class QuaternionRotation:
|
|
28
|
+
"""Quaternion rotation fragment using scalar-first Python arguments."""
|
|
29
|
+
|
|
30
|
+
scalar: float
|
|
31
|
+
x: float
|
|
32
|
+
y: float
|
|
33
|
+
z: float
|
|
34
|
+
|
|
35
|
+
def to_wire(self) -> dict[str, Any]:
|
|
36
|
+
"""Lower to an ASTROX quaternion orientation fragment."""
|
|
37
|
+
return {
|
|
38
|
+
"$type": "Quaternion",
|
|
39
|
+
"QS": self.scalar,
|
|
40
|
+
"QX": self.x,
|
|
41
|
+
"QY": self.y,
|
|
42
|
+
"QZ": self.z,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass(frozen=True, kw_only=True)
|
|
47
|
+
class EulerRotation:
|
|
48
|
+
"""Euler-angle rotation fragment."""
|
|
49
|
+
|
|
50
|
+
sequence: str
|
|
51
|
+
a_deg: float
|
|
52
|
+
b_deg: float
|
|
53
|
+
c_deg: float
|
|
54
|
+
|
|
55
|
+
def to_wire(self) -> dict[str, Any]:
|
|
56
|
+
"""Lower to an ASTROX EulerAngles orientation fragment."""
|
|
57
|
+
return {
|
|
58
|
+
"$type": "EulerAngles",
|
|
59
|
+
"Sequence": self.sequence,
|
|
60
|
+
"A": self.a_deg,
|
|
61
|
+
"B": self.b_deg,
|
|
62
|
+
"C": self.c_deg,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
Rotation: TypeAlias = AzElRotation | QuaternionRotation | EulerRotation
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
_ROTATION_TYPES = (AzElRotation, QuaternionRotation, EulerRotation)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def az_el_rotation(*, azimuth_deg: float, elevation_deg: float) -> AzElRotation:
|
|
73
|
+
"""Create an azimuth/elevation rotation fragment."""
|
|
74
|
+
return AzElRotation(
|
|
75
|
+
azimuth_deg=_real_number(azimuth_deg, parameter="azimuth_deg"),
|
|
76
|
+
elevation_deg=_real_number(elevation_deg, parameter="elevation_deg"),
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def quaternion_rotation(
|
|
81
|
+
*,
|
|
82
|
+
scalar: float,
|
|
83
|
+
x: float,
|
|
84
|
+
y: float,
|
|
85
|
+
z: float,
|
|
86
|
+
) -> QuaternionRotation:
|
|
87
|
+
"""Create a quaternion rotation fragment."""
|
|
88
|
+
return QuaternionRotation(
|
|
89
|
+
scalar=_real_number(scalar, parameter="scalar"),
|
|
90
|
+
x=_real_number(x, parameter="x"),
|
|
91
|
+
y=_real_number(y, parameter="y"),
|
|
92
|
+
z=_real_number(z, parameter="z"),
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def euler_rotation(
|
|
97
|
+
*,
|
|
98
|
+
sequence: str,
|
|
99
|
+
a_deg: float,
|
|
100
|
+
b_deg: float,
|
|
101
|
+
c_deg: float,
|
|
102
|
+
) -> EulerRotation:
|
|
103
|
+
"""Create an Euler-angle rotation fragment."""
|
|
104
|
+
return EulerRotation(
|
|
105
|
+
sequence=_string(sequence, parameter="sequence"),
|
|
106
|
+
a_deg=_real_number(a_deg, parameter="a_deg"),
|
|
107
|
+
b_deg=_real_number(b_deg, parameter="b_deg"),
|
|
108
|
+
c_deg=_real_number(c_deg, parameter="c_deg"),
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _rotation_to_wire(rotation: Rotation) -> dict[str, Any]:
|
|
113
|
+
if not isinstance(rotation, _ROTATION_TYPES):
|
|
114
|
+
raise TypeError("rotation must be an astrox.components rotation value")
|
|
115
|
+
return rotation.to_wire()
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Sensor component value objects."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Any, TypeAlias
|
|
7
|
+
|
|
8
|
+
from ._common import _include_if_supplied, _optional_string
|
|
9
|
+
from ._rotations import Rotation, _rotation_to_wire
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True, kw_only=True)
|
|
12
|
+
class FixedSensorPointing:
|
|
13
|
+
"""Fixed sensor pointing using a rotation fragment."""
|
|
14
|
+
|
|
15
|
+
rotation: Rotation
|
|
16
|
+
text: str | None = None
|
|
17
|
+
|
|
18
|
+
def to_wire(self) -> dict[str, Any]:
|
|
19
|
+
"""Lower to an ASTROX fixed sensor-pointing fragment."""
|
|
20
|
+
payload: dict[str, Any] = {
|
|
21
|
+
"$type": "Fixed",
|
|
22
|
+
"Orientation": _rotation_to_wire(self.rotation),
|
|
23
|
+
}
|
|
24
|
+
_include_if_supplied(payload, "Text", self.text)
|
|
25
|
+
return payload
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
SensorPointing: TypeAlias = FixedSensorPointing
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
_SENSOR_POINTING_TYPES = (FixedSensorPointing,)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True, kw_only=True)
|
|
35
|
+
class ConicSensor:
|
|
36
|
+
"""Conic sensor shape metadata."""
|
|
37
|
+
|
|
38
|
+
inner_half_angle_deg: float | None = None
|
|
39
|
+
outer_half_angle_deg: float | None = None
|
|
40
|
+
minimum_clock_angle_deg: float | None = None
|
|
41
|
+
maximum_clock_angle_deg: float | None = None
|
|
42
|
+
text: str | None = None
|
|
43
|
+
|
|
44
|
+
def to_wire(self) -> dict[str, Any]:
|
|
45
|
+
"""Lower to an ASTROX conic sensor fragment."""
|
|
46
|
+
payload: dict[str, Any] = {"$type": "Conic"}
|
|
47
|
+
_include_if_supplied(payload, "innerHalfAngle", self.inner_half_angle_deg)
|
|
48
|
+
_include_if_supplied(payload, "outerHalfAngle", self.outer_half_angle_deg)
|
|
49
|
+
_include_if_supplied(payload, "minimumClockAngle", self.minimum_clock_angle_deg)
|
|
50
|
+
_include_if_supplied(payload, "maximumClockAngle", self.maximum_clock_angle_deg)
|
|
51
|
+
_include_if_supplied(payload, "Text", self.text)
|
|
52
|
+
return payload
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass(frozen=True, kw_only=True)
|
|
56
|
+
class RectangularSensor:
|
|
57
|
+
"""Rectangular sensor shape metadata."""
|
|
58
|
+
|
|
59
|
+
x_half_angle_deg: float | None = None
|
|
60
|
+
y_half_angle_deg: float | None = None
|
|
61
|
+
text: str | None = None
|
|
62
|
+
|
|
63
|
+
def to_wire(self) -> dict[str, Any]:
|
|
64
|
+
"""Lower to an ASTROX rectangular sensor fragment."""
|
|
65
|
+
payload: dict[str, Any] = {"$type": "Rectangular"}
|
|
66
|
+
_include_if_supplied(payload, "xHalfAngle", self.x_half_angle_deg)
|
|
67
|
+
_include_if_supplied(payload, "yHalfAngle", self.y_half_angle_deg)
|
|
68
|
+
_include_if_supplied(payload, "Text", self.text)
|
|
69
|
+
return payload
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
EntitySensor: TypeAlias = ConicSensor | RectangularSensor
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
_SENSOR_TYPES = (ConicSensor, RectangularSensor)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def fixed_sensor_pointing(
|
|
79
|
+
*,
|
|
80
|
+
rotation: Rotation,
|
|
81
|
+
text: str | None = None,
|
|
82
|
+
) -> FixedSensorPointing:
|
|
83
|
+
"""Create fixed sensor-pointing metadata."""
|
|
84
|
+
_rotation_to_wire(rotation)
|
|
85
|
+
return FixedSensorPointing(
|
|
86
|
+
rotation=rotation,
|
|
87
|
+
text=_optional_string(text, parameter="text"),
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def conic_sensor(
|
|
92
|
+
*,
|
|
93
|
+
inner_half_angle_deg: float | None = None,
|
|
94
|
+
outer_half_angle_deg: float | None = None,
|
|
95
|
+
minimum_clock_angle_deg: float | None = None,
|
|
96
|
+
maximum_clock_angle_deg: float | None = None,
|
|
97
|
+
text: str | None = None,
|
|
98
|
+
) -> ConicSensor:
|
|
99
|
+
"""Create conic sensor metadata."""
|
|
100
|
+
return ConicSensor(
|
|
101
|
+
inner_half_angle_deg=inner_half_angle_deg,
|
|
102
|
+
outer_half_angle_deg=outer_half_angle_deg,
|
|
103
|
+
minimum_clock_angle_deg=minimum_clock_angle_deg,
|
|
104
|
+
maximum_clock_angle_deg=maximum_clock_angle_deg,
|
|
105
|
+
text=text,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def rectangular_sensor(
|
|
110
|
+
*,
|
|
111
|
+
x_half_angle_deg: float | None = None,
|
|
112
|
+
y_half_angle_deg: float | None = None,
|
|
113
|
+
text: str | None = None,
|
|
114
|
+
) -> RectangularSensor:
|
|
115
|
+
"""Create rectangular sensor metadata."""
|
|
116
|
+
return RectangularSensor(
|
|
117
|
+
x_half_angle_deg=x_half_angle_deg,
|
|
118
|
+
y_half_angle_deg=y_half_angle_deg,
|
|
119
|
+
text=text,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _sensor_to_wire(sensor: EntitySensor) -> dict[str, Any]:
|
|
124
|
+
if not isinstance(sensor, _SENSOR_TYPES):
|
|
125
|
+
raise TypeError("sensor must be an astrox.components sensor value")
|
|
126
|
+
return sensor.to_wire()
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _sensor_pointing_to_wire(pointing: SensorPointing) -> dict[str, Any]:
|
|
130
|
+
if not isinstance(pointing, _SENSOR_POINTING_TYPES):
|
|
131
|
+
raise TypeError(
|
|
132
|
+
"sensor_pointing must be an astrox.components sensor-pointing value"
|
|
133
|
+
)
|
|
134
|
+
return pointing.to_wire()
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
"""VGT component value objects."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Sequence
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Any, TypeAlias
|
|
8
|
+
|
|
9
|
+
from ._axes import EntityAxes, _axes_reference_name, _axes_tuple
|
|
10
|
+
from ._common import (
|
|
11
|
+
_include_if_supplied,
|
|
12
|
+
_optional_string,
|
|
13
|
+
_real_number,
|
|
14
|
+
_string,
|
|
15
|
+
_typed_tuple,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True, kw_only=True)
|
|
19
|
+
class XyzDirection:
|
|
20
|
+
"""XYZ direction fragment for named VGT vectors."""
|
|
21
|
+
|
|
22
|
+
x: float
|
|
23
|
+
y: float
|
|
24
|
+
z: float
|
|
25
|
+
|
|
26
|
+
def to_wire(self) -> dict[str, Any]:
|
|
27
|
+
"""Lower to an ASTROX XYZ direction fragment."""
|
|
28
|
+
return {"$type": "XYZ", "X": self.x, "Y": self.y, "Z": self.z}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass(frozen=True, kw_only=True)
|
|
32
|
+
class RaDecDirection:
|
|
33
|
+
"""Right-ascension/declination direction fragment for named VGT vectors."""
|
|
34
|
+
|
|
35
|
+
ra_deg: float
|
|
36
|
+
dec_deg: float
|
|
37
|
+
magnitude: float | None = None
|
|
38
|
+
|
|
39
|
+
def to_wire(self) -> dict[str, Any]:
|
|
40
|
+
"""Lower to an ASTROX RADec direction fragment."""
|
|
41
|
+
payload: dict[str, Any] = {
|
|
42
|
+
"$type": "RADec",
|
|
43
|
+
"RA": self.ra_deg,
|
|
44
|
+
"Dec": self.dec_deg,
|
|
45
|
+
}
|
|
46
|
+
_include_if_supplied(payload, "Magnitude", self.magnitude)
|
|
47
|
+
return payload
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
VgtDirection: TypeAlias = XyzDirection | RaDecDirection
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
_DIRECTION_TYPES = (XyzDirection, RaDecDirection)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass(frozen=True, kw_only=True)
|
|
57
|
+
class VgtFixedVector:
|
|
58
|
+
"""Named VGT vector fixed in reference axes."""
|
|
59
|
+
|
|
60
|
+
name: str
|
|
61
|
+
reference_axes: EntityAxes | str
|
|
62
|
+
direction: VgtDirection
|
|
63
|
+
description: str | None = None
|
|
64
|
+
|
|
65
|
+
def to_wire(self) -> dict[str, Any]:
|
|
66
|
+
"""Lower to an ASTROX FixedInAxes vector fragment."""
|
|
67
|
+
payload: dict[str, Any] = {
|
|
68
|
+
"$type": "FixedInAxes",
|
|
69
|
+
"Direction": _direction_to_wire(self.direction),
|
|
70
|
+
"ReferenceAxesName": _axes_reference_name(
|
|
71
|
+
self.reference_axes,
|
|
72
|
+
parameter="reference_axes",
|
|
73
|
+
),
|
|
74
|
+
"Name": self.name,
|
|
75
|
+
}
|
|
76
|
+
_include_if_supplied(payload, "Description", self.description)
|
|
77
|
+
return payload
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
VgtVector: TypeAlias = VgtFixedVector
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
_VGT_VECTOR_TYPES = (VgtFixedVector,)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@dataclass(frozen=True, kw_only=True)
|
|
87
|
+
class VgtPoint:
|
|
88
|
+
"""Named VGT point definition."""
|
|
89
|
+
|
|
90
|
+
name: str
|
|
91
|
+
description: str | None = None
|
|
92
|
+
|
|
93
|
+
def to_wire(self) -> dict[str, Any]:
|
|
94
|
+
"""Lower to an ASTROX VGT point fragment."""
|
|
95
|
+
payload: dict[str, Any] = {"Name": self.name}
|
|
96
|
+
_include_if_supplied(payload, "Description", self.description)
|
|
97
|
+
return payload
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@dataclass(frozen=True, kw_only=True)
|
|
101
|
+
class VgtSystem:
|
|
102
|
+
"""Named VGT coordinate-system definition."""
|
|
103
|
+
|
|
104
|
+
name: str
|
|
105
|
+
description: str | None = None
|
|
106
|
+
|
|
107
|
+
def to_wire(self) -> dict[str, Any]:
|
|
108
|
+
"""Lower to an ASTROX VGT system fragment."""
|
|
109
|
+
payload: dict[str, Any] = {"Name": self.name}
|
|
110
|
+
_include_if_supplied(payload, "Description", self.description)
|
|
111
|
+
return payload
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@dataclass(frozen=True, kw_only=True)
|
|
115
|
+
class VgtAngle:
|
|
116
|
+
"""Named VGT angle between two named vectors."""
|
|
117
|
+
|
|
118
|
+
name: str
|
|
119
|
+
from_vector: VgtVector | str
|
|
120
|
+
to_vector: VgtVector | str
|
|
121
|
+
description: str | None = None
|
|
122
|
+
|
|
123
|
+
def to_wire(self) -> dict[str, Any]:
|
|
124
|
+
"""Lower to an ASTROX VGT angle fragment."""
|
|
125
|
+
payload: dict[str, Any] = {
|
|
126
|
+
"$type": "BetweenVectors",
|
|
127
|
+
"Name": self.name,
|
|
128
|
+
"FromVectorName": _vector_reference_name(
|
|
129
|
+
self.from_vector,
|
|
130
|
+
parameter="from_vector",
|
|
131
|
+
),
|
|
132
|
+
"ToVectorName": _vector_reference_name(
|
|
133
|
+
self.to_vector,
|
|
134
|
+
parameter="to_vector",
|
|
135
|
+
),
|
|
136
|
+
}
|
|
137
|
+
_include_if_supplied(payload, "Description", self.description)
|
|
138
|
+
return payload
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@dataclass(frozen=True, kw_only=True)
|
|
142
|
+
class VgtPlane:
|
|
143
|
+
"""Named VGT plane definition."""
|
|
144
|
+
|
|
145
|
+
name: str
|
|
146
|
+
plane_type: str | None = None
|
|
147
|
+
description: str | None = None
|
|
148
|
+
|
|
149
|
+
def to_wire(self) -> dict[str, Any]:
|
|
150
|
+
"""Lower to an ASTROX VGT plane fragment."""
|
|
151
|
+
payload: dict[str, Any] = {"Name": self.name}
|
|
152
|
+
_include_if_supplied(payload, "Description", self.description)
|
|
153
|
+
_include_if_supplied(payload, "Type", self.plane_type)
|
|
154
|
+
return payload
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@dataclass(frozen=True, kw_only=True)
|
|
158
|
+
class VgtProvider:
|
|
159
|
+
"""Named geometry definitions attached to an entity.
|
|
160
|
+
|
|
161
|
+
ASTROX exposes this field as ``Vgt``. The SDK keeps the same advanced name
|
|
162
|
+
because VGT definitions are name-reference objects, not general geometry
|
|
163
|
+
math values. ``axes`` are entity attitude frames; ``vectors`` and the other
|
|
164
|
+
collections are named definitions that ASTROX can resolve by name from
|
|
165
|
+
orientation branches such as ``Fixed`` or ``AlignedAndConstrained``.
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
axes: tuple[EntityAxes, ...]
|
|
169
|
+
vectors: tuple[VgtVector, ...] | None = None
|
|
170
|
+
points: tuple[VgtPoint, ...] | None = None
|
|
171
|
+
systems: tuple[VgtSystem, ...] | None = None
|
|
172
|
+
angles: tuple[VgtAngle, ...] | None = None
|
|
173
|
+
planes: tuple[VgtPlane, ...] | None = None
|
|
174
|
+
|
|
175
|
+
def to_wire(self) -> dict[str, Any]:
|
|
176
|
+
"""Lower to an ASTROX CrdnProvider fragment."""
|
|
177
|
+
payload: dict[str, Any] = {
|
|
178
|
+
"Axes": [axes.to_wire() for axes in self.axes],
|
|
179
|
+
}
|
|
180
|
+
if self.vectors is not None:
|
|
181
|
+
payload["Vectors"] = [vector.to_wire() for vector in self.vectors]
|
|
182
|
+
if self.points is not None:
|
|
183
|
+
payload["Points"] = [point.to_wire() for point in self.points]
|
|
184
|
+
if self.systems is not None:
|
|
185
|
+
payload["Systems"] = [system.to_wire() for system in self.systems]
|
|
186
|
+
if self.angles is not None:
|
|
187
|
+
payload["Angles"] = [angle.to_wire() for angle in self.angles]
|
|
188
|
+
if self.planes is not None:
|
|
189
|
+
payload["Planes"] = [plane.to_wire() for plane in self.planes]
|
|
190
|
+
return payload
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def xyz_direction(*, x: float, y: float, z: float) -> XyzDirection:
|
|
194
|
+
"""Create an XYZ direction fragment for a VGT vector."""
|
|
195
|
+
return XyzDirection(
|
|
196
|
+
x=_real_number(x, parameter="x"),
|
|
197
|
+
y=_real_number(y, parameter="y"),
|
|
198
|
+
z=_real_number(z, parameter="z"),
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def ra_dec_direction(
|
|
203
|
+
*,
|
|
204
|
+
ra_deg: float,
|
|
205
|
+
dec_deg: float,
|
|
206
|
+
magnitude: float | None = None,
|
|
207
|
+
) -> RaDecDirection:
|
|
208
|
+
"""Create a right-ascension/declination direction fragment."""
|
|
209
|
+
return RaDecDirection(
|
|
210
|
+
ra_deg=_real_number(ra_deg, parameter="ra_deg"),
|
|
211
|
+
dec_deg=_real_number(dec_deg, parameter="dec_deg"),
|
|
212
|
+
magnitude=_real_number(magnitude, parameter="magnitude")
|
|
213
|
+
if magnitude is not None
|
|
214
|
+
else None,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def vgt_fixed_vector(
|
|
219
|
+
*,
|
|
220
|
+
name: str,
|
|
221
|
+
reference_axes: EntityAxes | str,
|
|
222
|
+
direction: VgtDirection,
|
|
223
|
+
description: str | None = None,
|
|
224
|
+
) -> VgtFixedVector:
|
|
225
|
+
"""Create a named VGT vector fixed in reference axes."""
|
|
226
|
+
_axes_reference_name(reference_axes, parameter="reference_axes")
|
|
227
|
+
_direction_to_wire(direction)
|
|
228
|
+
return VgtFixedVector(
|
|
229
|
+
name=_string(name, parameter="name"),
|
|
230
|
+
reference_axes=reference_axes,
|
|
231
|
+
direction=direction,
|
|
232
|
+
description=_optional_string(description, parameter="description"),
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def vgt_point(*, name: str, description: str | None = None) -> VgtPoint:
|
|
237
|
+
"""Create a named VGT point definition."""
|
|
238
|
+
return VgtPoint(
|
|
239
|
+
name=_string(name, parameter="name"),
|
|
240
|
+
description=_optional_string(description, parameter="description"),
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def vgt_system(*, name: str, description: str | None = None) -> VgtSystem:
|
|
245
|
+
"""Create a named VGT system definition."""
|
|
246
|
+
return VgtSystem(
|
|
247
|
+
name=_string(name, parameter="name"),
|
|
248
|
+
description=_optional_string(description, parameter="description"),
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def vgt_angle(
|
|
253
|
+
*,
|
|
254
|
+
name: str,
|
|
255
|
+
from_vector: VgtVector | str,
|
|
256
|
+
to_vector: VgtVector | str,
|
|
257
|
+
description: str | None = None,
|
|
258
|
+
) -> VgtAngle:
|
|
259
|
+
"""Create a named VGT angle between two named vectors."""
|
|
260
|
+
_vector_reference_name(from_vector, parameter="from_vector")
|
|
261
|
+
_vector_reference_name(to_vector, parameter="to_vector")
|
|
262
|
+
return VgtAngle(
|
|
263
|
+
name=_string(name, parameter="name"),
|
|
264
|
+
from_vector=from_vector,
|
|
265
|
+
to_vector=to_vector,
|
|
266
|
+
description=_optional_string(description, parameter="description"),
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def vgt_plane(
|
|
271
|
+
*,
|
|
272
|
+
name: str,
|
|
273
|
+
plane_type: str | None = None,
|
|
274
|
+
description: str | None = None,
|
|
275
|
+
) -> VgtPlane:
|
|
276
|
+
"""Create a named VGT plane definition."""
|
|
277
|
+
return VgtPlane(
|
|
278
|
+
name=_string(name, parameter="name"),
|
|
279
|
+
plane_type=_optional_string(plane_type, parameter="plane_type"),
|
|
280
|
+
description=_optional_string(description, parameter="description"),
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def vgt(
|
|
285
|
+
*,
|
|
286
|
+
axes: Sequence[EntityAxes],
|
|
287
|
+
vectors: Sequence[VgtVector] | None = None,
|
|
288
|
+
points: Sequence[VgtPoint] | None = None,
|
|
289
|
+
systems: Sequence[VgtSystem] | None = None,
|
|
290
|
+
angles: Sequence[VgtAngle] | None = None,
|
|
291
|
+
planes: Sequence[VgtPlane] | None = None,
|
|
292
|
+
) -> VgtProvider:
|
|
293
|
+
"""Create a VGT provider for named entity geometry definitions."""
|
|
294
|
+
return VgtProvider(
|
|
295
|
+
axes=_axes_tuple(axes, parameter="axes"),
|
|
296
|
+
vectors=_typed_tuple(vectors, _VGT_VECTOR_TYPES, parameter="vectors")
|
|
297
|
+
if vectors is not None
|
|
298
|
+
else None,
|
|
299
|
+
points=_typed_tuple(points, (VgtPoint,), parameter="points")
|
|
300
|
+
if points is not None
|
|
301
|
+
else None,
|
|
302
|
+
systems=_typed_tuple(systems, (VgtSystem,), parameter="systems")
|
|
303
|
+
if systems is not None
|
|
304
|
+
else None,
|
|
305
|
+
angles=_typed_tuple(angles, (VgtAngle,), parameter="angles")
|
|
306
|
+
if angles is not None
|
|
307
|
+
else None,
|
|
308
|
+
planes=_typed_tuple(planes, (VgtPlane,), parameter="planes")
|
|
309
|
+
if planes is not None
|
|
310
|
+
else None,
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def _vgt_to_wire(provider: VgtProvider) -> dict[str, Any]:
|
|
315
|
+
if not isinstance(provider, VgtProvider):
|
|
316
|
+
raise TypeError("vgt must be an astrox.components.VgtProvider value")
|
|
317
|
+
return provider.to_wire()
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def _direction_to_wire(direction: VgtDirection) -> dict[str, Any]:
|
|
321
|
+
if not isinstance(direction, _DIRECTION_TYPES):
|
|
322
|
+
raise TypeError("direction must be an astrox.components VGT direction value")
|
|
323
|
+
return direction.to_wire()
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def _vector_reference_name(value: VgtVector | str, *, parameter: str) -> str:
|
|
327
|
+
if isinstance(value, str):
|
|
328
|
+
return value
|
|
329
|
+
if isinstance(value, _VGT_VECTOR_TYPES):
|
|
330
|
+
if value.name is None:
|
|
331
|
+
raise TypeError(
|
|
332
|
+
f"{parameter} object must have a name before it can be referenced"
|
|
333
|
+
)
|
|
334
|
+
return value.name
|
|
335
|
+
raise TypeError(
|
|
336
|
+
f"{parameter} must be an astrox.components VGT vector value or string name"
|
|
337
|
+
)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Coverage analysis functions, grids, reports, and figure-of-merit routes."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from . import coverage_time, number_of_assets, response_time, revisit_time, simple_coverage
|
|
6
|
+
from ._core import (
|
|
7
|
+
CbLatLonGrid,
|
|
8
|
+
CoverageGrid,
|
|
9
|
+
GlobalGrid,
|
|
10
|
+
LatitudeGrid,
|
|
11
|
+
LatLonGrid,
|
|
12
|
+
cb_lat_lon_grid,
|
|
13
|
+
compute,
|
|
14
|
+
coverage_by_asset,
|
|
15
|
+
global_grid,
|
|
16
|
+
grid_points,
|
|
17
|
+
lat_lon_grid,
|
|
18
|
+
latitude_grid,
|
|
19
|
+
percent_coverage,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"CbLatLonGrid",
|
|
24
|
+
"CoverageGrid",
|
|
25
|
+
"GlobalGrid",
|
|
26
|
+
"LatitudeGrid",
|
|
27
|
+
"LatLonGrid",
|
|
28
|
+
"cb_lat_lon_grid",
|
|
29
|
+
"compute",
|
|
30
|
+
"coverage_by_asset",
|
|
31
|
+
"coverage_time",
|
|
32
|
+
"global_grid",
|
|
33
|
+
"grid_points",
|
|
34
|
+
"lat_lon_grid",
|
|
35
|
+
"latitude_grid",
|
|
36
|
+
"number_of_assets",
|
|
37
|
+
"percent_coverage",
|
|
38
|
+
"response_time",
|
|
39
|
+
"revisit_time",
|
|
40
|
+
"simple_coverage",
|
|
41
|
+
]
|