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
@@ -0,0 +1,106 @@
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from imap_processing import imap_module_directory
5
+ from imap_processing.cdf.utils import load_cdf
6
+ from imap_processing.ialirt.l0.process_codicelo import (
7
+ append_cod_lo_data,
8
+ find_groups,
9
+ process_codicelo,
10
+ )
11
+ from imap_processing.utils import packet_file_to_datasets
12
+
13
+
14
+ @pytest.fixture(scope="session")
15
+ def xtce_codicelo_path():
16
+ """Returns the xtce directory."""
17
+ return (
18
+ imap_module_directory / "ialirt" / "packet_definitions" / "ialirt_codicelo.xml"
19
+ )
20
+
21
+
22
+ @pytest.fixture(scope="session")
23
+ def binary_packet_path():
24
+ """Returns the xtce directory."""
25
+ return (
26
+ imap_module_directory
27
+ / "tests"
28
+ / "ialirt"
29
+ / "test_data"
30
+ / "l0"
31
+ / "apid01152.tlm"
32
+ )
33
+
34
+
35
+ @pytest.fixture(scope="session")
36
+ def codicelo_validation_data():
37
+ """Returns the test data directory."""
38
+ data_path = (
39
+ imap_module_directory
40
+ / "tests"
41
+ / "ialirt"
42
+ / "test_data"
43
+ / "l0"
44
+ / "imap_codice_l1a_lo-ialirt_20241110193700_v0.0.0.cdf"
45
+ )
46
+ data = load_cdf(data_path)
47
+
48
+ return data
49
+
50
+
51
+ @pytest.fixture()
52
+ def codicelo_test_data(binary_packet_path, xtce_codicelo_path):
53
+ """Create xarray data"""
54
+ apid = 1152
55
+ codicelo_test_data = packet_file_to_datasets(
56
+ binary_packet_path, xtce_codicelo_path
57
+ )[apid]
58
+
59
+ return codicelo_test_data
60
+
61
+
62
+ def test_find_groups(codicelo_test_data):
63
+ """Tests find_groups"""
64
+
65
+ grouped_data = find_groups(codicelo_test_data)
66
+ unique_groups = np.unique(grouped_data["group"])
67
+ for group in unique_groups:
68
+ group_data = grouped_data["cod_lo_counter"].values[
69
+ grouped_data["group"] == group
70
+ ]
71
+ np.testing.assert_array_equal(group_data, np.arange(233))
72
+
73
+
74
+ def test_append_cod_lo_data(codicelo_test_data):
75
+ """Tests append_cod_lo_data"""
76
+
77
+ grouped_data = find_groups(codicelo_test_data)
78
+ unique_groups = np.unique(grouped_data["group"])
79
+ for group in unique_groups:
80
+ mask = grouped_data["group"] == group
81
+ filtered_indices = np.where(mask)[0]
82
+ group_data = grouped_data.isel(epoch=filtered_indices)
83
+ expected_cod_lo_counter = np.repeat(group_data["cod_lo_counter"].values, 15)
84
+ appended_data = append_cod_lo_data(group_data)
85
+ assert np.array_equal(
86
+ appended_data["cod_lo_counter"].values, expected_cod_lo_counter.astype(int)
87
+ )
88
+
89
+
90
+ def test_process_codicelo(codicelo_test_data, codicelo_validation_data, caplog):
91
+ """Tests process_codicelo."""
92
+ codicelo_product = process_codicelo(codicelo_test_data)
93
+ assert codicelo_product == [{}]
94
+
95
+ indices = (codicelo_test_data["cod_lo_acq"] != 0).values.nonzero()[0]
96
+ codicelo_test_data["cod_lo_counter"].values[indices[0] : indices[0] + 233] = (
97
+ np.random.permutation(233)
98
+ )
99
+
100
+ with caplog.at_level("WARNING"):
101
+ process_codicelo(codicelo_test_data)
102
+
103
+ assert any(
104
+ "does not contain all values from 0 to 232 without duplicates" in message
105
+ for message in caplog.text.splitlines()
106
+ )
@@ -0,0 +1,109 @@
1
+ """Test processEphemeris functions."""
2
+
3
+ import numpy as np
4
+ import pytest
5
+
6
+ from imap_processing.ialirt import process_ephemeris
7
+ from imap_processing.spice.time import str_to_et
8
+
9
+
10
+ def test_calculate_doppler():
11
+ """
12
+ Test the calculate_doppler() function.
13
+ """
14
+ assert process_ephemeris.calculate_doppler(805794429.1837295) == 1
15
+
16
+
17
+ @pytest.mark.external_kernel()
18
+ def test_latitude_longitude_to_ecef(furnish_kernels):
19
+ """
20
+ Test the latitude_longitude_to_ecef() function.
21
+
22
+ Test data is from https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/
23
+ georec_c.html.
24
+ """
25
+ longitude = 118.0 # degrees
26
+ latitude = 30.0 # degrees
27
+ altitude = 0.0 # kilometers
28
+
29
+ kernels = ["pck00011.tpc"]
30
+ with furnish_kernels(kernels):
31
+ rect_coords = process_ephemeris.latitude_longitude_to_ecef(
32
+ longitude, latitude, altitude
33
+ )
34
+
35
+ np.testing.assert_allclose(
36
+ rect_coords, [-2595.359123, 4881.160589, 3170.373523], atol=1e-6
37
+ )
38
+
39
+
40
+ @pytest.mark.external_kernel()
41
+ def test_calculate_azimuth_and_elevation(furnish_kernels):
42
+ """
43
+ Test the calculate_azimuth_and_elevation() function.
44
+ """
45
+ longitude = -71.41 # longitude in degrees
46
+ latitude = -33.94 # latitude in degrees
47
+ altitude = 0.157 # altitude in kilometers
48
+ # test single observation time
49
+ observation_time = 805794429.1837295 # "2025-07-14T19:46:00.000"
50
+
51
+ kernels = [
52
+ "pck00011.tpc",
53
+ "de440s.bsp",
54
+ ]
55
+ with furnish_kernels(kernels):
56
+ azimuth_result, elevation_result = (
57
+ process_ephemeris.calculate_azimuth_and_elevation(
58
+ longitude, latitude, altitude, observation_time
59
+ )
60
+ )
61
+ assert azimuth_result, elevation_result is not None
62
+
63
+ # test array of observation times
64
+ time_endpoints = ("2026 SEP 22 00:00:00", "2026 SEP 22 23:59:59")
65
+ time_interval = int(1e3) # seconds between data points
66
+ observation_time = np.arange(
67
+ str_to_et(time_endpoints[0]), str_to_et(time_endpoints[1]), time_interval
68
+ )
69
+ with furnish_kernels(kernels):
70
+ azimuth_result, elevation_result = (
71
+ process_ephemeris.calculate_azimuth_and_elevation(
72
+ longitude, latitude, altitude, observation_time
73
+ )
74
+ )
75
+ assert len(azimuth_result) == len(observation_time)
76
+
77
+
78
+ @pytest.mark.external_kernel()
79
+ def test_build_output(furnish_kernels):
80
+ """
81
+ Test the build_output() function.
82
+ """
83
+ # Example usage for Longovilo-Hacienda, Chile
84
+ # https://www.findlatitudeandlongitude.com/l/LONGOVILO%2C+CHILE/
85
+ longitude = -71.41 # longitude in degrees
86
+ latitude = -33.94 # latitude in degrees
87
+ altitude = 0.157 # altitude in kilometers
88
+ time_endpoints = ("2026 SEP 22 00:00:00", "2026 SEP 22 23:59:59")
89
+ time_interval = int(1e3) # seconds between data points
90
+
91
+ kernels = [
92
+ "naif0012.tls",
93
+ "pck00011.tpc",
94
+ "de440s.bsp",
95
+ ]
96
+ with furnish_kernels(kernels):
97
+ output_dict = process_ephemeris.build_output(
98
+ longitude, latitude, altitude, time_endpoints, time_interval
99
+ )
100
+
101
+ for key_name in ["azimuth", "elevation", "time", "doppler"]:
102
+ assert key_name in output_dict.keys()
103
+ assert len(output_dict[key_name]) == len(
104
+ np.arange(
105
+ str_to_et(time_endpoints[0]),
106
+ str_to_et(time_endpoints[1]),
107
+ time_interval,
108
+ )
109
+ )
@@ -20,7 +20,7 @@ def xtce_hit_path():
20
20
 
