imap-processing 0.18.0__py3-none-any.whl → 0.19.2__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.
Potentially problematic release.
This version of imap-processing might be problematic. Click here for more details.
- imap_processing/_version.py +2 -2
- imap_processing/ancillary/ancillary_dataset_combiner.py +161 -1
- imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +6 -0
- imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +221 -1057
- imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +307 -283
- imap_processing/cdf/config/imap_codice_l2_variable_attrs.yaml +1044 -203
- imap_processing/cdf/config/imap_constant_attrs.yaml +4 -2
- imap_processing/cdf/config/imap_enamaps_l2-common_variable_attrs.yaml +11 -0
- imap_processing/cdf/config/imap_glows_l1b_variable_attrs.yaml +15 -1
- imap_processing/cdf/config/imap_hi_global_cdf_attrs.yaml +5 -0
- imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +10 -4
- imap_processing/cdf/config/imap_idex_l2a_variable_attrs.yaml +33 -4
- imap_processing/cdf/config/imap_idex_l2b_variable_attrs.yaml +8 -91
- imap_processing/cdf/config/imap_idex_l2c_variable_attrs.yaml +106 -16
- imap_processing/cdf/config/imap_lo_global_cdf_attrs.yaml +5 -4
- imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +4 -15
- imap_processing/cdf/config/imap_lo_l1c_variable_attrs.yaml +189 -98
- imap_processing/cdf/config/imap_mag_global_cdf_attrs.yaml +85 -2
- imap_processing/cdf/config/imap_mag_l1c_variable_attrs.yaml +24 -1
- imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +20 -8
- imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +45 -35
- imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +110 -7
- imap_processing/cli.py +138 -93
- imap_processing/codice/codice_l0.py +2 -1
- imap_processing/codice/codice_l1a.py +167 -69
- imap_processing/codice/codice_l1b.py +42 -32
- imap_processing/codice/codice_l2.py +215 -9
- imap_processing/codice/constants.py +790 -603
- imap_processing/codice/data/lo_stepping_values.csv +1 -1
- imap_processing/decom.py +1 -4
- imap_processing/ena_maps/ena_maps.py +71 -43
- imap_processing/ena_maps/utils/corrections.py +291 -0
- imap_processing/ena_maps/utils/map_utils.py +20 -4
- imap_processing/ena_maps/utils/naming.py +8 -2
- imap_processing/glows/ancillary/imap_glows_exclusions-by-instr-team_20250923_v002.dat +10 -0
- imap_processing/glows/ancillary/imap_glows_map-of-excluded-regions_20250923_v002.dat +393 -0
- imap_processing/glows/ancillary/imap_glows_map-of-uv-sources_20250923_v002.dat +593 -0
- imap_processing/glows/ancillary/imap_glows_pipeline-settings_20250923_v002.json +54 -0
- imap_processing/glows/ancillary/imap_glows_suspected-transients_20250923_v002.dat +10 -0
- imap_processing/glows/l1b/glows_l1b.py +123 -18
- imap_processing/glows/l1b/glows_l1b_data.py +358 -47
- imap_processing/glows/l2/glows_l2.py +11 -0
- imap_processing/hi/hi_l1a.py +124 -3
- imap_processing/hi/hi_l1b.py +154 -71
- imap_processing/hi/hi_l1c.py +4 -109
- imap_processing/hi/hi_l2.py +104 -60
- imap_processing/hi/utils.py +262 -8
- imap_processing/hit/l0/constants.py +3 -0
- imap_processing/hit/l0/decom_hit.py +3 -6
- imap_processing/hit/l1a/hit_l1a.py +311 -21
- imap_processing/hit/l1b/hit_l1b.py +54 -126
- imap_processing/hit/l2/hit_l2.py +6 -6
- imap_processing/ialirt/calculate_ingest.py +219 -0
- imap_processing/ialirt/constants.py +12 -2
- imap_processing/ialirt/generate_coverage.py +15 -2
- imap_processing/ialirt/l0/ialirt_spice.py +6 -2
- imap_processing/ialirt/l0/parse_mag.py +293 -42
- imap_processing/ialirt/l0/process_hit.py +5 -3
- imap_processing/ialirt/l0/process_swapi.py +41 -25
- imap_processing/ialirt/process_ephemeris.py +70 -14
- imap_processing/ialirt/utils/create_xarray.py +1 -1
- imap_processing/idex/idex_l0.py +2 -2
- imap_processing/idex/idex_l1a.py +2 -3
- imap_processing/idex/idex_l1b.py +2 -3
- imap_processing/idex/idex_l2a.py +130 -4
- imap_processing/idex/idex_l2b.py +158 -143
- imap_processing/idex/idex_utils.py +1 -3
- imap_processing/lo/ancillary_data/imap_lo_hydrogen-geometric-factor_v001.csv +75 -0
- imap_processing/lo/ancillary_data/imap_lo_oxygen-geometric-factor_v001.csv +75 -0
- imap_processing/lo/l0/lo_science.py +25 -24
- imap_processing/lo/l1b/lo_l1b.py +93 -19
- imap_processing/lo/l1c/lo_l1c.py +273 -93
- imap_processing/lo/l2/lo_l2.py +949 -135
- imap_processing/lo/lo_ancillary.py +55 -0
- imap_processing/mag/l1a/mag_l1a.py +1 -0
- imap_processing/mag/l1a/mag_l1a_data.py +26 -0
- imap_processing/mag/l1b/mag_l1b.py +3 -2
- imap_processing/mag/l1c/interpolation_methods.py +14 -15
- imap_processing/mag/l1c/mag_l1c.py +23 -6
- imap_processing/mag/l1d/mag_l1d.py +57 -14
- imap_processing/mag/l1d/mag_l1d_data.py +202 -32
- imap_processing/mag/l2/mag_l2.py +2 -0
- imap_processing/mag/l2/mag_l2_data.py +14 -5
- imap_processing/quality_flags.py +23 -1
- imap_processing/spice/geometry.py +89 -39
- imap_processing/spice/pointing_frame.py +4 -8
- imap_processing/spice/repoint.py +78 -2
- imap_processing/spice/spin.py +28 -8
- imap_processing/spice/time.py +12 -22
- imap_processing/swapi/l1/swapi_l1.py +10 -4
- imap_processing/swapi/l2/swapi_l2.py +15 -17
- imap_processing/swe/l1b/swe_l1b.py +1 -2
- imap_processing/ultra/constants.py +30 -24
- imap_processing/ultra/l0/ultra_utils.py +9 -11
- imap_processing/ultra/l1a/ultra_l1a.py +1 -2
- imap_processing/ultra/l1b/badtimes.py +35 -11
- imap_processing/ultra/l1b/de.py +95 -31
- imap_processing/ultra/l1b/extendedspin.py +31 -16
- imap_processing/ultra/l1b/goodtimes.py +112 -0
- imap_processing/ultra/l1b/lookup_utils.py +281 -28
- imap_processing/ultra/l1b/quality_flag_filters.py +10 -1
- imap_processing/ultra/l1b/ultra_l1b.py +7 -7
- imap_processing/ultra/l1b/ultra_l1b_culling.py +169 -7
- imap_processing/ultra/l1b/ultra_l1b_extended.py +311 -69
- imap_processing/ultra/l1c/helio_pset.py +139 -37
- imap_processing/ultra/l1c/l1c_lookup_utils.py +289 -0
- imap_processing/ultra/l1c/spacecraft_pset.py +140 -29
- imap_processing/ultra/l1c/ultra_l1c.py +33 -24
- imap_processing/ultra/l1c/ultra_l1c_culling.py +92 -0
- imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +400 -292
- imap_processing/ultra/l2/ultra_l2.py +54 -11
- imap_processing/ultra/utils/ultra_l1_utils.py +37 -7
- imap_processing/utils.py +3 -4
- {imap_processing-0.18.0.dist-info → imap_processing-0.19.2.dist-info}/METADATA +2 -2
- {imap_processing-0.18.0.dist-info → imap_processing-0.19.2.dist-info}/RECORD +118 -109
- imap_processing/idex/idex_l2c.py +0 -84
- imap_processing/spice/kernels.py +0 -187
- imap_processing/ultra/l1b/cullingmask.py +0 -87
- imap_processing/ultra/l1c/histogram.py +0 -36
- {imap_processing-0.18.0.dist-info → imap_processing-0.19.2.dist-info}/LICENSE +0 -0
- {imap_processing-0.18.0.dist-info → imap_processing-0.19.2.dist-info}/WHEEL +0 -0
- {imap_processing-0.18.0.dist-info → imap_processing-0.19.2.dist-info}/entry_points.txt +0 -0
|
@@ -11,20 +11,17 @@ Paradigms for developing this module:
|
|
|
11
11
|
|
|
12
12
|
import typing
|
|
13
13
|
from enum import IntEnum
|
|
14
|
-
from typing import Union
|
|
15
14
|
|
|
16
15
|
import numpy as np
|
|
17
16
|
import numpy.typing as npt
|
|
18
17
|
import spiceypy
|
|
19
18
|
from numpy.typing import NDArray
|
|
20
19
|
|
|
21
|
-
from imap_processing.spice.kernels import ensure_spice
|
|
22
|
-
|
|
23
20
|
|
|
24
21
|
class SpiceBody(IntEnum):
|
|
25
22
|
"""Enum containing SPICE IDs for bodies that we use."""
|
|
26
23
|
|
|
27
|
-
# A subset of IMAP Specific bodies as defined in
|
|
24
|
+
# A subset of IMAP Specific bodies as defined in imap_001.tf
|
|
28
25
|
IMAP = -43
|
|
29
26
|
IMAP_SPACECRAFT = -43000
|
|
30
27
|
# IMAP Pointing Frame (Despun) as defined in imap_science_xxx.tf
|
|
@@ -36,7 +33,7 @@ class SpiceBody(IntEnum):
|
|
|
36
33
|
|
|
37
34
|
|
|
38
35
|
class SpiceFrame(IntEnum):
|
|
39
|
-
"""SPICE IDs for reference frames in
|
|
36
|
+
"""SPICE IDs for reference frames in imap_###.tf and imap_science_xxx.tf."""
|
|
40
37
|
|
|
41
38
|
# Standard SPICE Frames
|
|
42
39
|
J2000 = spiceypy.irfnum("J2000")
|
|
@@ -44,7 +41,7 @@ class SpiceFrame(IntEnum):
|
|
|
44
41
|
ITRF93 = 13000
|
|
45
42
|
# IMAP Pointing Frame (Despun) as defined in imap_science_xxx.tf
|
|
46
43
|
IMAP_DPS = -43901
|
|
47
|
-
# IMAP specific as defined in
|
|
44
|
+
# IMAP specific as defined in imap_###.tf
|
|
48
45
|
IMAP_SPACECRAFT = -43000
|
|
49
46
|
IMAP_LO_BASE = -43100
|
|
50
47
|
IMAP_LO_STAR_SENSOR = -43103
|
|
@@ -53,13 +50,17 @@ class SpiceFrame(IntEnum):
|
|
|
53
50
|
IMAP_HI_90 = -43160
|
|
54
51
|
IMAP_ULTRA_45 = -43200
|
|
55
52
|
IMAP_ULTRA_90 = -43210
|
|
56
|
-
IMAP_MAG
|
|
53
|
+
# TODO: remove IMAP_MAG frame once all usages have been removed
|
|
54
|
+
IMAP_MAG = -43999
|
|
55
|
+
IMAP_MAG_BOOM = -43250
|
|
56
|
+
IMAP_MAG_I = -43251
|
|
57
|
+
IMAP_MAG_O = -43252
|
|
57
58
|
IMAP_SWE = -43300
|
|
58
59
|
IMAP_SWAPI = -43350
|
|
59
60
|
IMAP_CODICE = -43400
|
|
60
61
|
IMAP_HIT = -43500
|
|
61
62
|
IMAP_IDEX = -43700
|
|
62
|
-
IMAP_GLOWS = -
|
|
63
|
+
IMAP_GLOWS = -43751
|
|
63
64
|
|
|
64
65
|
# IMAP Science Frames (new additions from imap_science_xxx.tf)
|
|
65
66
|
IMAP_OMD = -43900
|
|
@@ -90,7 +91,8 @@ BORESIGHT_LOOKUP = {
|
|
|
90
91
|
SpiceFrame.IMAP_HI_90: np.array([0, 1, 0]),
|
|
91
92
|
SpiceFrame.IMAP_ULTRA_45: np.array([0, 0, 1]),
|
|
92
93
|
SpiceFrame.IMAP_ULTRA_90: np.array([0, 0, 1]),
|
|
93
|
-
SpiceFrame.
|
|
94
|
+
SpiceFrame.IMAP_MAG_I: np.array([0, 0, 1]),
|
|
95
|
+
SpiceFrame.IMAP_MAG_O: np.array([0, 0, 1]),
|
|
94
96
|
SpiceFrame.IMAP_SWE: np.array([-1, 0, 0]),
|
|
95
97
|
SpiceFrame.IMAP_SWAPI: np.array([0, 1, 0]),
|
|
96
98
|
SpiceFrame.IMAP_CODICE: np.array([0, 0, 1]),
|
|
@@ -100,10 +102,8 @@ BORESIGHT_LOOKUP = {
|
|
|
100
102
|
}
|
|
101
103
|
|
|
102
104
|
|
|
103
|
-
@typing.no_type_check
|
|
104
|
-
@ensure_spice
|
|
105
105
|
def imap_state(
|
|
106
|
-
et:
|
|
106
|
+
et: np.ndarray | float,
|
|
107
107
|
ref_frame: SpiceFrame = SpiceFrame.ECLIPJ2000,
|
|
108
108
|
abcorr: str = "NONE",
|
|
109
109
|
observer: SpiceBody = SpiceBody.SUN,
|
|
@@ -137,14 +137,66 @@ def imap_state(
|
|
|
137
137
|
return np.asarray(state)
|
|
138
138
|
|
|
139
139
|
|
|
140
|
+
def get_instrument_mounting_az_el(instrument: SpiceFrame) -> np.ndarray:
|
|
141
|
+
"""
|
|
142
|
+
Calculate the azimuth and elevation angle of instrument mounting.
|
|
143
|
+
|
|
144
|
+
Azimuth and elevation to instrument mounting in the spacecraft frame.
|
|
145
|
+
Azimuth is measured in degrees from the spacecraft x-axis. Elevation is measured
|
|
146
|
+
in degrees from the spacecraft x-y plane.
|
|
147
|
+
|
|
148
|
+
Parameters
|
|
149
|
+
----------
|
|
150
|
+
instrument : SpiceFrame
|
|
151
|
+
Instrument to get the azimuth and elevation angles for.
|
|
152
|
+
|
|
153
|
+
Returns
|
|
154
|
+
-------
|
|
155
|
+
instrument_mounting_az_el : np.ndarray
|
|
156
|
+
2-element array containing azimuth and elevation of the instrument
|
|
157
|
+
mounting in the spacecraft frame. Azimuth is measured in degrees from
|
|
158
|
+
the spacecraft x-axis. Elevation is measured in degrees from the
|
|
159
|
+
spacecraft x-y plane.
|
|
160
|
+
"""
|
|
161
|
+
# Each instrument can have a unique basis vector in the instrument
|
|
162
|
+
# frame that is used to compute the s/c to instrument mounting.
|
|
163
|
+
# Most of these vectors are the same as the instrument boresight vector.
|
|
164
|
+
mounting_normal_vector = {
|
|
165
|
+
SpiceFrame.IMAP_LO_BASE: np.array([0, -1, 0]),
|
|
166
|
+
SpiceFrame.IMAP_HI_45: np.array([0, 1, 0]),
|
|
167
|
+
SpiceFrame.IMAP_HI_90: np.array([0, 1, 0]),
|
|
168
|
+
SpiceFrame.IMAP_ULTRA_45: np.array([0, 0, 1]),
|
|
169
|
+
SpiceFrame.IMAP_ULTRA_90: np.array([0, 0, 1]),
|
|
170
|
+
SpiceFrame.IMAP_MAG_I: np.array([-1, 0, 0]),
|
|
171
|
+
SpiceFrame.IMAP_MAG_O: np.array([-1, 0, 0]),
|
|
172
|
+
SpiceFrame.IMAP_SWE: np.array([-1, 0, 0]),
|
|
173
|
+
SpiceFrame.IMAP_SWAPI: np.array([0, 0, -1]),
|
|
174
|
+
SpiceFrame.IMAP_CODICE: np.array([-1, 0, 0]),
|
|
175
|
+
SpiceFrame.IMAP_HIT: np.array([0, 1, 0]),
|
|
176
|
+
SpiceFrame.IMAP_IDEX: np.array([0, 1, 0]),
|
|
177
|
+
SpiceFrame.IMAP_GLOWS: np.array([0, 0, -1]),
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
# Get the instrument mounting normal vector expressed in the spacecraft frame
|
|
181
|
+
# The reference frames are fixed, so the et argument can be fixed at 0
|
|
182
|
+
instrument_normal_sc = frame_transform(
|
|
183
|
+
0, mounting_normal_vector[instrument], instrument, SpiceFrame.IMAP_SPACECRAFT
|
|
184
|
+
)
|
|
185
|
+
# Convert the cartesian coordinate to azimuth/elevation angles in degrees
|
|
186
|
+
return np.rad2deg(
|
|
187
|
+
spiceypy.recazl(instrument_normal_sc, azccw=True, elplsz=True)[1:]
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
|
|
140
191
|
def get_spacecraft_to_instrument_spin_phase_offset(instrument: SpiceFrame) -> float:
|
|
141
192
|
"""
|
|
142
193
|
Get the spin phase offset from the spacecraft to the instrument.
|
|
143
194
|
|
|
144
195
|
For now, the offset is a fixed lookup based on `Table 1: Nominal Instrument
|
|
145
|
-
to S/C CS Transformations` in document `7516-0011_drw.pdf`.
|
|
146
|
-
|
|
147
|
-
|
|
196
|
+
to S/C CS Transformations` in document `7516-0011_drw.pdf`. That Table
|
|
197
|
+
defines the angle from the spacecraft y-axis. We add 90 and take the modulous
|
|
198
|
+
with 360 in order to get the angle from the spacecraft x-axis. These fixed
|
|
199
|
+
values will need to be updated based on calibration data.
|
|
148
200
|
|
|
149
201
|
Parameters
|
|
150
202
|
----------
|
|
@@ -156,26 +208,26 @@ def get_spacecraft_to_instrument_spin_phase_offset(instrument: SpiceFrame) -> fl
|
|
|
156
208
|
spacecraft_to_instrument_spin_phase_offset : float
|
|
157
209
|
The spin phase offset from the spacecraft to the instrument.
|
|
158
210
|
"""
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
SpiceFrame.
|
|
162
|
-
SpiceFrame.
|
|
163
|
-
SpiceFrame.
|
|
164
|
-
SpiceFrame.
|
|
165
|
-
SpiceFrame.
|
|
166
|
-
SpiceFrame.
|
|
167
|
-
SpiceFrame.
|
|
168
|
-
SpiceFrame.
|
|
169
|
-
SpiceFrame.
|
|
170
|
-
SpiceFrame.
|
|
171
|
-
SpiceFrame.
|
|
172
|
-
SpiceFrame.
|
|
211
|
+
phase_offset_lookup = {
|
|
212
|
+
SpiceFrame.IMAP_LO_BASE: 60 / 360, # (330 + 90) % 360 = 60
|
|
213
|
+
SpiceFrame.IMAP_HI_45: 345 / 360, # 255 + 90 = 345
|
|
214
|
+
SpiceFrame.IMAP_HI_90: 15 / 360, # (285 + 90) % 360 = 15
|
|
215
|
+
SpiceFrame.IMAP_ULTRA_45: 123 / 360, # 33 + 90 = 123
|
|
216
|
+
SpiceFrame.IMAP_ULTRA_90: 300 / 360, # 210 + 90 = 300
|
|
217
|
+
SpiceFrame.IMAP_SWAPI: 258 / 360, # 168 + 90 = 258
|
|
218
|
+
SpiceFrame.IMAP_IDEX: 180 / 360, # 90 + 90 = 180
|
|
219
|
+
SpiceFrame.IMAP_CODICE: 226 / 360, # 136 + 90 = 226
|
|
220
|
+
SpiceFrame.IMAP_HIT: 120 / 360, # 30 + 90 = 120
|
|
221
|
+
SpiceFrame.IMAP_SWE: 243 / 360, # 153 + 90 = 243
|
|
222
|
+
SpiceFrame.IMAP_GLOWS: 217 / 360, # 127 + 90 = 217
|
|
223
|
+
SpiceFrame.IMAP_MAG_I: 90 / 360, # 0 + 90 = 90
|
|
224
|
+
SpiceFrame.IMAP_MAG_O: 90 / 360, # 0 + 90 = 90
|
|
173
225
|
}
|
|
174
|
-
return
|
|
226
|
+
return phase_offset_lookup[instrument]
|
|
175
227
|
|
|
176
228
|
|
|
177
229
|
def frame_transform(
|
|
178
|
-
et:
|
|
230
|
+
et: float | npt.NDArray,
|
|
179
231
|
position: npt.NDArray,
|
|
180
232
|
from_frame: SpiceFrame,
|
|
181
233
|
to_frame: SpiceFrame,
|
|
@@ -249,7 +301,7 @@ def frame_transform(
|
|
|
249
301
|
|
|
250
302
|
|
|
251
303
|
def frame_transform_az_el(
|
|
252
|
-
et:
|
|
304
|
+
et: float | npt.NDArray,
|
|
253
305
|
az_el: npt.NDArray,
|
|
254
306
|
from_frame: SpiceFrame,
|
|
255
307
|
to_frame: SpiceFrame,
|
|
@@ -298,10 +350,8 @@ def frame_transform_az_el(
|
|
|
298
350
|
return to_frame_az_el[..., 1:3]
|
|
299
351
|
|
|
300
352
|
|
|
301
|
-
@typing.no_type_check
|
|
302
|
-
@ensure_spice
|
|
303
353
|
def get_rotation_matrix(
|
|
304
|
-
et:
|
|
354
|
+
et: float | npt.NDArray,
|
|
305
355
|
from_frame: SpiceFrame,
|
|
306
356
|
to_frame: SpiceFrame,
|
|
307
357
|
) -> npt.NDArray:
|
|
@@ -339,7 +389,7 @@ def get_rotation_matrix(
|
|
|
339
389
|
|
|
340
390
|
|
|
341
391
|
def instrument_pointing(
|
|
342
|
-
et:
|
|
392
|
+
et: float | npt.NDArray,
|
|
343
393
|
instrument: SpiceFrame,
|
|
344
394
|
to_frame: SpiceFrame,
|
|
345
395
|
cartesian: bool = False,
|
|
@@ -377,7 +427,7 @@ def instrument_pointing(
|
|
|
377
427
|
|
|
378
428
|
|
|
379
429
|
def basis_vectors(
|
|
380
|
-
et:
|
|
430
|
+
et: float | npt.NDArray,
|
|
381
431
|
from_frame: SpiceFrame,
|
|
382
432
|
to_frame: SpiceFrame,
|
|
383
433
|
) -> npt.NDArray:
|
|
@@ -550,9 +600,9 @@ def cartesian_to_latitudinal(coords: NDArray, degrees: bool = True) -> NDArray:
|
|
|
550
600
|
|
|
551
601
|
|
|
552
602
|
def solar_longitude(
|
|
553
|
-
et:
|
|
603
|
+
et: np.ndarray | float,
|
|
554
604
|
degrees: bool = True,
|
|
555
|
-
) ->
|
|
605
|
+
) -> float | npt.NDArray:
|
|
556
606
|
"""
|
|
557
607
|
Compute the solar longitude of the Imap Spacecraft.
|
|
558
608
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""Functions for retrieving repointing table data."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
import typing
|
|
5
4
|
from collections.abc import Generator
|
|
6
5
|
from contextlib import contextmanager
|
|
7
6
|
from datetime import datetime, timezone
|
|
@@ -13,7 +12,6 @@ from imap_data_access import SPICEFilePath
|
|
|
13
12
|
from numpy.typing import NDArray
|
|
14
13
|
|
|
15
14
|
from imap_processing.spice.geometry import SpiceFrame
|
|
16
|
-
from imap_processing.spice.kernels import ensure_spice
|
|
17
15
|
from imap_processing.spice.repoint import get_repoint_data
|
|
18
16
|
from imap_processing.spice.time import (
|
|
19
17
|
TICK_DURATION,
|
|
@@ -164,8 +162,6 @@ def write_pointing_frame_ck(
|
|
|
164
162
|
)
|
|
165
163
|
|
|
166
164
|
|
|
167
|
-
@typing.no_type_check
|
|
168
|
-
@ensure_spice
|
|
169
165
|
def calculate_pointing_attitude_segments(
|
|
170
166
|
ck_path: Path,
|
|
171
167
|
) -> NDArray:
|
|
@@ -199,7 +195,7 @@ def calculate_pointing_attitude_segments(
|
|
|
199
195
|
|
|
200
196
|
- Latest NAIF leapseconds kernel (naif0012.tls)
|
|
201
197
|
- The latest IMAP sclk (imap_sclk_NNNN.tsc)
|
|
202
|
-
- The latest IMAP frame kernel (
|
|
198
|
+
- The latest IMAP frame kernel (imap_###.tf)
|
|
203
199
|
- IMAP DPS frame kernel (imap_science_100.tf)
|
|
204
200
|
- IMAP historical attitude kernel from which the pointing frame kernel will
|
|
205
201
|
be generated.
|
|
@@ -214,7 +210,9 @@ def calculate_pointing_attitude_segments(
|
|
|
214
210
|
count = spiceypy.ktotal("ck")
|
|
215
211
|
loaded_ck_kernel, _, _, _ = spiceypy.kdata(count - 1, "ck")
|
|
216
212
|
if str(ck_path) != loaded_ck_kernel:
|
|
217
|
-
raise ValueError(
|
|
213
|
+
raise ValueError(
|
|
214
|
+
f"Error: Expected CK kernel {ck_path} but loaded {loaded_ck_kernel}"
|
|
215
|
+
)
|
|
218
216
|
|
|
219
217
|
id_imap_spacecraft = spiceypy.gipool("FRAME_IMAP_SPACECRAFT", 0, 1)
|
|
220
218
|
|
|
@@ -292,8 +290,6 @@ def calculate_pointing_attitude_segments(
|
|
|
292
290
|
return pointing_segments
|
|
293
291
|
|
|
294
292
|
|
|
295
|
-
@typing.no_type_check
|
|
296
|
-
@ensure_spice
|
|
297
293
|
def _average_quaternions(et_times: np.ndarray) -> NDArray:
|
|
298
294
|
"""
|
|
299
295
|
Average the quaternions.
|
imap_processing/spice/repoint.py
CHANGED
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
import functools
|
|
4
4
|
import logging
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import Union
|
|
7
6
|
|
|
8
7
|
import numpy as np
|
|
9
8
|
import pandas as pd
|
|
10
9
|
from numpy import typing as npt
|
|
11
10
|
|
|
12
11
|
from imap_processing.spice import config
|
|
12
|
+
from imap_processing.spice.geometry import imap_state
|
|
13
|
+
from imap_processing.spice.time import met_to_sclkticks, sct_to_et
|
|
13
14
|
|
|
14
15
|
logger = logging.getLogger(__name__)
|
|
15
16
|
|
|
@@ -113,7 +114,7 @@ def _load_repoint_data_with_cache(csv_path: Path) -> pd.DataFrame:
|
|
|
113
114
|
|
|
114
115
|
|
|
115
116
|
def interpolate_repoint_data(
|
|
116
|
-
query_met_times:
|
|
117
|
+
query_met_times: float | npt.NDArray,
|
|
117
118
|
) -> pd.DataFrame:
|
|
118
119
|
"""
|
|
119
120
|
Interpolate repointing data to the queried MET times.
|
|
@@ -194,3 +195,78 @@ def interpolate_repoint_data(
|
|
|
194
195
|
out_df["repoint_in_progress"] = query_met_times < out_df["repoint_end_met"].values
|
|
195
196
|
|
|
196
197
|
return out_df
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def get_pointing_times(met_time: float) -> tuple[float, float]:
|
|
201
|
+
"""
|
|
202
|
+
Get the start and end MET times for the pointing that contains the query MET time.
|
|
203
|
+
|
|
204
|
+
Parameters
|
|
205
|
+
----------
|
|
206
|
+
met_time : float
|
|
207
|
+
The MET time in a pointing.
|
|
208
|
+
|
|
209
|
+
Returns
|
|
210
|
+
-------
|
|
211
|
+
pointing_start_time : float
|
|
212
|
+
The MET time of the repoint maneuver that ends before the query MET time.
|
|
213
|
+
pointing_end_time : float
|
|
214
|
+
The MET time of the repoint maneuver that starts after the query MET time.
|
|
215
|
+
"""
|
|
216
|
+
# Find the pointing start time by finding the repoint end time
|
|
217
|
+
repoint_df = interpolate_repoint_data(met_time)
|
|
218
|
+
pointing_start_met = repoint_df["repoint_end_met"].item()
|
|
219
|
+
# Find the pointing end time by finding the next repoint start time
|
|
220
|
+
repoint_df = get_repoint_data()
|
|
221
|
+
pointing_idx = repoint_df.index[
|
|
222
|
+
repoint_df["repoint_end_met"] == pointing_start_met
|
|
223
|
+
][0]
|
|
224
|
+
pointing_end_met = repoint_df["repoint_start_met"].iloc[pointing_idx + 1].item()
|
|
225
|
+
return pointing_start_met, pointing_end_met
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def get_pointing_mid_time(met_time: float) -> float:
|
|
229
|
+
"""
|
|
230
|
+
Get mid-point of the pointing for the given MET time.
|
|
231
|
+
|
|
232
|
+
Get the mid-point time between the end of one repoint and
|
|
233
|
+
start of the next. Input could be a MET time.
|
|
234
|
+
|
|
235
|
+
Parameters
|
|
236
|
+
----------
|
|
237
|
+
met_time : float
|
|
238
|
+
The MET time in a repoint.
|
|
239
|
+
|
|
240
|
+
Returns
|
|
241
|
+
-------
|
|
242
|
+
repoint_mid_time : float
|
|
243
|
+
The mid MET time of the repoint maneuver.
|
|
244
|
+
"""
|
|
245
|
+
pointing_start_met, pointing_end_met = get_pointing_times(met_time)
|
|
246
|
+
return (pointing_start_met + pointing_end_met) / 2
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def get_mid_point_state(met_time: float) -> npt.NDArray:
|
|
250
|
+
"""
|
|
251
|
+
Get IMAP state for the mid-point.
|
|
252
|
+
|
|
253
|
+
Get IMAP state for the mid-point of the pointing in
|
|
254
|
+
reference frame, ECLIPJ2000 and observer, SUN.
|
|
255
|
+
|
|
256
|
+
Parameters
|
|
257
|
+
----------
|
|
258
|
+
met_time : float
|
|
259
|
+
The MET time in a pointing.
|
|
260
|
+
|
|
261
|
+
Returns
|
|
262
|
+
-------
|
|
263
|
+
mid_point_state : numpy.ndarray
|
|
264
|
+
The mid state of the pointing maneuver.
|
|
265
|
+
"""
|
|
266
|
+
# Get mid point time in ET
|
|
267
|
+
mid_point_time = get_pointing_mid_time(met_time)
|
|
268
|
+
mid_point_time_et = sct_to_et(met_to_sclkticks(mid_point_time))
|
|
269
|
+
|
|
270
|
+
# Convert mid point time to state
|
|
271
|
+
pointing_state = imap_state(mid_point_time_et)
|
|
272
|
+
return pointing_state
|
imap_processing/spice/spin.py
CHANGED
|
@@ -4,7 +4,6 @@ import functools
|
|
|
4
4
|
import logging
|
|
5
5
|
from functools import reduce
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import Union
|
|
8
7
|
|
|
9
8
|
import numpy as np
|
|
10
9
|
import pandas as pd
|
|
@@ -129,7 +128,7 @@ def _load_spin_data_with_cache(csv_paths: tuple[Path]) -> pd.DataFrame:
|
|
|
129
128
|
return combined_df
|
|
130
129
|
|
|
131
130
|
|
|
132
|
-
def interpolate_spin_data(query_met_times:
|
|
131
|
+
def interpolate_spin_data(query_met_times: float | npt.NDArray) -> pd.DataFrame:
|
|
133
132
|
"""
|
|
134
133
|
Interpolate spin table data to the queried MET times.
|
|
135
134
|
|
|
@@ -213,10 +212,31 @@ def interpolate_spin_data(query_met_times: Union[float, npt.NDArray]) -> pd.Data
|
|
|
213
212
|
return out_df
|
|
214
213
|
|
|
215
214
|
|
|
215
|
+
def get_spin_number(met_time: float) -> int:
|
|
216
|
+
"""
|
|
217
|
+
Get the spin number for the input query time.
|
|
218
|
+
|
|
219
|
+
The spin number is the index of the spin table row that contains the
|
|
220
|
+
spin data for the input query time.
|
|
221
|
+
|
|
222
|
+
Parameters
|
|
223
|
+
----------
|
|
224
|
+
met_time : float
|
|
225
|
+
Query time in Mission Elapsed Time (MET).
|
|
226
|
+
|
|
227
|
+
Returns
|
|
228
|
+
-------
|
|
229
|
+
spin_number : int
|
|
230
|
+
Spin number for the input query time.
|
|
231
|
+
"""
|
|
232
|
+
spin_df = interpolate_spin_data(met_time)
|
|
233
|
+
return spin_df["spin_number"].item()
|
|
234
|
+
|
|
235
|
+
|
|
216
236
|
def get_spin_angle(
|
|
217
|
-
spin_phases:
|
|
237
|
+
spin_phases: float | npt.NDArray,
|
|
218
238
|
degrees: bool = False,
|
|
219
|
-
) ->
|
|
239
|
+
) -> float | npt.NDArray:
|
|
220
240
|
"""
|
|
221
241
|
Convert spin_phases to radians or degrees.
|
|
222
242
|
|
|
@@ -249,8 +269,8 @@ def get_spin_angle(
|
|
|
249
269
|
|
|
250
270
|
|
|
251
271
|
def get_spacecraft_spin_phase(
|
|
252
|
-
query_met_times:
|
|
253
|
-
) ->
|
|
272
|
+
query_met_times: float | npt.NDArray,
|
|
273
|
+
) -> float | npt.NDArray:
|
|
254
274
|
"""
|
|
255
275
|
Get the spacecraft spin phase for the input query times.
|
|
256
276
|
|
|
@@ -274,8 +294,8 @@ def get_spacecraft_spin_phase(
|
|
|
274
294
|
|
|
275
295
|
|
|
276
296
|
def get_instrument_spin_phase(
|
|
277
|
-
query_met_times:
|
|
278
|
-
) ->
|
|
297
|
+
query_met_times: float | npt.NDArray, instrument: SpiceFrame
|
|
298
|
+
) -> float | npt.NDArray:
|
|
279
299
|
"""
|
|
280
300
|
Get the instrument spin phase for the input query times.
|
|
281
301
|
|
imap_processing/spice/time.py
CHANGED
|
@@ -3,14 +3,12 @@
|
|
|
3
3
|
import typing
|
|
4
4
|
from collections.abc import Collection, Iterable
|
|
5
5
|
from datetime import datetime
|
|
6
|
-
from typing import Union
|
|
7
6
|
|
|
8
7
|
import numpy as np
|
|
9
8
|
import numpy.typing as npt
|
|
10
9
|
import spiceypy
|
|
11
10
|
|
|
12
11
|
from imap_processing.spice import IMAP_SC_ID
|
|
13
|
-
from imap_processing.spice.kernels import ensure_spice
|
|
14
12
|
|
|
15
13
|
TICK_DURATION = 2e-5 # 20 microseconds as defined in imap_sclk_0000.tsc
|
|
16
14
|
|
|
@@ -103,7 +101,6 @@ def met_to_ttj2000ns(
|
|
|
103
101
|
|
|
104
102
|
|
|
105
103
|
@typing.no_type_check
|
|
106
|
-
@ensure_spice
|
|
107
104
|
def ttj2000ns_to_et(tt_ns: npt.ArrayLike) -> npt.NDArray[float]:
|
|
108
105
|
"""
|
|
109
106
|
Convert TT J2000 epoch nanoseconds to TDB J2000 epoch seconds.
|
|
@@ -131,7 +128,6 @@ def ttj2000ns_to_et(tt_ns: npt.ArrayLike) -> npt.NDArray[float]:
|
|
|
131
128
|
|
|
132
129
|
|
|
133
130
|
@typing.no_type_check
|
|
134
|
-
@ensure_spice
|
|
135
131
|
def et_to_ttj2000ns(et: npt.ArrayLike) -> npt.NDArray[float]:
|
|
136
132
|
"""
|
|
137
133
|
Convert TDB J2000 epoch seconds to TT J2000 epoch nanoseconds.
|
|
@@ -157,7 +153,6 @@ def et_to_ttj2000ns(et: npt.ArrayLike) -> npt.NDArray[float]:
|
|
|
157
153
|
|
|
158
154
|
|
|
159
155
|
@typing.no_type_check
|
|
160
|
-
@ensure_spice(time_kernels_only=True)
|
|
161
156
|
def met_to_utc(met: npt.ArrayLike, precision: int = 9) -> npt.NDArray[str]:
|
|
162
157
|
"""
|
|
163
158
|
Convert mission elapsed time (MET) to UTC.
|
|
@@ -184,7 +179,7 @@ def met_to_utc(met: npt.ArrayLike, precision: int = 9) -> npt.NDArray[str]:
|
|
|
184
179
|
|
|
185
180
|
def met_to_datetime64(
|
|
186
181
|
met: npt.ArrayLike,
|
|
187
|
-
) ->
|
|
182
|
+
) -> np.datetime64 | npt.NDArray[np.datetime64]:
|
|
188
183
|
"""
|
|
189
184
|
Convert mission elapsed time (MET) to datetime.datetime.
|
|
190
185
|
|
|
@@ -203,7 +198,7 @@ def met_to_datetime64(
|
|
|
203
198
|
|
|
204
199
|
def et_to_datetime64(
|
|
205
200
|
et: npt.ArrayLike,
|
|
206
|
-
) ->
|
|
201
|
+
) -> np.datetime64 | npt.NDArray[np.datetime64]:
|
|
207
202
|
"""
|
|
208
203
|
Convert ET to numpy datetime64.
|
|
209
204
|
|
|
@@ -221,10 +216,9 @@ def et_to_datetime64(
|
|
|
221
216
|
|
|
222
217
|
|
|
223
218
|
@typing.no_type_check
|
|
224
|
-
@ensure_spice
|
|
225
219
|
def et_to_met(
|
|
226
|
-
et:
|
|
227
|
-
) ->
|
|
220
|
+
et: float | Collection[float],
|
|
221
|
+
) -> float | np.ndarray:
|
|
228
222
|
"""
|
|
229
223
|
Convert ephemeris time to mission elapsed time (MET).
|
|
230
224
|
|
|
@@ -272,10 +266,9 @@ def ttj2000ns_to_met(
|
|
|
272
266
|
|
|
273
267
|
|
|
274
268
|
@typing.no_type_check
|
|
275
|
-
@ensure_spice
|
|
276
269
|
def sct_to_et(
|
|
277
|
-
sclk_ticks:
|
|
278
|
-
) ->
|
|
270
|
+
sclk_ticks: float | Collection[float],
|
|
271
|
+
) -> float | np.ndarray:
|
|
279
272
|
"""
|
|
280
273
|
Convert encoded spacecraft clock "ticks" to ephemeris time.
|
|
281
274
|
|
|
@@ -298,10 +291,9 @@ def sct_to_et(
|
|
|
298
291
|
|
|
299
292
|
|
|
300
293
|
@typing.no_type_check
|
|
301
|
-
@ensure_spice
|
|
302
294
|
def sct_to_ttj2000s(
|
|
303
|
-
sclk_ticks:
|
|
304
|
-
) ->
|
|
295
|
+
sclk_ticks: float | Iterable[float],
|
|
296
|
+
) -> float | np.ndarray:
|
|
305
297
|
"""
|
|
306
298
|
Convert encoded spacecraft clock "ticks" to terrestrial time (TT).
|
|
307
299
|
|
|
@@ -330,10 +322,9 @@ def sct_to_ttj2000s(
|
|
|
330
322
|
|
|
331
323
|
|
|
332
324
|
@typing.no_type_check
|
|
333
|
-
@ensure_spice
|
|
334
325
|
def str_to_et(
|
|
335
|
-
time_str:
|
|
336
|
-
) ->
|
|
326
|
+
time_str: str | Iterable[str],
|
|
327
|
+
) -> float | np.ndarray:
|
|
337
328
|
"""
|
|
338
329
|
Convert string to ephemeris time.
|
|
339
330
|
|
|
@@ -356,13 +347,12 @@ def str_to_et(
|
|
|
356
347
|
|
|
357
348
|
|
|
358
349
|
@typing.no_type_check
|
|
359
|
-
@ensure_spice
|
|
360
350
|
def et_to_utc(
|
|
361
|
-
et:
|
|
351
|
+
et: float | Iterable[float],
|
|
362
352
|
format_str: str = "ISOC",
|
|
363
353
|
precision: int = 3,
|
|
364
354
|
utclen: int = 24,
|
|
365
|
-
) ->
|
|
355
|
+
) -> str | np.ndarray:
|
|
366
356
|
"""
|
|
367
357
|
Convert ephemeris time to UTC.
|
|
368
358
|
|
|
@@ -138,7 +138,7 @@ def decompress_count(
|
|
|
138
138
|
|
|
139
139
|
# SWAPI suggested using big value to indicate overflow.
|
|
140
140
|
new_count[compressed_indices & (count_data == 0xFFFF)] = np.iinfo(np.int32).max
|
|
141
|
-
return new_count
|
|
141
|
+
return (new_count).astype(np.float32)
|
|
142
142
|
|
|
143
143
|
|
|
144
144
|
def find_sweep_starts(packets: xr.Dataset) -> npt.NDArray:
|
|
@@ -474,6 +474,12 @@ def process_swapi_science(
|
|
|
474
474
|
swp_scem_counts = decompress_count(raw_scem_count, scem_compression_flags)
|
|
475
475
|
swp_coin_counts = decompress_count(raw_coin_count, coin_compression_flags)
|
|
476
476
|
|
|
477
|
+
# Fill first index of 72 steps with nan value per
|
|
478
|
+
# SWAPI team's instruction. nan helps with plotting.
|
|
479
|
+
swp_pcem_counts[:, 0] = np.nan
|
|
480
|
+
swp_scem_counts[:, 0] = np.nan
|
|
481
|
+
swp_coin_counts[:, 0] = np.nan
|
|
482
|
+
|
|
477
483
|
# ====================================================
|
|
478
484
|
# Load the CDF attributes
|
|
479
485
|
# ====================================================
|
|
@@ -600,17 +606,17 @@ def process_swapi_science(
|
|
|
600
606
|
)
|
|
601
607
|
|
|
602
608
|
dataset["swp_pcem_counts"] = xr.DataArray(
|
|
603
|
-
|
|
609
|
+
swp_pcem_counts,
|
|
604
610
|
dims=["epoch", "esa_step"],
|
|
605
611
|
attrs=cdf_manager.get_variable_attributes("pcem_counts"),
|
|
606
612
|
)
|
|
607
613
|
dataset["swp_scem_counts"] = xr.DataArray(
|
|
608
|
-
|
|
614
|
+
swp_scem_counts,
|
|
609
615
|
dims=["epoch", "esa_step"],
|
|
610
616
|
attrs=cdf_manager.get_variable_attributes("scem_counts"),
|
|
611
617
|
)
|
|
612
618
|
dataset["swp_coin_counts"] = xr.DataArray(
|
|
613
|
-
|
|
619
|
+
swp_coin_counts,
|
|
614
620
|
dims=["epoch", "esa_step"],
|
|
615
621
|
attrs=cdf_manager.get_variable_attributes("coin_counts"),
|
|
616
622
|
)
|