imap-processing 0.7.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 (172) hide show
  1. imap_processing/__init__.py +1 -1
  2. imap_processing/_version.py +2 -2
  3. imap_processing/ccsds/excel_to_xtce.py +36 -2
  4. imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +1 -1
  5. imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +145 -30
  6. imap_processing/cdf/config/imap_glows_l1b_variable_attrs.yaml +36 -36
  7. imap_processing/cdf/config/imap_hi_variable_attrs.yaml +136 -9
  8. imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +14 -0
  9. imap_processing/cdf/config/imap_hit_l1a_variable_attrs.yaml +63 -1
  10. imap_processing/cdf/config/imap_hit_l1b_variable_attrs.yaml +9 -0
  11. imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +14 -7
  12. imap_processing/cdf/config/imap_idex_l1a_variable_attrs.yaml +577 -235
  13. imap_processing/cdf/config/imap_idex_l1b_variable_attrs.yaml +326 -0
  14. imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +33 -23
  15. imap_processing/cdf/config/imap_mag_l1_variable_attrs.yaml +24 -28
  16. imap_processing/cdf/config/imap_ultra_l1a_variable_attrs.yaml +1 -0
  17. imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +137 -79
  18. imap_processing/cdf/config/imap_variable_schema.yaml +13 -0
  19. imap_processing/cdf/imap_cdf_manager.py +31 -27
  20. imap_processing/cdf/utils.py +3 -5
  21. imap_processing/cli.py +25 -14
  22. imap_processing/codice/codice_l1a.py +153 -63
  23. imap_processing/codice/constants.py +10 -10
  24. imap_processing/codice/decompress.py +10 -11
  25. imap_processing/codice/utils.py +1 -0
  26. imap_processing/glows/l1a/glows_l1a.py +1 -2
  27. imap_processing/glows/l1b/glows_l1b.py +3 -3
  28. imap_processing/glows/l1b/glows_l1b_data.py +59 -37
  29. imap_processing/glows/l2/glows_l2_data.py +123 -0
  30. imap_processing/hi/l1a/hi_l1a.py +4 -4
  31. imap_processing/hi/l1a/histogram.py +107 -109
  32. imap_processing/hi/l1a/science_direct_event.py +92 -225
  33. imap_processing/hi/l1b/hi_l1b.py +85 -11
  34. imap_processing/hi/l1c/hi_l1c.py +23 -1
  35. imap_processing/hi/packet_definitions/TLM_HI_COMBINED_SCI.xml +3994 -0
  36. imap_processing/hi/utils.py +1 -1
  37. imap_processing/hit/hit_utils.py +221 -0
  38. imap_processing/hit/l0/constants.py +118 -0
  39. imap_processing/hit/l0/decom_hit.py +100 -156
  40. imap_processing/hit/l1a/hit_l1a.py +170 -184
  41. imap_processing/hit/l1b/hit_l1b.py +33 -153
  42. imap_processing/ialirt/l0/process_codicelo.py +153 -0
  43. imap_processing/ialirt/l0/process_hit.py +5 -5
  44. imap_processing/ialirt/packet_definitions/ialirt_codicelo.xml +281 -0
  45. imap_processing/ialirt/process_ephemeris.py +212 -0
  46. imap_processing/idex/idex_l1a.py +65 -84
  47. imap_processing/idex/idex_l1b.py +192 -0
  48. imap_processing/idex/idex_variable_unpacking_and_eu_conversion.csv +33 -0
  49. imap_processing/idex/packet_definitions/idex_packet_definition.xml +97 -595
  50. imap_processing/lo/l0/decompression_tables/decompression_tables.py +17 -1
  51. imap_processing/lo/l0/lo_science.py +45 -13
  52. imap_processing/lo/l1a/lo_l1a.py +76 -8
  53. imap_processing/lo/packet_definitions/lo_xtce.xml +8344 -1849
  54. imap_processing/mag/l0/decom_mag.py +4 -3
  55. imap_processing/mag/l1a/mag_l1a.py +12 -13
  56. imap_processing/mag/l1a/mag_l1a_data.py +1 -2
  57. imap_processing/mag/l1b/mag_l1b.py +90 -7
  58. imap_processing/spice/geometry.py +156 -16
  59. imap_processing/spice/time.py +144 -2
  60. imap_processing/swapi/l1/swapi_l1.py +4 -4
  61. imap_processing/swapi/l2/swapi_l2.py +1 -1
  62. imap_processing/swapi/packet_definitions/swapi_packet_definition.xml +1535 -446
  63. imap_processing/swe/l1b/swe_l1b_science.py +8 -8
  64. imap_processing/swe/l2/swe_l2.py +134 -17
  65. imap_processing/tests/ccsds/test_data/expected_output.xml +2 -1
  66. imap_processing/tests/ccsds/test_excel_to_xtce.py +4 -4
  67. imap_processing/tests/cdf/test_imap_cdf_manager.py +0 -10
  68. imap_processing/tests/codice/conftest.py +1 -17
  69. imap_processing/tests/codice/data/imap_codice_l0_raw_20241110_v001.pkts +0 -0
  70. imap_processing/tests/codice/test_codice_l0.py +8 -2
  71. imap_processing/tests/codice/test_codice_l1a.py +127 -107
  72. imap_processing/tests/codice/test_codice_l1b.py +1 -0
  73. imap_processing/tests/codice/test_decompress.py +7 -7
  74. imap_processing/tests/conftest.py +100 -58
  75. imap_processing/tests/glows/conftest.py +6 -0
  76. imap_processing/tests/glows/test_glows_l1b.py +9 -9
  77. imap_processing/tests/glows/test_glows_l1b_data.py +9 -9
  78. imap_processing/tests/hi/test_data/l0/H90_NHK_20241104.bin +0 -0
  79. imap_processing/tests/hi/test_data/l0/H90_sci_cnt_20241104.bin +0 -0
  80. imap_processing/tests/hi/test_data/l0/H90_sci_de_20241104.bin +0 -0
  81. imap_processing/tests/hi/test_data/l1a/imap_hi_l1a_45sensor-de_20250415_v000.cdf +0 -0
  82. imap_processing/tests/hi/test_hi_l1b.py +73 -3
  83. imap_processing/tests/hi/test_hi_l1c.py +10 -2
  84. imap_processing/tests/hi/test_l1a.py +31 -58
  85. imap_processing/tests/hi/test_science_direct_event.py +58 -0
  86. imap_processing/tests/hi/test_utils.py +4 -3
  87. imap_processing/tests/hit/test_data/sci_sample1.ccsds +0 -0
  88. imap_processing/tests/hit/{test_hit_decom.py → test_decom_hit.py} +95 -36
  89. imap_processing/tests/hit/test_hit_l1a.py +299 -179
  90. imap_processing/tests/hit/test_hit_l1b.py +231 -24
  91. imap_processing/tests/hit/test_hit_utils.py +218 -0
  92. imap_processing/tests/hit/validation_data/hskp_sample_eu.csv +89 -0
  93. imap_processing/tests/hit/validation_data/sci_sample_raw1.csv +29 -0
  94. imap_processing/tests/ialirt/test_data/l0/apid01152.tlm +0 -0
  95. imap_processing/tests/ialirt/test_data/l0/imap_codice_l1a_lo-ialirt_20241110193700_v0.0.0.cdf +0 -0
  96. imap_processing/tests/ialirt/unit/test_process_codicelo.py +106 -0
  97. imap_processing/tests/ialirt/unit/test_process_ephemeris.py +109 -0
  98. imap_processing/tests/ialirt/unit/test_process_hit.py +9 -6
  99. imap_processing/tests/idex/conftest.py +2 -2
  100. imap_processing/tests/idex/imap_idex_l0_raw_20231214_v001.pkts +0 -0
  101. imap_processing/tests/idex/impact_14_tof_high_data.txt +4444 -4444
  102. imap_processing/tests/idex/test_idex_l0.py +4 -4
  103. imap_processing/tests/idex/test_idex_l1a.py +8 -2
  104. imap_processing/tests/idex/test_idex_l1b.py +126 -0
  105. imap_processing/tests/lo/test_lo_l1a.py +7 -16
  106. imap_processing/tests/lo/test_lo_science.py +69 -5
  107. imap_processing/tests/lo/test_pkts/imap_lo_l0_raw_20240803_v002.pkts +0 -0
  108. imap_processing/tests/lo/validation_data/Instrument_FM1_T104_R129_20240803_ILO_SCI_DE_dec_DN_with_fills.csv +1999 -0
  109. imap_processing/tests/mag/imap_mag_l1a_norm-magi_20251017_v001.cdf +0 -0
  110. imap_processing/tests/mag/test_mag_l1b.py +97 -7
  111. imap_processing/tests/spice/test_data/imap_ena_sim_metakernel.template +3 -1
  112. imap_processing/tests/spice/test_geometry.py +115 -9
  113. imap_processing/tests/spice/test_time.py +135 -6
  114. imap_processing/tests/swapi/test_swapi_decom.py +75 -69
  115. imap_processing/tests/swapi/test_swapi_l1.py +4 -4
  116. imap_processing/tests/swe/conftest.py +33 -0
  117. imap_processing/tests/swe/l1_validation/swe_l0_unpacked-data_20240510_v001_VALIDATION_L1B_v3.dat +4332 -0
  118. imap_processing/tests/swe/test_swe_l1b.py +29 -8
  119. imap_processing/tests/swe/test_swe_l2.py +64 -8
  120. imap_processing/tests/test_utils.py +2 -2
  121. imap_processing/tests/ultra/test_data/l0/ultra45_raw_sc_ultrarawimg_withFSWcalcs_FM45_40P_Phi28p5_BeamCal_LinearScan_phi2850_theta-000_20240207T102740.csv +3314 -3314
  122. imap_processing/tests/ultra/test_data/l1/dps_exposure_helio_45_E12.cdf +0 -0
  123. imap_processing/tests/ultra/test_data/l1/dps_exposure_helio_45_E24.cdf +0 -0
  124. imap_processing/tests/ultra/unit/test_de.py +113 -0
  125. imap_processing/tests/ultra/unit/test_spatial_utils.py +125 -0
  126. imap_processing/tests/ultra/unit/test_ultra_l1b.py +27 -3
  127. imap_processing/tests/ultra/unit/test_ultra_l1b_annotated.py +31 -10
  128. imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +55 -35
  129. imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +10 -68
  130. imap_processing/ultra/constants.py +12 -3
  131. imap_processing/ultra/l1b/de.py +168 -30
  132. imap_processing/ultra/l1b/ultra_l1b_annotated.py +24 -10
  133. imap_processing/ultra/l1b/ultra_l1b_extended.py +46 -80
  134. imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +60 -144
  135. imap_processing/ultra/utils/spatial_utils.py +221 -0
  136. {imap_processing-0.7.0.dist-info → imap_processing-0.9.0.dist-info}/METADATA +15 -14
  137. {imap_processing-0.7.0.dist-info → imap_processing-0.9.0.dist-info}/RECORD +142 -139
  138. imap_processing/cdf/cdf_attribute_manager.py +0 -322
  139. imap_processing/cdf/config/shared/default_global_cdf_attrs_schema.yaml +0 -246
  140. imap_processing/cdf/config/shared/default_variable_cdf_attrs_schema.yaml +0 -466
  141. imap_processing/hi/l0/decom_hi.py +0 -24
  142. imap_processing/hi/packet_definitions/hi_packet_definition.xml +0 -482
  143. imap_processing/hit/l0/data_classes/housekeeping.py +0 -240
  144. imap_processing/hit/l0/data_classes/science_packet.py +0 -259
  145. imap_processing/hit/l0/utils/hit_base.py +0 -57
  146. imap_processing/tests/cdf/shared/default_global_cdf_attrs_schema.yaml +0 -246
  147. imap_processing/tests/cdf/shared/default_variable_cdf_attrs_schema.yaml +0 -466
  148. imap_processing/tests/cdf/test_cdf_attribute_manager.py +0 -353
  149. imap_processing/tests/codice/data/imap_codice_l0_hi-counters-aggregated_20240429_v001.pkts +0 -0
  150. imap_processing/tests/codice/data/imap_codice_l0_hi-counters-singles_20240429_v001.pkts +0 -0
  151. imap_processing/tests/codice/data/imap_codice_l0_hi-omni_20240429_v001.pkts +0 -0
  152. imap_processing/tests/codice/data/imap_codice_l0_hi-pha_20240429_v001.pkts +0 -0
  153. imap_processing/tests/codice/data/imap_codice_l0_hi-sectored_20240429_v001.pkts +0 -0
  154. imap_processing/tests/codice/data/imap_codice_l0_hskp_20100101_v001.pkts +0 -0
  155. imap_processing/tests/codice/data/imap_codice_l0_lo-counters-aggregated_20240429_v001.pkts +0 -0
  156. imap_processing/tests/codice/data/imap_codice_l0_lo-counters-singles_20240429_v001.pkts +0 -0
  157. imap_processing/tests/codice/data/imap_codice_l0_lo-nsw-angular_20240429_v001.pkts +0 -0
  158. imap_processing/tests/codice/data/imap_codice_l0_lo-nsw-priority_20240429_v001.pkts +0 -0
  159. imap_processing/tests/codice/data/imap_codice_l0_lo-nsw-species_20240429_v001.pkts +0 -0
  160. imap_processing/tests/codice/data/imap_codice_l0_lo-pha_20240429_v001.pkts +0 -0
  161. imap_processing/tests/codice/data/imap_codice_l0_lo-sw-angular_20240429_v001.pkts +0 -0
  162. imap_processing/tests/codice/data/imap_codice_l0_lo-sw-priority_20240429_v001.pkts +0 -0
  163. imap_processing/tests/codice/data/imap_codice_l0_lo-sw-species_20240429_v001.pkts +0 -0
  164. imap_processing/tests/hi/test_decom.py +0 -55
  165. imap_processing/tests/hi/test_l1a_sci_de.py +0 -72
  166. imap_processing/tests/idex/imap_idex_l0_raw_20230725_v001.pkts +0 -0
  167. imap_processing/tests/mag/imap_mag_l1a_burst-magi_20231025_v001.cdf +0 -0
  168. /imap_processing/{hi/l0/__init__.py → tests/glows/test_glows_l2_data.py} +0 -0
  169. /imap_processing/tests/hit/test_data/{imap_hit_l0_hk_20100105_v001.pkts → imap_hit_l0_raw_20100105_v001.pkts} +0 -0
  170. {imap_processing-0.7.0.dist-info → imap_processing-0.9.0.dist-info}/LICENSE +0 -0
  171. {imap_processing-0.7.0.dist-info → imap_processing-0.9.0.dist-info}/WHEEL +0 -0
  172. {imap_processing-0.7.0.dist-info → imap_processing-0.9.0.dist-info}/entry_points.txt +0 -0
