imap-processing 0.8.0__py3-none-any.whl → 0.9.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 (99) hide show
  1. imap_processing/_version.py +2 -2
  2. imap_processing/ccsds/excel_to_xtce.py +2 -0
  3. imap_processing/cdf/config/imap_hi_variable_attrs.yaml +100 -1
  4. imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +14 -0
  5. imap_processing/cdf/config/imap_hit_l1a_variable_attrs.yaml +63 -1
  6. imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +7 -0
  7. imap_processing/cdf/config/imap_idex_l1a_variable_attrs.yaml +574 -231
  8. imap_processing/cdf/config/imap_idex_l1b_variable_attrs.yaml +326 -0
  9. imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +33 -23
  10. imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +7 -4
  11. imap_processing/cdf/utils.py +3 -5
  12. imap_processing/cli.py +13 -4
  13. imap_processing/codice/codice_l1a.py +5 -5
  14. imap_processing/codice/constants.py +9 -9
  15. imap_processing/codice/decompress.py +6 -2
  16. imap_processing/glows/l1a/glows_l1a.py +1 -2
  17. imap_processing/hi/l1a/hi_l1a.py +4 -4
  18. imap_processing/hi/l1a/histogram.py +106 -108
  19. imap_processing/hi/l1a/science_direct_event.py +91 -224
  20. imap_processing/hi/packet_definitions/TLM_HI_COMBINED_SCI.xml +3994 -0
  21. imap_processing/hit/l0/constants.py +2 -2
  22. imap_processing/hit/l0/decom_hit.py +12 -101
  23. imap_processing/hit/l1a/hit_l1a.py +164 -23
  24. imap_processing/ialirt/l0/process_codicelo.py +153 -0
  25. imap_processing/ialirt/l0/process_hit.py +5 -5
  26. imap_processing/ialirt/packet_definitions/ialirt_codicelo.xml +281 -0
  27. imap_processing/ialirt/process_ephemeris.py +212 -0
  28. imap_processing/idex/idex_l1a.py +55 -75
  29. imap_processing/idex/idex_l1b.py +192 -0
  30. imap_processing/idex/idex_variable_unpacking_and_eu_conversion.csv +33 -0
  31. imap_processing/idex/packet_definitions/idex_packet_definition.xml +97 -595
  32. imap_processing/lo/l0/decompression_tables/decompression_tables.py +16 -0
  33. imap_processing/lo/l0/lo_science.py +44 -12
  34. imap_processing/lo/l1a/lo_l1a.py +76 -8
  35. imap_processing/lo/packet_definitions/lo_xtce.xml +9877 -87
  36. imap_processing/mag/l1a/mag_l1a.py +1 -2
  37. imap_processing/mag/l1a/mag_l1a_data.py +1 -2
  38. imap_processing/mag/l1b/mag_l1b.py +2 -1
  39. imap_processing/spice/geometry.py +37 -19
  40. imap_processing/spice/time.py +144 -2
  41. imap_processing/swapi/l1/swapi_l1.py +3 -3
  42. imap_processing/swapi/packet_definitions/swapi_packet_definition.xml +1535 -446
  43. imap_processing/swe/l2/swe_l2.py +134 -17
  44. imap_processing/tests/ccsds/test_data/expected_output.xml +1 -1
  45. imap_processing/tests/codice/test_codice_l1a.py +8 -8
  46. imap_processing/tests/codice/test_decompress.py +4 -4
  47. imap_processing/tests/conftest.py +46 -43
  48. imap_processing/tests/hi/test_data/l0/H90_NHK_20241104.bin +0 -0
  49. imap_processing/tests/hi/test_data/l0/H90_sci_cnt_20241104.bin +0 -0
  50. imap_processing/tests/hi/test_data/l0/H90_sci_de_20241104.bin +0 -0
  51. imap_processing/tests/hi/test_hi_l1b.py +2 -2
  52. imap_processing/tests/hi/test_l1a.py +31 -58
  53. imap_processing/tests/hi/test_science_direct_event.py +58 -0
  54. imap_processing/tests/hit/test_data/sci_sample1.ccsds +0 -0
  55. imap_processing/tests/hit/test_decom_hit.py +60 -50
  56. imap_processing/tests/hit/test_hit_l1a.py +327 -12
  57. imap_processing/tests/hit/test_hit_l1b.py +76 -0
  58. imap_processing/tests/hit/validation_data/hskp_sample_eu.csv +89 -0
  59. imap_processing/tests/hit/validation_data/sci_sample_raw1.csv +29 -0
  60. imap_processing/tests/ialirt/test_data/l0/apid01152.tlm +0 -0
  61. imap_processing/tests/ialirt/test_data/l0/imap_codice_l1a_lo-ialirt_20241110193700_v0.0.0.cdf +0 -0
  62. imap_processing/tests/ialirt/unit/test_process_codicelo.py +106 -0
  63. imap_processing/tests/ialirt/unit/test_process_ephemeris.py +109 -0
  64. imap_processing/tests/ialirt/unit/test_process_hit.py +9 -6
  65. imap_processing/tests/idex/conftest.py +1 -1
  66. imap_processing/tests/idex/test_idex_l0.py +1 -1
  67. imap_processing/tests/idex/test_idex_l1a.py +7 -1
  68. imap_processing/tests/idex/test_idex_l1b.py +126 -0
  69. imap_processing/tests/lo/test_lo_l1a.py +7 -16
  70. imap_processing/tests/lo/test_lo_science.py +67 -3
  71. imap_processing/tests/lo/test_pkts/imap_lo_l0_raw_20240803_v002.pkts +0 -0
  72. imap_processing/tests/lo/validation_data/Instrument_FM1_T104_R129_20240803_ILO_SCI_DE_dec_DN_with_fills.csv +1999 -0
  73. imap_processing/tests/mag/test_mag_l1b.py +39 -5
  74. imap_processing/tests/spice/test_geometry.py +32 -6
  75. imap_processing/tests/spice/test_time.py +135 -6
  76. imap_processing/tests/swapi/test_swapi_decom.py +75 -69
  77. imap_processing/tests/swapi/test_swapi_l1.py +4 -4
  78. imap_processing/tests/swe/test_swe_l2.py +64 -8
  79. imap_processing/tests/test_utils.py +1 -1
  80. imap_processing/tests/ultra/test_data/l0/ultra45_raw_sc_ultrarawimg_withFSWcalcs_FM45_40P_Phi28p5_BeamCal_LinearScan_phi2850_theta-000_20240207T102740.csv +3314 -3314
  81. imap_processing/tests/ultra/unit/test_de.py +8 -3
  82. imap_processing/tests/ultra/unit/test_spatial_utils.py +125 -0
  83. imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +39 -29
  84. imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +2 -25
  85. imap_processing/ultra/constants.py +4 -0
  86. imap_processing/ultra/l1b/de.py +8 -14
  87. imap_processing/ultra/l1b/ultra_l1b_extended.py +29 -70
  88. imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +1 -36
  89. imap_processing/ultra/utils/spatial_utils.py +221 -0
  90. {imap_processing-0.8.0.dist-info → imap_processing-0.9.0.dist-info}/METADATA +1 -1
  91. {imap_processing-0.8.0.dist-info → imap_processing-0.9.0.dist-info}/RECORD +94 -76
  92. imap_processing/hi/l0/__init__.py +0 -0
  93. imap_processing/hi/l0/decom_hi.py +0 -24
  94. imap_processing/hi/packet_definitions/hi_packet_definition.xml +0 -482
  95. imap_processing/tests/hi/test_decom.py +0 -55
  96. imap_processing/tests/hi/test_l1a_sci_de.py +0 -72
  97. {imap_processing-0.8.0.dist-info → imap_processing-0.9.0.dist-info}/LICENSE +0 -0
  98. {imap_processing-0.8.0.dist-info → imap_processing-0.9.0.dist-info}/WHEEL +0 -0
  99. {imap_processing-0.8.0.dist-info → imap_processing-0.9.0.dist-info}/entry_points.txt +0 -0
