anise 0.9.1__cp313-cp313-win_amd64.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.
- anise/__init__.py +42 -0
- anise/__init__.pyi +638 -0
- anise/_anise.cp313-win_amd64.pyd +0 -0
- anise/analysis.py +8 -0
- anise/analysis.pyi +475 -0
- anise/astro.py +8 -0
- anise/astro.pyi +1092 -0
- anise/constants.py +8 -0
- anise/constants.pyi +91 -0
- anise/instrument/__init__.py +8 -0
- anise/instrument/__init__.pyi +881 -0
- anise/py.typed +0 -0
- anise/rotation.py +8 -0
- anise/rotation.pyi +287 -0
- anise/time.py +8 -0
- anise/time.pyi +1686 -0
- anise/utils.py +8 -0
- anise/utils.pyi +9 -0
- anise-0.9.1.dist-info/METADATA +241 -0
- anise-0.9.1.dist-info/RECORD +22 -0
- anise-0.9.1.dist-info/WHEEL +4 -0
- anise-0.9.1.dist-info/entry_points.txt +2 -0
anise/__init__.pyi
ADDED
|
@@ -0,0 +1,638 @@
|
|
|
1
|
+
import numpy
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
@typing.final
|
|
5
|
+
class Aberration:
|
|
6
|
+
"""Represents the aberration correction options in ANISE.
|
|
7
|
+
|
|
8
|
+
In space science and engineering, accurately pointing instruments (like optical cameras or radio antennas) at a target is crucial. This task is complicated by the finite speed of light, necessitating corrections for the apparent position of the target.
|
|
9
|
+
|
|
10
|
+
This structure holds parameters for aberration corrections applied to a target's position or state vector. These corrections account for the difference between the target's geometric (true) position and its apparent position as observed.
|
|
11
|
+
|
|
12
|
+
# Rule of tumb
|
|
13
|
+
In most Earth orbits, one does _not_ need to provide any aberration corrections. Light time to the target is less than one second (the Moon is about one second away).
|
|
14
|
+
In near Earth orbits, e.g. inner solar system, preliminary analysis can benefit from enabling unconverged light time correction. Stellar aberration is probably not required.
|
|
15
|
+
For deep space missions, preliminary analysis would likely require both light time correction and stellar aberration. Mission planning and operations will definitely need converged light-time calculations.
|
|
16
|
+
|
|
17
|
+
For more details, <https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/abcorr.html>.
|
|
18
|
+
|
|
19
|
+
# SPICE Validation
|
|
20
|
+
|
|
21
|
+
The validation test `validate_jplde_de440s_aberration_lt` checks 101,000 pairs of ephemeris computations and shows that the unconverged Light Time computation matches the SPICE computations almost all the time.
|
|
22
|
+
More specifically, the 99th percentile of error is less than 5 meters, the 75th percentile is less than one meter, and the median error is less than 2 millimeters."""
|
|
23
|
+
converged: bool
|
|
24
|
+
stellar: bool
|
|
25
|
+
transmit_mode: bool
|
|
26
|
+
|
|
27
|
+
def __init__(self, name: str) -> Aberration:
|
|
28
|
+
"""Represents the aberration correction options in ANISE.
|
|
29
|
+
|
|
30
|
+
In space science and engineering, accurately pointing instruments (like optical cameras or radio antennas) at a target is crucial. This task is complicated by the finite speed of light, necessitating corrections for the apparent position of the target.
|
|
31
|
+
|
|
32
|
+
This structure holds parameters for aberration corrections applied to a target's position or state vector. These corrections account for the difference between the target's geometric (true) position and its apparent position as observed.
|
|
33
|
+
|
|
34
|
+
# Rule of tumb
|
|
35
|
+
In most Earth orbits, one does _not_ need to provide any aberration corrections. Light time to the target is less than one second (the Moon is about one second away).
|
|
36
|
+
In near Earth orbits, e.g. inner solar system, preliminary analysis can benefit from enabling unconverged light time correction. Stellar aberration is probably not required.
|
|
37
|
+
For deep space missions, preliminary analysis would likely require both light time correction and stellar aberration. Mission planning and operations will definitely need converged light-time calculations.
|
|
38
|
+
|
|
39
|
+
For more details, <https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/abcorr.html>.
|
|
40
|
+
|
|
41
|
+
# SPICE Validation
|
|
42
|
+
|
|
43
|
+
The validation test `validate_jplde_de440s_aberration_lt` checks 101,000 pairs of ephemeris computations and shows that the unconverged Light Time computation matches the SPICE computations almost all the time.
|
|
44
|
+
More specifically, the 99th percentile of error is less than 5 meters, the 75th percentile is less than one meter, and the median error is less than 2 millimeters."""
|
|
45
|
+
|
|
46
|
+
def __eq__(self, value: typing.Any) -> bool:
|
|
47
|
+
"""Return self==value."""
|
|
48
|
+
|
|
49
|
+
def __ge__(self, value: typing.Any) -> bool:
|
|
50
|
+
"""Return self>=value."""
|
|
51
|
+
|
|
52
|
+
def __gt__(self, value: typing.Any) -> bool:
|
|
53
|
+
"""Return self>value."""
|
|
54
|
+
|
|
55
|
+
def __le__(self, value: typing.Any) -> bool:
|
|
56
|
+
"""Return self<=value."""
|
|
57
|
+
|
|
58
|
+
def __lt__(self, value: typing.Any) -> bool:
|
|
59
|
+
"""Return self<value."""
|
|
60
|
+
|
|
61
|
+
def __ne__(self, value: typing.Any) -> bool:
|
|
62
|
+
"""Return self!=value."""
|
|
63
|
+
|
|
64
|
+
def __repr__(self) -> str:
|
|
65
|
+
"""Return repr(self)."""
|
|
66
|
+
|
|
67
|
+
def __str__(self) -> str:
|
|
68
|
+
"""Return str(self)."""
|
|
69
|
+
|
|
70
|
+
@typing.final
|
|
71
|
+
class Almanac:
|
|
72
|
+
"""An Almanac contains all of the loaded SPICE and ANISE data. It is the context for all computations."""
|
|
73
|
+
|
|
74
|
+
def __init__(self, path: str) -> Almanac:
|
|
75
|
+
"""An Almanac contains all of the loaded SPICE and ANISE data. It is the context for all computations."""
|
|
76
|
+
|
|
77
|
+
def angular_velocity_deg_s(self, from_frame: Frame, to_frame: Frame, epoch: Epoch) -> numpy.array:
|
|
78
|
+
"""Returns the angular velocity vector in deg/s of the from_frame wrt to the to_frame.
|
|
79
|
+
|
|
80
|
+
This can be used to compute the angular velocity of the Earth ITRF93 frame with respect to the J2000 frame for example."""
|
|
81
|
+
|
|
82
|
+
def angular_velocity_rad_s(self, from_frame: Frame, to_frame: Frame, epoch: Epoch) -> numpy.array:
|
|
83
|
+
"""Returns the angular velocity vector in rad/s of the from_frame wrt to the to_frame.
|
|
84
|
+
|
|
85
|
+
This can be used to compute the angular velocity of the Earth ITRF93 frame with respect to the J2000 frame for example."""
|
|
86
|
+
|
|
87
|
+
def angular_velocity_wrt_j2000_deg_s(self, from_frame: Frame, epoch: Epoch) -> numpy.array:
|
|
88
|
+
"""Returns the angular velocity vector in deg/s of the from_frame wrt to the J2000 frame."""
|
|
89
|
+
|
|
90
|
+
def angular_velocity_wrt_j2000_rad_s(self, from_frame: Frame, epoch: Epoch) -> numpy.array:
|
|
91
|
+
"""Returns the angular velocity vector in rad/s of the from_frame wrt to the J2000 frame."""
|
|
92
|
+
|
|
93
|
+
def azimuth_elevation_range_sez(self, rx: Orbit, tx: Orbit, obstructing_body: Frame=None, ab_corr: Aberration=None) -> AzElRange:
|
|
94
|
+
"""Computes the azimuth (in degrees), elevation (in degrees), and range (in kilometers) of the
|
|
95
|
+
receiver state (`rx`) seen from the transmitter state (`tx`), once converted into the SEZ frame of the transmitter.
|
|
96
|
+
|
|
97
|
+
# Warning
|
|
98
|
+
The obstructing body _should_ be a tri-axial ellipsoid body, e.g. IAU_MOON_FRAME.
|
|
99
|
+
|
|
100
|
+
# Algorithm
|
|
101
|
+
1. If any obstructing_bodies are provided, ensure that none of these are obstructing the line of sight between the receiver and transmitter.
|
|
102
|
+
2. Compute the SEZ (South East Zenith) frame of the transmitter.
|
|
103
|
+
3. Rotate the receiver position vector into the transmitter SEZ frame.
|
|
104
|
+
4. Rotate the transmitter position vector into that same SEZ frame.
|
|
105
|
+
5. Compute the range as the norm of the difference between these two position vectors.
|
|
106
|
+
6. Compute the elevation, and ensure it is between +/- 180 degrees.
|
|
107
|
+
7. Compute the azimuth with a quadrant check, and ensure it is between 0 and 360 degrees."""
|
|
108
|
+
|
|
109
|
+
def azimuth_elevation_range_sez_from_location(self, rx: Orbit, location: Location, obstructing_body: Frame=None, ab_corr: Aberration=None) -> AzElRange:
|
|
110
|
+
"""Computes the azimuth (in degrees), elevation (in degrees), and range (in kilometers) of the
|
|
111
|
+
receiver state (`rx`) seen from the provided location (as transmitter state, once converted into the SEZ frame of the transmitter.
|
|
112
|
+
Refer to [azimuth_elevation_range_sez] for algorithm details.
|
|
113
|
+
Location terrain masks are always applied, i.e. if the terrain masks the object, all data is set to f64::NAN, unless specified otherwise in the Location."""
|
|
114
|
+
|
|
115
|
+
def azimuth_elevation_range_sez_from_location_id(self, rx: Orbit, location_id: int, obstructing_body: Frame=None, ab_corr: Aberration=None) -> AzElRange:
|
|
116
|
+
"""Computes the azimuth (in degrees), elevation (in degrees), and range (in kilometers) of the
|
|
117
|
+
receiver state (`rx`) seen from the location ID (as transmitter state, once converted into the SEZ frame of the transmitter.
|
|
118
|
+
Refer to [azimuth_elevation_range_sez] for algorithm details."""
|
|
119
|
+
|
|
120
|
+
def azimuth_elevation_range_sez_from_location_name(self, rx: Orbit, location_name: str, obstructing_body: Frame=None, ab_corr: Aberration=None) -> AzElRange:
|
|
121
|
+
"""Computes the azimuth (in degrees), elevation (in degrees), and range (in kilometers) of the
|
|
122
|
+
receiver state (`rx`) seen from the location ID (as transmitter state, once converted into the SEZ frame of the transmitter.
|
|
123
|
+
Refer to [azimuth_elevation_range_sez] for algorithm details."""
|
|
124
|
+
|
|
125
|
+
def azimuth_elevation_range_sez_many(self, rx_tx_states: typing.List[Orbit], obstructing_body: Frame=None, ab_corr: Aberration=None) -> typing.List[AzElRange]:
|
|
126
|
+
"""Computes the azimuth (in degrees), elevation (in degrees), and range (in kilometers) of the
|
|
127
|
+
receiver states (first item in tuple) seen from the transmitter state (second item in states tuple), once converted into the SEZ frame of the transmitter.
|
|
128
|
+
|
|
129
|
+
Note: if any computation fails, the error will be printed to the stderr.
|
|
130
|
+
Note: the output AER will be chronologically sorted, regardless of transmitter.
|
|
131
|
+
|
|
132
|
+
Refer to [azimuth_elevation_range_sez] for details."""
|
|
133
|
+
|
|
134
|
+
def beta_angle_deg(self, state: Orbit, ab_corr: Aberration=None) -> float:
|
|
135
|
+
"""Computes the Beta angle (β) for a given orbital state, in degrees. A Beta angle of 0° indicates that the orbit plane is edge-on to the Sun, leading to maximum eclipse time. Conversely, a Beta angle of +90° or -90° means the orbit plane is face-on to the Sun, resulting in continuous sunlight exposure and no eclipses.
|
|
136
|
+
|
|
137
|
+
The Beta angle (β) is defined as the angle between the orbit plane of a spacecraft and the vector from the central body (e.g., Earth) to the Sun. In simpler terms, it measures how much of the time a satellite in orbit is exposed to direct sunlight.
|
|
138
|
+
The mathematical formula for the Beta angle is: β=arcsin(h⋅usun\u200b)
|
|
139
|
+
Where:
|
|
140
|
+
- h is the unit vector of the orbital momentum.
|
|
141
|
+
- usun\u200b is the unit vector pointing from the central body to the Sun.
|
|
142
|
+
|
|
143
|
+
Original code from GMAT, <https://github.com/ChristopherRabotin/GMAT/blob/GMAT-R2022a/src/gmatutil/util/CalculationUtilities.cpp#L209-L219>"""
|
|
144
|
+
|
|
145
|
+
def bpc_domain(self, id: int) -> typing.Tuple:
|
|
146
|
+
"""Returns the applicable domain of the request id, i.e. start and end epoch that the provided id has loaded data."""
|
|
147
|
+
|
|
148
|
+
def bpc_domains(self) -> typing.Dict:
|
|
149
|
+
"""Returns a map of each loaded BPC ID to its domain validity.
|
|
150
|
+
|
|
151
|
+
# Warning
|
|
152
|
+
This function performs a memory allocation."""
|
|
153
|
+
|
|
154
|
+
def bpc_summaries(self, id: int) -> typing.List:
|
|
155
|
+
"""Returns a vector of the summaries whose ID matches the desired `id`, in the order in which they will be used, i.e. in reverse loading order.
|
|
156
|
+
|
|
157
|
+
# Warning
|
|
158
|
+
This function performs a memory allocation."""
|
|
159
|
+
|
|
160
|
+
def bpc_swap(self, alias: str, new_bpc_path: str, new_alias: str) -> None:
|
|
161
|
+
"""Load a new DAF/BPC file in place of the one in the provided alias.
|
|
162
|
+
|
|
163
|
+
This reuses the existing memory buffer, growing it only if the new file
|
|
164
|
+
is larger than the previous capacity. This effectively adopts a
|
|
165
|
+
"high watermark" memory strategy, where the memory usage for this slot
|
|
166
|
+
is determined by the largest file ever loaded into it."""
|
|
167
|
+
|
|
168
|
+
def bpc_unload(self, alias: str) -> None:
|
|
169
|
+
"""Unloads (in-place) the BPC with the provided alias.
|
|
170
|
+
**WARNING:** This causes the order of the loaded files to be perturbed, which may be an issue if several SPKs with the same IDs are loaded."""
|
|
171
|
+
|
|
172
|
+
def describe(self, spk: bool=None, bpc: bool=None, planetary: bool=None, spacecraft: bool=None, eulerparams: bool=None, locations: bool=None, time_scale: TimeScale=None, round_time: bool=None) -> None:
|
|
173
|
+
"""Pretty prints the description of this Almanac, showing everything by default. Default time scale is TDB.
|
|
174
|
+
If any parameter is set to true, then nothing other than that will be printed."""
|
|
175
|
+
|
|
176
|
+
def frame_info(self, uid: Frame) -> Frame:
|
|
177
|
+
"""Returns the frame information (gravitational param, shape) as defined in this Almanac from an empty frame"""
|
|
178
|
+
|
|
179
|
+
@staticmethod
|
|
180
|
+
def from_ccsds_oem_file(path: str, naif_id: int) -> Almanac:
|
|
181
|
+
"""Initializes a new Almanac from a file path to CCSDS OEM file, after converting to to SPICE SPK/BSP"""
|
|
182
|
+
|
|
183
|
+
def line_of_sight_obstructed(self, observer: Orbit, observed: Orbit, obstructing_body: Frame, ab_corr: Aberration=None) -> bool:
|
|
184
|
+
"""Computes whether the line of sight between an observer and an observed Cartesian state is obstructed by the obstructing body.
|
|
185
|
+
Returns true if the obstructing body is in the way, false otherwise.
|
|
186
|
+
|
|
187
|
+
For example, if the Moon is in between a Lunar orbiter (observed) and a ground station (observer), then this function returns `true`
|
|
188
|
+
because the Moon (obstructing body) is indeed obstructing the line of sight.
|
|
189
|
+
|
|
190
|
+
```text
|
|
191
|
+
Observed
|
|
192
|
+
o -
|
|
193
|
+
+ -
|
|
194
|
+
+ -
|
|
195
|
+
+ *** -
|
|
196
|
+
* + * -
|
|
197
|
+
* + + * + + o
|
|
198
|
+
* * Observer
|
|
199
|
+
****
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Key Elements:
|
|
203
|
+
- `o` represents the positions of the observer and observed objects.
|
|
204
|
+
- The dashed line connecting the observer and observed is the line of sight.
|
|
205
|
+
|
|
206
|
+
Algorithm (source: Algorithm 35 of Vallado, 4th edition, page 308.):
|
|
207
|
+
- `r1` and `r2` are the transformed radii of the observed and observer objects, respectively.
|
|
208
|
+
- `r1sq` and `r2sq` are the squared magnitudes of these vectors.
|
|
209
|
+
- `r1dotr2` is the dot product of `r1` and `r2`.
|
|
210
|
+
- `tau` is a parameter that determines the intersection point along the line of sight.
|
|
211
|
+
- The condition `(1.0 - tau) * r1sq + r1dotr2 * tau <= ob_mean_eq_radius_km^2` checks if the line of sight is within the obstructing body's radius, indicating an obstruction."""
|
|
212
|
+
|
|
213
|
+
def list_kernels(self, spk: bool=None, bpc: bool=None, planetary: bool=None, spacecraft: bool=None, eulerparams: bool=None, locations: bool=None) -> list:
|
|
214
|
+
"""Returns the list of loaded kernels"""
|
|
215
|
+
|
|
216
|
+
def load(self, path: str) -> Almanac:
|
|
217
|
+
"""Generic function that tries to load the provided path guessing to the file type."""
|
|
218
|
+
|
|
219
|
+
def load_ccsds_oem_file(self, path: str, naif_id: int) -> Almanac:
|
|
220
|
+
"""Converts the provided CCSDS OEM to SPICE SPK/BSP and loads it in the Almanac."""
|
|
221
|
+
|
|
222
|
+
def load_from_metafile(self, metafile: MetaFile, autodelete: bool) -> Almanac:
|
|
223
|
+
"""Load from the provided MetaFile, downloading it if necessary.
|
|
224
|
+
Set autodelete to true to automatically delete lock files. Lock files are important in multi-threaded loads."""
|
|
225
|
+
|
|
226
|
+
def load_stk_e_file(self, path: str, naif_id: int) -> Almanac:
|
|
227
|
+
"""Converts the provided Ansys STK .e file to SPICE SPK/BSP and loads it in the Almanac."""
|
|
228
|
+
|
|
229
|
+
def location_from_id(self, id: int) -> Location:
|
|
230
|
+
"""Returns the Location from its ID, searching through all loaded location datasets in reverse order."""
|
|
231
|
+
|
|
232
|
+
def location_from_name(self, name: str) -> Location:
|
|
233
|
+
"""Returns the Location from its name, searching through all loaded location datasets in reverse order."""
|
|
234
|
+
|
|
235
|
+
def occultation(self, back_frame: Frame, front_frame: Frame, observer: Orbit, ab_corr: Aberration=None) -> Occultation:
|
|
236
|
+
"""Computes the occultation percentage of the `back_frame` object by the `front_frame` object as seen from the observer, when according for the provided aberration correction.
|
|
237
|
+
|
|
238
|
+
A zero percent occultation means that the back object is fully visible from the observer.
|
|
239
|
+
A 100% percent occultation means that the back object is fully hidden from the observer because of the front frame (i.e. _umbra_ if the back object is the Sun).
|
|
240
|
+
A value in between means that the back object is partially hidden from the observser (i.e. _penumbra_ if the back object is the Sun).
|
|
241
|
+
Refer to the [MathSpec](https://nyxspace.com/nyxspace/MathSpec/celestial/eclipse/) for modeling details."""
|
|
242
|
+
|
|
243
|
+
def report_event_arcs(self, state_spec: StateSpec, event: Event, start_epoch: Epoch, end_epoch: Epoch) -> list:
|
|
244
|
+
"""Report the rising and falling edges/states where the event arc happens.
|
|
245
|
+
|
|
246
|
+
For example, for a scalar expression less than X, this will report all of the times when the expression falls below X and rises above X.
|
|
247
|
+
This method uses the report_events function under the hood."""
|
|
248
|
+
|
|
249
|
+
def report_events(self, state_spec: StateSpec, event: Event, start_epoch: Epoch, end_epoch: Epoch) -> list:
|
|
250
|
+
"""Report all of the states when the provided event happens.
|
|
251
|
+
This method may only be used for equality events, minimum, and maximum events. For spanned events (e.g. Less Than/Greater Than), use report_event_arcs.
|
|
252
|
+
|
|
253
|
+
# Method
|
|
254
|
+
The report event function starts by lineraly scanning the whole state spec from the start to the end epoch.
|
|
255
|
+
This uses an adaptive step scan modeled on the Runge Kutta adaptive step integrator, but the objective is to ensure that the scalar expression
|
|
256
|
+
of the event is evaluated at steps where it is linearly changing (to within 10% of linearity). This allows finding coarse brackets where
|
|
257
|
+
the expression changes signs exactly once.
|
|
258
|
+
Then, each bracket it sent in parallel to a Brent's method root finder to find the exact time of the event.
|
|
259
|
+
|
|
260
|
+
# Limitation
|
|
261
|
+
While this approach is both very robust and very fast, if you think the finder may be missing some events, you should _reduce_ the epoch precision
|
|
262
|
+
of the event as a multiplicative factor of that precision is used to scan the trajectory linearly. Alternatively, you may export the scalars at
|
|
263
|
+
a fixed interval using the report_scalars or report_scalars_flat function and manually analyze the results of the scalar expression."""
|
|
264
|
+
|
|
265
|
+
def report_scalars(self, report: ReportScalars, time_series: TimeSeries) -> dict:
|
|
266
|
+
"""Report a set of scalar expressions, optionally with aliases, at a fixed time step defined in the TimeSeries."""
|
|
267
|
+
|
|
268
|
+
def report_visibility_arcs(self, state_spec: StateSpec, location_id: int, start_epoch: Epoch, end_epoch: Epoch, sample_rate: Duration, obstructing_body: Frame=None) -> list:
|
|
269
|
+
"""Report the list of visibility arcs for the desired location ID."""
|
|
270
|
+
|
|
271
|
+
def rotate(self, from_frame: Frame, to_frame: Frame, epoch: Epoch) -> DCM:
|
|
272
|
+
"""Returns the 6x6 DCM needed to rotation the `from_frame` to the `to_frame`.
|
|
273
|
+
|
|
274
|
+
# Warning
|
|
275
|
+
This function only performs the rotation and no translation whatsoever. Use the `transform_from_to` function instead to include rotations.
|
|
276
|
+
|
|
277
|
+
# Note
|
|
278
|
+
This function performs a recursion of no more than twice the MAX_TREE_DEPTH."""
|
|
279
|
+
|
|
280
|
+
def rotate_to(self, state: Orbit, observer_frame: Frame) -> Orbit:
|
|
281
|
+
"""Rotates the provided Cartesian state into the requested observer frame
|
|
282
|
+
|
|
283
|
+
**WARNING:** This function only performs the translation and no rotation _whatsoever_. Use the `transform_to` function instead to include rotations."""
|
|
284
|
+
|
|
285
|
+
def solar_eclipsing(self, eclipsing_frame: Frame, observer: Orbit, ab_corr: Aberration=None) -> Occultation:
|
|
286
|
+
"""Computes the solar eclipsing of the observer due to the eclipsing_frame.
|
|
287
|
+
|
|
288
|
+
This function calls `occultation` where the back object is the Sun in the J2000 frame, and the front object
|
|
289
|
+
is the provided eclipsing frame."""
|
|
290
|
+
|
|
291
|
+
def solar_eclipsing_many(self, eclipsing_frame: Frame, observers: typing.List[Orbit], ab_corr: Aberration=None) -> typing.List[Occultation]:
|
|
292
|
+
"""Computes the solar eclipsing of all the observers due to the eclipsing_frame, computed in parallel under the hood.
|
|
293
|
+
|
|
294
|
+
Note: if any computation fails, the error will be printed to the stderr.
|
|
295
|
+
Note: the output AER will be chronologically sorted, regardless of transmitter.
|
|
296
|
+
|
|
297
|
+
Refer to [solar_eclipsing] for details."""
|
|
298
|
+
|
|
299
|
+
def spk_domain(self, id: int) -> typing.Tuple:
|
|
300
|
+
"""Returns the applicable domain of the request id, i.e. start and end epoch that the provided id has loaded data."""
|
|
301
|
+
|
|
302
|
+
def spk_domains(self) -> typing.Dict:
|
|
303
|
+
"""Returns a map of each loaded SPK ID to its domain validity.
|
|
304
|
+
|
|
305
|
+
# Warning
|
|
306
|
+
This function performs a memory allocation."""
|
|
307
|
+
|
|
308
|
+
def spk_ezr(self, target: int, epoch: Epoch, frame: int, observer: int, ab_corr: Aberration=None) -> Orbit:
|
|
309
|
+
"""Alias fo SPICE's `spkezr` where the inputs must be the NAIF IDs of the objects and frames with the caveat that the aberration is moved to the last positional argument."""
|
|
310
|
+
|
|
311
|
+
def spk_summaries(self, id: int) -> typing.List:
|
|
312
|
+
"""Returns a vector of the summaries whose ID matches the desired `id`, in the order in which they will be used, i.e. in reverse loading order.
|
|
313
|
+
|
|
314
|
+
# Warning
|
|
315
|
+
This function performs a memory allocation."""
|
|
316
|
+
|
|
317
|
+
def spk_swap(self, alias: str, new_spk_path: str, new_alias: str) -> None:
|
|
318
|
+
"""Load a new DAF/SPK file in place of the one in the provided alias.
|
|
319
|
+
|
|
320
|
+
This reuses the existing memory buffer, growing it only if the new file
|
|
321
|
+
is larger than the previous capacity. This effectively adopts a
|
|
322
|
+
"high watermark" memory strategy, where the memory usage for this slot
|
|
323
|
+
is determined by the largest file ever loaded into it
|
|
324
|
+
."""
|
|
325
|
+
|
|
326
|
+
def spk_unload(self, alias: str) -> None:
|
|
327
|
+
"""Unloads (in-place) the SPK with the provided alias.
|
|
328
|
+
**WARNING:** This causes the order of the loaded files to be perturbed, which may be an issue if several SPKs with the same IDs are loaded."""
|
|
329
|
+
|
|
330
|
+
def state_of(self, object_id: int, observer: Frame, epoch: Epoch, ab_corr: Aberration=None) -> Orbit:
|
|
331
|
+
"""Returns the Cartesian state of the object as seen from the provided observer frame (essentially `spkezr`).
|
|
332
|
+
|
|
333
|
+
# Note
|
|
334
|
+
The units will be those of the underlying ephemeris data (typically km and km/s)"""
|
|
335
|
+
|
|
336
|
+
def sun_angle_deg(self, target_id: int, observer_id: int, epoch: Epoch, ab_corr: Aberration) -> float:
|
|
337
|
+
"""Returns the angular separation (between 0 and 180 degrees) between the observer and the Sun, and the observer and the target body ID.
|
|
338
|
+
This is formally known as the "solar elongation".
|
|
339
|
+
This computes the Sun Probe Earth angle (SPE) if the probe is in a loaded SPK, its ID is the "observer_id", and the target is set to its central body.
|
|
340
|
+
|
|
341
|
+
# Geometry
|
|
342
|
+
If the SPE is greater than 90 degrees, then the celestial object below the probe is in sunlight.
|
|
343
|
+
|
|
344
|
+
This angle determines the illumination phase of the target as seen by the observer:
|
|
345
|
+
* **~0° (Conjunction):** The Target is in the same direction as the Sun. The observer sees the unlit side ("New Moon").
|
|
346
|
+
* **~180° (Opposition):** The Target is in the opposite direction of the Sun. The observer sees the fully lit side ("Full Moon").
|
|
347
|
+
* **> 90°:** The observer is generally on the "day" side of the target.
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
## Sunrise at nadir
|
|
351
|
+
```text
|
|
352
|
+
Sun
|
|
353
|
+
| \\
|
|
354
|
+
| \\
|
|
355
|
+
| \\
|
|
356
|
+
Obs. -- Target
|
|
357
|
+
```
|
|
358
|
+
## Sun high at nadir
|
|
359
|
+
```text
|
|
360
|
+
Sun
|
|
361
|
+
\\
|
|
362
|
+
\\ __ θ > 90
|
|
363
|
+
\\ \\
|
|
364
|
+
Obs. ---------- Target
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
## Sunset at nadir
|
|
368
|
+
```text
|
|
369
|
+
Sun
|
|
370
|
+
/
|
|
371
|
+
/ __ θ < 90
|
|
372
|
+
/ /
|
|
373
|
+
Obs. -- Target
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
# Algorithm
|
|
377
|
+
1. Compute the position of the Sun as seen from the observer
|
|
378
|
+
2. Compute the position of the target as seen from the observer
|
|
379
|
+
3. Return the arccosine of the dot product of the norms of these vectors."""
|
|
380
|
+
|
|
381
|
+
def sun_angle_deg_from_frame(self, target: Frame, observer: Frame, epoch: Epoch, ab_corr: Aberration) -> float:
|
|
382
|
+
"""Convenience function that calls `sun_angle_deg` with the provided frames instead of the ephemeris ID."""
|
|
383
|
+
|
|
384
|
+
def to_metaalmanac(self) -> MetaAlmanac:
|
|
385
|
+
"""Saves the current configuration to a MetaAlmanac for future reloading from the local file system.
|
|
386
|
+
|
|
387
|
+
WARNING: If data was loaded from its raw bytes, or if a custom alias was used, then the MetaFile produced will not be usable.
|
|
388
|
+
The alias used for each data type is expected to be a path. Further, all paths are ASSUMED to be loaded from the same directory.
|
|
389
|
+
The Almanac does not resolve directories for you."""
|
|
390
|
+
|
|
391
|
+
def transform(self, target_frame: Frame, observer_frame: Frame, epoch: Epoch, ab_corr: Aberration=None) -> Orbit:
|
|
392
|
+
"""Returns the Cartesian state needed to transform the `from_frame` to the `to_frame`.
|
|
393
|
+
|
|
394
|
+
# SPICE Compatibility
|
|
395
|
+
This function is the SPICE equivalent of spkezr: `spkezr(TARGET_ID, EPOCH_TDB_S, ORIENTATION_ID, ABERRATION, OBSERVER_ID)`
|
|
396
|
+
In ANISE, the TARGET_ID and ORIENTATION are provided in the first argument (TARGET_FRAME), as that frame includes BOTH
|
|
397
|
+
the target ID and the orientation of that target. The EPOCH_TDB_S is the epoch in the TDB time system, which is computed
|
|
398
|
+
in ANISE using Hifitime. THe ABERRATION is computed by providing the optional Aberration flag. Finally, the OBSERVER
|
|
399
|
+
argument is replaced by OBSERVER_FRAME: if the OBSERVER_FRAME argument has the same orientation as the TARGET_FRAME, then this call
|
|
400
|
+
will return exactly the same data as the spkerz SPICE call.
|
|
401
|
+
|
|
402
|
+
# Note
|
|
403
|
+
The units will be those of the underlying ephemeris data (typically km and km/s)"""
|
|
404
|
+
|
|
405
|
+
def transform_many(self, target_frame: Frame, observer_frame: Frame, time_series: TimeSeries, ab_corr: Aberration=None) -> typing.List[Orbit]:
|
|
406
|
+
"""Returns a chronologically sorted list of the Cartesian states that transform the `from_frame` to the `to_frame` for each epoch of the time series, computed in parallel under the hood.
|
|
407
|
+
Note: if any transformation fails, the error will be printed to the stderr.
|
|
408
|
+
|
|
409
|
+
Refer to [transform] for details."""
|
|
410
|
+
|
|
411
|
+
def transform_many_to(self, states: typing.List[Orbit], observer_frame: Frame, ab_corr: Aberration=None) -> typing.List[Orbit]:
|
|
412
|
+
"""Returns a chronologically sorted list of the provided states as seen from the observer frame, given the aberration.
|
|
413
|
+
Note: if any transformation fails, the error will be printed to the stderr.
|
|
414
|
+
Note: the input ordering is lost: the output states will not be in the same order as the input states if these are not chronologically sorted!
|
|
415
|
+
|
|
416
|
+
Refer to [transform_to] for details."""
|
|
417
|
+
|
|
418
|
+
def transform_to(self, state: Orbit, observer_frame: Frame, ab_corr: Aberration=None) -> Orbit:
|
|
419
|
+
"""Returns the provided state as seen from the observer frame, given the aberration."""
|
|
420
|
+
|
|
421
|
+
def translate(self, target_frame: Frame, observer_frame: Frame, epoch: Epoch, ab_corr: Aberration=None) -> Orbit:
|
|
422
|
+
"""Returns the Cartesian state of the target frame as seen from the observer frame at the provided epoch, and optionally given the aberration correction.
|
|
423
|
+
|
|
424
|
+
# SPICE Compatibility
|
|
425
|
+
This function is the SPICE equivalent of spkezr: `spkezr(TARGET_ID, EPOCH_TDB_S, ORIENTATION_ID, ABERRATION, OBSERVER_ID)`
|
|
426
|
+
In ANISE, the TARGET_ID and ORIENTATION are provided in the first argument (TARGET_FRAME), as that frame includes BOTH
|
|
427
|
+
the target ID and the orientation of that target. The EPOCH_TDB_S is the epoch in the TDB time system, which is computed
|
|
428
|
+
in ANISE using Hifitime. THe ABERRATION is computed by providing the optional Aberration flag. Finally, the OBSERVER
|
|
429
|
+
argument is replaced by OBSERVER_FRAME: if the OBSERVER_FRAME argument has the same orientation as the TARGET_FRAME, then this call
|
|
430
|
+
will return exactly the same data as the spkerz SPICE call.
|
|
431
|
+
|
|
432
|
+
# Warning
|
|
433
|
+
This function only performs the translation and no rotation whatsoever. Use the `transform` function instead to include rotations.
|
|
434
|
+
|
|
435
|
+
# Note
|
|
436
|
+
This function performs a recursion of no more than twice the [MAX_TREE_DEPTH]."""
|
|
437
|
+
|
|
438
|
+
def translate_geometric(self, target_frame: Frame, observer_frame: Frame, epoch: Epoch) -> Orbit:
|
|
439
|
+
"""Returns the geometric position vector, velocity vector, and acceleration vector needed to translate the `from_frame` to the `to_frame`, where the distance is in km, the velocity in km/s, and the acceleration in km/s^2."""
|
|
440
|
+
|
|
441
|
+
def translate_to(self, state: Orbit, observer_frame: Frame, ab_corr: Aberration=None) -> Orbit:
|
|
442
|
+
"""Translates the provided Cartesian state into the requested observer frame
|
|
443
|
+
|
|
444
|
+
**WARNING:** This function only performs the translation and no rotation _whatsoever_. Use the `transform_to` function instead to include rotations."""
|
|
445
|
+
|
|
446
|
+
def translate_to_parent(self, source: Frame, epoch: Epoch) -> Orbit:
|
|
447
|
+
"""Performs the GEOMETRIC translation to the parent. Use translate_from_to for aberration."""
|
|
448
|
+
|
|
449
|
+
def __repr__(self) -> str:
|
|
450
|
+
"""Return repr(self)."""
|
|
451
|
+
|
|
452
|
+
def __str__(self) -> str:
|
|
453
|
+
"""Return str(self)."""
|
|
454
|
+
|
|
455
|
+
@typing.final
|
|
456
|
+
class LocationDataSet:
|
|
457
|
+
"""A wrapper around a location dataset kernel (PyO3 does not handle type aliases).
|
|
458
|
+
Use this class to load and unload kernels. Manipulate using its LocationDhallSet representation."""
|
|
459
|
+
|
|
460
|
+
def __init__(self) -> None:
|
|
461
|
+
"""A wrapper around a location dataset kernel (PyO3 does not handle type aliases).
|
|
462
|
+
Use this class to load and unload kernels. Manipulate using its LocationDhallSet representation."""
|
|
463
|
+
|
|
464
|
+
@staticmethod
|
|
465
|
+
def load(path: str) -> LocationDataSet:
|
|
466
|
+
"""Loads a Location Dataset kernel from the provided path"""
|
|
467
|
+
|
|
468
|
+
def save_as(self, path: str, overwrite: bool=False) -> None:
|
|
469
|
+
"""Save this dataset as a kernel, optionally specifying whether to overwrite the existing file."""
|
|
470
|
+
|
|
471
|
+
def to_dhallset(self) -> LocationDhallSet:
|
|
472
|
+
"""Converts this location dataset into a manipulable location Dhall set."""
|
|
473
|
+
|
|
474
|
+
@typing.final
|
|
475
|
+
class LocationDhallSet:
|
|
476
|
+
"""A Dhall-serializable Location DataSet that serves as an optional intermediate to the LocationDataSet kernels."""
|
|
477
|
+
data: list
|
|
478
|
+
|
|
479
|
+
def __init__(self, data: list) -> None:
|
|
480
|
+
"""A Dhall-serializable Location DataSet that serves as an optional intermediate to the LocationDataSet kernels."""
|
|
481
|
+
|
|
482
|
+
def dumps(self) -> str:
|
|
483
|
+
"""Returns the Dhall representation of this LocationDhallSet. Equivalent to to_dhall."""
|
|
484
|
+
|
|
485
|
+
@staticmethod
|
|
486
|
+
def from_dhall(repr: str) -> LocationDhallSet:
|
|
487
|
+
"""Loads this Location dataset from its Dhall representation as a string"""
|
|
488
|
+
|
|
489
|
+
@staticmethod
|
|
490
|
+
def loads(repr: str) -> LocationDhallSet:
|
|
491
|
+
"""Loads this Location dataset from its Dhall representation as a string. Equivalent to from_dhall."""
|
|
492
|
+
|
|
493
|
+
def to_dataset(self) -> LocationDataSet:
|
|
494
|
+
"""Converts this location Dhall set into a Python-compatible Location DataSet."""
|
|
495
|
+
|
|
496
|
+
def to_dhall(self) -> str:
|
|
497
|
+
"""Returns the Dhall representation of this Location"""
|
|
498
|
+
|
|
499
|
+
@typing.final
|
|
500
|
+
class LocationDhallSetEntry:
|
|
501
|
+
"""Entry of a Location Dhall set"""
|
|
502
|
+
alias: str
|
|
503
|
+
id: int
|
|
504
|
+
value: Location
|
|
505
|
+
|
|
506
|
+
def __init__(self, value: Location, id: int=None, alias: str=None) -> None:
|
|
507
|
+
"""Entry of a Location Dhall set"""
|
|
508
|
+
|
|
509
|
+
@typing.final
|
|
510
|
+
class MetaAlmanac:
|
|
511
|
+
"""A structure to set up an Almanac, with automatic downloading, local storage, checksum checking, and more.
|
|
512
|
+
|
|
513
|
+
# Behavior
|
|
514
|
+
If the URI is a local path, relative or absolute, nothing will be fetched from a remote. Relative paths are relative to the execution folder (i.e. the current working directory).
|
|
515
|
+
If the URI is a remote path, the MetaAlmanac will first check if the file exists locally. If it exists, it will check that the CRC32 checksum of this file matches that of the specs.
|
|
516
|
+
If it does not match, the file will be downloaded again. If no CRC32 is provided but the file exists, then the MetaAlmanac will fetch the remote file and overwrite the existing file.
|
|
517
|
+
The downloaded path will be stored in the "AppData" folder."""
|
|
518
|
+
files: typing.List
|
|
519
|
+
|
|
520
|
+
def __init__(self, maybe_path: str=None) -> MetaAlmanac:
|
|
521
|
+
"""A structure to set up an Almanac, with automatic downloading, local storage, checksum checking, and more.
|
|
522
|
+
|
|
523
|
+
# Behavior
|
|
524
|
+
If the URI is a local path, relative or absolute, nothing will be fetched from a remote. Relative paths are relative to the execution folder (i.e. the current working directory).
|
|
525
|
+
If the URI is a remote path, the MetaAlmanac will first check if the file exists locally. If it exists, it will check that the CRC32 checksum of this file matches that of the specs.
|
|
526
|
+
If it does not match, the file will be downloaded again. If no CRC32 is provided but the file exists, then the MetaAlmanac will fetch the remote file and overwrite the existing file.
|
|
527
|
+
The downloaded path will be stored in the "AppData" folder."""
|
|
528
|
+
|
|
529
|
+
def dumps(self) -> str:
|
|
530
|
+
"""Dumps the configured Meta Almanac into a Dhall string. Equivalent to to_dhall()."""
|
|
531
|
+
|
|
532
|
+
@staticmethod
|
|
533
|
+
def from_dhall(repr: str) -> MetaAlmanac:
|
|
534
|
+
"""Loads this Meta Almanac from its Dhall string representation"""
|
|
535
|
+
|
|
536
|
+
@staticmethod
|
|
537
|
+
def latest(autodelete: bool=None) -> Almanac:
|
|
538
|
+
"""Returns an Almanac loaded from the latest NAIF data via the `default` MetaAlmanac.
|
|
539
|
+
The MetaAlmanac will download the DE440s.bsp file, the PCK0008.PCA, the full Moon Principal Axis BPC (moon_pa_de440_200625) and the latest high precision Earth kernel from JPL.
|
|
540
|
+
|
|
541
|
+
# File list
|
|
542
|
+
- <http://public-data.nyxspace.com/anise/de440s.bsp>
|
|
543
|
+
- <http://public-data.nyxspace.com/anise/v0.5/pck08.pca>
|
|
544
|
+
- <http://public-data.nyxspace.com/anise/moon_pa_de440_200625.bpc>
|
|
545
|
+
- <https://naif.jpl.nasa.gov/pub/naif/generic_kernels/pck/earth_latest_high_prec.bpc>
|
|
546
|
+
|
|
547
|
+
# Reproducibility
|
|
548
|
+
|
|
549
|
+
Note that the `earth_latest_high_prec.bpc` file is regularly updated daily (or so). As such,
|
|
550
|
+
if queried at some future time, the Earth rotation parameters may have changed between two queries.
|
|
551
|
+
|
|
552
|
+
Set `autodelete` to true to delete lock file if a dead lock is detected after 10 seconds."""
|
|
553
|
+
|
|
554
|
+
@staticmethod
|
|
555
|
+
def loads(s: str) -> MetaAlmanac:
|
|
556
|
+
"""Loads the provided string as a Dhall configuration to build a MetaAlmanac"""
|
|
557
|
+
|
|
558
|
+
def process(self, autodelete: bool=None) -> Almanac:
|
|
559
|
+
"""Fetch all of the URIs and return a loaded Almanac.
|
|
560
|
+
When downloading the data, ANISE will create a temporarily lock file to prevent race conditions
|
|
561
|
+
where multiple processes download the data at the same time. Set `autodelete` to true to delete
|
|
562
|
+
this lock file if a dead lock is detected after 10 seconds. Set this flag to false if you have
|
|
563
|
+
more than ten processes which may attempt to download files in parallel."""
|
|
564
|
+
|
|
565
|
+
def to_dhall(self) -> str:
|
|
566
|
+
"""Serializes the configurated Meta Almanac into a Dhall string. Equivalent to dumps()."""
|
|
567
|
+
|
|
568
|
+
def __eq__(self, value: typing.Any) -> bool:
|
|
569
|
+
"""Return self==value."""
|
|
570
|
+
|
|
571
|
+
def __ge__(self, value: typing.Any) -> bool:
|
|
572
|
+
"""Return self>=value."""
|
|
573
|
+
|
|
574
|
+
def __gt__(self, value: typing.Any) -> bool:
|
|
575
|
+
"""Return self>value."""
|
|
576
|
+
|
|
577
|
+
def __le__(self, value: typing.Any) -> bool:
|
|
578
|
+
"""Return self<=value."""
|
|
579
|
+
|
|
580
|
+
def __lt__(self, value: typing.Any) -> bool:
|
|
581
|
+
"""Return self<value."""
|
|
582
|
+
|
|
583
|
+
def __ne__(self, value: typing.Any) -> bool:
|
|
584
|
+
"""Return self!=value."""
|
|
585
|
+
|
|
586
|
+
def __repr__(self) -> str:
|
|
587
|
+
"""Return repr(self)."""
|
|
588
|
+
|
|
589
|
+
def __str__(self) -> str:
|
|
590
|
+
"""Return str(self)."""
|
|
591
|
+
|
|
592
|
+
@typing.final
|
|
593
|
+
class MetaFile:
|
|
594
|
+
"""MetaFile allows downloading a remote file from a URL (http, https only), and interpolation of paths in environment variable using the Dhall syntax `env:MY_ENV_VAR`.
|
|
595
|
+
|
|
596
|
+
The data is stored in the user's local temp directory (i.e. `~/.local/share/nyx-space/anise/` on Linux and `AppData/Local/nyx-space/anise/` on Windows).
|
|
597
|
+
Prior to loading a remote resource, if the local resource exists, its CRC32 will be computed: if it matches the CRC32 of this instance of MetaFile,
|
|
598
|
+
then the file will not be downloaded a second time."""
|
|
599
|
+
crc32: int
|
|
600
|
+
uri: str
|
|
601
|
+
|
|
602
|
+
def __init__(self, uri: str, crc32: int=None) -> MetaFile:
|
|
603
|
+
"""MetaFile allows downloading a remote file from a URL (http, https only), and interpolation of paths in environment variable using the Dhall syntax `env:MY_ENV_VAR`.
|
|
604
|
+
|
|
605
|
+
The data is stored in the user's local temp directory (i.e. `~/.local/share/nyx-space/anise/` on Linux and `AppData/Local/nyx-space/anise/` on Windows).
|
|
606
|
+
Prior to loading a remote resource, if the local resource exists, its CRC32 will be computed: if it matches the CRC32 of this instance of MetaFile,
|
|
607
|
+
then the file will not be downloaded a second time."""
|
|
608
|
+
|
|
609
|
+
def process(self, autodelete: bool=None) -> None:
|
|
610
|
+
"""Processes this MetaFile by downloading it if it's a URL.
|
|
611
|
+
|
|
612
|
+
This function modified `self` and changes the URI to be the path to the downloaded file."""
|
|
613
|
+
|
|
614
|
+
def __eq__(self, value: typing.Any) -> bool:
|
|
615
|
+
"""Return self==value."""
|
|
616
|
+
|
|
617
|
+
def __ge__(self, value: typing.Any) -> bool:
|
|
618
|
+
"""Return self>=value."""
|
|
619
|
+
|
|
620
|
+
def __gt__(self, value: typing.Any) -> bool:
|
|
621
|
+
"""Return self>value."""
|
|
622
|
+
|
|
623
|
+
def __le__(self, value: typing.Any) -> bool:
|
|
624
|
+
"""Return self<=value."""
|
|
625
|
+
|
|
626
|
+
def __lt__(self, value: typing.Any) -> bool:
|
|
627
|
+
"""Return self<value."""
|
|
628
|
+
|
|
629
|
+
def __ne__(self, value: typing.Any) -> bool:
|
|
630
|
+
"""Return self!=value."""
|
|
631
|
+
|
|
632
|
+
def __repr__(self) -> str:
|
|
633
|
+
"""Return repr(self)."""
|
|
634
|
+
|
|
635
|
+
def __str__(self) -> str:
|
|
636
|
+
"""Return str(self)."""
|
|
637
|
+
|
|
638
|
+
def exec_gui():...
|
|
Binary file
|