@@ -102,24 +102,24 @@ def test_validation_data_histogram(l1a_dataset):
102
102
  # "imap_spin_angle_bin_cntr": "imap_spin_angle_bin_cntr",
103
103
  # "histogram_flag_array": "histogram_flag_array",
104
104
  "filter_temperature_average": "filter_temperature_average",
105
- "filter_temperature_std_dev": "filter_temperature_variance",
105
+ "filter_temperature_std_dev": "filter_temperature_std_dev",
106
106
  "hv_voltage_average": "hv_voltage_average",
107
- "hv_voltage_std_dev": "hv_voltage_variance",
107
+ "hv_voltage_std_dev": "hv_voltage_std_dev",
108
108
  "spin_period_average": "spin_period_average",
109
- "spin_period_std_dev": "spin_period_variance",
109
+ "spin_period_std_dev": "spin_period_std_dev",
110
110
  "pulse_length_average": "pulse_length_average",
111
- "pulse_length_std_dev": "pulse_length_variance",
111
+ "pulse_length_std_dev": "pulse_length_std_dev",
112
112
  # TODO uncomment when spice is complete
113
113
  # "spin_period_ground_average": "spin_period_ground_average",
114
- # "spin_period_ground_std_dev": "spin_period_ground_variance",
114
+ # "spin_period_ground_std_dev": "spin_period_ground_std_dev",
115
115
  # "position_angle_offset_average": "position_angle_offset_average",
