imap-processing 0.16.1__py3-none-any.whl → 0.17.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.

Files changed (46) hide show
  1. imap_processing/_version.py +2 -2
  2. imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +24 -0
  3. imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +24 -0
  4. imap_processing/cdf/config/imap_hi_variable_attrs.yaml +8 -8
  5. imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +1 -1
  6. imap_processing/cdf/config/imap_hit_l2_variable_attrs.yaml +394 -411
  7. imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +9 -9
  8. imap_processing/cdf/config/imap_idex_l2b_variable_attrs.yaml +150 -57
  9. imap_processing/cdf/config/imap_swapi_variable_attrs.yaml +19 -0
  10. imap_processing/cdf/config/imap_swe_l1b_variable_attrs.yaml +20 -0
  11. imap_processing/cdf/config/imap_swe_l2_variable_attrs.yaml +39 -0
  12. imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +108 -0
  13. imap_processing/cdf/config/imap_ultra_l1a_variable_attrs.yaml +103 -2
  14. imap_processing/cdf/utils.py +7 -1
  15. imap_processing/cli.py +14 -8
  16. imap_processing/codice/codice_l1a.py +89 -30
  17. imap_processing/hi/hi_l1a.py +4 -4
  18. imap_processing/hi/hi_l1b.py +2 -2
  19. imap_processing/hi/packet_definitions/TLM_HI_COMBINED_SCI.xml +218 -38
  20. imap_processing/hit/hit_utils.py +2 -2
  21. imap_processing/hit/l0/decom_hit.py +2 -1
  22. imap_processing/hit/l2/hit_l2.py +2 -1
  23. imap_processing/ialirt/l0/process_codice.py +4 -34
  24. imap_processing/idex/idex_constants.py +7 -0
  25. imap_processing/idex/idex_l2b.py +372 -55
  26. imap_processing/lo/l0/lo_star_sensor.py +48 -0
  27. imap_processing/lo/l1a/lo_l1a.py +32 -32
  28. imap_processing/mag/l0/decom_mag.py +9 -6
  29. imap_processing/mag/l0/mag_l0_data.py +46 -0
  30. imap_processing/swapi/l1/swapi_l1.py +12 -2
  31. imap_processing/swapi/l2/swapi_l2.py +7 -6
  32. imap_processing/swe/l1b/swe_l1b.py +9 -0
  33. imap_processing/swe/l2/swe_l2.py +111 -17
  34. imap_processing/ultra/l0/decom_tools.py +13 -6
  35. imap_processing/ultra/l0/decom_ultra.py +190 -4
  36. imap_processing/ultra/l0/ultra_utils.py +184 -3
  37. imap_processing/ultra/l1a/ultra_l1a.py +52 -4
  38. imap_processing/ultra/packet_definitions/ULTRA_SCI_COMBINED.xml +3 -3
  39. imap_processing/utils.py +20 -42
  40. {imap_processing-0.16.1.dist-info → imap_processing-0.17.0.dist-info}/METADATA +1 -1
  41. {imap_processing-0.16.1.dist-info → imap_processing-0.17.0.dist-info}/RECORD +44 -45
  42. imap_processing/lo/l0/data_classes/star_sensor.py +0 -98
  43. imap_processing/lo/l0/utils/lo_base.py +0 -57
  44. {imap_processing-0.16.1.dist-info → imap_processing-0.17.0.dist-info}/LICENSE +0 -0
  45. {imap_processing-0.16.1.dist-info → imap_processing-0.17.0.dist-info}/WHEEL +0 -0
  46. {imap_processing-0.16.1.dist-info → imap_processing-0.17.0.dist-info}/entry_points.txt +0 -0
@@ -15,8 +15,19 @@ from imap_processing.ultra.l0.decom_tools import (
15
15
  read_image_raw_events_binary,
16
16
  )
17
17
  from imap_processing.ultra.l0.ultra_utils import (
18
+ CMD_ECHO_MAP,
19
+ ENERGY_EVENT_FIELD_RANGES,
20
+ ENERGY_RATES_KEYS,
18
21
  EVENT_FIELD_RANGES,
19
22
  RATES_KEYS,
23
+ ULTRA_ENERGY_EVENTS,
24
+ ULTRA_ENERGY_RATES,
25
+ ULTRA_ENERGY_SPECTRA,
26
+ ULTRA_EVENTS,
27
+ ULTRA_PRI_1_EVENTS,
28
+ ULTRA_PRI_2_EVENTS,
29
+ ULTRA_PRI_3_EVENTS,
30
+ ULTRA_PRI_4_EVENTS,
20
31
  ULTRA_RATES,
21
32
  ULTRA_TOF,
22
33
  )
