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.
- imap_processing/_version.py +2 -2
- imap_processing/ccsds/excel_to_xtce.py +2 -0
- imap_processing/cdf/config/imap_hi_variable_attrs.yaml +100 -1
- imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +14 -0
- imap_processing/cdf/config/imap_hit_l1a_variable_attrs.yaml +63 -1
- imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +7 -0
- imap_processing/cdf/config/imap_idex_l1a_variable_attrs.yaml +574 -231
- imap_processing/cdf/config/imap_idex_l1b_variable_attrs.yaml +326 -0
- imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +33 -23
- imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +7 -4
- imap_processing/cdf/utils.py +3 -5
- imap_processing/cli.py +13 -4
- imap_processing/codice/codice_l1a.py +5 -5
- imap_processing/codice/constants.py +9 -9
- imap_processing/codice/decompress.py +6 -2
- imap_processing/glows/l1a/glows_l1a.py +1 -2
- imap_processing/hi/l1a/hi_l1a.py +4 -4
- imap_processing/hi/l1a/histogram.py +106 -108
- imap_processing/hi/l1a/science_direct_event.py +91 -224
- imap_processing/hi/packet_definitions/TLM_HI_COMBINED_SCI.xml +3994 -0
- imap_processing/hit/l0/constants.py +2 -2
- imap_processing/hit/l0/decom_hit.py +12 -101
- imap_processing/hit/l1a/hit_l1a.py +164 -23
- imap_processing/ialirt/l0/process_codicelo.py +153 -0
- imap_processing/ialirt/l0/process_hit.py +5 -5
- imap_processing/ialirt/packet_definitions/ialirt_codicelo.xml +281 -0
- imap_processing/ialirt/process_ephemeris.py +212 -0
- imap_processing/idex/idex_l1a.py +55 -75
- imap_processing/idex/idex_l1b.py +192 -0
- imap_processing/idex/idex_variable_unpacking_and_eu_conversion.csv +33 -0
- imap_processing/idex/packet_definitions/idex_packet_definition.xml +97 -595
- imap_processing/lo/l0/decompression_tables/decompression_tables.py +16 -0
- imap_processing/lo/l0/lo_science.py +44 -12
- imap_processing/lo/l1a/lo_l1a.py +76 -8
- imap_processing/lo/packet_definitions/lo_xtce.xml +9877 -87
- imap_processing/mag/l1a/mag_l1a.py +1 -2
- imap_processing/mag/l1a/mag_l1a_data.py +1 -2
- imap_processing/mag/l1b/mag_l1b.py +2 -1
- imap_processing/spice/geometry.py +37 -19
- imap_processing/spice/time.py +144 -2
- imap_processing/swapi/l1/swapi_l1.py +3 -3
- imap_processing/swapi/packet_definitions/swapi_packet_definition.xml +1535 -446
- imap_processing/swe/l2/swe_l2.py +134 -17
- imap_processing/tests/ccsds/test_data/expected_output.xml +1 -1
- imap_processing/tests/codice/test_codice_l1a.py +8 -8
- imap_processing/tests/codice/test_decompress.py +4 -4
- imap_processing/tests/conftest.py +46 -43
- imap_processing/tests/hi/test_data/l0/H90_NHK_20241104.bin +0 -0
- imap_processing/tests/hi/test_data/l0/H90_sci_cnt_20241104.bin +0 -0
- imap_processing/tests/hi/test_data/l0/H90_sci_de_20241104.bin +0 -0
- imap_processing/tests/hi/test_hi_l1b.py +2 -2
- imap_processing/tests/hi/test_l1a.py +31 -58
- imap_processing/tests/hi/test_science_direct_event.py +58 -0
- imap_processing/tests/hit/test_data/sci_sample1.ccsds +0 -0
- imap_processing/tests/hit/test_decom_hit.py +60 -50
- imap_processing/tests/hit/test_hit_l1a.py +327 -12
- imap_processing/tests/hit/test_hit_l1b.py +76 -0
- imap_processing/tests/hit/validation_data/hskp_sample_eu.csv +89 -0
- imap_processing/tests/hit/validation_data/sci_sample_raw1.csv +29 -0
- imap_processing/tests/ialirt/test_data/l0/apid01152.tlm +0 -0
- imap_processing/tests/ialirt/test_data/l0/imap_codice_l1a_lo-ialirt_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/ialirt/unit/test_process_codicelo.py +106 -0
- imap_processing/tests/ialirt/unit/test_process_ephemeris.py +109 -0
- imap_processing/tests/ialirt/unit/test_process_hit.py +9 -6
- imap_processing/tests/idex/conftest.py +1 -1
- imap_processing/tests/idex/test_idex_l0.py +1 -1
- imap_processing/tests/idex/test_idex_l1a.py +7 -1
- imap_processing/tests/idex/test_idex_l1b.py +126 -0
- imap_processing/tests/lo/test_lo_l1a.py +7 -16
- imap_processing/tests/lo/test_lo_science.py +67 -3
- imap_processing/tests/lo/test_pkts/imap_lo_l0_raw_20240803_v002.pkts +0 -0
- imap_processing/tests/lo/validation_data/Instrument_FM1_T104_R129_20240803_ILO_SCI_DE_dec_DN_with_fills.csv +1999 -0
- imap_processing/tests/mag/test_mag_l1b.py +39 -5
- imap_processing/tests/spice/test_geometry.py +32 -6
- imap_processing/tests/spice/test_time.py +135 -6
- imap_processing/tests/swapi/test_swapi_decom.py +75 -69
- imap_processing/tests/swapi/test_swapi_l1.py +4 -4
- imap_processing/tests/swe/test_swe_l2.py +64 -8
- imap_processing/tests/test_utils.py +1 -1
- imap_processing/tests/ultra/test_data/l0/ultra45_raw_sc_ultrarawimg_withFSWcalcs_FM45_40P_Phi28p5_BeamCal_LinearScan_phi2850_theta-000_20240207T102740.csv +3314 -3314
- imap_processing/tests/ultra/unit/test_de.py +8 -3
- imap_processing/tests/ultra/unit/test_spatial_utils.py +125 -0
- imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +39 -29
- imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +2 -25
- imap_processing/ultra/constants.py +4 -0
- imap_processing/ultra/l1b/de.py +8 -14
- imap_processing/ultra/l1b/ultra_l1b_extended.py +29 -70
- imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +1 -36
- imap_processing/ultra/utils/spatial_utils.py +221 -0
- {imap_processing-0.8.0.dist-info → imap_processing-0.9.0.dist-info}/METADATA +1 -1
- {imap_processing-0.8.0.dist-info → imap_processing-0.9.0.dist-info}/RECORD +94 -76
- imap_processing/hi/l0/__init__.py +0 -0
- imap_processing/hi/l0/decom_hi.py +0 -24
- imap_processing/hi/packet_definitions/hi_packet_definition.xml +0 -482
- imap_processing/tests/hi/test_decom.py +0 -55
- imap_processing/tests/hi/test_l1a_sci_de.py +0 -72
- {imap_processing-0.8.0.dist-info → imap_processing-0.9.0.dist-info}/LICENSE +0 -0
- {imap_processing-0.8.0.dist-info → imap_processing-0.9.0.dist-info}/WHEEL +0 -0
- {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
|
|
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
|
|
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
|
|
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) ==
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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"] =
|
|
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
|
|
Binary file
|