116
- # "position_angle_offset_std_dev": "position_angle_offset_variance",
116
+ # "position_angle_offset_std_dev": "position_angle_offset_std_dev",
117
117
  # "spin_axis_orientation_average": "spin_axis_orientation_average",
118
- # "spin_axis_orientation_std_dev": "spin_axis_orientation_variance",
118
+ # "spin_axis_orientation_std_dev": "spin_axis_orientation_std_dev",
119
119
  # "spacecraft_location_average": "spacecraft_location_average",
120
- # "spacecraft_location_std_dev": "spacecraft_location_variance",
120
+ # "spacecraft_location_std_dev": "spacecraft_location_std_dev",
121
121
  # "spacecraft_velocity_average": "spacecraft_velocity_average",
122
- # "spacecraft_velocity_std_dev": "spacecraft_velocity_variance",
122
+ # "spacecraft_velocity_std_dev": "spacecraft_velocity_std_dev",
123
123
  }
124
124
 
125
125
  for index, validation_output in enumerate(out["output"]):
@@ -12,6 +12,8 @@ from imap_processing.hi.l1b.hi_l1b import (
12
12
  CoincidenceBitmap,
13
13
  compute_coincidence_type_and_time_deltas,
14
14
  compute_hae_coordinates,
15
+ de_esa_energy_step,
16
+ de_nominal_bin_and_spin_phase,
15
17
  hi_l1b,
16
18
  )
