imap-processing 0.18.0__py3-none-any.whl → 0.19.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.
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_l1a_variable_attrs.yaml +301 -274
- imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +28 -28
- 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_glows_l1b_variable_attrs.yaml +12 -0
- 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_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_l1b_variable_attrs.yaml +12 -4
- imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +50 -7
- imap_processing/cli.py +95 -41
- imap_processing/codice/codice_l1a.py +131 -31
- imap_processing/codice/codice_l2.py +118 -10
- imap_processing/codice/constants.py +740 -595
- imap_processing/decom.py +1 -4
- imap_processing/ena_maps/ena_maps.py +32 -25
- 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 +99 -9
- imap_processing/glows/l1b/glows_l1b_data.py +350 -38
- 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_l2.py +84 -51
- imap_processing/hi/utils.py +153 -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 +5 -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/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/l0/lo_science.py +25 -24
- imap_processing/lo/l1b/lo_l1b.py +3 -3
- imap_processing/lo/l1c/lo_l1c.py +116 -50
- imap_processing/lo/l2/lo_l2.py +29 -29
- 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 +167 -30
- imap_processing/mag/l2/mag_l2_data.py +10 -2
- imap_processing/quality_flags.py +9 -1
- imap_processing/spice/geometry.py +76 -33
- imap_processing/spice/pointing_frame.py +0 -6
- imap_processing/spice/repoint.py +29 -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 +1 -24
- imap_processing/ultra/l0/ultra_utils.py +9 -11
- imap_processing/ultra/l1a/ultra_l1a.py +1 -2
- imap_processing/ultra/l1b/cullingmask.py +6 -3
- imap_processing/ultra/l1b/de.py +81 -23
- imap_processing/ultra/l1b/extendedspin.py +13 -10
- 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_culling.py +161 -3
- imap_processing/ultra/l1b/ultra_l1b_extended.py +253 -47
- imap_processing/ultra/l1c/helio_pset.py +97 -24
- imap_processing/ultra/l1c/l1c_lookup_utils.py +256 -0
- imap_processing/ultra/l1c/spacecraft_pset.py +83 -16
- imap_processing/ultra/l1c/ultra_l1c.py +6 -2
- imap_processing/ultra/l1c/ultra_l1c_culling.py +85 -0
- imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +385 -277
- imap_processing/ultra/l2/ultra_l2.py +0 -1
- imap_processing/ultra/utils/ultra_l1_utils.py +28 -3
- imap_processing/utils.py +3 -4
- {imap_processing-0.18.0.dist-info → imap_processing-0.19.0.dist-info}/METADATA +2 -2
- {imap_processing-0.18.0.dist-info → imap_processing-0.19.0.dist-info}/RECORD +102 -95
- imap_processing/idex/idex_l2c.py +0 -84
- imap_processing/spice/kernels.py +0 -187
- {imap_processing-0.18.0.dist-info → imap_processing-0.19.0.dist-info}/LICENSE +0 -0
- {imap_processing-0.18.0.dist-info → imap_processing-0.19.0.dist-info}/WHEEL +0 -0
- {imap_processing-0.18.0.dist-info → imap_processing-0.19.0.dist-info}/entry_points.txt +0 -0
|
@@ -11,15 +11,12 @@ 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."""
|
|
@@ -100,10 +97,8 @@ BORESIGHT_LOOKUP = {
|
|
|
100
97
|
}
|
|
101
98
|
|
|
102
99
|
|
|
103
|
-
@typing.no_type_check
|
|
104
|
-
@ensure_spice
|
|
105
100
|
def imap_state(
|
|
106
|
-
et:
|
|
101
|
+
et: np.ndarray | float,
|
|
107
102
|
ref_frame: SpiceFrame = SpiceFrame.ECLIPJ2000,
|
|
108
103
|
abcorr: str = "NONE",
|
|
109
104
|
observer: SpiceBody = SpiceBody.SUN,
|
|
@@ -137,14 +132,65 @@ def imap_state(
|
|
|
137
132
|
return np.asarray(state)
|
|
138
133
|
|
|
139
134
|
|
|
135
|
+
def get_instrument_mounting_az_el(instrument: SpiceFrame) -> np.ndarray:
|
|
136
|
+
"""
|
|
137
|
+
Calculate the azimuth and elevation angle of instrument mounting.
|
|
138
|
+
|
|
139
|
+
Azimuth and elevation to instrument mounting in the spacecraft frame.
|
|
140
|
+
Azimuth is measured in degrees from the spacecraft x-axis. Elevation is measured
|
|
141
|
+
in degrees from the spacecraft x-y plane.
|
|
142
|
+
|
|
143
|
+
Parameters
|
|
144
|
+
----------
|
|
145
|
+
instrument : SpiceFrame
|
|
146
|
+
Instrument to get the azimuth and elevation angles for.
|
|
147
|
+
|
|
148
|
+
Returns
|
|
149
|
+
-------
|
|
150
|
+
instrument_mounting_az_el : np.ndarray
|
|
151
|
+
2-element array containing azimuth and elevation of the instrument
|
|
152
|
+
mounting in the spacecraft frame. Azimuth is measured in degrees from
|
|
153
|
+
the spacecraft x-axis. Elevation is measured in degrees from the
|
|
154
|
+
spacecraft x-y plane.
|
|
155
|
+
"""
|
|
156
|
+
# Each instrument can have a unique basis vector in the instrument
|
|
157
|
+
# frame that is used to compute the s/c to instrument mounting.
|
|
158
|
+
# Most of these vectors are the same as the instrument boresight vector.
|
|
159
|
+
mounting_normal_vector = {
|
|
160
|
+
SpiceFrame.IMAP_LO_BASE: np.array([0, -1, 0]),
|
|
161
|
+
SpiceFrame.IMAP_HI_45: np.array([0, 1, 0]),
|
|
162
|
+
SpiceFrame.IMAP_HI_90: np.array([0, 1, 0]),
|
|
163
|
+
SpiceFrame.IMAP_ULTRA_45: np.array([0, 0, 1]),
|
|
164
|
+
SpiceFrame.IMAP_ULTRA_90: np.array([0, 0, 1]),
|
|
165
|
+
SpiceFrame.IMAP_MAG: np.array([-1, 0, 0]),
|
|
166
|
+
SpiceFrame.IMAP_SWE: np.array([-1, 0, 0]),
|
|
167
|
+
SpiceFrame.IMAP_SWAPI: np.array([0, 0, -1]),
|
|
168
|
+
SpiceFrame.IMAP_CODICE: np.array([-1, 0, 0]),
|
|
169
|
+
SpiceFrame.IMAP_HIT: np.array([0, 1, 0]),
|
|
170
|
+
SpiceFrame.IMAP_IDEX: np.array([0, 1, 0]),
|
|
171
|
+
SpiceFrame.IMAP_GLOWS: np.array([0, 0, -1]),
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
# Get the instrument mounting normal vector expressed in the spacecraft frame
|
|
175
|
+
# The reference frames are fixed, so the et argument can be fixed at 0
|
|
176
|
+
instrument_normal_sc = frame_transform(
|
|
177
|
+
0, mounting_normal_vector[instrument], instrument, SpiceFrame.IMAP_SPACECRAFT
|
|
178
|
+
)
|
|
179
|
+
# Convert the cartesian coordinate to azimuth/elevation angles in degrees
|
|
180
|
+
return np.rad2deg(
|
|
181
|
+
spiceypy.recazl(instrument_normal_sc, azccw=True, elplsz=True)[1:]
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
|
|
140
185
|
def get_spacecraft_to_instrument_spin_phase_offset(instrument: SpiceFrame) -> float:
|
|
141
186
|
"""
|
|
142
187
|
Get the spin phase offset from the spacecraft to the instrument.
|
|
143
188
|
|
|
144
189
|
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
|
-
|
|
190
|
+
to S/C CS Transformations` in document `7516-0011_drw.pdf`. That Table
|
|
191
|
+
defines the angle from the spacecraft y-axis. We add 90 and take the modulous
|
|
192
|
+
with 360 in order to get the angle from the spacecraft x-axis. These fixed
|
|
193
|
+
values will need to be updated based on calibration data.
|
|
148
194
|
|
|
149
195
|
Parameters
|
|
150
196
|
----------
|
|
@@ -156,26 +202,25 @@ def get_spacecraft_to_instrument_spin_phase_offset(instrument: SpiceFrame) -> fl
|
|
|
156
202
|
spacecraft_to_instrument_spin_phase_offset : float
|
|
157
203
|
The spin phase offset from the spacecraft to the instrument.
|
|
158
204
|
"""
|
|
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.IMAP_MAG: 0 / 360,
|
|
205
|
+
phase_offset_lookup = {
|
|
206
|
+
SpiceFrame.IMAP_LO_BASE: 60 / 360, # (330 + 90) % 360 = 60
|
|
207
|
+
SpiceFrame.IMAP_HI_45: 345 / 360, # 255 + 90 = 345
|
|
208
|
+
SpiceFrame.IMAP_HI_90: 15 / 360, # (285 + 90) % 360 = 15
|
|
209
|
+
SpiceFrame.IMAP_ULTRA_45: 123 / 360, # 33 + 90 = 123
|
|
210
|
+
SpiceFrame.IMAP_ULTRA_90: 300 / 360, # 210 + 90 = 300
|
|
211
|
+
SpiceFrame.IMAP_SWAPI: 258 / 360, # 168 + 90 = 258
|
|
212
|
+
SpiceFrame.IMAP_IDEX: 180 / 360, # 90 + 90 = 180
|
|
213
|
+
SpiceFrame.IMAP_CODICE: 226 / 360, # 136 + 90 = 226
|
|
214
|
+
SpiceFrame.IMAP_HIT: 120 / 360, # 30 + 90 = 120
|
|
215
|
+
SpiceFrame.IMAP_SWE: 243 / 360, # 153 + 90 = 243
|
|
216
|
+
SpiceFrame.IMAP_GLOWS: 217 / 360, # 127 + 90 = 217
|
|
217
|
+
SpiceFrame.IMAP_MAG: 90 / 360, # 0 + 90 = 90
|
|
173
218
|
}
|
|
174
|
-
return
|
|
219
|
+
return phase_offset_lookup[instrument]
|
|
175
220
|
|
|
176
221
|
|
|
177
222
|
def frame_transform(
|
|
178
|
-
et:
|
|
223
|
+
et: float | npt.NDArray,
|
|
179
224
|
position: npt.NDArray,
|
|
180
225
|
from_frame: SpiceFrame,
|
|
181
226
|
to_frame: SpiceFrame,
|
|
@@ -249,7 +294,7 @@ def frame_transform(
|
|
|
249
294
|
|
|
250
295
|
|
|
251
296
|
def frame_transform_az_el(
|
|
252
|
-
et:
|
|
297
|
+
et: float | npt.NDArray,
|
|
253
298
|
az_el: npt.NDArray,
|
|
254
299
|
from_frame: SpiceFrame,
|
|
255
300
|
to_frame: SpiceFrame,
|
|
@@ -298,10 +343,8 @@ def frame_transform_az_el(
|
|
|
298
343
|
return to_frame_az_el[..., 1:3]
|
|
299
344
|
|
|
300
345
|
|
|
301
|
-
@typing.no_type_check
|
|
302
|
-
@ensure_spice
|
|
303
346
|
def get_rotation_matrix(
|
|
304
|
-
et:
|
|
347
|
+
et: float | npt.NDArray,
|
|
305
348
|
from_frame: SpiceFrame,
|
|
306
349
|
to_frame: SpiceFrame,
|
|
307
350
|
) -> npt.NDArray:
|
|
@@ -339,7 +382,7 @@ def get_rotation_matrix(
|
|
|
339
382
|
|
|
340
383
|
|
|
341
384
|
def instrument_pointing(
|
|
342
|
-
et:
|
|
385
|
+
et: float | npt.NDArray,
|
|
343
386
|
instrument: SpiceFrame,
|
|
344
387
|
to_frame: SpiceFrame,
|
|
345
388
|
cartesian: bool = False,
|
|
@@ -377,7 +420,7 @@ def instrument_pointing(
|
|
|
377
420
|
|
|
378
421
|
|
|
379
422
|
def basis_vectors(
|
|
380
|
-
et:
|
|
423
|
+
et: float | npt.NDArray,
|
|
381
424
|
from_frame: SpiceFrame,
|
|
382
425
|
to_frame: SpiceFrame,
|
|
383
426
|
) -> npt.NDArray:
|
|
@@ -550,9 +593,9 @@ def cartesian_to_latitudinal(coords: NDArray, degrees: bool = True) -> NDArray:
|
|
|
550
593
|
|
|
551
594
|
|
|
552
595
|
def solar_longitude(
|
|
553
|
-
et:
|
|
596
|
+
et: np.ndarray | float,
|
|
554
597
|
degrees: bool = True,
|
|
555
|
-
) ->
|
|
598
|
+
) -> float | npt.NDArray:
|
|
556
599
|
"""
|
|
557
600
|
Compute the solar longitude of the Imap Spacecraft.
|
|
558
601
|
|
|
@@ -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:
|
|
@@ -292,8 +288,6 @@ def calculate_pointing_attitude_segments(
|
|
|
292
288
|
return pointing_segments
|
|
293
289
|
|
|
294
290
|
|
|
295
|
-
@typing.no_type_check
|
|
296
|
-
@ensure_spice
|
|
297
291
|
def _average_quaternions(et_times: np.ndarray) -> NDArray:
|
|
298
292
|
"""
|
|
299
293
|
Average the quaternions.
|
imap_processing/spice/repoint.py
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
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
|
|
@@ -113,7 +112,7 @@ def _load_repoint_data_with_cache(csv_path: Path) -> pd.DataFrame:
|
|
|
113
112
|
|
|
114
113
|
|
|
115
114
|
def interpolate_repoint_data(
|
|
116
|
-
query_met_times:
|
|
115
|
+
query_met_times: float | npt.NDArray,
|
|
117
116
|
) -> pd.DataFrame:
|
|
118
117
|
"""
|
|
119
118
|
Interpolate repointing data to the queried MET times.
|
|
@@ -194,3 +193,31 @@ def interpolate_repoint_data(
|
|
|
194
193
|
out_df["repoint_in_progress"] = query_met_times < out_df["repoint_end_met"].values
|
|
195
194
|
|
|
196
195
|
return out_df
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def get_pointing_times(met_time: float) -> tuple[float, float]:
|
|
199
|
+
"""
|
|
200
|
+
Get the start and end MET times for the pointing that contains the query MET time.
|
|
201
|
+
|
|
202
|
+
Parameters
|
|
203
|
+
----------
|
|
204
|
+
met_time : float
|
|
205
|
+
The MET time in a pointing.
|
|
206
|
+
|
|
207
|
+
Returns
|
|
208
|
+
-------
|
|
209
|
+
pointing_start_time : float
|
|
210
|
+
The MET time of the repoint maneuver that ends before the query MET time.
|
|
211
|
+
pointing_end_time : float
|
|
212
|
+
The MET time of the repoint maneuver that starts after the query MET time.
|
|
213
|
+
"""
|
|
214
|
+
# Find the pointing start time by finding the repoint end time
|
|
215
|
+
repoint_df = interpolate_repoint_data(met_time)
|
|
216
|
+
pointing_start_met = repoint_df["repoint_end_met"].item()
|
|
217
|
+
# Find the pointing end time by finding the next repoint start time
|
|
218
|
+
repoint_df = get_repoint_data()
|
|
219
|
+
pointing_idx = repoint_df.index[
|
|
220
|
+
repoint_df["repoint_end_met"] == pointing_start_met
|
|
221
|
+
][0]
|
|
222
|
+
pointing_end_met = repoint_df["repoint_start_met"].iloc[pointing_idx + 1].item()
|
|
223
|
+
return pointing_start_met, pointing_end_met
|
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
|
)
|
|
@@ -12,7 +12,7 @@ from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
|
|
|
12
12
|
logger = logging.getLogger(__name__)
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
SWAPI_LIVETIME = 0.145 # seconds
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
def solve_full_sweep_energy(
|
|
@@ -66,7 +66,7 @@ def solve_full_sweep_energy(
|
|
|
66
66
|
|
|
67
67
|
first_63_energies = []
|
|
68
68
|
|
|
69
|
-
for time, sweep_id in zip(data_time, sweep_table):
|
|
69
|
+
for time, sweep_id in zip(data_time, sweep_table, strict=False):
|
|
70
70
|
# Find the sweep's ESA data for the given time and sweep_id
|
|
71
71
|
subset = esa_table_df[
|
|
72
72
|
(esa_table_df["timestamp"] <= time) & (esa_table_df["Sweep #"] == sweep_id)
|
|
@@ -159,14 +159,12 @@ def swapi_l2(
|
|
|
159
159
|
|
|
160
160
|
To process science data to L2, we need to:
|
|
161
161
|
- convert counts to rates. This is done by dividing the counts by the
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
(12 s, coarse + fine sweep) by the total energy steps (72),
|
|
165
|
-
i.e., TIME_PER_BIN = 12/72 = 0.167 s. This will be constant.
|
|
162
|
+
SWAPI_LIVETIME time. LIVETIME is data acquisition time. It will
|
|
163
|
+
be constant, SWAPI_LIVETIME = 0.145 s.
|
|
166
164
|
|
|
167
165
|
- update uncertainty. Calculate new uncertainty value using
|
|
168
|
-
SWP_PCEM_ERR data from level one and divide by
|
|
169
|
-
SWP_PCEM_UNC = SWP_PCEM_ERR /
|
|
166
|
+
SWP_PCEM_ERR data from level one and divide by SWAPI_LIVETIME. Eg.
|
|
167
|
+
SWP_PCEM_UNC = SWP_PCEM_ERR / SWAPI_LIVETIME
|
|
170
168
|
Do the same for SCEM and COIN data.
|
|
171
169
|
|
|
172
170
|
Parameters
|
|
@@ -233,9 +231,9 @@ def swapi_l2(
|
|
|
233
231
|
]
|
|
234
232
|
|
|
235
233
|
# convert counts to rate
|
|
236
|
-
l2_dataset["swp_pcem_rate"] = l1_dataset["swp_pcem_counts"] /
|
|
237
|
-
l2_dataset["swp_scem_rate"] = l1_dataset["swp_scem_counts"] /
|
|
238
|
-
l2_dataset["swp_coin_rate"] = l1_dataset["swp_coin_counts"] /
|
|
234
|
+
l2_dataset["swp_pcem_rate"] = l1_dataset["swp_pcem_counts"] / SWAPI_LIVETIME
|
|
235
|
+
l2_dataset["swp_scem_rate"] = l1_dataset["swp_scem_counts"] / SWAPI_LIVETIME
|
|
236
|
+
l2_dataset["swp_coin_rate"] = l1_dataset["swp_coin_counts"] / SWAPI_LIVETIME
|
|
239
237
|
# update attrs
|
|
240
238
|
l2_dataset["swp_pcem_rate"].attrs = cdf_manager.get_variable_attributes("pcem_rate")
|
|
241
239
|
l2_dataset["swp_scem_rate"].attrs = cdf_manager.get_variable_attributes("scem_rate")
|
|
@@ -243,22 +241,22 @@ def swapi_l2(
|
|
|
243
241
|
|
|
244
242
|
# update uncertainty
|
|
245
243
|
l2_dataset["swp_pcem_rate_stat_uncert_plus"] = (
|
|
246
|
-
l1_dataset["swp_pcem_counts_stat_uncert_plus"] /
|
|
244
|
+
l1_dataset["swp_pcem_counts_stat_uncert_plus"] / SWAPI_LIVETIME
|
|
247
245
|
)
|
|
248
246
|
l2_dataset["swp_pcem_rate_stat_uncert_minus"] = (
|
|
249
|
-
l1_dataset["swp_pcem_counts_stat_uncert_minus"] /
|
|
247
|
+
l1_dataset["swp_pcem_counts_stat_uncert_minus"] / SWAPI_LIVETIME
|
|
250
248
|
)
|
|
251
249
|
l2_dataset["swp_scem_rate_stat_uncert_plus"] = (
|
|
252
|
-
l1_dataset["swp_scem_counts_stat_uncert_plus"] /
|
|
250
|
+
l1_dataset["swp_scem_counts_stat_uncert_plus"] / SWAPI_LIVETIME
|
|
253
251
|
)
|
|
254
252
|
l2_dataset["swp_scem_rate_stat_uncert_minus"] = (
|
|
255
|
-
l1_dataset["swp_scem_counts_stat_uncert_minus"] /
|
|
253
|
+
l1_dataset["swp_scem_counts_stat_uncert_minus"] / SWAPI_LIVETIME
|
|
256
254
|
)
|
|
257
255
|
l2_dataset["swp_coin_rate_stat_uncert_plus"] = (
|
|
258
|
-
l1_dataset["swp_coin_counts_stat_uncert_plus"] /
|
|
256
|
+
l1_dataset["swp_coin_counts_stat_uncert_plus"] / SWAPI_LIVETIME
|
|
259
257
|
)
|
|
260
258
|
l2_dataset["swp_coin_rate_stat_uncert_minus"] = (
|
|
261
|
-
l1_dataset["swp_coin_counts_stat_uncert_minus"] /
|
|
259
|
+
l1_dataset["swp_coin_counts_stat_uncert_minus"] / SWAPI_LIVETIME
|
|
262
260
|
)
|
|
263
261
|
# update attrs
|
|
264
262
|
l2_dataset[
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import Union
|
|
6
5
|
|
|
7
6
|
import numpy as np
|
|
8
7
|
import numpy.typing as npt
|
|
@@ -51,7 +50,7 @@ def get_esa_dataframe(esa_table_number: int) -> pd.DataFrame:
|
|
|
51
50
|
|
|
52
51
|
|
|
53
52
|
def deadtime_correction(
|
|
54
|
-
counts: np.ndarray, acq_duration:
|
|
53
|
+
counts: np.ndarray, acq_duration: int | npt.NDArray
|
|
55
54
|
) -> npt.NDArray:
|
|
56
55
|
"""
|
|
57
56
|
Calculate deadtime correction.
|
|
@@ -51,7 +51,6 @@ class UltraConstants:
|
|
|
51
51
|
Z_DSTOP: float = 2.6 / 2 # Position of stop foil on Z axis [mm]
|
|
52
52
|
Z_DS: float = 46.19 - (2.6 / 2) # Position of slit on Z axis [mm]
|
|
53
53
|
DF: float = 3.39 # Distance from slit to foil [mm]
|
|
54
|
-
|
|
55
54
|
# Derived constants
|
|
56
55
|
DMIN_PH_CTOF: float = (
|
|
57
56
|
Z_DS - (2**0.5) * DF
|
|
@@ -79,52 +78,30 @@ class UltraConstants:
|
|
|
79
78
|
CULLING_RPM_MIN = 2.0
|
|
80
79
|
CULLING_RPM_MAX = 6.0
|
|
81
80
|
|
|
82
|
-
# Thresholds for culling based on counts.
|
|
81
|
+
# Thresholds for culling based on counts (keV).
|
|
83
82
|
CULLING_ENERGY_BIN_EDGES: ClassVar[list] = [
|
|
84
83
|
3.385,
|
|
85
84
|
4.13722222222222,
|
|
86
|
-
4.13722222222222,
|
|
87
|
-
5.05660493827161,
|
|
88
85
|
5.05660493827161,
|
|
89
86
|
6.18029492455419,
|
|
90
|
-
6.18029492455419,
|
|
91
|
-
7.55369379667734,
|
|
92
87
|
7.55369379667734,
|
|
93
88
|
9.23229241816119,
|
|
94
|
-
9.23229241816119,
|
|
95
89
|
11.2839129555303,
|
|
96
|
-
11.2839129555303,
|
|
97
|
-
13.7914491678704,
|
|
98
90
|
13.7914491678704,
|
|
99
91
|
16.8562156496194,
|
|
100
|
-
16.8562156496194,
|
|
101
92
|
20.6020413495348,
|
|
102
|
-
20.6020413495348,
|
|
103
|
-
25.1802727605426,
|
|
104
93
|
25.1802727605426,
|
|
105
94
|
30.775888929552,
|
|
106
|
-
30.775888929552,
|
|
107
95
|
37.6149753583414,
|
|
108
|
-
37.6149753583414,
|
|
109
|
-
45.9738587713061,
|
|
110
96
|
45.9738587713061,
|
|
111
97
|
56.1902718315964,
|
|
112
|
-
56.1902718315964,
|
|
113
98
|
68.6769989052845,
|
|
114
|
-
68.6769989052845,
|
|
115
|
-
83.93855421757,
|
|
116
99
|
83.93855421757,
|
|
117
100
|
102.591566265919,
|
|
118
|
-
102.591566265919,
|
|
119
101
|
125.38969210279,
|
|
120
|
-
125.38969210279,
|
|
121
|
-
153.254068125632,
|
|
122
102
|
153.254068125632,
|
|
123
103
|
187.310527709106,
|
|
124
|
-
187.310527709106,
|
|
125
104
|
228.93508942224,
|
|
126
|
-
228.93508942224,
|
|
127
|
-
279.809553738294,
|
|
128
105
|
279.809553738294,
|
|
129
106
|
341.989454569026,
|
|
130
107
|
1e5,
|