21
21
  @pytest.fixture(scope="session")
22
22
  def binary_packet_path():
23
- """Returns the xtce auxiliary directory."""
23
+ """Returns the xtce directory."""
24
24
  return (
25
25
  imap_module_directory
26
26
  / "tests"
@@ -33,7 +33,7 @@ def binary_packet_path():
33
33
 
34
34
  @pytest.fixture(scope="session")
35
35
  def hit_test_data():
36
- """Returns the xtce auxiliary directory."""
36
+ """Returns the test data directory."""
37
37
  data_path = (
38
38
  imap_module_directory
39
39
  / "tests"
@@ -141,7 +141,7 @@ def test_create_l1(xarray_data):
141
141
  assert l1["L4IBHG"] == 2
142
142
 
143
143
 
144
- def test_process_hit(xarray_data):
144
+ def test_process_hit(xarray_data, caplog):
145
145
  """Tests process_hit."""
146
146
 
147
147
  # Tests that it functions normally
@@ -167,11 +167,14 @@ def test_process_hit(xarray_data):
167
167
  i for i in range(29) for _ in range(2)
168
168
  ] + [59, 59]
169
169
 
170
- with pytest.raises(
171
- ValueError, match="does not contain all values from 0 to 59 without duplicates"
172
- ):
170
+ with caplog.at_level("WARNING"):
173
171
  process_hit(subset)
174
172
 
173
+ assert any(
174
+ "does not contain all values from 0 to 59 without duplicates" in message
175
+ for message in caplog.text.splitlines()
176
+ )
177
+
175
178
 
176
179
  def test_decom_packets(xarray_data, hit_test_data):
177
180
  """This function checks that all instrument parameters are accounted for."""
@@ -7,7 +7,7 @@ from imap_processing import imap_module_directory
7
7
  from imap_processing.idex.idex_l1a import PacketParser
8
8
 
9
9
 
10
- @pytest.fixture()
10
+ @pytest.fixture(scope="module")
11
11
  def decom_test_data() -> xr.Dataset:
12
12
  """Return a ``xarray`` dataset containing test data.
13
13
 
@@ -14,7 +14,7 @@ def test_idex_decom_length(decom_test_data: xr.Dataset):
14
14
  decom_test_data : xarray.Dataset
15
15
  The dataset to test with
16
16
  """
17
- assert len(decom_test_data) == 42
17
+ assert len(decom_test_data) == 106
18
18
 
19
19
 
20
20
  def test_idex_decom_event_num(decom_test_data: xr.Dataset):
@@ -32,11 +32,15 @@ def test_bad_cdf_attributes(decom_test_data: xr.Dataset):
32
32
  decom_test_data : xarray.Dataset
33
33
  The dataset to test with
34
34
  """
35
- del decom_test_data["TOF_High"].attrs["DEPEND_1"]
35
+ tof_catdesc = decom_test_data["TOF_High"].attrs["CATDESC"]
36
+ del decom_test_data["TOF_High"].attrs["CATDESC"]
36
37
 
37
38
  with pytest.raises(ISTPError):
38
39
  write_cdf(decom_test_data)
39
40
 
41
+ # Add attributes back so future tests do not fail
42
+ decom_test_data["TOF_High"].attrs["CATDESC"] = tof_catdesc
43
+
40
44
 
41
45
  def test_bad_cdf_file_data(decom_test_data: xr.Dataset):
42
46
  """Ensure an ``ISTPError`` is raised when using bad data.
@@ -72,6 +76,8 @@ def test_bad_cdf_file_data(decom_test_data: xr.Dataset):
72
76
  with pytest.raises(ISTPError):
73
77
  write_cdf(decom_test_data)
74
78
 
79
+ del decom_test_data["Bad_data"]
80
+
75
81
 
76
82
  def test_idex_tof_high_data_from_cdf(decom_test_data: xr.Dataset):
77
83
  """Verify that a sample of the data is correct inside the CDF file.
@@ -0,0 +1,126 @@
1
+ """Tests the L1b processing for IDEX data"""
2
+
3
+ from unittest import mock
4
+
5
+ import numpy as np
6
+ import pandas as pd
7
+ import pytest
8
+ import xarray as xr
9
+
10
+ from imap_processing import imap_module_directory
11
+ from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
12
+ from imap_processing.cdf.utils import write_cdf
13
+ from imap_processing.idex.idex_l1b import idex_l1b, unpack_instrument_settings
14
+
15
+
16
+ @pytest.fixture(scope="module")
17
+ def l1b_dataset(decom_test_data: xr.Dataset) -> xr.Dataset:
18
+ """Return a ``xarray`` dataset containing test data.
19
+
20
+ Returns
21
+ -------
22
+ dataset : xr.Dataset
23
+ A ``xarray`` dataset containing the test data
24
+ """
25
+ dataset = idex_l1b(decom_test_data, data_version="001")
26
+ return dataset
27
+
28
+
29
+ def test_l1b_cdf_filenames(l1b_dataset: xr.Dataset):
30
+ """Tests that the ``idex_l1b`` function generates datasets
31
+ with the expected logical source.
32
+
33
+ Parameters
34
+ ----------
35
+ l1b_dataset : xr.Dataset
36
+ A ``xarray`` dataset containing the test data
37
+ """
38
+ expected_src = "imap_idex_l1b_sci"
39
+ assert l1b_dataset.attrs["Logical_source"] == expected_src
40
+
41
+
42
+ def test_idex_cdf_file(l1b_dataset: xr.Dataset):
43
+ """Verify the CDF file can be created with no errors.
44
+
45
+ Parameters
46
+ ----------
47
+ l1b_dataset : xarray.Dataset
48
+ The dataset to test with
49
+ """
50
+
51
+ file_name = write_cdf(l1b_dataset)
52
+
53
+ assert file_name.exists()
54
+ assert file_name.name == "imap_idex_l1b_sci_20231214_v001.cdf"
55
+
56
+
57
+ def test_idex_waveform_units(l1b_dataset: xr.Dataset):
58
+ """Verify the CDF instrument settings and waveforms have the correct units.
59
+
60
+ Parameters
61
+ ----------
62
+ l1b_dataset : xarray.Dataset
63
+ The dataset to test with
64
+ """
65
+ cdf_var_defs_path = (
66
+ f"{imap_module_directory}/idex/idex_variable_unpacking_and_eu_conversion.csv"
67
+ )
68
+ cdf_var_defs = pd.read_csv(cdf_var_defs_path)
69
+
70
+ # Check instrument setting units
71
+ for _, row in cdf_var_defs.iterrows():
72
+ var_name = row["mnemonic"]
73
+ assert l1b_dataset[var_name].attrs["units"] == row["unit"]
74
+
75
+ # Check waveform units
76
+ waveform_var_names = [
77
+ "TOF_High",
78
+ "TOF_Low",
79
+ "TOF_Mid",
80
+ "Ion_Grid",
81
+ "Target_Low",
82
+ "Target_High",
83
+ ]
84
+
85
+ for var_name in waveform_var_names:
86
+ assert l1b_dataset[var_name].attrs["UNITS"] == "pC"
87
+
88
+
89
+ def test_unpack_instrument_settings():
90
+ """
91
+ Check that the instrument setting variables are being unpacked correctly
92
+
93
+ Example
94
+ -------
95
+ In this example, we are using a test variable that has five bits
96
+ Idx__test_var01 = 0b10010
97
+
98
+ Int(0b10010) = 18
99
+
100
+ This should unpack into test_var0, and test_var1
101
+ - test_var0 is two bits long and starts at 0, and the unpacked value should be 2
102
+ - test_var1 is three bits long and starts at 3, and the unpacked value should be 4
103
+ """
104
+ # Create test dataset with an array shape = 5 all values = 18
105
+ test_ds = xr.Dataset({"idx__test_var01": xr.DataArray(np.full(5, 18))})
106
+
107
+ test_cdf_defs_df = pd.DataFrame(
108
+ {
109
+ "mnemonic": ["test_var0", "test_var1"],
110
+ "var_name": ["idx__test_var01", "idx__test_var01"],
111
+ "starting_bit": [0, 2],
112
+ "nbits_padding_before": [0, 0],
113
+ "unsigned_nbits": [2, 3],
114
+ }
115
+ )
116
+ idex_attrs = ImapCdfAttributes()
117
+ # Mock attribute manager variable attrs
118
+ with mock.patch.object(
119
+ idex_attrs, "get_variable_attributes", return_value={"CATDESC": "Test var"}
120
+ ):
121
+ unpacked_dict = unpack_instrument_settings(
122
+ test_ds, test_cdf_defs_df, idex_attrs
123
+ )
124
+
125
+ assert np.all(unpacked_dict["test_var0"] == 2)
126
+ assert np.all(unpacked_dict["test_var1"] == 4)
@@ -1,29 +1,20 @@
1
- from pathlib import Path
2
-
3
1
  import numpy as np
4
- import pytest
5
2
 
6
3
  from imap_processing import imap_module_directory
7
4
  from imap_processing.lo.l1a.lo_l1a import lo_l1a
8
5
 
9
6
 
10
- @pytest.mark.skip(reason="not implemented")
11
- @pytest.mark.parametrize(
12
- ("dependency", "expected_logical_source"),
13
- [
14
- (Path("imap_lo_l0_de_20100101_v001.pkts"), "imap_lo_l1a_de"),
15
- (
16
- Path("imap_lo_l0_spin_20100101_v001.pkt"),
17
- "imap_lo_l1a_spin",
18
- ),
19
- ],
20
- )
21
- def test_lo_l1a(dependency, expected_logical_source):
7
+ def test_lo_l1a():
22
8
  # Act
9
+ dependency = (
10
+ imap_module_directory / "tests/lo/test_pkts/imap_lo_l0_raw_20240803_v002.pkts"
11
+ )
12
+ expected_logical_source = ["imap_lo_l1a_histogram", "imap_lo_l1a_de"]
23
13
  output_dataset = lo_l1a(dependency, "001")
24
14
 
25
15
  # Assert
26
- assert expected_logical_source == output_dataset.attrs["Logical_source"]
16
+ for dataset, logical_source in zip(output_dataset, expected_logical_source):
17
+ assert logical_source == dataset.attrs["Logical_source"]
27
18
 
28
19
 
29
20
  def test_lo_l1a_dataset():
@@ -1,8 +1,11 @@
1
1
  import numpy as np
2
+ import pandas as pd
2
3
  import pytest
3
4
  import xarray as xr
4
5
 
6
+ from imap_processing import imap_module_directory
5
7
  from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
8
+ from imap_processing.lo.l0.lo_apid import LoAPID
6
9
  from imap_processing.lo.l0.lo_science import (
7
10
  combine_segmented_packets,
8
11
  parse_de_bin,
@@ -10,10 +13,14 @@ from imap_processing.lo.l0.lo_science import (
10
13
  parse_fixed_fields,
11
14
  parse_variable_fields,
12
15
  )
16
+ from imap_processing.utils import convert_to_binary_string, packet_file_to_datasets
13
17
 
14
18
 
15
19
  @pytest.fixture()
16
20
  def fake_de_dataset():
21
+ # binary packet fields
22
+ count = "0000000000000010" # 2
23
+ passes = "00000000000000000000000000000001" # 1
17
24
  # DE One
18
25
  absent_1 = "0000" # case 0
19
26
  time_1 = "000001100100" # 100
@@ -37,7 +44,9 @@ def fake_de_dataset():
37
44
  pos_2 = "00" # 0
38
45
 
39
46
  de_data = (
40
- absent_1
47
+ count
48
+ + passes
49
+ + absent_1
41
50
  + time_1
42
51
  + energy_1
43
52
  + mode_1
@@ -55,13 +64,27 @@ def fake_de_dataset():
55
64
  dataset = xr.Dataset(
56
65
  data_vars=dict(
57
66
  count=(["time"], np.array([2])),
58
- data=(["time"], np.array([de_data])),
67
+ events=(["time"], np.array([de_data])),
59
68
  )
60
69
  )
61
70
 
62
71
  return dataset
63
72
 
64
73
 
74
+ @pytest.fixture()
75
+ def sample_data():
76
+ xtce_file = imap_module_directory / "lo/packet_definitions/lo_xtce.xml"
77
+ dependency = (
78
+ imap_module_directory / "tests/lo/test_pkts/imap_lo_l0_raw_20240803_v002.pkts"
79
+ )
80
+ datasets_by_apid = packet_file_to_datasets(
81
+ packet_file=dependency.resolve(),
82
+ xtce_packet_definition=xtce_file.resolve(),
83
+ use_derived_value=False,
84
+ )
85
+ return datasets_by_apid
86
+
87
+
65
88
  @pytest.fixture()
66
89
  def segmented_pkts_fake_data():
67
90
  dataset = xr.Dataset(
@@ -142,6 +165,9 @@ def test_parse_events(fake_de_dataset, attr_mgr):
142
165
 
143
166
 
144
167
  def test_parse_fixed_fields(initialized_dataset):
168
+ # Arrange
169
+ initialized_dataset.attrs["bit_pos"] = 48
170
+
145
171
  # Act
146
172
  dataset = parse_fixed_fields(initialized_dataset, 0, 0)
147
173
 
@@ -158,7 +184,7 @@ def test_parse_variable_fields(initialized_dataset):
158
184
  # Arrange
159
185
  initialized_dataset["coincidence_type"].values = np.array([0, 255])
160
186
  initialized_dataset["mode"].values = np.array([1, 255])
161
- initialized_dataset.attrs["bit_pos"] = 20
187
+ initialized_dataset.attrs["bit_pos"] = 68
162
188
 
163
189
  # Act
164
190
  dataset = parse_variable_fields(initialized_dataset, 0, 0)
@@ -202,3 +228,41 @@ def test_combine_segmented_packets(segmented_pkts_fake_data):
202
228
  ),
203
229
  )
204
230
  np.testing.assert_array_equal(dataset["epoch"].values, np.array([0, 10, 30]))
231
+
232
+
233
+ def test_validate_parse_events(sample_data, attr_mgr):
234
+ de_data = sample_data[LoAPID.ILO_SCI_DE]
235
+ validation_path = (
236
+ imap_module_directory / "tests/lo/validation_data/"
237
+ "Instrument_FM1_T104_R129_20240803_ILO_SCI_DE_dec_DN_with_fills.csv"
238
+ )
239
+
240
+ validation_data = pd.read_csv(validation_path)
241
+ de_fields = [
242
+ "coincidence_type",
243
+ "de_time",
244
+ "esa_step",
245
+ "mode",
246
+ "tof0",
247
+ "tof1",
248
+ "tof2",
249
+ "tof3",
250
+ "cksm",
251
+ "pos",
252
+ ]
253
+
254
+ de_data["data"] = xr.DataArray(
255
+ [convert_to_binary_string(data) for data in de_data["data"].values],
256
+ dims=de_data["data"].dims,
257
+ attrs=de_data["data"].attrs,
258
+ )
259
+ de_data = combine_segmented_packets(de_data)
260
+ dataset = parse_events(de_data, attr_mgr)
261
+
262
+ for field in de_fields:
263
+ np.testing.assert_array_equal(
264
+ dataset[field].values, validation_data[field.upper()].values
265
+ )
266
+
267
+ assert dataset["de_count"].values == 1998
268
+ assert dataset["passes"].values == 8