17
19
  from imap_processing.hi.utils import HiConstants
@@ -22,19 +24,23 @@ def test_hi_l1b_hk(hi_l0_test_data_path):
22
24
  """Test coverage for imap_processing.hi.hi_l1b.hi_l1b() with
23
25
  housekeeping L1A as input"""
24
26
  # TODO: once things are more stable, check in an L1A HK file as test data
25
- bin_data_path = hi_l0_test_data_path / "20231030_H45_APP_NHK.bin"
27
+ bin_data_path = hi_l0_test_data_path / "H90_NHK_20241104.bin"
26
28
  data_version = "001"
27
29
  processed_data = hi_l1a(packet_file_path=bin_data_path, data_version=data_version)
28
30
 
29
31
  l1b_dataset = hi_l1b(processed_data[0], data_version=data_version)
30
- assert l1b_dataset.attrs["Logical_source"] == "imap_hi_l1b_45sensor-hk"
32
+ assert l1b_dataset.attrs["Logical_source"] == "imap_hi_l1b_90sensor-hk"
31
33
 
32
34
 
33
35
  @pytest.mark.external_kernel()
34
36
  @pytest.mark.use_test_metakernel("imap_ena_sim_metakernel.template")
35
- def test_hi_l1b_de(hi_l1a_test_data_path):
37
+ def test_hi_l1b_de(
38
+ hi_l1a_test_data_path, spice_test_data_path, use_fake_spin_data_for_time
39
+ ):
36
40
  """Test coverage for imap_processing.hi.hi_l1b.hi_l1b() with
37
41
  direct events L1A as input"""
42
+ # Start MET time of spin for simulated input data is 482372988
43
+ use_fake_spin_data_for_time(482372988)
38
44
  l1a_test_file_path = (
39
45
  hi_l1a_test_data_path / "imap_hi_l1a_45sensor-de_20250415_v000.cdf"
40
46
  )
@@ -152,6 +158,53 @@ def test_compute_coincidence_type_and_time_deltas(synthetic_trigger_id_and_tof_d
152
158
  )
153
159
 
154
160
 
161
+ @mock.patch("imap_processing.hi.l1b.hi_l1b.parse_sensor_number", return_value=90)
162
+ @mock.patch("imap_processing.hi.l1b.hi_l1b.get_instrument_spin_phase")
163
+ @mock.patch("imap_processing.hi.l1b.hi_l1b.get_spacecraft_spin_phase")
164
+ def test_de_nominal_bin_and_spin_phase(
165
+ spacecraft_phase_moc, instrument_phase_mock, parse_sensor_number_mock
166
+ ):
167
+ """Test coverage for de_nominal_bin_and_spin_phase."""
168
+ # set the spacecraft_phase_mock to return an array of values between 0 and 1
169
+ # that is rolled 30 places for easy testing
170
+ spacecraft_phase_roll = 30
171
+ spacecraft_phase_moc.side_effect = lambda x: np.roll(
172
+ np.arange(0, 1, 1 / len(x)), spacecraft_phase_roll
173
+ )
174
+ # set the get_instrument_spin_phase mock to return an array of values between
175
+ # 0 and 1
176
+ instrument_phase_mock.side_effect = lambda x, y: np.arange(0, 1, 1 / len(x))
177
+ # generate a fake dataset with epoch coordinate and event_met variable
178
+ de_list_length = 720
179
+ synthetic_ds = xr.Dataset(
180
+ coords={
181
+ "epoch": xr.DataArray(
182
+ np.arange(de_list_length), name="epoch", dims=["epoch"]
183
+ )
184
+ },
185
+ data_vars={
186
+ "event_met": xr.DataArray(np.arange(de_list_length), dims=["epoch"])
187
+ },
188
+ attrs={"Logical_source": "foo_source"},
189
+ )
190
+
191
+ new_vars = de_nominal_bin_and_spin_phase(synthetic_ds)
192
+ # Check spin_phase
193
+ assert "spin_phase" in new_vars
194
+ assert new_vars["spin_phase"].shape == (de_list_length,)
195
+ np.testing.assert_array_equal(
196
+ new_vars["spin_phase"].values,
197
+ np.linspace(0, 1, de_list_length + 1, dtype=np.float32)[:-1],
198
+ )
199
+ # Check nominal_bin
200
+ assert "nominal_bin" in new_vars
201
+ expected_nominal_bin = np.roll(
202
+ np.digitize(np.arange(0, 360, 360 / de_list_length), np.arange(90) * 4) - 1,
203
+ spacecraft_phase_roll,
204
+ )
205
+ np.testing.assert_array_equal(new_vars["nominal_bin"].values, expected_nominal_bin)
206
+
207
+
155
208
  @pytest.mark.parametrize("sensor_number", [45, 90])
156
209
  @mock.patch("imap_processing.hi.l1b.hi_l1b.instrument_pointing")
157
210
  def test_compute_hae_coordinates(mock_instrument_pointing, sensor_number):
@@ -183,3 +236,20 @@ def test_compute_hae_coordinates(mock_instrument_pointing, sensor_number):
183
236
  assert "hae_longitude" in new_vars
184
237
  assert new_vars["hae_longitude"].shape == fake_dataset.epoch.shape
185
238
  np.testing.assert_allclose(new_vars["hae_longitude"].values, sensor_number)