@@ -13,6 +13,16 @@ DE_BIT_SHIFT = {
13
13
  "pos": 0,
14
14
  }
15
15
 
16
+ # Named tuple for all the packet fields in the
17
+ # direct event packet that need to be parsed
18
+ # but are not part of the compressed DE data.
19
+ PacketFields = namedtuple(
20
+ "PacketFields",
21
+ [
22
+ "passes",
23
+ ],
24
+ )
25
+
16
26
  # Named tuple for all the fixed fields in the
17
27
  # direct event. These fields will always be transmitted.
18
28
  FixedFields = namedtuple(
@@ -39,6 +49,11 @@ VariableFields = namedtuple(
39
49
  "pos",
40
50
  ],
41
51
  )
52
+
53
+ # number of bits for each packet field
54
+ # passes: 32 bits
55
+ PACKET_FIELD_BITS = PacketFields(32)
56
+
42
57
  # number of bits for each fixed field
43
58
  # coincidence_type: 4 bits
44
59
  # de_time: 12 bits
@@ -55,6 +70,7 @@ FIXED_FIELD_BITS = FixedFields(4, 12, 3, 1)
55
70
  # pos: 2 bits
56
71
  VARIABLE_FIELD_BITS = VariableFields(10, 9, 9, 6, 4, 2)