@@ -136,7 +147,7 @@ def get_event_id(shcoarse: NDArray) -> NDArray:
136
147
  return np.array(event_ids, dtype=np.int64)
137
148
 
138
149
 
139
- def process_ultra_events(ds: xr.Dataset) -> xr.Dataset:
150
+ def process_ultra_events(ds: xr.Dataset, apid: int) -> xr.Dataset:
140
151
  """
141
152
  Unpack and decode Ultra EVENTS packets.
142
153
 
@@ -144,12 +155,28 @@ def process_ultra_events(ds: xr.Dataset) -> xr.Dataset:
144
155
  ----------
145
156
  ds : xarray.Dataset
146
157
  Events dataset.
158
+ apid : int
159
+ APID of the events dataset.
147
160
 
148
161
  Returns
149
162
  -------
150
163
  ds : xarray.Dataset
151
164
  Dataset containing the decoded and decompressed data.
152
165
  """
166
+ all_event_apids = set(
167
+ ULTRA_EVENTS.apid
168
+ + ULTRA_PRI_1_EVENTS.apid
169
+ + ULTRA_PRI_2_EVENTS.apid
170
+ + ULTRA_PRI_3_EVENTS.apid
171
+ + ULTRA_PRI_4_EVENTS.apid
172
+ )
173
+ if apid in all_event_apids:
174
+ field_ranges = EVENT_FIELD_RANGES
175
+ elif apid in ULTRA_ENERGY_EVENTS.apid:
176
+ field_ranges = ENERGY_EVENT_FIELD_RANGES
177
+ else:
178
+ raise ValueError(f"APID {apid} not recognized for Ultra events processing.")
179
+
153
180
  all_events = []
154
181
  all_indices = []
155
182
 
@@ -160,7 +187,7 @@ def process_ultra_events(ds: xr.Dataset) -> xr.Dataset:
160
187
  field: attrs.get_variable_attributes(field).get(
161
188
  "FILLVAL", np.iinfo(np.int64).min
162
189
  )
163
- for field in EVENT_FIELD_RANGES
190
+ for field in field_ranges
164
191
  }
165
192
 
166
193
  counts = ds["count"].values
@@ -173,7 +200,9 @@ def process_ultra_events(ds: xr.Dataset) -> xr.Dataset:
173
200
  else:
174
201
  # Here there are multiple images in a single packet,
175
202
  # so we need to loop through each image and decompress it.
176
- event_data_list = read_image_raw_events_binary(eventdata_array[i], count)
203
+ event_data_list = read_image_raw_events_binary(
204
+ eventdata_array[i], count, field_ranges
205
+ )
177
206
  all_events.extend(event_data_list)
178
207
  # Keep track of how many times does the event occurred at this epoch.
179
208
  all_indices.extend([i] * count)
@@ -189,7 +218,7 @@ def process_ultra_events(ds: xr.Dataset) -> xr.Dataset:
189
218
  }
190
219
 
191
220
  # Add the event data to the expanded dataset.
192
- for key in EVENT_FIELD_RANGES:
221
+ for key in field_ranges:
193
222
  expanded_data[key] = np.array([event[key] for event in all_events])
194
223
 
195
224
  event_ids = get_event_id(expanded_data["shcoarse"])
@@ -242,3 +271,160 @@ def process_ultra_rates(ds: xr.Dataset) -> xr.Dataset:
242
271
  ds[key] = xr.DataArray(np.array(values), dims=["epoch"])
243
272
 
244
273
  return ds