239
+
240
+
241
+ def test_de_esa_energy_step():
242
+ """Test coverage for de_esa_energy_step function."""
243
+ n_epoch = 20
244
+ fake_dataset = xr.Dataset(
245
+ coords={
246
+ "epoch": xr.DataArray(np.arange(n_epoch), name="epoch", dims=["epoch"])
247
+ },
248
+ data_vars={"esa_step": xr.DataArray(np.arange(n_epoch) % 9, dims=["epoch"])},
249
+ )
250
+ esa_energy_step_var = de_esa_energy_step(fake_dataset)
251
+ # TODO: The below check is for the temporary implementation and should be
252
+ # removed when the function is update.
253
+ np.testing.assert_array_equal(
254
+ esa_energy_step_var["esa_energy_step"].values, fake_dataset.esa_step.values
255
+ )
@@ -3,6 +3,7 @@
3
3
  import numpy as np
4
4
  import pytest
5
5
 
6
+ from imap_processing.cdf.utils import write_cdf
6
7
  from imap_processing.hi.l1a.hi_l1a import hi_l1a
7
8
  from imap_processing.hi.l1b.hi_l1b import hi_l1b
8
9
  from imap_processing.hi.l1c import hi_l1c
@@ -30,19 +31,26 @@ def test_generate_pset_dataset(create_de_data):
30
31
  def test_allocate_pset_dataset():
31
32
  """Test coverage for allocate_pset_dataset function"""
32
33
  n_esa_steps = 10
34
+ n_calibration_prods = 5
33
35
  sensor_str = HIAPID.H90_SCI_DE.sensor
34
36
  dataset = hi_l1c.allocate_pset_dataset(n_esa_steps, sensor_str)
35
37
 
36
38
  assert dataset.epoch.size == 1
37
39
  assert dataset.spin_angle_bin.size == 3600
40
+ assert dataset.esa_energy_step.size == n_esa_steps
41
+ assert dataset.calibration_prod.size == n_calibration_prods
38
42
  np.testing.assert_array_equal(dataset.despun_z.data.shape, (1, 3))
39
43
  np.testing.assert_array_equal(dataset.hae_latitude.data.shape, (1, 3600))
40
44
  np.testing.assert_array_equal(dataset.hae_longitude.data.shape, (1, 3600))
41
- n_esa_step = dataset.esa_energy_step.data.size
42
45
  for var in [
43
46
  "counts",
44
47
  "exposure_times",
45
48
  "background_rates",
46
49
  "background_rates_uncertainty",
47
50
  ]:
48
- np.testing.assert_array_equal(dataset[var].data.shape, (1, n_esa_step, 3600))
51
+ np.testing.assert_array_equal(
52
+ dataset[var].data.shape, (1, n_esa_steps, n_calibration_prods, 3600)
53
+ )
54
+ # Verify resulting CDF is ISTP compliant by writing to disk
55
+ dataset.attrs["Data_version"] = 1
56
+ write_cdf(dataset)
@@ -1,43 +1,25 @@
1
1
  import numpy as np
2
- import pytest
3
2
 
4
3
  from imap_processing.cdf.utils import write_cdf
5
- from imap_processing.hi.l1a import histogram as hist
6
4
  from imap_processing.hi.l1a.hi_l1a import hi_l1a
5
+ from imap_processing.hi.l1a.histogram import unpack_hist_counter
7
6
  from imap_processing.hi.utils import HIAPID
8
7
 
9
8
 
10
- def test_sci_de_decom(create_de_data):
9
+ def test_sci_de_decom(hi_l0_test_data_path):
11
10
  """Test science direct event data"""
12
11
 
13
- # Process using test data
14
- processed_data = hi_l1a(
15
- packet_file_path=create_de_data(HIAPID.H45_SCI_DE.value), data_version="001"
16
- )
12
+ bin_data_path = hi_l0_test_data_path / "H90_sci_de_20241104.bin"
13
+ processed_data = hi_l1a(bin_data_path, data_version="001")
17
14
 
18
- assert processed_data[0].attrs["Logical_source"] == "imap_hi_l1a_45sensor-de"
15
+ assert processed_data[0].attrs["Logical_source"] == "imap_hi_l1a_90sensor-de"
19
16
  assert processed_data[0].attrs["Data_version"] == "001"
20
17
 
21
- # unique ESA steps should be [1, 2]
22
- assert np.array_equal(
23
- np.sort(np.unique(processed_data[0]["esa_step"].values)),
24
- np.array([1, 2]),
25
- )
26
- # unique trigger_id should be [1, 2, 3]
27
- assert np.array_equal(
28
- np.sort(np.unique(processed_data[0]["trigger_id"].values)), np.array([1, 2, 3])
29
- )
30
- # tof_x should be in this range [0, 1023]
31
- assert processed_data[0]["tof_1"].min() >= 0
32
- assert processed_data[0]["tof_1"].max() <= 1023
33
- assert processed_data[0]["tof_2"].min() >= 0
34
- assert processed_data[0]["tof_2"].max() <= 1023
35
- assert processed_data[0]["tof_3"].min() >= 0
36
- assert processed_data[0]["tof_3"].max() <= 1023
18
+ # TODO: Verify correct unpacking of sample data. Issue: #1186
37
19
 
38
20
  # Write to CDF
39
- cdf_filename = "imap_hi_l1a_45sensor-de_20230927_v001.cdf"
40
- # TODO: Dropping duplicates to ignore ISTP for now. Need to update test data
21
+ cdf_filename = "imap_hi_l1a_90sensor-de_20241105_v001.cdf"
22
+ # TODO: Dropping duplicates to ignore ISTP for now. Should be fixed by #1186
41
23
  processed_data[0] = processed_data[0].sortby("epoch").groupby("epoch").first()
42
24
  cdf_filepath = write_cdf(processed_data[0])
43
25
  assert cdf_filepath.name == cdf_filename
@@ -47,54 +29,45 @@ def test_app_nhk_decom(hi_l0_test_data_path):
47
29
  """Test housekeeping data"""