57
72
 
73
+
58
74
  # Variable fields that are transmitted for each case and mode.
59
75
  # (case, mode): tof0, tof1, tof2, tof3, cksm, pos
60
76
  CASE_DECODER = {
@@ -1,5 +1,6 @@
1
1
  """Processing function for Lo Science Data."""
2
2
 
3
+ import logging
3
4
  from collections import namedtuple
4
5
 
5
6
  import numpy as np
@@ -11,6 +12,7 @@ from imap_processing.lo.l0.decompression_tables.decompression_tables import (
11
12
  CASE_DECODER,
12
13
  DE_BIT_SHIFT,
13
14
  FIXED_FIELD_BITS,
15
+ PACKET_FIELD_BITS,
14
16
  VARIABLE_FIELD_BITS,
15
17
  )
16
18
  from imap_processing.lo.l0.utils.bit_decompression import (
@@ -20,6 +22,9 @@ from imap_processing.lo.l0.utils.bit_decompression import (
20
22
  )
21
23
  from imap_processing.utils import convert_to_binary_string
22
24
 
25
+ logger = logging.getLogger(__name__)
26
+ logger.setLevel(logging.INFO)
27
+
23
28
  HistPacking = namedtuple(
24
29
  "HistPacking",
25
30
  [
@@ -174,30 +179,55 @@ def parse_events(dataset: xr.Dataset, attr_mgr: ImapCdfAttributes) -> xr.Dataset
174
179
  dataset : xr.Dataset
175
180
  Parsed and decompressed direct event data.
176
181
  """
177
- # TODO: Add logging. Want to wait until I have a better understanding of how the
178
- # DEs spread across multiple packets will work first
179
-
182
+ logger.info("\n Parsing Lo L1A Direct Events")
180
183
  # Sum each count to get the total number of direct events for the pointing
181
- num_de: int = np.sum(dataset["count"].values)
184
+ # parse the count and passes fields. These fields only occur once
185
+ # at the beginning of each packet group and are not part of the
186
+ # compressed direct event data
187
+ dataset["de_count"] = xr.DataArray(
188
+ [int(pkt[0:16], 2) for pkt in dataset["events"].values],
189
+ dims="epoch",
190
+ attrs=attr_mgr.get_variable_attributes("de_count"),
191
+ )
192
+ num_de: int = np.sum(dataset["de_count"].values)
193
+
194
+ logger.info(f"Total number of direct events in this ASC: {num_de}")
182
195
 
183
- de_fields = list(FIXED_FIELD_BITS._asdict().keys()) + list(
184
- VARIABLE_FIELD_BITS._asdict().keys()
196
+ de_fields = (
197
+ list(PACKET_FIELD_BITS._asdict().keys())
198
+ + list(FIXED_FIELD_BITS._asdict().keys())
199
+ + list(VARIABLE_FIELD_BITS._asdict().keys())
185
200
  )
186
201
  # Initialize all Direct Event fields with their fill value
187
202
  # L1A Direct event data will not be tied to an epoch
188
- # data will use a direct event index for the pointing as its coordinate/dimension
203
+ # data will use a direct event index for the
204
+ # pointing as its coordinate/dimension
189
205
  for field in de_fields:
190
206
  dataset[field] = xr.DataArray(
191
207
  np.full(num_de, attr_mgr.get_variable_attributes(field)["FILLVAL"]),
192
208
  dims="direct_events",
209
+ attrs=attr_mgr.get_variable_attributes(field),
193
210
  )
211
+ dataset["passes"] = xr.DataArray(
212
+ np.full(
213
+ len(dataset["events"].values),
214
+ attr_mgr.get_variable_attributes("passes")["FILLVAL"],
215
+ ),
216
+ dims="epoch",
217
+ attrs=attr_mgr.get_variable_attributes("passes"),
218
+ )
194
219
 
195
220
  # The DE index for the entire pointing
196
221
  pointing_de = 0
197
222
  # for each direct event packet in the pointing
198
- for pkt_idx, de_count in enumerate(dataset["count"].values):
223
+ for pkt_idx, de_count in enumerate(dataset["de_count"].values):
199
224
  # initialize the bit position for the packet
200
- dataset.attrs["bit_pos"] = 0
225
+ # after the counts field
226
+ dataset.attrs["bit_pos"] = 16
227
+ # Parse the passes field for the packet
228
+ dataset["passes"].values[pkt_idx] = parse_de_bin(dataset, pkt_idx, 32)
229
+ dataset.attrs["bit_pos"] = dataset.attrs["bit_pos"] + 32
230
+
201
231
  # for each direct event in the packet
202
232
  for _ in range(de_count):
203
233
  # Parse the fixed fields for the direct event
@@ -209,6 +239,8 @@ def parse_events(dataset: xr.Dataset, attr_mgr: ImapCdfAttributes) -> xr.Dataset
209
239
 
210
240
  pointing_de += 1
211
241
 
242
+ del dataset.attrs["bit_pos"]
243
+ logger.info("\n Returning Lo L1A Direct Events Dataset")
212
244
  return dataset
213
245
 
214
246
 
@@ -314,9 +346,10 @@ def parse_de_bin(
314
346
  Parsed integer for the direct event field.
315
347
  """
316
348
  bit_pos = dataset.attrs["bit_pos"]
349
+
317
350
  parsed_int = (
318
351
  int(
319
- dataset["data"].values[pkt_idx][bit_pos : bit_pos + bit_length],
352
+ dataset["events"].values[pkt_idx][bit_pos : bit_pos + bit_length],
320
353
  2,
321
354
  )
322
355
  << bit_shift
@@ -364,12 +397,11 @@ def combine_segmented_packets(dataset: xr.Dataset) -> xr.Dataset:
364
397
  valid_groups = find_valid_groups(seq_ctrs, seg_starts, seg_ends)
365
398
 
366
399
  # Combine the segmented packets into a single binary string
367
- # Mark the segment splits with comma to identify padding bits
368
- # when parsing the binary
369
400
  dataset["events"] = [
370
401
  "".join(dataset["data"].values[start : end + 1])
371
402
  for start, end in zip(seg_starts, seg_ends)
372
403
  ]
404
+
373
405
  # drop any group of segmented packets that aren't sequential
374
406
  dataset["events"] = dataset["events"].values[valid_groups]
375
407
 
@@ -9,8 +9,12 @@ import xarray as xr
9
9
  from imap_processing import imap_module_directory
10
10
  from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
11
11
  from imap_processing.lo.l0.lo_apid import LoAPID
12
- from imap_processing.lo.l0.lo_science import parse_histogram
13
- from imap_processing.utils import packet_file_to_datasets
12
+ from imap_processing.lo.l0.lo_science import (
13
+ combine_segmented_packets,
14
+ parse_events,
15
+ parse_histogram,
16
+ )
17
+ from imap_processing.utils import convert_to_binary_string, packet_file_to_datasets
14
18
 
15
19
  logger = logging.getLogger(__name__)
16
20
  logger.setLevel(logging.INFO)
@@ -48,11 +52,11 @@ def lo_l1a(dependency: Path, data_version: str) -> list[xr.Dataset]:
48
52
  attr_mgr.add_instrument_variable_attrs(instrument="lo", level="l1a")
49
53
  attr_mgr.add_global_attribute("Data_version", data_version)
50
54
 
51
- logger.info(
52
- f"\nProcessing {LoAPID(LoAPID.ILO_SCI_CNT).name} "
53
- f"packet (APID: {LoAPID.ILO_SCI_CNT.value})"
54
- )
55
55
  if LoAPID.ILO_SCI_CNT in datasets_by_apid:
56
+ logger.info(
57
+ f"\nProcessing {LoAPID(LoAPID.ILO_SCI_CNT).name} "
58
+ f"packet (APID: {LoAPID.ILO_SCI_CNT.value})"
59
+ )
56
60
  logical_source = "imap_lo_l1a_histogram"
57
61
  datasets_by_apid[LoAPID.ILO_SCI_CNT] = parse_histogram(
58
62
  datasets_by_apid[LoAPID.ILO_SCI_CNT], attr_mgr
@@ -60,8 +64,33 @@ def lo_l1a(dependency: Path, data_version: str) -> list[xr.Dataset]:
60
64
  datasets_by_apid[LoAPID.ILO_SCI_CNT] = add_dataset_attrs(
61
65
  datasets_by_apid[LoAPID.ILO_SCI_CNT], attr_mgr, logical_source
62
66
  )
67
+ if LoAPID.ILO_SCI_DE in datasets_by_apid:
68
+ logger.info(
69
+ f"\nProcessing {LoAPID(LoAPID.ILO_SCI_DE).name} "
70
+ f"packet (APID: {LoAPID.ILO_SCI_DE.value})"
71
+ )
72
+ logical_source = "imap_lo_l1a_de"
73
+ datasets_by_apid[LoAPID.ILO_SCI_DE]["data"] = xr.DataArray(
74
+ [
75
+ convert_to_binary_string(data)
76
+ for data in datasets_by_apid[LoAPID.ILO_SCI_DE]["data"].values
77
+ ],
78
+ dims=datasets_by_apid[LoAPID.ILO_SCI_DE]["data"].dims,
79
+ attrs=datasets_by_apid[LoAPID.ILO_SCI_DE]["data"].attrs,
80
+ )
81
+
82
+ datasets_by_apid[LoAPID.ILO_SCI_DE] = combine_segmented_packets(
83
+ datasets_by_apid[LoAPID.ILO_SCI_DE]
84
+ )
63
85
 
64
- good_apids = [LoAPID.ILO_SCI_CNT]
86
+ datasets_by_apid[LoAPID.ILO_SCI_DE] = parse_events(
87
+ datasets_by_apid[LoAPID.ILO_SCI_DE], attr_mgr
88
+ )
89
+ datasets_by_apid[LoAPID.ILO_SCI_DE] = add_dataset_attrs(
90
+ datasets_by_apid[LoAPID.ILO_SCI_DE], attr_mgr, logical_source
91
+ )
92
+
93
+ good_apids = [LoAPID.ILO_SCI_CNT, LoAPID.ILO_SCI_DE]
65
94
  logger.info(f"\nReturning datasets: {[LoAPID(apid) for apid in good_apids]}")
66
95
  return [datasets_by_apid[good_apid] for good_apid in good_apids]
67
96
 
@@ -89,6 +118,7 @@ def add_dataset_attrs(
89
118
  # TODO: may want up split up these if statements into their
90
119
  # own functions
91
120
  if logical_source == "imap_lo_l1a_histogram":
121
+ # Create coordinates for the dataset
92
122
  azimuth_60 = xr.DataArray(
93
123
  data=np.arange(0, 6, dtype=np.uint8),
94
124
  name="azimuth_60",
@@ -118,7 +148,7 @@ def add_dataset_attrs(
118
148
  data=np.arange(1, 8, dtype=np.uint8),
119
149
  name="esa_step",
120
150
  dims=["esa_step"],
121
- attrs=attr_mgr.get_variable_attributes("esa_step"),
151
+ attrs=attr_mgr.get_variable_attributes("esa_step_coord"),
122
152
  )
123
153
  esa_step_label = xr.DataArray(
124
154
  esa_step.values.astype(str),
@@ -127,6 +157,7 @@ def add_dataset_attrs(
127
157
  attrs=attr_mgr.get_variable_attributes("esa_step_label"),
128
158
  )
129
159
 
160
+ # Get attributes for shcoarse and epoch
130
161
  dataset.shcoarse.attrs.update(attr_mgr.get_variable_attributes("shcoarse"))
131
162
  dataset.epoch.attrs.update(attr_mgr.get_variable_attributes("epoch"))
132
163
 
@@ -153,5 +184,42 @@ def add_dataset_attrs(
153
184
  "pkt_len",
154
185
  ]
155
186
  )
187
+ elif logical_source == "imap_lo_l1a_de":
188
+ # Create the coordinates for the dataset
189
+ direct_events = xr.DataArray(
190
+ data=np.arange(sum(dataset["de_count"].values), dtype=np.uint16),
191
+ name="direct_events",
192
+ dims=["direct_events"],
193
+ attrs=attr_mgr.get_variable_attributes("direct_events"),
194
+ )
195
+
196
+ direct_events_label = xr.DataArray(
197
+ direct_events.values.astype(str),
198
+ name="direct_events_label",
199
+ dims=["direct_events_label"],
200
+ attrs=attr_mgr.get_variable_attributes("direct_events_label"),
201
+ )
202
+
203
+ dataset = dataset.assign_coords(
204
+ direct_events=direct_events,
205
+ direct_events_label=direct_events_label,
206
+ )
207
+ # add the epoch and global attributes
208
+ dataset.epoch.attrs.update(attr_mgr.get_variable_attributes("epoch"))
209
+ dataset.attrs.update(attr_mgr.get_global_attributes(logical_source))
210
+ dataset = dataset.drop_vars(
211
+ [
212
+ "version",
213
+ "type",
214
+ "sec_hdr_flg",
215
+ "pkt_apid",
216
+ "seq_flgs",
217
+ "src_seq_ctr",
218
+ "pkt_len",
219
+ "shcoarse",
220
+ "data",
221
+ "events",
222
+ ]
223
+ )
156
224
 
157
225
  return dataset