imap-processing 0.15.0__py3-none-any.whl → 0.16.1__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/cdf/config/imap_hit_l1a_variable_attrs.yaml +1404 -93
- imap_processing/cdf/config/imap_ialirt_l1_variable_attrs.yaml +113 -130
- imap_processing/cli.py +1 -4
- imap_processing/codice/codice_l1a.py +87 -62
- imap_processing/codice/codice_l2.py +0 -8
- imap_processing/codice/constants.py +16 -5
- imap_processing/hi/hi_l1a.py +447 -0
- imap_processing/hi/{l1b/hi_l1b.py → hi_l1b.py} +1 -1
- imap_processing/hi/{l1c/hi_l1c.py → hi_l1c.py} +21 -21
- imap_processing/hi/{l2/hi_l2.py → hi_l2.py} +13 -13
- imap_processing/hi/utils.py +10 -9
- imap_processing/hit/l0/constants.py +3 -1
- imap_processing/hit/l0/decom_hit.py +45 -11
- imap_processing/hit/l1a/hit_l1a.py +31 -24
- imap_processing/hit/l1b/hit_l1b.py +30 -11
- imap_processing/hit/l2/hit_l2.py +8 -11
- imap_processing/ialirt/constants.py +38 -0
- imap_processing/ialirt/l0/parse_mag.py +1 -1
- imap_processing/ialirt/l0/process_codice.py +91 -0
- imap_processing/ialirt/l0/process_hit.py +12 -21
- imap_processing/ialirt/l0/process_swapi.py +172 -23
- imap_processing/ialirt/l0/process_swe.py +3 -10
- imap_processing/ialirt/utils/constants.py +16 -2
- imap_processing/ialirt/utils/create_xarray.py +59 -11
- imap_processing/ultra/utils/ultra_l1_utils.py +4 -2
- {imap_processing-0.15.0.dist-info → imap_processing-0.16.1.dist-info}/METADATA +1 -1
- {imap_processing-0.15.0.dist-info → imap_processing-0.16.1.dist-info}/RECORD +31 -37
- imap_processing/hi/l1a/__init__.py +0 -0
- imap_processing/hi/l1a/hi_l1a.py +0 -98
- imap_processing/hi/l1a/histogram.py +0 -152
- imap_processing/hi/l1a/science_direct_event.py +0 -214
- imap_processing/hi/l1b/__init__.py +0 -0
- imap_processing/hi/l1c/__init__.py +0 -0
- imap_processing/hi/l2/__init__.py +0 -0
- imap_processing/ialirt/l0/process_codicehi.py +0 -156
- imap_processing/ialirt/l0/process_codicelo.py +0 -41
- {imap_processing-0.15.0.dist-info → imap_processing-0.16.1.dist-info}/LICENSE +0 -0
- {imap_processing-0.15.0.dist-info → imap_processing-0.16.1.dist-info}/WHEEL +0 -0
- {imap_processing-0.15.0.dist-info → imap_processing-0.16.1.dist-info}/entry_points.txt +0 -0
imap_processing/hi/l1a/hi_l1a.py
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
"""IMAP-HI L1A processing module."""
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
from typing import Union
|
|
6
|
-
|
|
7
|
-
import xarray as xr
|
|
8
|
-
|
|
9
|
-
from imap_processing import imap_module_directory
|
|
10
|
-
from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
|
|
11
|
-
from imap_processing.hi.l1a.histogram import create_dataset as hist_create_dataset
|
|
12
|
-
from imap_processing.hi.l1a.science_direct_event import science_direct_event
|
|
13
|
-
from imap_processing.hi.utils import HIAPID
|
|
14
|
-
from imap_processing.utils import packet_file_to_datasets
|
|
15
|
-
|
|
16
|
-
logger = logging.getLogger(__name__)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def hi_l1a(packet_file_path: Union[str, Path]) -> list[xr.Dataset]:
|
|
20
|
-
"""
|
|
21
|
-
Will process IMAP raw data to l1a.
|
|
22
|
-
|
|
23
|
-
Parameters
|
|
24
|
-
----------
|
|
25
|
-
packet_file_path : str
|
|
26
|
-
Data packet file path.
|
|
27
|
-
|
|
28
|
-
Returns
|
|
29
|
-
-------
|
|
30
|
-
processed_data : list[xarray.Dataset]
|
|
31
|
-
List of processed xarray dataset.
|
|
32
|
-
"""
|
|
33
|
-
datasets_by_apid = hi_packet_file_to_datasets(packet_file_path)
|
|
34
|
-
|
|
35
|
-
# Process science to l1a.
|
|
36
|
-
processed_data = []
|
|
37
|
-
for apid in datasets_by_apid:
|
|
38
|
-
try:
|
|
39
|
-
apid_enum = HIAPID(apid)
|
|
40
|
-
except ValueError as err:
|
|
41
|
-
raise RuntimeError(f"Encountered unexpected APID [{apid}]") from err
|
|
42
|
-
|
|
43
|
-
logger.info(f"Processing IMAP-Hi data for {apid_enum.name} packets")
|
|
44
|
-
|
|
45
|
-
if apid_enum in [HIAPID.H45_SCI_CNT, HIAPID.H90_SCI_CNT]:
|
|
46
|
-
data = hist_create_dataset(datasets_by_apid[apid])
|
|
47
|
-
gattr_key = "imap_hi_l1a_hist_attrs"
|
|
48
|
-
elif apid_enum in [HIAPID.H45_SCI_DE, HIAPID.H90_SCI_DE]:
|
|
49
|
-
data = science_direct_event(datasets_by_apid[apid])
|
|
50
|
-
gattr_key = "imap_hi_l1a_de_attrs"
|
|
51
|
-
elif apid_enum in [HIAPID.H45_APP_NHK, HIAPID.H90_APP_NHK]:
|
|
52
|
-
data = datasets_by_apid[apid]
|
|
53
|
-
gattr_key = "imap_hi_l1a_hk_attrs"
|
|
54
|
-
elif apid_enum in [HIAPID.H45_DIAG_FEE, HIAPID.H90_DIAG_FEE]:
|
|
55
|
-
data = datasets_by_apid[apid]
|
|
56
|
-
gattr_key = "imap_hi_l1a_diagfee_attrs"
|
|
57
|
-
|
|
58
|
-
# Update dataset global attributes
|
|
59
|
-
attr_mgr = ImapCdfAttributes()
|
|
60
|
-
attr_mgr.add_instrument_global_attrs("hi")
|
|
61
|
-
data.attrs.update(attr_mgr.get_global_attributes(gattr_key))
|
|
62
|
-
|
|
63
|
-
# set the sensor string in Logical_source
|
|
64
|
-
sensor_str = apid_enum.sensor
|
|
65
|
-
data.attrs["Logical_source"] = data.attrs["Logical_source"].format(
|
|
66
|
-
sensor=sensor_str
|
|
67
|
-
)
|
|
68
|
-
processed_data.append(data)
|
|
69
|
-
return processed_data
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def hi_packet_file_to_datasets(
|
|
73
|
-
packet_file_path: Union[str, Path], use_derived_value: bool = False
|
|
74
|
-
) -> dict[int, xr.Dataset]:
|
|
75
|
-
"""
|
|
76
|
-
Extract hi datasets from packet file.
|
|
77
|
-
|
|
78
|
-
Parameters
|
|
79
|
-
----------
|
|
80
|
-
packet_file_path : str
|
|
81
|
-
L0 packet file path.
|
|
82
|
-
use_derived_value : bool
|
|
83
|
-
Whether to use the derived value from the XTCE definition. Default is False.
|
|
84
|
-
|
|
85
|
-
Returns
|
|
86
|
-
-------
|
|
87
|
-
datasets : dict[int, xarray.Dataset]
|
|
88
|
-
Dictionary of xarray datasets keyed by APID.
|
|
89
|
-
"""
|
|
90
|
-
packet_def_file = (
|
|
91
|
-
imap_module_directory / "hi/packet_definitions/TLM_HI_COMBINED_SCI.xml"
|
|
92
|
-
)
|
|
93
|
-
datasets_by_apid = packet_file_to_datasets(
|
|
94
|
-
packet_file=packet_file_path,
|
|
95
|
-
xtce_packet_definition=packet_def_file,
|
|
96
|
-
use_derived_value=use_derived_value,
|
|
97
|
-
)
|
|
98
|
-
return datasets_by_apid
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
"""Unpack IMAP-Hi histogram data."""
|
|
2
|
-
|
|
3
|
-
import numpy as np
|
|
4
|
-
import xarray as xr
|
|
5
|
-
from numpy._typing import NDArray
|
|
6
|
-
|
|
7
|
-
from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
|
|
8
|
-
|
|
9
|
-
# define the names of the 24 counter arrays
|
|
10
|
-
# contained in the histogram packet
|
|
11
|
-
QUALIFIED_COUNTERS = (
|
|
12
|
-
"ab_qualified",
|
|
13
|
-
"c1c2_qualified",
|
|
14
|
-
"ac1_qualified",
|
|
15
|
-
"bc1_qualified",
|
|
16
|
-
"abc1_qualified",
|
|
17
|
-
"ac1c2_qualified",
|
|
18
|
-
"bc1c2_qualified",
|
|
19
|
-
"abc1c2_qualified",
|
|
20
|
-
)
|
|
21
|
-
LONG_COUNTERS = (
|
|
22
|
-
"a_first_only",
|
|
23
|
-
"b_first_only",
|
|
24
|
-
"c_first_only",
|
|
25
|
-
"ab_long",
|
|
26
|
-
"c1c2_long",
|
|
27
|
-
"ac1_long",
|
|
28
|
-
"bc1_long",
|
|
29
|
-
"abc1_long",
|
|
30
|
-
"ac1c2_long",
|
|
31
|
-
"bc1c2_long",
|
|
32
|
-
"abc1c2_long",
|
|
33
|
-
)
|
|
34
|
-
TOTAL_COUNTERS = ("a_total", "b_total", "c_total", "fee_de_recd", "fee_de_sent")
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def create_dataset(input_ds: xr.Dataset) -> xr.Dataset:
|
|
38
|
-
"""
|
|
39
|
-
Create dataset for a number of Hi Histogram packets.
|
|
40
|
-
|
|
41
|
-
Parameters
|
|
42
|
-
----------
|
|
43
|
-
input_ds : xarray.Dataset
|
|
44
|
-
Dataset of packets generated using the
|
|
45
|
-
`imap_processing.utils.packet_file_to_datasets` function.
|
|
46
|
-
|
|
47
|
-
Returns
|
|
48
|
-
-------
|
|
49
|
-
dataset : xarray.Dataset
|
|
50
|
-
Dataset with all metadata field data in xr.DataArray.
|
|
51
|
-
"""
|
|
52
|
-
attr_mgr = ImapCdfAttributes()
|
|
53
|
-
attr_mgr.add_instrument_global_attrs(instrument="hi")
|
|
54
|
-
attr_mgr.add_instrument_variable_attrs(instrument="hi", level=None)
|
|
55
|
-
|
|
56
|
-
# Rename shcoarse variable (do this first since it copies the input_ds)
|
|
57
|
-
dataset = input_ds.rename_vars({"shcoarse": "ccsds_met"})
|
|
58
|
-
|
|
59
|
-
dataset.epoch.attrs.update(
|
|
60
|
-
attr_mgr.get_variable_attributes("epoch"),
|
|
61
|
-
)
|
|
62
|
-
# Add the hist_angle coordinate
|
|
63
|
-
# Histogram data is binned in 90, 4-degree bins
|
|
64
|
-
attrs = attr_mgr.get_variable_attributes("hi_hist_angle")
|
|
65
|
-
dataset.coords.update(
|
|
66
|
-
{
|
|
67
|
-
"angle": xr.DataArray(
|
|
68
|
-
np.arange(2, 360, 4),
|
|
69
|
-
name="angle",
|
|
70
|
-
dims=["angle"],
|
|
71
|
-
attrs=attrs,
|
|
72
|
-
)
|
|
73
|
-
}
|
|
74
|
-
)
|
|
75
|
-
# Update existing variable attributes
|
|
76
|
-
for var_name in [
|
|
77
|
-
"version",
|
|
78
|
-
"type",
|
|
79
|
-
"sec_hdr_flg",
|
|
80
|
-
"pkt_apid",
|
|
81
|
-
"seq_flgs",
|
|
82
|
-
"src_seq_ctr",
|
|
83
|
-
"pkt_len",
|
|
84
|
-
"ccsds_met",
|
|
85
|
-
"esa_step",
|
|
86
|
-
"num_of_spins",
|
|
87
|
-
"cksum",
|
|
88
|
-
]:
|
|
89
|
-
attrs = attr_mgr.get_variable_attributes(f"hi_hist_{var_name}")
|
|
90
|
-
dataset.data_vars[var_name].attrs.update(attrs)
|
|
91
|
-
|
|
92
|
-
new_vars = dict()
|
|
93
|
-
# Populate 90-element histogram counters
|
|
94
|
-
default_counter_attrs = attr_mgr.get_variable_attributes("hi_hist_counters")
|
|
95
|
-
for counter_name in (*QUALIFIED_COUNTERS, *LONG_COUNTERS, *TOTAL_COUNTERS):
|
|
96
|
-
# Inject counter name into generic counter attributes
|
|
97
|
-
counter_attrs = default_counter_attrs.copy()
|
|
98
|
-
for key, val in counter_attrs.items():
|
|
99
|
-
if isinstance(val, str) and "{counter_name}" in val:
|
|
100
|
-
counter_attrs[key] = val.format(counter_name=counter_name)
|
|
101
|
-
# Instantiate the counter DataArray
|
|
102
|
-
new_vars[counter_name] = xr.DataArray(
|
|
103
|
-
data=unpack_hist_counter(input_ds[counter_name].data.sum()),
|
|
104
|
-
dims=["epoch", "angle"],
|
|
105
|
-
attrs=counter_attrs,
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
# Generate label variable for angle coordinate
|
|
109
|
-
new_vars["angle_label"] = xr.DataArray(
|
|
110
|
-
dataset.coords["angle"].values.astype(str),
|
|
111
|
-
name="angle_label",
|
|
112
|
-
dims=["angle"],
|
|
113
|
-
attrs=attr_mgr.get_variable_attributes(
|
|
114
|
-
"hi_hist_angle_label", check_schema=False
|
|
115
|
-
),
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
dataset.update(new_vars)
|
|
119
|
-
|
|
120
|
-
return dataset
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
def unpack_hist_counter(counter_bytes: bytes) -> NDArray[np.uint16]:
|
|
124
|
-
"""
|
|
125
|
-
Unpack Hi SCI_CNT counter data for a single counter.
|
|
126
|
-
|
|
127
|
-
Parameters
|
|
128
|
-
----------
|
|
129
|
-
counter_bytes : bytes
|
|
130
|
-
Sum individual bytes for all epochs of a Hi SCI_CNT counter.
|
|
131
|
-
|
|
132
|
-
Returns
|
|
133
|
-
-------
|
|
134
|
-
output_array : numpy.ndarray[numpy.uint16]
|
|
135
|
-
The unpacked 12-bit unsigned integers for the input bytes. The
|
|
136
|
-
output array has a shape of (n, 90) where n is the number of SCI_CNT
|
|
137
|
-
packets in the input dataset.
|
|
138
|
-
"""
|
|
139
|
-
# Interpret bytes for all epochs of current counter as uint8 array
|
|
140
|
-
counter_uint8 = np.frombuffer(counter_bytes, dtype=np.uint8)
|
|
141
|
-
# Split into triplets of upper-byte, split-byte and lower-byte arrays
|
|
142
|
-
upper_uint8, split_unit8, lower_uint8 = np.reshape(
|
|
143
|
-
counter_uint8, (3, -1), order="F"
|
|
144
|
-
).astype(np.uint16)
|
|
145
|
-
# Compute even indexed uint12 values from upper-byte and first 4-bits of
|
|
146
|
-
# split-byte
|
|
147
|
-
even_uint12 = (upper_uint8 << 4) + (split_unit8 >> 4)
|
|
148
|
-
# Compute odd indexed uint12 values from lower 4-bits of split-byte and
|
|
149
|
-
# lower-byte
|
|
150
|
-
odd_uint12 = ((split_unit8 & (2**4 - 1)) << 8) + lower_uint8
|
|
151
|
-
output_array = np.column_stack((even_uint12, odd_uint12)).reshape(-1, 90)
|
|
152
|
-
return output_array
|
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
"""IMAP-Hi direct event processing."""
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
from collections import defaultdict
|
|
5
|
-
|
|
6
|
-
import numpy as np
|
|
7
|
-
import numpy._typing as npt
|
|
8
|
-
import xarray as xr
|
|
9
|
-
|
|
10
|
-
from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
|
|
11
|
-
from imap_processing.spice.time import met_to_ttj2000ns
|
|
12
|
-
|
|
13
|
-
# TODO: read DE_CLOCK_TICK_US from
|
|
14
|
-
# instrument status summary later. This value
|
|
15
|
-
# is rarely change but want to be able to change
|
|
16
|
-
# it if needed. It stores information about how
|
|
17
|
-
# fast the time was ticking. It is in microseconds.
|
|
18
|
-
DE_CLOCK_TICK_US = 1999
|
|
19
|
-
DE_CLOCK_TICK_S = DE_CLOCK_TICK_US / 1e6
|
|
20
|
-
HALF_CLOCK_TICK_S = DE_CLOCK_TICK_S / 2
|
|
21
|
-
|
|
22
|
-
MILLISECOND_TO_S = 1e-3
|
|
23
|
-
|
|
24
|
-
logger = logging.getLogger(__name__)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def parse_direct_events(de_data: bytes) -> dict[str, npt.ArrayLike]:
|
|
28
|
-
"""
|
|
29
|
-
Parse event data from a binary blob.
|
|
30
|
-
|
|
31
|
-
IMAP-Hi direct event data information is stored in
|
|
32
|
-
48-bits as follows:
|
|
33
|
-
|
|
34
|
-
| Read 48-bits into 16, 2, 10, 10, 10, bits. Each of these breaks
|
|
35
|
-
| down as:
|
|
36
|
-
|
|
|
37
|
-
| de_tag - 16 bits
|
|
38
|
-
| start_bitmask_data - 2 bits (tA=1, tB=2, tC1=3)
|
|
39
|
-
| tof_1 - 10 bit counter
|
|
40
|
-
| tof_2 - 10 bit counter
|
|
41
|
-
| tof_3 - 10 bit counter
|
|
42
|
-
|
|
43
|
-
There are at most total of 664 of 48-bits in each data packet.
|
|
44
|
-
This data packet is of variable length. If there is one event, then
|
|
45
|
-
DE_TOF will contain 48-bits. If there are 664 events, then
|
|
46
|
-
DE_TOF will contain 664 x 48-bits. If there is no event, then
|
|
47
|
-
DE_TOF will contain 0-bits.
|
|
48
|
-
|
|
49
|
-
There should be two data packets per ESA. Each packet contains meta-event
|
|
50
|
-
data that is identical between the two packets for a common ESA.
|
|
51
|
-
If there is no event record for certain ESA step, then both packets will
|
|
52
|
-
contain 0-bits in DE_TOF.
|
|
53
|
-
|
|
54
|
-
Parameters
|
|
55
|
-
----------
|
|
56
|
-
de_data : bytes
|
|
57
|
-
Binary blob from de_tag field of SCI_DE packet. Must be an integer
|
|
58
|
-
multiple of 48-bits of data.
|
|
59
|
-
|
|
60
|
-
Returns
|
|
61
|
-
-------
|
|
62
|
-
Dict[str, list]
|
|
63
|
-
Parsed event data.
|
|
64
|
-
"""
|
|
65
|
-
# The de_data is a binary blob with Nx6 bytes of data where N = number of
|
|
66
|
-
# direct events encoded into the binary blob. Interpreting the data as
|
|
67
|
-
# big-endian uint16 data and reshaping into a (3, -1) ndarray results
|
|
68
|
-
# in an array with shape (3, N). Indexing the first axis of that array
|
|
69
|
-
# (e.g. data_uint16[i]) gives the ith 2-bytes of data for each of the N
|
|
70
|
-
# direct events.
|
|
71
|
-
# Considering the 6-bytes of data for each DE as 3 2-byte words,
|
|
72
|
-
# each word contains the following:
|
|
73
|
-
# word_0: full 16-bits is the de_tag
|
|
74
|
-
# word_1: 2-bits of Trigger ID, 10-bits tof_1, upper 4-bits of tof_2
|
|
75
|
-
# word_2: lower 6-bits of tof_2, 10-bits of tof_3
|
|
76
|
-
data_uint16 = np.reshape(
|
|
77
|
-
np.frombuffer(de_data, dtype=">u2"), (3, -1), order="F"
|
|
78
|
-
).astype(np.uint16)
|
|
79
|
-
|
|
80
|
-
de_dict = dict()
|
|
81
|
-
de_dict["de_tag"] = data_uint16[0]
|
|
82
|
-
de_dict["trigger_id"] = (data_uint16[1] >> 14).astype(np.uint8)
|
|
83
|
-
de_dict["tof_1"] = (data_uint16[1] & int(b"00111111_11110000", 2)) >> 4
|
|
84
|
-
de_dict["tof_2"] = ((data_uint16[1] & int(b"00000000_00001111", 2)) << 6) + (
|
|
85
|
-
data_uint16[2] >> 10
|
|
86
|
-
)
|
|
87
|
-
de_dict["tof_3"] = data_uint16[2] & int(b"00000011_11111111", 2)
|
|
88
|
-
|
|
89
|
-
return de_dict
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
def create_dataset(de_data_dict: dict[str, npt.ArrayLike]) -> xr.Dataset:
|
|
93
|
-
"""
|
|
94
|
-
Create xarray dataset.
|
|
95
|
-
|
|
96
|
-
Parameters
|
|
97
|
-
----------
|
|
98
|
-
de_data_dict : Dict[list]
|
|
99
|
-
Dictionary of packet telemetry and direct event data lists.
|
|
100
|
-
|
|
101
|
-
Returns
|
|
102
|
-
-------
|
|
103
|
-
dataset : xarray.Dataset
|
|
104
|
-
Xarray dataset.
|
|
105
|
-
"""
|
|
106
|
-
# Load the CDF attributes
|
|
107
|
-
attr_mgr = ImapCdfAttributes()
|
|
108
|
-
attr_mgr.add_instrument_global_attrs("hi")
|
|
109
|
-
attr_mgr.add_instrument_variable_attrs(instrument="hi", level=None)
|
|
110
|
-
|
|
111
|
-
# check_schema=False keeps DEPEND_0 = '' from being auto added
|
|
112
|
-
epoch_attrs = attr_mgr.get_variable_attributes("epoch", check_schema=False)
|
|
113
|
-
epoch_attrs["CATDESC"] = (
|
|
114
|
-
"CCSDS creation time, number of nanoseconds since J2000 with leap "
|
|
115
|
-
"seconds included"
|
|
116
|
-
)
|
|
117
|
-
epoch = xr.DataArray(
|
|
118
|
-
met_to_ttj2000ns(de_data_dict["ccsds_met"]),
|
|
119
|
-
name="epoch",
|
|
120
|
-
dims=["epoch"],
|
|
121
|
-
attrs=epoch_attrs,
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
event_met_attrs = attr_mgr.get_variable_attributes(
|
|
125
|
-
"hi_de_event_met", check_schema=False
|
|
126
|
-
)
|
|
127
|
-
# For L1A DE, event_met is its own dimension, so we remove the DEPEND_0 attribute
|
|
128
|
-
_ = event_met_attrs.pop("DEPEND_0")
|
|
129
|
-
|
|
130
|
-
# Compute the meta-event MET in seconds
|
|
131
|
-
meta_event_met = (
|
|
132
|
-
np.array(de_data_dict["meta_seconds"]).astype(np.float64)
|
|
133
|
-
+ np.array(de_data_dict["meta_subseconds"]) * MILLISECOND_TO_S
|
|
134
|
-
)
|
|
135
|
-
# Compute the MET of each event in seconds
|
|
136
|
-
# event MET = meta_event_met + de_clock
|
|
137
|
-
# See Hi Algorithm Document section 2.2.5
|
|
138
|
-
event_met_array = np.array(
|
|
139
|
-
meta_event_met[de_data_dict["ccsds_index"]]
|
|
140
|
-
+ np.array(de_data_dict["de_tag"]) * DE_CLOCK_TICK_S,
|
|
141
|
-
dtype=event_met_attrs.pop("dtype"),
|
|
142
|
-
)
|
|
143
|
-
event_met = xr.DataArray(
|
|
144
|
-
event_met_array,
|
|
145
|
-
name="event_met",
|
|
146
|
-
dims=["event_met"],
|
|
147
|
-
attrs=event_met_attrs,
|
|
148
|
-
)
|
|
149
|
-
|
|
150
|
-
dataset = xr.Dataset(
|
|
151
|
-
coords={"epoch": epoch, "event_met": event_met},
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
for var_name, data in de_data_dict.items():
|
|
155
|
-
attrs = attr_mgr.get_variable_attributes(
|
|
156
|
-
f"hi_de_{var_name}", check_schema=False
|
|
157
|
-
).copy()
|
|
158
|
-
dtype = attrs.pop("dtype")
|
|
159
|
-
dataset[var_name] = xr.DataArray(
|
|
160
|
-
np.array(data, dtype=np.dtype(dtype)),
|
|
161
|
-
dims=attrs["DEPEND_0"],
|
|
162
|
-
attrs=attrs,
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
return dataset
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
def science_direct_event(packets_data: xr.Dataset) -> xr.Dataset:
|
|
169
|
-
"""
|
|
170
|
-
Unpack IMAP-Hi direct event data.
|
|
171
|
-
|
|
172
|
-
Processing step:
|
|
173
|
-
|
|
174
|
-
| 1. Break binary stream data into unit of 48-bits
|
|
175
|
-
| 2. Parse direct event data
|
|
176
|
-
| 5. Save the data into xarray dataset.
|
|
177
|
-
|
|
178
|
-
Parameters
|
|
179
|
-
----------
|
|
180
|
-
packets_data : xarray.Dataset
|
|
181
|
-
Packets extracted into a dataset.
|
|
182
|
-
|
|
183
|
-
Returns
|
|
184
|
-
-------
|
|
185
|
-
dataset : xarray.Dataset
|
|
186
|
-
Xarray dataset.
|
|
187
|
-
"""
|
|
188
|
-
de_data_dict: dict[str, list] = defaultdict(list)
|
|
189
|
-
|
|
190
|
-
# Add packet data to the dictionary, renaming some fields
|
|
191
|
-
# This is done first so that these variables are first in the CDF
|
|
192
|
-
for from_key, to_key in {
|
|
193
|
-
"shcoarse": "ccsds_met",
|
|
194
|
-
"src_seq_ctr": "src_seq_ctr",
|
|
195
|
-
"pkt_len": "pkt_len",
|
|
196
|
-
"last_spin_num": "last_spin_num",
|
|
197
|
-
"spin_invalids": "spin_invalids",
|
|
198
|
-
"esa_step_num": "esa_step",
|
|
199
|
-
"meta_seconds": "meta_seconds",
|
|
200
|
-
"meta_subseconds": "meta_subseconds",
|
|
201
|
-
}.items():
|
|
202
|
-
de_data_dict[to_key] = packets_data[from_key].data
|
|
203
|
-
|
|
204
|
-
# For each packet, parse the DE data and add it to the Pointing
|
|
205
|
-
# list of DE data usint `extend()`
|
|
206
|
-
for i, data in enumerate(packets_data["de_tof"].data):
|
|
207
|
-
parsed_de_data = parse_direct_events(data)
|
|
208
|
-
for key, new_data in parsed_de_data.items():
|
|
209
|
-
de_data_dict[key].extend(new_data)
|
|
210
|
-
# Record the ccsds packet index for each DE
|
|
211
|
-
de_data_dict["ccsds_index"].extend([i] * len(parsed_de_data["de_tag"]))
|
|
212
|
-
|
|
213
|
-
# create dataset
|
|
214
|
-
return create_dataset(de_data_dict)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
"""Functions to support I-ALiRT CoDICE Hi processing."""
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
from typing import Any
|
|
5
|
-
|
|
6
|
-
import numpy as np
|
|
7
|
-
import xarray as xr
|
|
8
|
-
|
|
9
|
-
logger = logging.getLogger(__name__)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def find_groups(data: xr.Dataset) -> xr.Dataset:
|
|
13
|
-
"""
|
|
14
|
-
Find all occurrences of the sequential set of 234 values 0-233.
|
|
15
|
-
|
|
16
|
-
If a value is missing, or we are starting/ending
|
|
17
|
-
in the middle of a sequence we do not count that as a valid group.
|
|
18
|
-
|
|
19
|
-
Parameters
|
|
20
|
-
----------
|
|
21
|
-
data : xr.Dataset
|
|
22
|
-
CoDICE Hi Dataset.
|
|
23
|
-
|
|
24
|
-
Returns
|
|
25
|
-
-------
|
|
26
|
-
grouped_data : xr.Dataset
|
|
27
|
-
Grouped data.
|
|
28
|
-
"""
|
|
29
|
-
subcom_range = (0, 233)
|
|
30
|
-
|
|
31
|
-
data = data.sortby("cod_hi_acq", ascending=True)
|
|
32
|
-
|
|
33
|
-
# Use cod_hi_counter == 0 to define the beginning of the group.
|
|
34
|
-
# Find cod_hi_acq at this index and use it as the beginning time for the group.
|
|
35
|
-
start_sc_ticks = data["cod_hi_acq"][(data["cod_hi_counter"] == subcom_range[0])]
|
|
36
|
-
start_sc_tick = start_sc_ticks.min()
|
|
37
|
-
# Use cod_hi_counter == 233 to define the end of the group.
|
|
38
|
-
last_sc_ticks = data["cod_hi_acq"][
|
|
39
|
-
([data["cod_hi_counter"] == subcom_range[-1]][-1])
|
|
40
|
-
]
|
|
41
|
-
last_sc_tick = last_sc_ticks.max()
|
|
42
|
-
|
|
43
|
-
# Filter out data before the first cod_hi_counter=0 and
|
|
44
|
-
# after the last cod_hi_counter=233 and cod_hi_counter values != 0-233.
|
|
45
|
-
grouped_data = data.where(
|
|
46
|
-
(data["cod_hi_acq"] >= start_sc_tick)
|
|
47
|
-
& (data["cod_hi_acq"] <= last_sc_tick)
|
|
48
|
-
& (data["cod_hi_counter"] >= subcom_range[0])
|
|
49
|
-
& (data["cod_hi_counter"] <= subcom_range[-1]),
|
|
50
|
-
drop=True,
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
# Assign labels based on the cod_hi_acq times.
|
|
54
|
-
group_labels = np.searchsorted(
|
|
55
|
-
start_sc_ticks, grouped_data["cod_hi_acq"], side="right"
|
|
56
|
-
)
|
|
57
|
-
# Example:
|
|
58
|
-
# grouped_data.coords
|
|
59
|
-
# Coordinates:
|
|
60
|
-
# * epoch (epoch) int64 7kB 315922822184000000 ... 315923721184000000
|
|
61
|
-
# * group (group) int64 7kB 1 1 1 1 1 1 1 1 1 ... 15 15 15 15 15 15 15 15 15
|
|
62
|
-
grouped_data["group"] = ("group", group_labels)
|
|
63
|
-
|
|
64
|
-
return grouped_data
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
def append_cod_hi_data(dataset: xr.Dataset) -> xr.Dataset:
|
|
68
|
-
"""
|
|
69
|
-
Append the cod_hi_## data values and create an xarray.
|
|
70
|
-
|
|
71
|
-
Parameters
|
|
72
|
-
----------
|
|
73
|
-
dataset : xr.Dataset
|
|
74
|
-
Original dataset of group.
|
|
75
|
-
|
|
76
|
-
Returns
|
|
77
|
-
-------
|
|
78
|
-
appended_dataset : xr.Dataset
|
|
79
|
-
Dataset with cod_hi_## stacked.
|
|
80
|
-
"""
|
|
81
|
-
# Number of codice hi data rows
|
|
82
|
-
num_cod_hi_rows = 5
|
|
83
|
-
cod_hi_data = np.stack(
|
|
84
|
-
[dataset[f"cod_hi_data_{i:02}"].values for i in range(num_cod_hi_rows)], axis=1
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
repeated_data = {
|
|
88
|
-
var: np.repeat(dataset[var].values, num_cod_hi_rows)
|
|
89
|
-
for var in dataset.data_vars
|
|
90
|
-
if not var.startswith("cod_hi_data_")
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
repeated_data["cod_hi_appended"] = cod_hi_data.flatten()
|
|
94
|
-
repeated_epoch = np.repeat(dataset["epoch"].values, num_cod_hi_rows)
|
|
95
|
-
|
|
96
|
-
appended_dataset = xr.Dataset(
|
|
97
|
-
data_vars={name: ("epoch", values) for name, values in repeated_data.items()},
|
|
98
|
-
coords={"epoch": repeated_epoch},
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
return appended_dataset
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def process_codicehi(xarray_data: xr.Dataset) -> list[dict]:
|
|
105
|
-
"""
|
|
106
|
-
Create final data products.
|
|
107
|
-
|
|
108
|
-
Parameters
|
|
109
|
-
----------
|
|
110
|
-
xarray_data : xr.Dataset
|
|
111
|
-
Parsed data.
|
|
112
|
-
|
|
113
|
-
Returns
|
|
114
|
-
-------
|
|
115
|
-
codicehi_data : list[dict]
|
|
116
|
-
Dictionary of final data product.
|
|
117
|
-
|
|
118
|
-
Notes
|
|
119
|
-
-----
|
|
120
|
-
This function is incomplete and will need to be updated to include the
|
|
121
|
-
necessary calculations and data products.
|
|
122
|
-
- Calculate species counts (pg 27 of Algorithm Document)
|
|
123
|
-
- Calculate rates (assume 4 minutes per group)
|
|
124
|
-
- Calculate L2 CoDICE pseudodensities (pg 37 of Algorithm Document)
|
|
125
|
-
- Calculate the public data products
|
|
126
|
-
"""
|
|
127
|
-
grouped_data = find_groups(xarray_data)
|
|
128
|
-
unique_groups = np.unique(grouped_data["group"])
|
|
129
|
-
codicehi_data: list[dict[str, Any]] = [{}]
|
|
130
|
-
|
|
131
|
-
for group in unique_groups:
|
|
132
|
-
# cod_hi_counter values for the group should be 0-233 with no duplicates.
|
|
133
|
-
subcom_values = grouped_data["cod_hi_counter"][
|
|
134
|
-
(grouped_data["group"] == group).values
|
|
135
|
-
]
|
|
136
|
-
|
|
137
|
-
# Ensure no duplicates and all values from 0 to 233 are present
|
|
138
|
-
if not np.array_equal(subcom_values, np.arange(234)):
|
|
139
|
-
logger.warning(
|
|
140
|
-
f"Group {group} does not contain all values from 0 to "
|
|
141
|
-
f"233 without duplicates."
|
|
142
|
-
)
|
|
143
|
-
continue
|
|
144
|
-
|
|
145
|
-
mask = grouped_data["group"] == group
|
|
146
|
-
filtered_indices = np.where(mask)[0]
|
|
147
|
-
group_data = grouped_data.isel(epoch=filtered_indices)
|
|
148
|
-
|
|
149
|
-
append_cod_hi_data(group_data)
|
|
150
|
-
|
|
151
|
-
# TODO: calculate species counts
|
|
152
|
-
# TODO: calculate rates
|
|
153
|
-
# TODO: calculate L2 CoDICE pseudodensities
|
|
154
|
-
# TODO: calculate the public data products
|
|
155
|
-
|
|
156
|
-
return codicehi_data
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
"""Functions to support I-ALiRT CoDICE Lo processing."""
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
|
|
5
|
-
import xarray as xr
|
|
6
|
-
|
|
7
|
-
from imap_processing.codice.codice_l1a import create_ialirt_dataset
|
|
8
|
-
|
|
9
|
-
logger = logging.getLogger(__name__)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def process_codicelo(dataset: xr.Dataset) -> list[dict]:
|
|
13
|
-
"""
|
|
14
|
-
Create final data products.
|
|
15
|
-
|
|
16
|
-
Parameters
|
|
17
|
-
----------
|
|
18
|
-
dataset : xr.Dataset
|
|
19
|
-
Decommed L0 data.
|
|
20
|
-
|
|
21
|
-
Returns
|
|
22
|
-
-------
|
|
23
|
-
codicelo_data : list[dict]
|
|
24
|
-
Dictionary of final data product.
|
|
25
|
-
|
|
26
|
-
Notes
|
|
27
|
-
-----
|
|
28
|
-
This function is incomplete and will need to be updated to include the
|
|
29
|
-
necessary calculations and data products.
|
|
30
|
-
- Calculate rates (assume 4 minutes per group)
|
|
31
|
-
- Calculate L2 CoDICE pseudodensities (pg 37 of Algorithm Document)
|
|
32
|
-
- Calculate the public data products
|
|
33
|
-
"""
|
|
34
|
-
apid = dataset.pkt_apid.data[0]
|
|
35
|
-
codicelo_data = create_ialirt_dataset(apid, dataset)
|
|
36
|
-
|
|
37
|
-
# TODO: calculate rates
|
|
38
|
-
# TODO: calculate L2 CoDICE pseudodensities
|
|
39
|
-
# TODO: calculate the public data products
|
|
40
|
-
|
|
41
|
-
return codicelo_data
|
|
File without changes
|
|
File without changes
|
|
File without changes
|