48
30
 
49
31
  # Unpack housekeeping data
50
- bin_data_path = hi_l0_test_data_path / "20231030_H45_APP_NHK.bin"
32
+ bin_data_path = hi_l0_test_data_path / "H90_NHK_20241104.bin"
51
33
  processed_data = hi_l1a(packet_file_path=bin_data_path, data_version="001")
52
34
 
53
- assert np.unique(processed_data[0]["pkt_apid"].values) == HIAPID.H45_APP_NHK.value
54
- assert processed_data[0].attrs["Logical_source"] == "imap_hi_l1a_45sensor-hk"
35
+ assert np.unique(processed_data[0]["pkt_apid"].values) == HIAPID.H90_APP_NHK.value
36
+ assert processed_data[0].attrs["Logical_source"] == "imap_hi_l1a_90sensor-hk"
55
37
  assert processed_data[0].attrs["Data_version"] == "001"
56
- # TODO: compare with validation data once we have it
38
+ # TODO: compare with validation data once we have it. Issue: #1184
57
39
 
58
40
  # Write CDF
59
41
  cem_raw_cdf_filepath = write_cdf(processed_data[0], istp=False)
60
42
 
61
43
  # TODO: ask Vivek about this date mismatch between the file name
62
44
  # and the data. May get resolved when we have good sample data.
63
- assert cem_raw_cdf_filepath.name == "imap_hi_l1a_45sensor-hk_20100313_v001.cdf"
45
+ assert cem_raw_cdf_filepath.name == "imap_hi_l1a_90sensor-hk_20241105_v001.cdf"
64
46
 
65
47
 
66
- @pytest.mark.skip(
67
- reason="Need new test data with monotonically increasing epoch values"
68
- )
69
48
  def test_app_hist_decom(hi_l0_test_data_path):
70
49
  """Test histogram (SCI_CNT) data"""
71
- bin_data_path = hi_l0_test_data_path / "20231030_H45_SCI_CNT.bin"
50
+ bin_data_path = hi_l0_test_data_path / "H90_sci_cnt_20241104.bin"
72
51
  processed_data = hi_l1a(packet_file_path=bin_data_path, data_version="001")
73
52
 
74
- assert processed_data[0].attrs["Logical_source"] == "imap_hi_l1a_45sensor-hist"
75
- # TODO: compare with validation data once we have it
76
- # TODO: Dropping duplicates to ignore ISTP for now. Need to update test data
77
- processed_data[0] = processed_data[0].sortby("epoch").groupby("epoch").first()
53
+ assert processed_data[0].attrs["Logical_source"] == "imap_hi_l1a_90sensor-hist"
54
+ # TODO: compare with validation data once we have it. Issue: #1185
78
55
 
79
56
  # Write CDF
80
57
  cem_raw_cdf_filepath = write_cdf(processed_data[0])
81
58
 
82
- assert cem_raw_cdf_filepath.name.startswith("imap_hi_l1a_45sensor-hist_")
83
-
84
-
85
- def test_allocate_histogram_dataset():
86
- """Test hi.l1a.histogram.allocate_histogram_dataset()"""
87
- n_packets = 5
88
- dataset = hist.allocate_histogram_dataset(n_packets)
89
-
90
- assert dataset.attrs["Data_type"] == "L1A_HIST>Level-1A Histogram"
91
- assert dataset.sizes["epoch"] == n_packets
92
- assert dataset.sizes["angle"] == 90
93
- for var_name in (
94
- "ccsds_met",
95
- "esa_stepping_num",
96
- *hist.QUALIFIED_COUNTERS,
97
- *hist.LONG_COUNTERS,
98
- *hist.TOTAL_COUNTERS,
99
- ):
100
- assert var_name in dataset
59
+ assert cem_raw_cdf_filepath.name.startswith("imap_hi_l1a_90sensor-hist_")
60
+
61
+
62
+ def test_unpack_hist_counter():
63
+ """Test hi.l1a.histogram.unpack_hist_counter()"""
64
+ # To ensure correct unpacking, use expected values with ones in the upper
65
+ # and lower parts of the 12-bit numbers
66
+ expected = (np.arange(180).reshape((2, 90)) + 2**10).astype(">u2")
67
+ # convert each expected uint16 to a 12-bit bitstring and join
68
+ bin_str = "".join([f"{val:012b}" for val in expected.ravel()])
69
+ # convert the bitstring to a bytes object
70
+ bytes_array = int(bin_str, 2).to_bytes(len(bin_str) // 8, byteorder="big")
71
+ output_array = unpack_hist_counter(bytes_array)
72
+ np.testing.assert_array_equal(output_array, expected)
73
+ assert output_array.dtype == np.uint16
@@ -0,0 +1,58 @@
1
+ import numpy as np
2
+
3
+ from imap_processing.hi.l1a.science_direct_event import (
4
+ create_dataset,
5
+ parse_direct_events,
6
+ )
7
+
8
+
9
+ def test_parse_direct_events():
10
+ """Test coverage for parse_direct_events function."""
11
+ # Generate fake, binary blob using random numbers
12
+ np.random.seed(2)
13
+ n_events = 10_000
14
+ exp_dict = dict()
15
+ exp_dict["trigger_id"] = np.random.randint(1, 4, size=n_events, dtype=np.uint8)
16
+ exp_dict["de_tag"] = np.random.randint(0, 2**16, size=n_events, dtype=np.uint16)
17
+ exp_dict["tof_1"] = np.random.randint(0, 2**10, size=n_events, dtype=np.uint16)
18
+ exp_dict["tof_2"] = np.random.randint(0, 2**10, size=n_events, dtype=np.uint16)
19
+ exp_dict["tof_3"] = np.random.randint(0, 2**10, size=n_events, dtype=np.uint16)
20
+
21
+ # Encode the random events data into a bit-string
22
+ bin_str = ""
23
+ for i in range(n_events):
24
+ bin_str += f"{exp_dict['trigger_id'][i]:02b}" # 2-bits for trigger_id
25
+ bin_str += f"{exp_dict['de_tag'][i]:016b}" # 16-bits for de_tag
26
+ bin_str += f"{exp_dict['tof_1'][i]:010b}" # 10-bits for tof_1
27
+ bin_str += f"{exp_dict['tof_2'][i]:010b}" # 10-bits for tof_2
28
+ bin_str += f"{exp_dict['tof_3'][i]:010b}" # 10-bits for tof_3
29
+ # Convert the bit-string into a bytes object
30
+ bytes_obj = bytes([int(bin_str[i : i + 8], 2) for i in range(0, len(bin_str), 8)])
31
+ # Parse the fake events and check values
32
+ de_dict = parse_direct_events(bytes_obj)
33
+ for key in exp_dict.keys():
34
+ np.testing.assert_array_equal(de_dict[key], exp_dict[key])
35
+
36
+
37
+ def test_create_dataset():
38
+ """Test create_dataset"""
39
+ # dummy data to test create_dataset
40
+ data_dict = {
41
+ "trigger_id": [1, 2, 3],
42
+ "tof_1": [512, 512, 512],
43
+ "tof_2": [512, 512, 512],
44
+ "tof_3": [512, 512, 512],
45
+ "de_tag": [1, 2, 3],
46
+ "meta_seconds": [433522962, 433522962, 433522962],
47
+ "meta_subseconds": [512, 512, 512],
48
+ "esa_step": [4, 4, 4],
49
+ "ccsds_met": [433522961, 433522961, 433522961],
50
+ "src_seq_ctr": [10, 10, 10],
51
+ "pkt_len": [146, 146, 146],
52
+ "last_spin_num": [4, 4, 4],
53
+ "spin_invalids": [0, 0, 0],
54
+ }
55
+
56
+ # Test for good data
57
+ dataset = create_dataset(data_dict)
58
+ assert dataset["epoch"].shape == (3,)
@@ -48,7 +48,7 @@ def test_parse_sensor_number(test_str, expected):
48
48
  [
49
49
  ("despun_z", (1, 3), (1, 3)),
50
50
  ("hae_latitude", None, (1, 360)),
51
- ("counts", None, (1, 10, 360)),
51
+ ("counts", None, (1, 10, 5, 360)),
52
52
  ],
53
53
  )
