imap-processing 0.16.2__py3-none-any.whl → 0.18.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/ccsds/excel_to_xtce.py +12 -0
- imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +6 -6
- imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +35 -0
- imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +35 -0
- imap_processing/cdf/config/imap_codice_l2_variable_attrs.yaml +24 -0
- imap_processing/cdf/config/imap_hi_variable_attrs.yaml +8 -8
- imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +1 -1
- imap_processing/cdf/config/imap_hit_l1a_variable_attrs.yaml +163 -100
- imap_processing/cdf/config/imap_hit_l2_variable_attrs.yaml +398 -415
- imap_processing/cdf/config/imap_ialirt_l1_variable_attrs.yaml +97 -54
- imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +9 -9
- imap_processing/cdf/config/imap_idex_l2b_variable_attrs.yaml +233 -57
- imap_processing/cdf/config/imap_idex_l2c_variable_attrs.yaml +16 -90
- imap_processing/cdf/config/imap_lo_global_cdf_attrs.yaml +30 -0
- imap_processing/cdf/config/imap_mag_global_cdf_attrs.yaml +15 -1
- imap_processing/cdf/config/imap_swapi_variable_attrs.yaml +19 -0
- imap_processing/cdf/config/imap_swe_l1b_variable_attrs.yaml +20 -0
- imap_processing/cdf/config/imap_swe_l2_variable_attrs.yaml +39 -0
- imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +168 -0
- imap_processing/cdf/config/imap_ultra_l1a_variable_attrs.yaml +103 -2
- imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +91 -11
- imap_processing/cdf/utils.py +7 -1
- imap_processing/cli.py +42 -13
- imap_processing/codice/codice_l1a.py +125 -78
- imap_processing/codice/codice_l1b.py +1 -1
- imap_processing/codice/codice_l2.py +0 -9
- imap_processing/codice/constants.py +481 -498
- imap_processing/hi/hi_l1a.py +4 -4
- imap_processing/hi/hi_l1b.py +2 -2
- imap_processing/hi/packet_definitions/TLM_HI_COMBINED_SCI.xml +218 -38
- imap_processing/hit/hit_utils.py +2 -2
- imap_processing/hit/l0/decom_hit.py +4 -3
- imap_processing/hit/l1a/hit_l1a.py +64 -24
- imap_processing/hit/l1b/constants.py +5 -0
- imap_processing/hit/l1b/hit_l1b.py +18 -16
- imap_processing/hit/l2/constants.py +1 -1
- imap_processing/hit/l2/hit_l2.py +4 -4
- imap_processing/ialirt/constants.py +21 -0
- imap_processing/ialirt/generate_coverage.py +188 -0
- imap_processing/ialirt/l0/parse_mag.py +62 -5
- imap_processing/ialirt/l0/process_swapi.py +1 -1
- imap_processing/ialirt/l0/process_swe.py +23 -7
- imap_processing/ialirt/utils/constants.py +22 -16
- imap_processing/ialirt/utils/create_xarray.py +42 -19
- imap_processing/idex/idex_constants.py +8 -5
- imap_processing/idex/idex_l2b.py +554 -58
- imap_processing/idex/idex_l2c.py +30 -196
- imap_processing/lo/l0/lo_apid.py +1 -0
- imap_processing/lo/l0/lo_star_sensor.py +48 -0
- imap_processing/lo/l1a/lo_l1a.py +74 -30
- imap_processing/lo/packet_definitions/lo_xtce.xml +5359 -106
- imap_processing/mag/constants.py +1 -0
- imap_processing/mag/l0/decom_mag.py +9 -6
- imap_processing/mag/l0/mag_l0_data.py +46 -0
- imap_processing/mag/l1d/__init__.py +0 -0
- imap_processing/mag/l1d/mag_l1d.py +133 -0
- imap_processing/mag/l1d/mag_l1d_data.py +588 -0
- imap_processing/mag/l2/__init__.py +0 -0
- imap_processing/mag/l2/mag_l2.py +25 -20
- imap_processing/mag/l2/mag_l2_data.py +191 -130
- imap_processing/quality_flags.py +20 -2
- imap_processing/spice/geometry.py +25 -3
- imap_processing/spice/pointing_frame.py +1 -1
- imap_processing/spice/spin.py +4 -0
- imap_processing/spice/time.py +51 -0
- imap_processing/swapi/l1/swapi_l1.py +12 -2
- imap_processing/swapi/l2/swapi_l2.py +59 -14
- imap_processing/swapi/swapi_utils.py +1 -1
- imap_processing/swe/l1b/swe_l1b.py +11 -4
- imap_processing/swe/l2/swe_l2.py +111 -17
- imap_processing/ultra/constants.py +49 -1
- imap_processing/ultra/l0/decom_tools.py +28 -14
- imap_processing/ultra/l0/decom_ultra.py +225 -15
- imap_processing/ultra/l0/ultra_utils.py +281 -8
- imap_processing/ultra/l1a/ultra_l1a.py +77 -8
- imap_processing/ultra/l1b/cullingmask.py +3 -3
- imap_processing/ultra/l1b/de.py +53 -15
- imap_processing/ultra/l1b/extendedspin.py +26 -2
- imap_processing/ultra/l1b/lookup_utils.py +171 -50
- imap_processing/ultra/l1b/quality_flag_filters.py +14 -0
- imap_processing/ultra/l1b/ultra_l1b_culling.py +198 -5
- imap_processing/ultra/l1b/ultra_l1b_extended.py +304 -66
- imap_processing/ultra/l1c/helio_pset.py +54 -7
- imap_processing/ultra/l1c/spacecraft_pset.py +9 -1
- imap_processing/ultra/l1c/ultra_l1c.py +2 -0
- imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +106 -109
- imap_processing/ultra/packet_definitions/ULTRA_SCI_COMBINED.xml +3 -3
- imap_processing/ultra/utils/ultra_l1_utils.py +13 -1
- imap_processing/utils.py +20 -42
- {imap_processing-0.16.2.dist-info → imap_processing-0.18.0.dist-info}/METADATA +2 -2
- {imap_processing-0.16.2.dist-info → imap_processing-0.18.0.dist-info}/RECORD +95 -103
- imap_processing/lo/l0/data_classes/star_sensor.py +0 -98
- imap_processing/lo/l0/utils/lo_base.py +0 -57
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_LeftSlit.csv +0 -526
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_RightSlit.csv +0 -526
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_LeftSlit.csv +0 -526
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_RightSlit.csv +0 -524
- imap_processing/ultra/lookup_tables/EgyNorm.mem.csv +0 -32769
- imap_processing/ultra/lookup_tables/FM45_Startup1_ULTRA_IMGPARAMS_20240719.csv +0 -2
- imap_processing/ultra/lookup_tables/FM90_Startup1_ULTRA_IMGPARAMS_20240719.csv +0 -2
- imap_processing/ultra/lookup_tables/dps_grid45_compressed.cdf +0 -0
- imap_processing/ultra/lookup_tables/ultra45_back-pos-luts.csv +0 -4097
- imap_processing/ultra/lookup_tables/ultra45_tdc_norm.csv +0 -2050
- imap_processing/ultra/lookup_tables/ultra90_back-pos-luts.csv +0 -4097
- imap_processing/ultra/lookup_tables/ultra90_tdc_norm.csv +0 -2050
- imap_processing/ultra/lookup_tables/yadjust.csv +0 -257
- {imap_processing-0.16.2.dist-info → imap_processing-0.18.0.dist-info}/LICENSE +0 -0
- {imap_processing-0.16.2.dist-info → imap_processing-0.18.0.dist-info}/WHEEL +0 -0
- {imap_processing-0.16.2.dist-info → imap_processing-0.18.0.dist-info}/entry_points.txt +0 -0
|
@@ -20,6 +20,7 @@ from imap_processing.ultra.l1b.lookup_utils import (
|
|
|
20
20
|
get_energy_norm,
|
|
21
21
|
get_image_params,
|
|
22
22
|
get_norm,
|
|
23
|
+
get_ph_corrected,
|
|
23
24
|
get_y_adjust,
|
|
24
25
|
)
|
|
25
26
|
|
|
@@ -50,7 +51,7 @@ class CoinType(Enum):
|
|
|
50
51
|
|
|
51
52
|
|
|
52
53
|
def get_front_x_position(
|
|
53
|
-
start_type: ndarray, start_position_tdc: ndarray, sensor: str
|
|
54
|
+
start_type: ndarray, start_position_tdc: ndarray, sensor: str, ancillary_files: dict
|
|
54
55
|
) -> ndarray:
|
|
55
56
|
"""
|
|
56
57
|
Calculate the front xf position.
|
|
@@ -68,6 +69,8 @@ def get_front_x_position(
|
|
|
68
69
|
Start Position Time to Digital Converter (TDC).
|
|
69
70
|
sensor : str
|
|
70
71
|
Sensor name.
|
|
72
|
+
ancillary_files : dict[Path]
|
|
73
|
+
Ancillary files containing the lookup tables.
|
|
71
74
|
|
|
72
75
|
Returns
|
|
73
76
|
-------
|
|
@@ -77,9 +80,9 @@ def get_front_x_position(
|
|
|
77
80
|
# Left and right start types.
|
|
78
81
|
indices = np.nonzero((start_type == 1) | (start_type == 2))
|
|
79
82
|
|
|
80
|
-
xftsc = get_image_params("XFTSC", sensor)
|
|
81
|
-
xft_lt_off = get_image_params("XFTLTOFF", sensor)
|
|
82
|
-
xft_rt_off = get_image_params("XFTRTOFF", sensor)
|
|
83
|
+
xftsc = get_image_params("XFTSC", sensor, ancillary_files)
|
|
84
|
+
xft_lt_off = get_image_params("XFTLTOFF", sensor, ancillary_files)
|
|
85
|
+
xft_rt_off = get_image_params("XFTRTOFF", sensor, ancillary_files)
|
|
83
86
|
xft_off = np.where(start_type[indices] == 1, xft_lt_off, xft_rt_off)
|
|
84
87
|
|
|
85
88
|
# Calculate xf and convert to hundredths of a millimeter
|
|
@@ -88,7 +91,9 @@ def get_front_x_position(
|
|
|
88
91
|
return xf
|
|
89
92
|
|
|
90
93
|
|
|
91
|
-
def get_front_y_position(
|
|
94
|
+
def get_front_y_position(
|
|
95
|
+
start_type: ndarray, yb: ndarray, ancillary_files: dict
|
|
96
|
+
) -> tuple[ndarray, ndarray]:
|
|
92
97
|
"""
|
|
93
98
|
Compute the adjustments for the front y position and distance front to back.
|
|
94
99
|
|
|
@@ -102,6 +107,8 @@ def get_front_y_position(start_type: ndarray, yb: ndarray) -> tuple[ndarray, nda
|
|
|
102
107
|
Start Type: 1=Left, 2=Right.
|
|
103
108
|
yb : np.array
|
|
104
109
|
Y back position in hundredths of a millimeter.
|
|
110
|
+
ancillary_files : dict[Path]
|
|
111
|
+
Ancillary files containing the lookup tables.
|
|
105
112
|
|
|
106
113
|
Returns
|
|
107
114
|
-------
|
|
@@ -125,7 +132,7 @@ def get_front_y_position(start_type: ndarray, yb: ndarray) -> tuple[ndarray, nda
|
|
|
125
132
|
+ 0.5
|
|
126
133
|
)
|
|
127
134
|
# y adjustment in mm
|
|
128
|
-
y_adjust_left = get_y_adjust(dy_lut_left) / 100
|
|
135
|
+
y_adjust_left = get_y_adjust(dy_lut_left, ancillary_files) / 100
|
|
129
136
|
# hundredths of a millimeter
|
|
130
137
|
yf[index_left] = (UltraConstants.YF_ESTIMATE_LEFT - y_adjust_left) * 100
|
|
131
138
|
# distance adjustment in mm
|
|
@@ -141,7 +148,7 @@ def get_front_y_position(start_type: ndarray, yb: ndarray) -> tuple[ndarray, nda
|
|
|
141
148
|
+ 0.5
|
|
142
149
|
)
|
|
143
150
|
# y adjustment in mm
|
|
144
|
-
y_adjust_right = get_y_adjust(dy_lut_right) / 100
|
|
151
|
+
y_adjust_right = get_y_adjust(dy_lut_right, ancillary_files) / 100
|
|
145
152
|
# hundredths of a millimeter
|
|
146
153
|
yf[index_right] = (UltraConstants.YF_ESTIMATE_RIGHT + y_adjust_right) * 100
|
|
147
154
|
# distance adjustment in mm
|
|
@@ -153,7 +160,7 @@ def get_front_y_position(start_type: ndarray, yb: ndarray) -> tuple[ndarray, nda
|
|
|
153
160
|
|
|
154
161
|
|
|
155
162
|
def get_ph_tof_and_back_positions(
|
|
156
|
-
de_dataset: xarray.Dataset, xf: np.ndarray, sensor: str
|
|
163
|
+
de_dataset: xarray.Dataset, xf: np.ndarray, sensor: str, ancillary_files: dict
|
|
157
164
|
) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
|
|
158
165
|
"""
|
|
159
166
|
Calculate back xb, yb position and tof.
|
|
@@ -176,6 +183,8 @@ def get_ph_tof_and_back_positions(
|
|
|
176
183
|
Has same length as de_dataset.
|
|
177
184
|
sensor : str
|
|
178
185
|
Sensor name.
|
|
186
|
+
ancillary_files : dict[Path]
|
|
187
|
+
Ancillary files containing the lookup tables.
|
|
179
188
|
|
|
180
189
|
Returns
|
|
181
190
|
-------
|
|
@@ -197,10 +206,18 @@ def get_ph_tof_and_back_positions(
|
|
|
197
206
|
|
|
198
207
|
# There are mismatches between the stop TDCs, i.e., SpN, SpS, SpE, and SpW.
|
|
199
208
|
# This normalizes the TDCs
|
|
200
|
-
sp_n_norm = get_norm(
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
209
|
+
sp_n_norm = get_norm(
|
|
210
|
+
de_filtered["stop_north_tdc"].data, "SpN", sensor, ancillary_files
|
|
211
|
+
)
|
|
212
|
+
sp_s_norm = get_norm(
|
|
213
|
+
de_filtered["stop_south_tdc"].data, "SpS", sensor, ancillary_files
|
|
214
|
+
)
|
|
215
|
+
sp_e_norm = get_norm(
|
|
216
|
+
de_filtered["stop_east_tdc"].data, "SpE", sensor, ancillary_files
|
|
217
|
+
)
|
|
218
|
+
sp_w_norm = get_norm(
|
|
219
|
+
de_filtered["stop_west_tdc"].data, "SpW", sensor, ancillary_files
|
|
220
|
+
)
|
|
204
221
|
|
|
205
222
|
# Convert normalized TDC values into units of hundredths of a
|
|
206
223
|
# millimeter using lookup tables.
|
|
@@ -227,35 +244,39 @@ def get_ph_tof_and_back_positions(
|
|
|
227
244
|
# Convert converts normalized TDC values into units of
|
|
228
245
|
# hundredths of a millimeter using lookup tables.
|
|
229
246
|
stop_type_top = de_filtered["stop_type"].data == StopType.Top.value
|
|
230
|
-
xb[stop_type_top] = get_back_position(
|
|
231
|
-
|
|
247
|
+
xb[stop_type_top] = get_back_position(
|
|
248
|
+
xb_index[stop_type_top], "XBkTp", sensor, ancillary_files
|
|
249
|
+
)
|
|
250
|
+
yb[stop_type_top] = get_back_position(
|
|
251
|
+
yb_index[stop_type_top], "YBkTp", sensor, ancillary_files
|
|
252
|
+
)
|
|
232
253
|
|
|
233
254
|
# Correction for the propagation delay of the start anode and other effects.
|
|
234
|
-
t2[stop_type_top] = get_image_params("TOFSC", sensor) * t1[
|
|
255
|
+
t2[stop_type_top] = get_image_params("TOFSC", sensor, ancillary_files) * t1[
|
|
235
256
|
stop_type_top
|
|
236
|
-
] + get_image_params("TOFTPOFF", sensor)
|
|
257
|
+
] + get_image_params("TOFTPOFF", sensor, ancillary_files)
|
|
237
258
|
# Variable xf_ph divided by 10 to convert to mm.
|
|
238
259
|
tof[stop_type_top] = t2[stop_type_top] + xf_ph[
|
|
239
260
|
stop_type_top
|
|
240
|
-
] / 10 * get_image_params("XFTTOF", sensor)
|
|
261
|
+
] / 10 * get_image_params("XFTTOF", sensor, ancillary_files)
|
|
241
262
|
|
|
242
263
|
stop_type_bottom = de_filtered["stop_type"].data == StopType.Bottom.value
|
|
243
264
|
xb[stop_type_bottom] = get_back_position(
|
|
244
|
-
xb_index[stop_type_bottom], "XBkBt", sensor
|
|
265
|
+
xb_index[stop_type_bottom], "XBkBt", sensor, ancillary_files
|
|
245
266
|
)
|
|
246
267
|
yb[stop_type_bottom] = get_back_position(
|
|
247
|
-
yb_index[stop_type_bottom], "YBkBt", sensor
|
|
268
|
+
yb_index[stop_type_bottom], "YBkBt", sensor, ancillary_files
|
|
248
269
|
)
|
|
249
270
|
|
|
250
271
|
# Correction for the propagation delay of the start anode and other effects.
|
|
251
|
-
t2[stop_type_bottom] = get_image_params("TOFSC", sensor) * t1[
|
|
272
|
+
t2[stop_type_bottom] = get_image_params("TOFSC", sensor, ancillary_files) * t1[
|
|
252
273
|
stop_type_bottom
|
|
253
|
-
] + get_image_params("TOFBTOFF", sensor) # 10*ns
|
|
274
|
+
] + get_image_params("TOFBTOFF", sensor, ancillary_files) # 10*ns
|
|
254
275
|
|
|
255
276
|
# Variable xf_ph divided by 10 to convert to mm.
|
|
256
277
|
tof[stop_type_bottom] = t2[stop_type_bottom] + xf_ph[
|
|
257
278
|
stop_type_bottom
|
|
258
|
-
] / 10 * get_image_params("XFTTOF", sensor)
|
|
279
|
+
] / 10 * get_image_params("XFTTOF", sensor, ancillary_files)
|
|
259
280
|
|
|
260
281
|
return tof, t2, xb, yb
|
|
261
282
|
|
|
@@ -290,7 +311,7 @@ def get_path_length(
|
|
|
290
311
|
|
|
291
312
|
|
|
292
313
|
def get_ssd_back_position_and_tof_offset(
|
|
293
|
-
de_dataset: xarray.Dataset, sensor: str
|
|
314
|
+
de_dataset: xarray.Dataset, sensor: str, ancillary_files: dict
|
|
294
315
|
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
295
316
|
"""
|
|
296
317
|
Lookup the Y SSD positions (yb), TOF Offset, and SSD number.
|
|
@@ -301,6 +322,8 @@ def get_ssd_back_position_and_tof_offset(
|
|
|
301
322
|
The input dataset containing STOP_TYPE and SSD_FLAG data.
|
|
302
323
|
sensor : str
|
|
303
324
|
Sensor name.
|
|
325
|
+
ancillary_files : dict[Path]
|
|
326
|
+
Ancillary files containing the lookup tables.
|
|
304
327
|
|
|
305
328
|
Returns
|
|
306
329
|
-------
|
|
@@ -326,21 +349,27 @@ def get_ssd_back_position_and_tof_offset(
|
|
|
326
349
|
ssd_flag_mask = de_filtered[f"ssd_flag_{i}"].data == 1
|
|
327
350
|
|
|
328
351
|
# Multiply ybs times 100 to convert to hundredths of a millimeter.
|
|
329
|
-
yb[ssd_flag_mask] =
|
|
352
|
+
yb[ssd_flag_mask] = (
|
|
353
|
+
get_image_params(f"YBKSSD{i}", sensor, ancillary_files) * 100
|
|
354
|
+
)
|
|
330
355
|
ssd_number[ssd_flag_mask] = i
|
|
331
356
|
|
|
332
357
|
tof_offset[
|
|
333
358
|
(de_filtered["start_type"] == StartType.Left.value) & ssd_flag_mask
|
|
334
|
-
] = get_image_params(f"TOFSSDLTOFF{i}", sensor)
|
|
359
|
+
] = get_image_params(f"TOFSSDLTOFF{i}", sensor, ancillary_files)
|
|
335
360
|
tof_offset[
|
|
336
361
|
(de_filtered["start_type"] == StartType.Right.value) & ssd_flag_mask
|
|
337
|
-
] = get_image_params(f"TOFSSDRTOFF{i}", sensor)
|
|
362
|
+
] = get_image_params(f"TOFSSDRTOFF{i}", sensor, ancillary_files)
|
|
338
363
|
|
|
339
364
|
return yb, tof_offset, ssd_number
|
|
340
365
|
|
|
341
366
|
|
|
342
367
|
def calculate_etof_xc(
|
|
343
|
-
de_subset: xarray.Dataset,
|
|
368
|
+
de_subset: xarray.Dataset,
|
|
369
|
+
particle_tof: np.ndarray,
|
|
370
|
+
sensor: str,
|
|
371
|
+
location: str,
|
|
372
|
+
ancillary_files: dict,
|
|
344
373
|
) -> tuple[np.ndarray, np.ndarray]:
|
|
345
374
|
"""
|
|
346
375
|
Calculate the etof and xc values for the given subset.
|
|
@@ -355,6 +384,8 @@ def calculate_etof_xc(
|
|
|
355
384
|
Sensor name.
|
|
356
385
|
location : str
|
|
357
386
|
Location indicator, either 'TP' (Top) or 'BT' (Bottom).
|
|
387
|
+
ancillary_files : dict[Path]
|
|
388
|
+
Ancillary files containing the lookup tables.
|
|
358
389
|
|
|
359
390
|
Returns
|
|
360
391
|
-------
|
|
@@ -365,17 +396,21 @@ def calculate_etof_xc(
|
|
|
365
396
|
X coincidence position (millimeters).
|
|
366
397
|
"""
|
|
367
398
|
# CoinNNorm
|
|
368
|
-
coin_n_norm = get_norm(
|
|
399
|
+
coin_n_norm = get_norm(
|
|
400
|
+
de_subset["coin_north_tdc"], "CoinN", sensor, ancillary_files
|
|
401
|
+
)
|
|
369
402
|
# CoinSNorm
|
|
370
|
-
coin_s_norm = get_norm(
|
|
371
|
-
|
|
403
|
+
coin_s_norm = get_norm(
|
|
404
|
+
de_subset["coin_south_tdc"], "CoinS", sensor, ancillary_files
|
|
405
|
+
)
|
|
406
|
+
xc = get_image_params(f"XCOIN{location}SC", sensor, ancillary_files) * (
|
|
372
407
|
coin_s_norm - coin_n_norm
|
|
373
|
-
) + get_image_params(f"XCOIN{location}OFF", sensor) # millimeter
|
|
408
|
+
) + get_image_params(f"XCOIN{location}OFF", sensor, ancillary_files) # millimeter
|
|
374
409
|
|
|
375
410
|
# Time for the electrons to travel back to coincidence anode.
|
|
376
|
-
t2 = get_image_params("ETOFSC", sensor) * (
|
|
411
|
+
t2 = get_image_params("ETOFSC", sensor, ancillary_files) * (
|
|
377
412
|
coin_n_norm + coin_s_norm
|
|
378
|
-
) + get_image_params(f"ETOF{location}OFF", sensor)
|
|
413
|
+
) + get_image_params(f"ETOF{location}OFF", sensor, ancillary_files)
|
|
379
414
|
|
|
380
415
|
# Multiply by 10 to convert to tenths of a nanosecond.
|
|
381
416
|
etof = t2 * 10 - particle_tof
|
|
@@ -384,7 +419,10 @@ def calculate_etof_xc(
|
|
|
384
419
|
|
|
385
420
|
|
|
386
421
|
def get_coincidence_positions(
|
|
387
|
-
de_dataset: xarray.Dataset,
|
|
422
|
+
de_dataset: xarray.Dataset,
|
|
423
|
+
particle_tof: np.ndarray,
|
|
424
|
+
sensor: str,
|
|
425
|
+
ancillary_files: dict,
|
|
388
426
|
) -> tuple[np.ndarray, np.ndarray]:
|
|
389
427
|
"""
|
|
390
428
|
Calculate coincidence positions.
|
|
@@ -408,6 +446,8 @@ def get_coincidence_positions(
|
|
|
408
446
|
(tenths of a nanosecond).
|
|
409
447
|
sensor : str
|
|
410
448
|
Sensor name.
|
|
449
|
+
ancillary_files : dict[Path]
|
|
450
|
+
Ancillary files containing the lookup tables.
|
|
411
451
|
|
|
412
452
|
Returns
|
|
413
453
|
-------
|
|
@@ -431,12 +471,14 @@ def get_coincidence_positions(
|
|
|
431
471
|
# Normalized TDCs
|
|
432
472
|
# For the stop anode, there are mismatches between the coincidence TDCs,
|
|
433
473
|
# i.e., CoinN and CoinS. They must be normalized via lookup tables.
|
|
434
|
-
etof_top, xc_top = calculate_etof_xc(
|
|
474
|
+
etof_top, xc_top = calculate_etof_xc(
|
|
475
|
+
de_top, particle_tof[index_top], sensor, "TP", ancillary_files
|
|
476
|
+
)
|
|
435
477
|
etof[index_top] = etof_top
|
|
436
478
|
xc_array[index_top] = xc_top
|
|
437
479
|
|
|
438
480
|
etof_bottom, xc_bottom = calculate_etof_xc(
|
|
439
|
-
de_bottom, particle_tof[index_bottom], sensor, "BT"
|
|
481
|
+
de_bottom, particle_tof[index_bottom], sensor, "BT", ancillary_files
|
|
440
482
|
)
|
|
441
483
|
etof[index_bottom] = etof_bottom
|
|
442
484
|
xc_array[index_bottom] = xc_bottom
|
|
@@ -501,7 +543,7 @@ def get_de_velocity(
|
|
|
501
543
|
|
|
502
544
|
|
|
503
545
|
def get_ssd_tof(
|
|
504
|
-
de_dataset: xarray.Dataset, xf: np.ndarray, sensor: str
|
|
546
|
+
de_dataset: xarray.Dataset, xf: np.ndarray, sensor: str, ancillary_files: dict
|
|
505
547
|
) -> NDArray[np.float64]:
|
|
506
548
|
"""
|
|
507
549
|
Calculate back xb, yb position for the SSDs.
|
|
@@ -527,25 +569,32 @@ def get_ssd_tof(
|
|
|
527
569
|
Front x position (hundredths of a millimeter).
|
|
528
570
|
sensor : str
|
|
529
571
|
Sensor name.
|
|
572
|
+
ancillary_files : dict[Path]
|
|
573
|
+
Ancillary files containing the lookup tables.
|
|
530
574
|
|
|
531
575
|
Returns
|
|
532
576
|
-------
|
|
533
577
|
tof : np.ndarray
|
|
534
578
|
Time of flight (tenths of a nanosecond).
|
|
535
579
|
"""
|
|
536
|
-
_, tof_offset, ssd_number = get_ssd_back_position_and_tof_offset(
|
|
580
|
+
_, tof_offset, ssd_number = get_ssd_back_position_and_tof_offset(
|
|
581
|
+
de_dataset, sensor, ancillary_files
|
|
582
|
+
)
|
|
537
583
|
indices = np.nonzero(np.isin(de_dataset["stop_type"], [StopType.SSD.value]))[0]
|
|
538
584
|
|
|
539
585
|
de_discrete = de_dataset.isel(epoch=indices)["coin_discrete_tdc"]
|
|
540
586
|
|
|
541
|
-
time =
|
|
587
|
+
time = (
|
|
588
|
+
get_image_params("TOFSSDSC", sensor, ancillary_files) * de_discrete.values
|
|
589
|
+
+ tof_offset
|
|
590
|
+
)
|
|
542
591
|
|
|
543
592
|
# The scale factor and offsets, and a multiplier to convert xf to a tof offset.
|
|
544
593
|
# Convert xf to mm by dividing by 100.
|
|
545
594
|
tof = (
|
|
546
595
|
time
|
|
547
|
-
+ get_image_params("TOFSSDTOTOFF", sensor)
|
|
548
|
-
+ xf[indices] / 100 * get_image_params("XFTTOF", sensor)
|
|
596
|
+
+ get_image_params("TOFSSDTOTOFF", sensor, ancillary_files)
|
|
597
|
+
+ xf[indices] / 100 * get_image_params("XFTTOF", sensor, ancillary_files)
|
|
549
598
|
) * 10
|
|
550
599
|
|
|
551
600
|
# Convert TOF to tenths of a nanosecond.
|
|
@@ -575,6 +624,7 @@ def get_de_energy_kev(v: np.ndarray, species: np.ndarray) -> NDArray:
|
|
|
575
624
|
index_hydrogen = np.where(species == 1)
|
|
576
625
|
energy = np.full_like(v2, np.nan)
|
|
577
626
|
|
|
627
|
+
# TODO: we will calculate the energies of the different species here.
|
|
578
628
|
# 1/2 mv^2 in Joules, convert to keV
|
|
579
629
|
energy[index_hydrogen] = (
|
|
580
630
|
0.5 * UltraConstants.MASS_H * v2[index_hydrogen] * UltraConstants.J_KEV
|
|
@@ -589,7 +639,9 @@ def get_energy_pulse_height(
|
|
|
589
639
|
xb: np.ndarray,
|
|
590
640
|
yb: np.ndarray,
|
|
591
641
|
sensor: str,
|
|
592
|
-
|
|
642
|
+
ancillary_files: dict,
|
|
643
|
+
quality_flags: NDArray,
|
|
644
|
+
) -> tuple[NDArray, NDArray]:
|
|
593
645
|
"""
|
|
594
646
|
Calculate the pulse-height energy.
|
|
595
647
|
|
|
@@ -611,6 +663,10 @@ def get_energy_pulse_height(
|
|
|
611
663
|
Y back position (hundredths of a millimeter).
|
|
612
664
|
sensor : str
|
|
613
665
|
Sensor name.
|
|
666
|
+
ancillary_files : dict[Path]
|
|
667
|
+
Ancillary files containing the lookup tables.
|
|
668
|
+
quality_flags : NDArray
|
|
669
|
+
Quality flag to set when there is an outlier.
|
|
614
670
|
|
|
615
671
|
Returns
|
|
616
672
|
-------
|
|
@@ -625,28 +681,59 @@ def get_energy_pulse_height(
|
|
|
625
681
|
ylut = np.zeros(len(stop_type), dtype=np.float64)
|
|
626
682
|
energy_ph = np.zeros(len(stop_type), dtype=np.float64)
|
|
627
683
|
|
|
684
|
+
# Full-length correction arrays
|
|
685
|
+
ph_correction = np.zeros(len(stop_type), dtype=np.float64)
|
|
686
|
+
|
|
628
687
|
# Stop type 1
|
|
629
|
-
xlut[indices_top] = (xb[indices_top] / 100 -
|
|
688
|
+
xlut[indices_top] = (xb[indices_top] / 100 - 24.5 / 2) * 20 / 50 # mm
|
|
630
689
|
ylut[indices_top] = (yb[indices_top] / 100 + 82 / 2) * 32 / 82 # mm
|
|
631
690
|
# Stop type 2
|
|
632
|
-
xlut[indices_bottom] = (xb[indices_bottom] / 100 + 50 +
|
|
691
|
+
xlut[indices_bottom] = (xb[indices_bottom] / 100 + 50 + 24.5 / 2) * 20 / 50 # mm
|
|
633
692
|
ylut[indices_bottom] = (yb[indices_bottom] / 100 + 82 / 2) * 32 / 82 # mm
|
|
634
693
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
"
|
|
638
|
-
|
|
639
|
-
|
|
694
|
+
ph_correction_top, updated_flags_top = get_ph_corrected(
|
|
695
|
+
"ultra45",
|
|
696
|
+
"tp",
|
|
697
|
+
ancillary_files,
|
|
698
|
+
np.round(xlut[indices_top]),
|
|
699
|
+
np.round(ylut[indices_top]),
|
|
700
|
+
quality_flags[indices_top].copy(),
|
|
701
|
+
)
|
|
702
|
+
quality_flags[indices_top] = updated_flags_top
|
|
703
|
+
ph_correction_bottom, updated_flags_bottom = get_ph_corrected(
|
|
704
|
+
"ultra45",
|
|
705
|
+
"bt",
|
|
706
|
+
ancillary_files,
|
|
707
|
+
np.round(xlut[indices_bottom]),
|
|
708
|
+
np.round(ylut[indices_bottom]),
|
|
709
|
+
quality_flags[indices_bottom].copy(),
|
|
710
|
+
)
|
|
711
|
+
quality_flags[indices_bottom] = updated_flags_bottom
|
|
712
|
+
|
|
713
|
+
ph_correction[indices_top] = ph_correction_top / 1024
|
|
714
|
+
ph_correction[indices_bottom] = ph_correction_bottom / 1024
|
|
640
715
|
|
|
641
|
-
energy_ph[
|
|
642
|
-
"
|
|
643
|
-
|
|
644
|
-
|
|
716
|
+
energy_ph[indices_top] = (
|
|
717
|
+
(energy[indices_top] - get_image_params("SPTPPHOFF", sensor, ancillary_files))
|
|
718
|
+
* ph_correction_top
|
|
719
|
+
/ 1024
|
|
720
|
+
)
|
|
721
|
+
|
|
722
|
+
energy_ph[indices_bottom] = (
|
|
723
|
+
(
|
|
724
|
+
energy[indices_bottom]
|
|
725
|
+
- get_image_params("SPBTPHOFF", sensor, ancillary_files)
|
|
726
|
+
)
|
|
727
|
+
* ph_correction_bottom
|
|
728
|
+
/ 1024.0
|
|
729
|
+
)
|
|
645
730
|
|
|
646
|
-
return energy_ph
|
|
731
|
+
return energy_ph, ph_correction
|
|
647
732
|
|
|
648
733
|
|
|
649
|
-
def get_energy_ssd(
|
|
734
|
+
def get_energy_ssd(
|
|
735
|
+
de_dataset: xarray.Dataset, ssd: np.ndarray, ancillary_files: dict
|
|
736
|
+
) -> NDArray[np.float64]:
|
|
650
737
|
"""
|
|
651
738
|
Get SSD energy.
|
|
652
739
|
|
|
@@ -664,6 +751,8 @@ def get_energy_ssd(de_dataset: xarray.Dataset, ssd: np.ndarray) -> NDArray[np.fl
|
|
|
664
751
|
Events dataset.
|
|
665
752
|
ssd : np.ndarray
|
|
666
753
|
SSD number.
|
|
754
|
+
ancillary_files : dict[Path]
|
|
755
|
+
Ancillary files containing the lookup tables.
|
|
667
756
|
|
|
668
757
|
Returns
|
|
669
758
|
-------
|
|
@@ -685,7 +774,7 @@ def get_energy_ssd(de_dataset: xarray.Dataset, ssd: np.ndarray) -> NDArray[np.fl
|
|
|
685
774
|
energy < UltraConstants.COMPOSITE_ENERGY_THRESHOLD
|
|
686
775
|
]
|
|
687
776
|
|
|
688
|
-
energy_norm = get_energy_norm(ssd, composite_energy)
|
|
777
|
+
energy_norm = get_energy_norm(ssd, composite_energy, ancillary_files)
|
|
689
778
|
|
|
690
779
|
return energy_norm
|
|
691
780
|
|
|
@@ -760,14 +849,9 @@ def determine_species(tof: np.ndarray, path_length: np.ndarray, type: str) -> ND
|
|
|
760
849
|
"""
|
|
761
850
|
# Event TOF normalization to Z axis
|
|
762
851
|
ctof, _ = get_ctof(tof, path_length, type)
|
|
763
|
-
#
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
# Assign Species 1 ("H") to bins where cTOF is within the specified range
|
|
767
|
-
species_bin[
|
|
768
|
-
(ctof > UltraConstants.CTOF_SPECIES_MIN)
|
|
769
|
-
& (ctof < UltraConstants.CTOF_SPECIES_MAX)
|
|
770
|
-
] = 1
|
|
852
|
+
# Assign Species 1 ("H") to bins
|
|
853
|
+
# TODO: this is a placeholder for future species assignments.
|
|
854
|
+
species_bin = np.full(len(ctof), 1, dtype=np.uint8)
|
|
771
855
|
|
|
772
856
|
return species_bin
|
|
773
857
|
|
|
@@ -805,6 +889,68 @@ def get_phi_theta(
|
|
|
805
889
|
return np.degrees(phi), np.degrees(theta)
|
|
806
890
|
|
|
807
891
|
|
|
892
|
+
def get_spin_number(de_met: NDArray, de_spin: NDArray) -> NDArray:
|
|
893
|
+
"""
|
|
894
|
+
Get the spin number.
|
|
895
|
+
|
|
896
|
+
Parameters
|
|
897
|
+
----------
|
|
898
|
+
de_met : NDArray
|
|
899
|
+
Mission elapsed time.
|
|
900
|
+
de_spin : NDArray
|
|
901
|
+
Spin number 0-255.
|
|
902
|
+
|
|
903
|
+
Returns
|
|
904
|
+
-------
|
|
905
|
+
assigned_spin_number : NDArray
|
|
906
|
+
Spin number for DE data product.
|
|
907
|
+
"""
|
|
908
|
+
# DE packet data.
|
|
909
|
+
# Since the spin number in the direct events packet
|
|
910
|
+
# is only 8 bits it goes from 0-255.
|
|
911
|
+
# Within a pointing that means we will always have duplicate spin numbers.
|
|
912
|
+
# In other words, different spins will be represented by the same spin number.
|
|
913
|
+
# Just to make certain that we won't accidentally combine
|
|
914
|
+
# multiple spins we need to sort by time here.
|
|
915
|
+
sort_idx = np.argsort(de_met)
|
|
916
|
+
de_met_sorted = de_met[sort_idx]
|
|
917
|
+
de_spin_sorted = de_spin[sort_idx]
|
|
918
|
+
# Here we are finding the start and end indices of each spin in the sorted array.
|
|
919
|
+
is_new_spin = np.concatenate([[True], de_spin_sorted[1:] != de_spin_sorted[:-1]])
|
|
920
|
+
spin_start_indices = np.where(is_new_spin)[0]
|
|
921
|
+
spin_end_indices = np.append(spin_start_indices[1:], len(de_met_sorted))
|
|
922
|
+
|
|
923
|
+
# Universal Spin Table.
|
|
924
|
+
spin_df = get_spin_data()
|
|
925
|
+
# Retrieve the met values of the start of the spin.
|
|
926
|
+
spin_start_mets = spin_df["spin_start_met"].values
|
|
927
|
+
# Retrieve the corresponding spin numbers.
|
|
928
|
+
spin_numbers = spin_df["spin_number"].values
|
|
929
|
+
assigned_spin_number_sorted = np.empty(de_spin_sorted.shape, dtype=np.uint32)
|
|
930
|
+
# These last 8 bits are the same as the spin number in the DE packet.
|
|
931
|
+
# So this will give us choices of which spins are
|
|
932
|
+
# available to assign to the DE data.
|
|
933
|
+
possible_spins = spin_numbers & 0xFF
|
|
934
|
+
|
|
935
|
+
# Assign each group based on time.
|
|
936
|
+
for start, end in zip(spin_start_indices, spin_end_indices):
|
|
937
|
+
# Now that we have the possible spins from the Universal Spin Table,
|
|
938
|
+
# we match the times of those spins to the nearest times in the DE data.
|
|
939
|
+
possible_times = spin_start_mets[possible_spins == de_spin_sorted[start]]
|
|
940
|
+
# Get nearest time for matching spins.
|
|
941
|
+
nearest_idx = np.abs(possible_times - de_met_sorted[start]).argmin()
|
|
942
|
+
nearest_value = possible_times[nearest_idx]
|
|
943
|
+
assigned_spin_number_sorted[start:end] = spin_numbers[
|
|
944
|
+
spin_start_mets == nearest_value
|
|
945
|
+
]
|
|
946
|
+
|
|
947
|
+
# Undo the sort to match original order.
|
|
948
|
+
assigned_spin_number = np.empty_like(assigned_spin_number_sorted)
|
|
949
|
+
assigned_spin_number[sort_idx] = assigned_spin_number_sorted
|
|
950
|
+
|
|
951
|
+
return assigned_spin_number
|
|
952
|
+
|
|
953
|
+
|
|
808
954
|
def get_eventtimes(
|
|
809
955
|
spin: NDArray, phase_angle: NDArray
|
|
810
956
|
) -> tuple[NDArray, NDArray, NDArray]:
|
|
@@ -896,6 +1042,7 @@ def get_fwhm(
|
|
|
896
1042
|
energy: NDArray,
|
|
897
1043
|
phi_inst: NDArray,
|
|
898
1044
|
theta_inst: NDArray,
|
|
1045
|
+
ancillary_files: dict,
|
|
899
1046
|
) -> tuple[NDArray, NDArray]:
|
|
900
1047
|
"""
|
|
901
1048
|
Interpolate phi and theta FWHM values for each event based on start type.
|
|
@@ -912,6 +1059,8 @@ def get_fwhm(
|
|
|
912
1059
|
Instrument-frame azimuth angle for each event.
|
|
913
1060
|
theta_inst : NDArray
|
|
914
1061
|
Instrument-frame elevation angle for each event.
|
|
1062
|
+
ancillary_files : dict
|
|
1063
|
+
Ancillary files containing lookup tables for angular profiles.
|
|
915
1064
|
|
|
916
1065
|
Returns
|
|
917
1066
|
-------
|
|
@@ -922,8 +1071,8 @@ def get_fwhm(
|
|
|
922
1071
|
"""
|
|
923
1072
|
phi_interp = np.full_like(phi_inst, np.nan, dtype=np.float64)
|
|
924
1073
|
theta_interp = np.full_like(theta_inst, np.nan, dtype=np.float64)
|
|
925
|
-
lt_table = get_angular_profiles("left", sensor)
|
|
926
|
-
rt_table = get_angular_profiles("right", sensor)
|
|
1074
|
+
lt_table = get_angular_profiles("left", sensor, ancillary_files)
|
|
1075
|
+
rt_table = get_angular_profiles("right", sensor, ancillary_files)
|
|
927
1076
|
|
|
928
1077
|
# Left start type
|
|
929
1078
|
idx_left = start_type == StartType.Left.value
|
|
@@ -982,3 +1131,92 @@ def get_efficiency(
|
|
|
982
1131
|
)
|
|
983
1132
|
|
|
984
1133
|
return interpolator((theta_inst, phi_inst, energy))
|
|
1134
|
+
|
|
1135
|
+
|
|
1136
|
+
def determine_ebin_pulse_height(
|
|
1137
|
+
energy: np.ndarray, tof: np.ndarray, path_length: np.ndarray
|
|
1138
|
+
) -> NDArray:
|
|
1139
|
+
"""
|
|
1140
|
+
Determine the species for pulse-height events.
|
|
1141
|
+
|
|
1142
|
+
Species is determined from the particle energy and velocity.
|
|
1143
|
+
For velocity, the particle TOF is normalized with respect
|
|
1144
|
+
to a fixed distance dmin between the front and back detectors.
|
|
1145
|
+
The normalized TOF is termed the corrected TOF (ctof).
|
|
1146
|
+
Particle species are determined from
|
|
1147
|
+
the energy and ctof using a lookup table.
|
|
1148
|
+
|
|
1149
|
+
Further description is available on pages 42-44 of
|
|
1150
|
+
IMAP-Ultra Flight Software Specification document
|
|
1151
|
+
(7523-9009_Rev_-.pdf).
|
|
1152
|
+
|
|
1153
|
+
Parameters
|
|
1154
|
+
----------
|
|
1155
|
+
energy : np.ndarray
|
|
1156
|
+
Energy from the PH event (keV).
|
|
1157
|
+
tof : np.ndarray
|
|
1158
|
+
Time of flight of the PH event (tenths of a nanosecond).
|
|
1159
|
+
path_length : np.ndarray
|
|
1160
|
+
Path length (r) (hundredths of a millimeter).
|
|
1161
|
+
|
|
1162
|
+
Returns
|
|
1163
|
+
-------
|
|
1164
|
+
bin : np.array
|
|
1165
|
+
Species bin.
|
|
1166
|
+
"""
|
|
1167
|
+
# PH event TOF normalization to Z axis
|
|
1168
|
+
ctof, _ = get_ctof(tof, path_length, type="PH")
|
|
1169
|
+
# TODO: need lookup tables
|
|
1170
|
+
# placeholder
|
|
1171
|
+
ebin = np.full(len(ctof), 255, dtype=np.uint8)
|
|
1172
|
+
|
|
1173
|
+
return ebin
|
|
1174
|
+
|
|
1175
|
+
|
|
1176
|
+
def determine_ebin_ssd(
|
|
1177
|
+
energy: np.ndarray, tof: np.ndarray, path_length: np.ndarray
|
|
1178
|
+
) -> NDArray:
|
|
1179
|
+
"""
|
|
1180
|
+
Determine the species for SSD events.
|
|
1181
|
+
|
|
1182
|
+
Species is determined from the particle's energy and velocity.
|
|
1183
|
+
For velocity, the particle's TOF is normalized with respect
|
|
1184
|
+
to a fixed distance dmin between the front and back detectors.
|
|
1185
|
+
For SSD events, an adjustment is also made to the path length
|
|
1186
|
+
to account for the shorter distances that such events
|
|
1187
|
+
travel to reach the detector. The normalized TOF is termed
|
|
1188
|
+
the corrected tof (ctof). Particle species are determined from
|
|
1189
|
+
the energy and cTOF using a lookup table.
|
|
1190
|
+
|
|
1191
|
+
Further description is available on pages 42-44 of
|
|
1192
|
+
IMAP-Ultra Flight Software Specification document
|
|
1193
|
+
(7523-9009_Rev_-.pdf).
|
|
1194
|
+
|
|
1195
|
+
Parameters
|
|
1196
|
+
----------
|
|
1197
|
+
energy : np.ndarray
|
|
1198
|
+
Energy from the SSD event (keV).
|
|
1199
|
+
tof : np.ndarray
|
|
1200
|
+
Time of flight of the SSD event (tenths of a nanosecond).
|
|
1201
|
+
path_length : np.ndarray
|
|
1202
|
+
Path length (r) (hundredths of a millimeter).
|
|
1203
|
+
|
|
1204
|
+
Returns
|
|
1205
|
+
-------
|
|
1206
|
+
bin : np.ndarray
|
|
1207
|
+
Species bin.
|
|
1208
|
+
"""
|
|
1209
|
+
# SSD event TOF normalization to Z axis
|
|
1210
|
+
ctof, _ = get_ctof(tof, path_length, type="SSD")
|
|
1211
|
+
|
|
1212
|
+
ebin = np.full(len(ctof), 255, dtype=np.uint8) # placeholder
|
|
1213
|
+
|
|
1214
|
+
# TODO: get these lookup tables
|
|
1215
|
+
# if r < get_image_params("PathSteepThresh"):
|
|
1216
|
+
# # bin = ExTOFSpeciesSteep[energy, ctof]
|
|
1217
|
+
# elif r < get_image_params("PathMediumThresh"):
|
|
1218
|
+
# # bin = ExTOFSpeciesMedium[energy, ctof]
|
|
1219
|
+
# else:
|
|
1220
|
+
# # bin = ExTOFSpeciesFlat[energy, ctof]
|
|
1221
|
+
|
|
1222
|
+
return ebin
|