274
+
275
+
276
+ def process_ultra_energy_rates(ds: xr.Dataset) -> xr.Dataset:
277
+ """
278
+ Unpack and decode Ultra ENERGY RATES packets.
279
+
280
+ Parameters
281
+ ----------
282
+ ds : xarray.Dataset
283
+ Energy rates dataset.
284
+
285
+ Returns
286
+ -------
287
+ dataset : xarray.Dataset
288
+ Dataset containing the decoded and decompressed data.
289
+ """
290
+ decom_data = defaultdict(list)
291
+
292
+ for rate in ds["ratedata"]:
293
+ raw_binary_string = convert_to_binary_string(rate.item())
294
+ decompressed_data = decompress_binary(
295
+ raw_binary_string,
296
+ cast(int, ULTRA_ENERGY_RATES.width),
297
+ cast(int, ULTRA_ENERGY_RATES.block),
298
+ cast(int, ULTRA_ENERGY_RATES.len_array),
299
+ cast(int, ULTRA_ENERGY_RATES.mantissa_bit_length),
300
+ )
301
+
302
+ for index in range(cast(int, ULTRA_ENERGY_RATES.len_array)):
303
+ decom_data[ENERGY_RATES_KEYS[index]].append(decompressed_data[index])
304
+
305
+ for key, values in decom_data.items():
306
+ ds[key] = xr.DataArray(np.array(values), dims=["epoch"])
307
+
308
+ return ds
309
+
310
+
311
+ def process_ultra_energy_spectra(ds: xr.Dataset) -> xr.Dataset:
312
+ """
313
+ Unpack and decode Ultra ENERGY SPECTRA packets.
314
+
315
+ Parameters
316
+ ----------
317
+ ds : xarray.Dataset
318
+ Energy rates dataset.
319
+
320
+ Returns
321
+ -------
322
+ dataset : xarray.Dataset
323
+ Dataset containing the decoded and decompressed data.
324
+ """
325
+ energy_spectra = []
326
+
327
+ for rate in ds["compdata"]:
328
+ raw_binary_string = convert_to_binary_string(rate.item())
329
+ decompressed_data = decompress_binary(
330
+ raw_binary_string,
331
+ cast(int, ULTRA_ENERGY_SPECTRA.width),
332
+ cast(int, ULTRA_ENERGY_SPECTRA.block),
333
+ cast(int, ULTRA_ENERGY_SPECTRA.len_array),
334
+ cast(int, ULTRA_ENERGY_SPECTRA.mantissa_bit_length),
335
+ )
336
+
337
+ energy_spectra.append(decompressed_data)
338
+
339
+ energy_spectra = np.array(energy_spectra)
340
+
341
+ ds["ssd_sum"] = xr.DataArray(
342
+ energy_spectra,
343
+ dims=["epoch", "energyspectrastate"],
344
+ coords={"epoch": ds["epoch"], "energyspectrastate": np.arange(16)},
345
+ )
346
+
347
+ return ds
348
+
349
+
350
+ def process_ultra_cmd_echo(ds: xr.Dataset) -> xr.Dataset:
351
+ """
352
+ Unpack and decode Ultra CMD ECHO packets.
353
+
354
+ Parameters
355
+ ----------
356
+ ds : xarray.Dataset
357
+ Energy rates dataset.
358
+
359
+ Returns
360
+ -------
361
+ dataset : xarray.Dataset
362
+ Dataset containing the decoded and decompressed data.
363
+ """
364
+ descriptions = []
365
+
366
+ fill = 0xFF
367
+ max_len = 10
368
+ arg_array = np.full((len(ds["epoch"]), max_len), fill, dtype=np.uint8)
369
+
370
+ for i, arg in enumerate(ds["args"].values):
371
+ # Converts to the numeric representations of each byte.
372
+ arg_array[i, : len(arg)] = np.frombuffer(arg, dtype=np.uint8)
373
+
374
+ # Default to "FILL" for unlisted values
375
+ for result in ds["result"].values:
376
+ descriptions.append(CMD_ECHO_MAP.get(result, "FILL"))
377
+
378
+ ds["arguments"] = xr.DataArray(
379
+ arg_array,
380
+ dims=["epoch", "arg_index"],
381
+ coords={
382
+ "epoch": ds["epoch"],
383
+ "arg_index": np.arange(10),
384
+ },
385
+ )
386
+
387
+ ds["result_description"] = xr.DataArray(
388
+ np.array(descriptions),
389
+ dims=["epoch"],
390
+ coords={"epoch": ds["epoch"]},
391
+ )
392
+
393
+ ds = ds.drop_vars(["args", "result"])
394
+
395
+ return ds
396
+
397
+
398
+ def process_ultra_macros_checksum(ds: xr.Dataset) -> xr.Dataset:
399
+ """
400
+ Unpack and decode Ultra MACROS CHECKSUM packets.
401
+
402
+ Parameters
403
+ ----------
404
+ ds : xarray.Dataset
405
+ Dataset containing macro checksums.
406
+
407
+ Returns
408
+ -------
409
+ dataset : xarray.Dataset
410
+ Dataset with unpacked and decoded checksum values.
411
+ """
412
+ # big endian uint16
413
+ packed_dtype = np.dtype(">u2")
414
+ fill = np.iinfo(packed_dtype).max
415
+ n_epochs = ds.sizes["epoch"]
416
+ max_len = 256
417
+
418
+ checksum_array = np.full((n_epochs, max_len), fill)
419
+
420
+ for i, checksum in enumerate(ds["checksums"]):
421
+ checksum_array[i, :] = np.frombuffer(checksum.item(), dtype=packed_dtype)
422
+
423
+ ds["checksum"] = xr.DataArray(
424
+ checksum_array,
425
+ dims=["epoch", "checksum_index"],
426
+ coords={"epoch": ds["epoch"], "checksum_index": np.arange(max_len)},
427
+ )
428
+ ds = ds.drop_vars(["checksums"])
429
+
430
+ return ds
@@ -40,6 +40,30 @@ ULTRA_RATES = PacketProperties(
40
40
  len_array=48,
41
41
  mantissa_bit_length=12,
42
42
  )