54
54
  def test_full_dataarray(name, shape, expected_shape):
@@ -56,10 +56,11 @@ def test_full_dataarray(name, shape, expected_shape):
56
56
  coords = {
57
57
  "epoch": xr.DataArray(np.array([0])),
58
58
  "esa_energy_step": xr.DataArray(np.arange(10)),
59
+ "calibration_prod": xr.DataArray(np.arange(5)),
59
60
  "spin_angle_bin": xr.DataArray(np.arange(360)),
60
61
  }
61
62
  cdf_manager = ImapCdfAttributes()
62
- cdf_manager.load_variable_attributes("imap_hi_variable_attrs.yaml")
63
+ cdf_manager.add_instrument_variable_attrs(instrument="hi", level=None)
63
64
 
64
65
  dataarray = full_dataarray(
65
66
  name, cdf_manager.get_variable_attributes(f"hi_pset_{name}"), coords, shape
@@ -83,7 +84,7 @@ def test_create_dataset_variables(var_names, shape, lookup_str):
83
84
  assert len(l1b_de_vars) == len(var_names)
84
85
  attr_mgr = ImapCdfAttributes()
85
86
  attr_mgr.add_instrument_global_attrs("hi")
86
- attr_mgr.load_variable_attributes("imap_hi_variable_attrs.yaml")
87
+ attr_mgr.add_instrument_variable_attrs(instrument="hi", level=None)
87
88
 
88
89
  for var_name, data_array in l1b_de_vars.items():
89
90
  attrs = attr_mgr.get_variable_attributes(
@@ -4,11 +4,14 @@ import numpy as np
4
4
  import pytest
5
5
 
6
6
  from imap_processing import imap_module_directory
7
+ from imap_processing.hit.hit_utils import (
8
+ HitAPID,
9
+ )
7
10
  from imap_processing.hit.l0.decom_hit import (
8
11
  assemble_science_frames,
9
12
  decom_hit,
10
- find_valid_starting_indices,
11
- get_valid_indices,
13
+ decompress_rates_16_to_32,
14
+ get_valid_starting_indices,
12
15
  is_sequential,
13
16
  parse_count_rates,
14
17
  parse_data,
@@ -30,9 +33,10 @@ def sci_dataset():
30
33
  datasets_by_apid = packet_file_to_datasets(
31
34
  packet_file=packet_file,
32
35
  xtce_packet_definition=packet_definition,
36
+ use_derived_value=False,
33
37
  )
34
38
 
35
- science_dataset = datasets_by_apid[1252]
39
+ science_dataset = datasets_by_apid[HitAPID.HIT_SCIENCE]
36
40
  return science_dataset
37
41
 
38
42
 
@@ -58,8 +62,6 @@ def test_parse_data():
58
62
  def test_parse_count_rates(sci_dataset):
59
63
  """Test the parse_count_rates function."""
60
64
 
61
- # TODO: complete this test once the function is complete
62
-
63
65
  # Update ccsds header fields to use sc_tick as dimension
64
66
  sci_dataset = update_ccsds_header_dims(sci_dataset)
65
67
 
@@ -71,7 +73,10 @@ def test_parse_count_rates(sci_dataset):
71
73
  count_rate_vars = [
72
74
  "hdr_unit_num",
73
75
  "hdr_frame_version",
74
- "hdr_status_bits",
76
+ "hdr_dynamic_threshold_state",
77
+ "hdr_leak_conv",
78
+ "hdr_heater_duty_cycle",
79
+ "hdr_code_ok",
75
80
  "hdr_minute_cnt",
76
81
  "spare",
77
82
  "livetime",
@@ -128,7 +133,7 @@ def test_is_sequential():
128
133
  assert True
129
134
 
130
135
 
131
- def test_find_valid_starting_indices():
136
+ def test_get_valid_starting_indices():
132
137
  """Test the find_valid_starting_indices function."""
133
138
  flags = np.array(
134
139
  [
@@ -170,35 +175,13 @@ def test_find_valid_starting_indices():
170
175
  ]
171
176
  )
172
177
  counters = np.arange(35)
173
- result = find_valid_starting_indices(flags, counters)
178
+ result = get_valid_starting_indices(flags, counters)
174
179
  # The only valid starting index for a science frame
175
180
  # in the flags array is 15.
176
181
  assert len(result) == 1
177
182
  assert result[0] == 15
178
183
 
179
184
 
180
- def test_get_valid_indices():
181
- """Test the get_valid_indices function."""
182
- # Array of starting indices for science frames
183
- # in the science data
184
- indices = np.array([0, 20, 40])
185
- # Array of counters
186
- counters = np.arange(60)
187
- # Array of valid indices where the packets in the science
188
- # frame have corresponding counters in sequential order
189
- result = get_valid_indices(indices, counters, 20)
190
- # All indices are valid with sequential counters
191
- assert len(result) == 3
192
-
193
- # Test array with invalid indices (use smaller sample size)
194
- indices = np.array([0, 5, 10])
195
- # Array of counters (missing counters 6-8)
196
- counters = np.array([0, 1, 2, 3, 4, 5, 9, 10, 11, 12, 13, 14, 15, 16, 17])
197
- result = get_valid_indices(indices, counters, 5)
198
- # Only indices 0 and 10 are valid with sequential counters
199
- assert len(result) == 2
200
-
201
-
202
185
  def test_update_ccsds_header_dims(sci_dataset):
203
186
  """Test the update_ccsds_header_data function.
204
187
 
@@ -213,8 +196,28 @@ def test_assemble_science_frames(sci_dataset):
213
196
  """Test the assemble_science_frames function."""
214
197
  updated_dataset = update_ccsds_header_dims(sci_dataset)
215
198
  updated_dataset = assemble_science_frames(updated_dataset)
216
- assert "count_rates_binary" in updated_dataset
217
- assert "pha_binary" in updated_dataset
199
+ assert "count_rates_raw" in updated_dataset
200
+ assert "pha_raw" in updated_dataset
201
+
202
+
203
+ @pytest.mark.parametrize(
204
+ "packed, expected",
205
+ [
206
+ (0, 0), # Test with zero
207
+ (15, 15), # Test with packed integer with no scaling
208
+ (4096, 4096), # Test with packed integer with power = 1
209
+ (64188, 112132096), # Test with packed integer requiring scaling
210
+ (65535, 134201344), # Test with maximum 16-bit value
211
+ (62218, 79855616), # Test with arbitrary packed integer
212
+ ],
213
+ )
214
+ def test_decompress_rates_16_to_32(packed, expected):
215
+ """Test the decompress_rates_16_to_32 function.
216
+
217
+ This function decompresses a 16-bit packed integer
218
+ to a 32-bit integer. Used to decompress rates data.
219
+ """
220
+ assert decompress_rates_16_to_32(packed) == expected
218
221
 
219
222
 
220
223
  def test_decom_hit(sci_dataset):
@@ -223,8 +226,64 @@ def test_decom_hit(sci_dataset):
223
226
  This function orchestrates the unpacking and decompression
224
227
  of the HIT science data.
225
228
  """
226
- # TODO: complete this test once the function is complete
227
229
  updated_dataset = decom_hit(sci_dataset)
228
- print(updated_dataset)
229
- assert "count_rates_binary" in updated_dataset
230
- assert "hdr_unit_num" in updated_dataset
230
+ # Check if the dataset has the expected data variables
231
+ sci_fields = [
232
+ "version",
233
+ "type",
234
+ "sec_hdr_flg",
235
+ "pkt_apid",
236
+ "seq_flgs",
237
+ "src_seq_ctr",
238
+ "pkt_len",
239
+ "pha_raw",
240
+ "hdr_unit_num",
241
+ "hdr_frame_version",
242
+ "hdr_dynamic_threshold_state",
243
+ "hdr_leak_conv",
244
+ "hdr_heater_duty_cycle",
245
+ "hdr_code_ok",
246
+ "hdr_minute_cnt",
247
+ "livetime",
248
+ "num_trig",
249
+ "num_reject",
250
+ "num_acc_w_pha",
251
+ "num_acc_no_pha",
252
+ "num_haz_trig",
253
+ "num_haz_reject",
254
+ "num_haz_acc_w_pha",
255
+ "num_haz_acc_no_pha",
256
+ "sngrates",
257
+ "nread",
258
+ "nhazard",
259
+ "nadcstim",
260
+ "nodd",
261
+ "noddfix",
262
+ "nmulti",
263
+ "nmultifix",
264
+ "nbadtraj",
265
+ "nl2",
266
+ "nl3",
267
+ "nl4",
268
+ "npen",
269
+ "nformat",
270
+ "naside",
271
+ "nbside",
272
+ "nerror",
273
+ "nbadtags",
274
+ "coinrates",
275
+ "bufrates",
276
+ "l2fgrates",
277
+ "l2bgrates",
278
+ "l3fgrates",
279
+ "l3bgrates",
280
+ "penfgrates",
281
+ "penbgrates",
282
+ "ialirtrates",
283
+ "sectorates",
284
+ "l4fgrates",
285
+ "l4bgrates",
286
+ ]
287
+
288
+ for field in sci_fields:
289
+ assert field in updated_dataset