43
+ ULTRA_ENERGY_RATES = PacketProperties(
44
+ apid=[882, 946],
45
+ logical_source=[
46
+ "imap_ultra_l1a_45sensor-energy-rates",
47
+ "imap_ultra_l1a_90sensor-energy-rates",
48
+ ],
49
+ addition_to_logical_desc="Energy Rates",
50
+ width=5,
51
+ block=16,
52
+ len_array=11,
53
+ mantissa_bit_length=12,
54
+ )
55
+ ULTRA_ENERGY_SPECTRA = PacketProperties(
56
+ apid=[889, 953],
57
+ logical_source=[
58
+ "imap_ultra_l1a_45sensor-energy-spectra",
59
+ "imap_ultra_l1a_90sensor-energy-spectra",
60
+ ],
61
+ addition_to_logical_desc="Energy Spectra",
62
+ width=4,
63
+ block=16,
64
+ len_array=1,
65
+ mantissa_bit_length=5,
66
+ )
43
67
  ULTRA_TOF = PacketProperties(
44
68
  apid=[883, 947],
45
69
  logical_source=[
@@ -61,20 +85,96 @@ ULTRA_EVENTS = PacketProperties(
61
85
  len_array=None,
62
86
  mantissa_bit_length=None,
63
87
  )
88
+ ULTRA_ENERGY_EVENTS = PacketProperties(
89
+ apid=[897, 961],
90
+ logical_source=[
91
+ "imap_ultra_l1a_45sensor-energy-de",
92
+ "imap_ultra_l1a_90sensor-energy-de",
93
+ ],
94
+ addition_to_logical_desc="Single Energy Events",
95
+ width=None,
96
+ block=None,
97
+ len_array=None,
98
+ mantissa_bit_length=None,
99
+ )
100
+ ULTRA_MACROS_CHECKSUM = PacketProperties(
101
+ apid=[872, 936],
102
+ logical_source=[
103
+ "imap_ultra_l1a_45sensor-macroschecksum",
104
+ "imap_ultra_l1a_90sensor-macroschecksum",
105
+ ],
106
+ addition_to_logical_desc="Macros Checksum",
107
+ width=None,
108
+ block=None,
109
+ len_array=None,
110
+ mantissa_bit_length=None,
111
+ )
112
+ ULTRA_PRI_1_EVENTS = PacketProperties(
113
+ apid=[898, 962],
114
+ logical_source=[
115
+ "imap_ultra_l1a_45sensor-priority-1-de",
116
+ "imap_ultra_l1a_90sensor-priority-1-de",
117
+ ],
118
+ addition_to_logical_desc="Primary 1 Events",
119
+ width=None,
120
+ block=None,
121
+ len_array=None,
122
+ mantissa_bit_length=None,
123
+ )
124
+ ULTRA_PRI_2_EVENTS = PacketProperties(
125
+ apid=[899, 963],
126
+ logical_source=[
127
+ "imap_ultra_l1a_45sensor-priority-2-de",
128
+ "imap_ultra_l1a_90sensor-priority-2-de",
129
+ ],
130
+ addition_to_logical_desc="Primary 2 Events",
131
+ width=None,
132
+ block=None,
133
+ len_array=None,
134
+ mantissa_bit_length=None,
135
+ )
136
+ ULTRA_PRI_3_EVENTS = PacketProperties(
137
+ apid=[900, 964],
138
+ logical_source=[
139
+ "imap_ultra_l1a_45sensor-priority-3-de",
140
+ "imap_ultra_l1a_90sensor-priority-3-de",
141
+ ],
142
+ addition_to_logical_desc="Primary 3 Events",
143
+ width=None,
144
+ block=None,
145
+ len_array=None,
146
+ mantissa_bit_length=None,
147
+ )
148
+ ULTRA_PRI_4_EVENTS = PacketProperties(
149
+ apid=[901, 965],
150
+ logical_source=[
151
+ "imap_ultra_l1a_45sensor-priority-4-de",
152
+ "imap_ultra_l1a_90sensor-priority-4-de",
153
+ ],
154
+ addition_to_logical_desc="Primary 4 Events",
155
+ width=None,
156
+ block=None,
157
+ len_array=None,
158
+ mantissa_bit_length=None,
159
+ )
64
160
  ULTRA_HK = PacketProperties(
65
161
  apid=[
66
162
  866,
67
163
  867,
164
+ 868,
68
165
  869,
69
166
  870,
167
+ 871,
70
168
  873,
71
169
  874,
72
170
  876,
73
171
  877,
74
172
  930,
75
173
  931,
174
+ 932,
76
175
  933,
77
176
  934,
177
+ 935,
78
178
  937,
79
179
  938,
80
180
  940,
@@ -83,16 +183,20 @@ ULTRA_HK = PacketProperties(
83
183
  logical_source=[
84
184
  "imap_ultra_l1a_45sensor-alarm",
85
185
  "imap_ultra_l1a_45sensor-memchecksum",
186
+ "imap_ultra_l1a_45sensor-memdump",
86
187
  "imap_ultra_l1a_45sensor-status",
87
188
  "imap_ultra_l1a_45sensor-bootstatus",
189
+ "imap_ultra_l1a_45sensor-macrodump",
88
190
  "imap_ultra_l1a_45sensor-monitorlimits",
89
191
  "imap_ultra_l1a_45sensor-params",
90
192
  "imap_ultra_l1a_45sensor-scauto",
91
193
  "imap_ultra_l1a_45sensor-imgparams",
92
194
  "imap_ultra_l1a_90sensor-alarm",
93
195
  "imap_ultra_l1a_90sensor-memchecksum",
196
+ "imap_ultra_l1a_90sensor-memdump",
94
197
  "imap_ultra_l1a_90sensor-status",
95
198
  "imap_ultra_l1a_90sensor-bootstatus",
199
+ "imap_ultra_l1a_90sensor-macrodump",
96
200
  "imap_ultra_l1a_90sensor-monitorlimits",
97
201
  "imap_ultra_l1a_90sensor-params",
98
202
  "imap_ultra_l1a_90sensor-scauto",
@@ -119,7 +223,21 @@ ULTRA_CMD_TEXT = PacketProperties(
119
223
  len_array=None,
120
224
  mantissa_bit_length=None,
121
225
  )
122
-
226
+ ULTRA_CMD_ECHO = PacketProperties(
227
+ apid=[
228
+ 865,
229
+ 929,
230
+ ],
231
+ logical_source=[
232
+ "imap_ultra_l1a_45sensor-cmdecho",
233
+ "imap_ultra_l1a_90sensor-cmdecho",
234
+ ],
235
+ addition_to_logical_desc="Command echo",
236
+ width=None,
237
+ block=None,
238
+ len_array=None,
239
+ mantissa_bit_length=None,
240
+ )
123
241
 
124
242
  # Module-level constant for event field ranges
125
243
  EVENT_FIELD_RANGES = {
@@ -206,6 +324,20 @@ EVENT_FIELD_RANGES = {
206
324
  "phase_angle": (156, 166),
207
325
  }
208
326
 
327
+ # Module-level constant for event field ranges
328
+ ENERGY_EVENT_FIELD_RANGES = {
329
+ # Stop Type
330
+ "stop_type": (0, 4),
331
+ # Energy/Pulse Height
332
+ "energy_ph": (4, 16),
333
+ # Pulse Width
334
+ "pulse_width": (16, 27),
335
+ # Bin
336
+ "bin": (27, 31),
337
+ # Phase Angle
338
+ "phase_angle": (31, 41),
339
+ }
340
+
209
341
 
210
342
  RATES_KEYS = [
211
343
  # Start Right Full Constant Fraction Discriminator (CFD) Pulses
@@ -323,8 +455,55 @@ RATES_KEYS = [
323
455
  # "discarded_events"
324
456
  ]
325
457
 
458
+ ENERGY_RATES_KEYS = [
459
+ # SSD0 Energy LED
460
+ "ssd0_energy_led",
461
+ # SSD1 Energy LED
462
+ "ssd1_energy_led",
463
+ # SSD2 Energy LED
464
+ "ssd2_energy_led",
465
+ # SSD3 Energy LED
466
+ "ssd3_energy_led",
467
+ # SSD4 Energy LED
468
+ "ssd4_energy_led",
469
+ # SSD5 Energy LED
470
+ "ssd5_energy_led",
471
+ # SSD6 Energy LED
472
+ "ssd6_energy_led",
473
+ # SSD7 Energy LED
474
+ "ssd7_energy_led",
475
+ # Event Active Time
476
+ "event_active_time",
477
+ # FIFO Valid Events
478
+ "fifo_valid_events",
479
+ # Processed Events
480
+ "processed_events",
481
+ ]
482
+
483
+ ENERGY_SPECTRA_KEYS = [
484
+ # Sum of the 8 SSDs
485
+ "ssd_sum",
486
+ ]
487
+
488
+ # Map of command echo fields
489
+ CMD_ECHO_MAP = {
490
+ 0x00: "No error command executed",
491
+ 0x01: "No error command appended to macro",
492
+ 0x02: "Unknown opcode or insufficient arguments",
493
+ 0x03: "Bad argument",
494
+ 0x04: "Cannot run macro; no contexts",
495
+ 0x05: "Cannot be used outside of a macro",
496
+ 0x06: "Macro compilation error",
497
+ 0x07: "Macro not killed (not running?)",
498
+ 0x08: "Cannot boot program; bad checksum",
499
+ 0x09: "Cannot restore macros; bad checksum",
500
+ 0x0A: "Cannot load memory; write disabled",
501
+ 0x10: "HV goal greater than limit",
502
+ 0x11: "Shutter deployment disabled",
503
+ }
504
+
326
505
 
327
- def parse_event(event_binary: str) -> dict:
506
+ def parse_event(event_binary: str, field_ranges: dict) -> dict:
328
507
  """
329
508
  Parse a binary string representing a single event.
330
509
 
@@ -332,6 +511,8 @@ def parse_event(event_binary: str) -> dict:
332
511
  ----------
333
512
  event_binary : str
334
513
  Event binary string.
514
+ field_ranges : dict
515
+ The field ranges for the event data.
335
516
 
336
517
  Returns
337
518
  -------
@@ -339,7 +520,7 @@ def parse_event(event_binary: str) -> dict:
339
520
  Dict of the fields for a single event.
340
521
  """
341
522
  fields_dict = {}
342
- for field, (start, end) in EVENT_FIELD_RANGES.items():
523
+ for field, (start, end) in field_ranges.items():
343
524
  field_value = int(event_binary[start:end], 2)
344
525
  fields_dict[field] = field_value
345
526
  return fields_dict
@@ -8,15 +8,28 @@ import xarray as xr
8
8
  from imap_processing import imap_module_directory
9
9
  from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
10
10
  from imap_processing.ultra.l0.decom_ultra import (
11
+ process_ultra_cmd_echo,
12
+ process_ultra_energy_rates,
13
+ process_ultra_energy_spectra,
11
14
  process_ultra_events,
15
+ process_ultra_macros_checksum,
12
16
  process_ultra_rates,
13
17
  process_ultra_tof,
14
18
  )
15
19
  from imap_processing.ultra.l0.ultra_utils import (
16
20
  ULTRA_AUX,
21
+ ULTRA_CMD_ECHO,
17
22
  ULTRA_CMD_TEXT,
23
+ ULTRA_ENERGY_EVENTS,
24
+ ULTRA_ENERGY_RATES,
25
+ ULTRA_ENERGY_SPECTRA,
18
26
  ULTRA_EVENTS,
19
27
  ULTRA_HK,
28
+ ULTRA_MACROS_CHECKSUM,
29
+ ULTRA_PRI_1_EVENTS,
30
+ ULTRA_PRI_2_EVENTS,
31
+ ULTRA_PRI_3_EVENTS,
32
+ ULTRA_PRI_4_EVENTS,
20
33
  ULTRA_RATES,
21
34
  ULTRA_TOF,
22
35
  )
@@ -25,7 +38,9 @@ from imap_processing.utils import packet_file_to_datasets
25
38
  logger = logging.getLogger(__name__)
26
39
 
27
40
 
28
- def ultra_l1a(packet_file: str, apid_input: Optional[int] = None) -> list[xr.Dataset]:
41
+ def ultra_l1a( # noqa: PLR0912
42
+ packet_file: str, apid_input: Optional[int] = None
43
+ ) -> list[xr.Dataset]:
29
44
  """
30
45
  Will process ULTRA L0 data into L1A CDF files at output_filepath.
31
46
 
@@ -59,6 +74,19 @@ def ultra_l1a(packet_file: str, apid_input: Optional[int] = None) -> list[xr.Dat
59
74
  else:
60
75
  apids = list(datasets_by_apid.keys())
61
76
 
77
+ all_event_apids = {
78
+ apid: group.logical_source[i]
79
+ for group in [
80
+ ULTRA_EVENTS,
81
+ ULTRA_ENERGY_EVENTS,
82
+ ULTRA_PRI_1_EVENTS,
83
+ ULTRA_PRI_2_EVENTS,
84
+ ULTRA_PRI_3_EVENTS,
85
+ ULTRA_PRI_4_EVENTS,
86
+ ]
87
+ for i, apid in enumerate(group.apid)
88
+ }
89
+
62
90
  # Update dataset global attributes
63
91
  attr_mgr = ImapCdfAttributes()
64
92
  attr_mgr.add_instrument_global_attrs("ultra")
@@ -75,12 +103,29 @@ def ultra_l1a(packet_file: str, apid_input: Optional[int] = None) -> list[xr.Dat
75
103
  decom_ultra_dataset = process_ultra_rates(datasets_by_apid[apid])
76
104
  decom_ultra_dataset = decom_ultra_dataset.drop_vars("fastdata_00")
77
105
  gattr_key = ULTRA_RATES.logical_source[ULTRA_RATES.apid.index(apid)]
78
- elif apid in ULTRA_EVENTS.apid:
79
- decom_ultra_dataset = process_ultra_events(datasets_by_apid[apid])
80
- gattr_key = ULTRA_EVENTS.logical_source[ULTRA_EVENTS.apid.index(apid)]
106
+ elif apid in ULTRA_ENERGY_RATES.apid:
107
+ decom_ultra_dataset = process_ultra_energy_rates(datasets_by_apid[apid])
108
+ decom_ultra_dataset = decom_ultra_dataset.drop_vars("ratedata")
109
+ gattr_key = ULTRA_ENERGY_RATES.logical_source[
110
+ ULTRA_ENERGY_RATES.apid.index(apid)
111
+ ]
112
+ elif apid in all_event_apids:
113
+ decom_ultra_dataset = process_ultra_events(datasets_by_apid[apid], apid)
114
+ gattr_key = all_event_apids[apid]
81
115
  # Add coordinate attributes
82
116
  attrs = attr_mgr.get_variable_attributes("event_id")
83
117
  decom_ultra_dataset.coords["event_id"].attrs.update(attrs)
118
+ elif apid in ULTRA_ENERGY_SPECTRA.apid:
119
+ decom_ultra_dataset = process_ultra_energy_spectra(datasets_by_apid[apid])
120
+ decom_ultra_dataset = decom_ultra_dataset.drop_vars("compdata")
121
+ gattr_key = ULTRA_ENERGY_SPECTRA.logical_source[
122
+ ULTRA_ENERGY_SPECTRA.apid.index(apid)
123
+ ]
124
+ elif apid in ULTRA_MACROS_CHECKSUM.apid:
125
+ decom_ultra_dataset = process_ultra_macros_checksum(datasets_by_apid[apid])
126
+ gattr_key = ULTRA_MACROS_CHECKSUM.logical_source[
127
+ ULTRA_MACROS_CHECKSUM.apid.index(apid)
128
+ ]
84
129
  elif apid in ULTRA_HK.apid:
85
130
  decom_ultra_dataset = datasets_by_apid[apid]
86
131
  gattr_key = ULTRA_HK.logical_source[ULTRA_HK.apid.index(apid)]
@@ -97,6 +142,9 @@ def ultra_l1a(packet_file: str, apid_input: Optional[int] = None) -> list[xr.Dat
97
142
  coords={"epoch": decom_ultra_dataset["epoch"]},
98
143
  )
99
144
  gattr_key = ULTRA_CMD_TEXT.logical_source[ULTRA_CMD_TEXT.apid.index(apid)]
145
+ elif apid in ULTRA_CMD_ECHO.apid:
146
+ decom_ultra_dataset = process_ultra_cmd_echo(datasets_by_apid[apid])
147
+ gattr_key = ULTRA_CMD_ECHO.logical_source[ULTRA_CMD_ECHO.apid.index(apid)]
100
148
  else:
101
149
  logger.error(f"APID {apid} not recognized.")
102
150
  continue
@@ -4726,15 +4726,15 @@
4726
4726
  <xtce:IntegerParameterType name="U45_IMG_ENA_PHXTOF_HI_TIME.SPIN" signed="false">
4727
4727
  <xtce:IntegerDataEncoding sizeInBits="8" encoding="unsigned" />
4728
4728
  </xtce:IntegerParameterType>
4729
+ <xtce:IntegerParameterType name="U45_IMG_ENA_PHXTOF_HI_TIME.ABORTFLAG" signed="false">
4730
+ <xtce:IntegerDataEncoding sizeInBits="1" encoding="unsigned" />
4731
+ </xtce:IntegerParameterType>
4729
4732
  <xtce:IntegerParameterType name="U45_IMG_ENA_PHXTOF_HI_TIME.STARTDELAY" signed="false">
4730
4733
  <xtce:IntegerDataEncoding sizeInBits="15" encoding="unsigned" />
4731
4734
  </xtce:IntegerParameterType>
4732
4735
  <xtce:IntegerParameterType name="U45_IMG_ENA_PHXTOF_HI_TIME.P00" signed="false">
4733
4736
  <xtce:IntegerDataEncoding sizeInBits="8" encoding="unsigned" />
4734
4737
  </xtce:IntegerParameterType>
4735
- <xtce:IntegerParameterType name="U45_IMG_ENA_PHXTOF_HI_TIME.ABORTFLAG" signed="false">
4736
- <xtce:IntegerDataEncoding sizeInBits="8" encoding="unsigned" />
4737
- </xtce:IntegerParameterType>
4738
4738
  <xtce:BinaryParameterType name="U45_IMG_ENA_PHXTOF_HI_TIME.PACKETDATA">
4739
4739
  <xtce:BinaryDataEncoding bitOrder="mostSignificantBitFirst">
4740
4740
  <xtce:SizeInBits>
imap_processing/utils.py CHANGED
@@ -15,48 +15,6 @@ from imap_processing.spice.time import met_to_ttj2000ns
15
15
  logger = logging.getLogger(__name__)
16
16
 
17
17
 
18
- def sort_by_time(packets: list, time_key: str) -> list:
19
- """
20
- Sort packets by specified key.
21
-
22
- Parameters
23
- ----------
24
- packets : list
25
- Decom data packets.
26
- time_key : str
27
- Key to sort by. Must be a key in the packets data dictionary.
28
- e.g. "SHCOARSE" or "MET_TIME" or "ACQ_START_COARSE".
29
-
30
- Returns
31
- -------
32
- sorted_packets : list
33
- Sorted packets.
34
- """
35
- sorted_packets = sorted(packets, key=lambda x: x[time_key])
36
- return sorted_packets
37
-
38
-
39
- def group_by_apid(packets: list) -> dict:
40
- """
41
- Group data by apid.
42
-
43
- Parameters
44
- ----------
45
- packets : list
46
- Packet list.
47
-
48
- Returns
49
- -------
50
- grouped_packets : dict
51
- Grouped data by apid.
52
- """
53
- grouped_packets: dict[list] = collections.defaultdict(list)
54
- for packet in packets:
55
- apid = packet["PKT_APID"]
56
- grouped_packets.setdefault(apid, []).append(packet)
57
- return grouped_packets
58
-
59
-
60
18
  def convert_raw_to_eu(
61
19
  dataset: xr.Dataset,
62
20
  conversion_table_path: str,
@@ -347,6 +305,26 @@ def packet_file_to_datasets(
347
305
  coords={"epoch": time_data},
348
306
  )
349
307
  ds = ds.sortby("epoch")
308
+ # We may get duplicate packets within the packet file if packets were
309
+ # ingested multiple times by the POC. We want to drop packets where
310
+ # apid, epoch, and src_seq_ctr are the same.
311
+
312
+ # xarray only supports dropping duplicates by index, so we instead go
313
+ # to pandas multi-index dataframe to identify the unique positions
314
+ unique_indices = (
315
+ ds[["src_seq_ctr"]]
316
+ .to_dataframe()
317
+ .reset_index()
318
+ .drop_duplicates()
319
+ .index.values
320
+ )
321
+ nduplicates = len(ds["epoch"]) - len(unique_indices)
322
+ if nduplicates != 0:
323
+ logger.warning(
324
+ f"Found [{nduplicates}] duplicate packets for APID {apid}. "
325
+ "Dropping duplicate packets and continuing processing."
326
+ )
327
+ ds = ds.isel(epoch=unique_indices)
350
328
 
351
329
  # Strip any leading characters before "." from the field names which was due
352
330
  # to the packet_name being a part of the variable name in the XTCE definition