imap-processing 0.18.0__py3-none-any.whl → 0.19.2__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/ancillary/ancillary_dataset_combiner.py +161 -1
- imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +6 -0
- imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +221 -1057
- imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +307 -283
- imap_processing/cdf/config/imap_codice_l2_variable_attrs.yaml +1044 -203
- imap_processing/cdf/config/imap_constant_attrs.yaml +4 -2
- imap_processing/cdf/config/imap_enamaps_l2-common_variable_attrs.yaml +11 -0
- imap_processing/cdf/config/imap_glows_l1b_variable_attrs.yaml +15 -1
- imap_processing/cdf/config/imap_hi_global_cdf_attrs.yaml +5 -0
- imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +10 -4
- imap_processing/cdf/config/imap_idex_l2a_variable_attrs.yaml +33 -4
- imap_processing/cdf/config/imap_idex_l2b_variable_attrs.yaml +8 -91
- imap_processing/cdf/config/imap_idex_l2c_variable_attrs.yaml +106 -16
- imap_processing/cdf/config/imap_lo_global_cdf_attrs.yaml +5 -4
- imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +4 -15
- imap_processing/cdf/config/imap_lo_l1c_variable_attrs.yaml +189 -98
- imap_processing/cdf/config/imap_mag_global_cdf_attrs.yaml +85 -2
- imap_processing/cdf/config/imap_mag_l1c_variable_attrs.yaml +24 -1
- imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +20 -8
- imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +45 -35
- imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +110 -7
- imap_processing/cli.py +138 -93
- imap_processing/codice/codice_l0.py +2 -1
- imap_processing/codice/codice_l1a.py +167 -69
- imap_processing/codice/codice_l1b.py +42 -32
- imap_processing/codice/codice_l2.py +215 -9
- imap_processing/codice/constants.py +790 -603
- imap_processing/codice/data/lo_stepping_values.csv +1 -1
- imap_processing/decom.py +1 -4
- imap_processing/ena_maps/ena_maps.py +71 -43
- imap_processing/ena_maps/utils/corrections.py +291 -0
- imap_processing/ena_maps/utils/map_utils.py +20 -4
- imap_processing/ena_maps/utils/naming.py +8 -2
- imap_processing/glows/ancillary/imap_glows_exclusions-by-instr-team_20250923_v002.dat +10 -0
- imap_processing/glows/ancillary/imap_glows_map-of-excluded-regions_20250923_v002.dat +393 -0
- imap_processing/glows/ancillary/imap_glows_map-of-uv-sources_20250923_v002.dat +593 -0
- imap_processing/glows/ancillary/imap_glows_pipeline-settings_20250923_v002.json +54 -0
- imap_processing/glows/ancillary/imap_glows_suspected-transients_20250923_v002.dat +10 -0
- imap_processing/glows/l1b/glows_l1b.py +123 -18
- imap_processing/glows/l1b/glows_l1b_data.py +358 -47
- imap_processing/glows/l2/glows_l2.py +11 -0
- imap_processing/hi/hi_l1a.py +124 -3
- imap_processing/hi/hi_l1b.py +154 -71
- imap_processing/hi/hi_l1c.py +4 -109
- imap_processing/hi/hi_l2.py +104 -60
- imap_processing/hi/utils.py +262 -8
- imap_processing/hit/l0/constants.py +3 -0
- imap_processing/hit/l0/decom_hit.py +3 -6
- imap_processing/hit/l1a/hit_l1a.py +311 -21
- imap_processing/hit/l1b/hit_l1b.py +54 -126
- imap_processing/hit/l2/hit_l2.py +6 -6
- imap_processing/ialirt/calculate_ingest.py +219 -0
- imap_processing/ialirt/constants.py +12 -2
- imap_processing/ialirt/generate_coverage.py +15 -2
- imap_processing/ialirt/l0/ialirt_spice.py +6 -2
- imap_processing/ialirt/l0/parse_mag.py +293 -42
- imap_processing/ialirt/l0/process_hit.py +5 -3
- imap_processing/ialirt/l0/process_swapi.py +41 -25
- imap_processing/ialirt/process_ephemeris.py +70 -14
- imap_processing/ialirt/utils/create_xarray.py +1 -1
- imap_processing/idex/idex_l0.py +2 -2
- imap_processing/idex/idex_l1a.py +2 -3
- imap_processing/idex/idex_l1b.py +2 -3
- imap_processing/idex/idex_l2a.py +130 -4
- imap_processing/idex/idex_l2b.py +158 -143
- imap_processing/idex/idex_utils.py +1 -3
- imap_processing/lo/ancillary_data/imap_lo_hydrogen-geometric-factor_v001.csv +75 -0
- imap_processing/lo/ancillary_data/imap_lo_oxygen-geometric-factor_v001.csv +75 -0
- imap_processing/lo/l0/lo_science.py +25 -24
- imap_processing/lo/l1b/lo_l1b.py +93 -19
- imap_processing/lo/l1c/lo_l1c.py +273 -93
- imap_processing/lo/l2/lo_l2.py +949 -135
- imap_processing/lo/lo_ancillary.py +55 -0
- imap_processing/mag/l1a/mag_l1a.py +1 -0
- imap_processing/mag/l1a/mag_l1a_data.py +26 -0
- imap_processing/mag/l1b/mag_l1b.py +3 -2
- imap_processing/mag/l1c/interpolation_methods.py +14 -15
- imap_processing/mag/l1c/mag_l1c.py +23 -6
- imap_processing/mag/l1d/mag_l1d.py +57 -14
- imap_processing/mag/l1d/mag_l1d_data.py +202 -32
- imap_processing/mag/l2/mag_l2.py +2 -0
- imap_processing/mag/l2/mag_l2_data.py +14 -5
- imap_processing/quality_flags.py +23 -1
- imap_processing/spice/geometry.py +89 -39
- imap_processing/spice/pointing_frame.py +4 -8
- imap_processing/spice/repoint.py +78 -2
- imap_processing/spice/spin.py +28 -8
- imap_processing/spice/time.py +12 -22
- imap_processing/swapi/l1/swapi_l1.py +10 -4
- imap_processing/swapi/l2/swapi_l2.py +15 -17
- imap_processing/swe/l1b/swe_l1b.py +1 -2
- imap_processing/ultra/constants.py +30 -24
- imap_processing/ultra/l0/ultra_utils.py +9 -11
- imap_processing/ultra/l1a/ultra_l1a.py +1 -2
- imap_processing/ultra/l1b/badtimes.py +35 -11
- imap_processing/ultra/l1b/de.py +95 -31
- imap_processing/ultra/l1b/extendedspin.py +31 -16
- imap_processing/ultra/l1b/goodtimes.py +112 -0
- imap_processing/ultra/l1b/lookup_utils.py +281 -28
- imap_processing/ultra/l1b/quality_flag_filters.py +10 -1
- imap_processing/ultra/l1b/ultra_l1b.py +7 -7
- imap_processing/ultra/l1b/ultra_l1b_culling.py +169 -7
- imap_processing/ultra/l1b/ultra_l1b_extended.py +311 -69
- imap_processing/ultra/l1c/helio_pset.py +139 -37
- imap_processing/ultra/l1c/l1c_lookup_utils.py +289 -0
- imap_processing/ultra/l1c/spacecraft_pset.py +140 -29
- imap_processing/ultra/l1c/ultra_l1c.py +33 -24
- imap_processing/ultra/l1c/ultra_l1c_culling.py +92 -0
- imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +400 -292
- imap_processing/ultra/l2/ultra_l2.py +54 -11
- imap_processing/ultra/utils/ultra_l1_utils.py +37 -7
- imap_processing/utils.py +3 -4
- {imap_processing-0.18.0.dist-info → imap_processing-0.19.2.dist-info}/METADATA +2 -2
- {imap_processing-0.18.0.dist-info → imap_processing-0.19.2.dist-info}/RECORD +118 -109
- imap_processing/idex/idex_l2c.py +0 -84
- imap_processing/spice/kernels.py +0 -187
- imap_processing/ultra/l1b/cullingmask.py +0 -87
- imap_processing/ultra/l1c/histogram.py +0 -36
- {imap_processing-0.18.0.dist-info → imap_processing-0.19.2.dist-info}/LICENSE +0 -0
- {imap_processing-0.18.0.dist-info → imap_processing-0.19.2.dist-info}/WHEEL +0 -0
- {imap_processing-0.18.0.dist-info → imap_processing-0.19.2.dist-info}/entry_points.txt +0 -0
|
@@ -54,6 +54,8 @@ class CoDICEL1aPipeline:
|
|
|
54
54
|
|
|
55
55
|
Methods
|
|
56
56
|
-------
|
|
57
|
+
apply_despinning()
|
|
58
|
+
Apply the despinning algorithm to lo- angular and priority products.
|
|
57
59
|
decompress_data(science_values)
|
|
58
60
|
Perform decompression on the data.
|
|
59
61
|
define_coordinates()
|
|
@@ -87,6 +89,73 @@ class CoDICEL1aPipeline:
|
|
|
87
89
|
self.plan_step = plan_step
|
|
88
90
|
self.view_id = view_id
|
|
89
91
|
|
|
92
|
+
def apply_despinning(self) -> None:
|
|
93
|
+
"""
|
|
94
|
+
Apply the despinning algorithm to lo- angular and priority products.
|
|
95
|
+
|
|
96
|
+
This only applies to CoDICE-Lo angular and priority data products. See
|
|
97
|
+
sections 9.3.4 and 9.3.5 of the algorithm document for more details.
|
|
98
|
+
"""
|
|
99
|
+
# Determine the appropriate dimensions for the despun data
|
|
100
|
+
num_energies = self.config["dims"]["esa_step"]
|
|
101
|
+
num_spin_sectors = self.config["dims"]["spin_sector"]
|
|
102
|
+
num_spins = num_spin_sectors * 2
|
|
103
|
+
num_counters = self.config["num_counters"]
|
|
104
|
+
num_positions = self.config["dims"].get(
|
|
105
|
+
"inst_az"
|
|
106
|
+
) # Defaults to None if not present
|
|
107
|
+
|
|
108
|
+
# The dimensions are dependent on the specific data product
|
|
109
|
+
if "angular" in self.config["dataset_name"]:
|
|
110
|
+
despun_dims: tuple[int, ...] = (
|
|
111
|
+
num_energies,
|
|
112
|
+
num_positions,
|
|
113
|
+
num_spins,
|
|
114
|
+
num_counters,
|
|
115
|
+
)
|
|
116
|
+
elif "priority" in self.config["dataset_name"]:
|
|
117
|
+
despun_dims = (num_energies, num_spins, num_counters)
|
|
118
|
+
|
|
119
|
+
# Placeholder for finalized despun data
|
|
120
|
+
self.data: list[np.ndarray] # Needed to appease mypy
|
|
121
|
+
despun_data = [np.zeros(despun_dims) for _ in range(len(self.data))]
|
|
122
|
+
|
|
123
|
+
# Iterate over the energy and spin sector indices, and determine the
|
|
124
|
+
# appropriate pixel orientation. The combination of the pixel
|
|
125
|
+
# orientation and the azimuth determine which spin sector the data
|
|
126
|
+
# gets stored in.
|
|
127
|
+
# TODO: All these nested for-loops are bad. Try to find a better
|
|
128
|
+
# solution. See GitHub issue #2136.
|
|
129
|
+
for i, epoch_data in enumerate(self.data):
|
|
130
|
+
for energy_index in range(num_energies):
|
|
131
|
+
pixel_orientation = constants.PIXEL_ORIENTATIONS[energy_index]
|
|
132
|
+
for spin_sector_index in range(num_spin_sectors):
|
|
133
|
+
for azimuth_index in range(num_spins):
|
|
134
|
+
if pixel_orientation == "A" and azimuth_index < 12:
|
|
135
|
+
despun_spin_sector = spin_sector_index
|
|
136
|
+
elif pixel_orientation == "A" and azimuth_index >= 12:
|
|
137
|
+
despun_spin_sector = spin_sector_index + 12
|
|
138
|
+
elif pixel_orientation == "B" and azimuth_index < 12:
|
|
139
|
+
despun_spin_sector = spin_sector_index + 12
|
|
140
|
+
elif pixel_orientation == "B" and azimuth_index >= 12:
|
|
141
|
+
despun_spin_sector = spin_sector_index
|
|
142
|
+
|
|
143
|
+
if "angular" in self.config["dataset_name"]:
|
|
144
|
+
spin_data = epoch_data[
|
|
145
|
+
energy_index, :, spin_sector_index, :
|
|
146
|
+
] # (5, 4)
|
|
147
|
+
despun_data[i][energy_index, :, despun_spin_sector, :] = (
|
|
148
|
+
spin_data
|
|
149
|
+
)
|
|
150
|
+
elif "priority" in self.config["dataset_name"]:
|
|
151
|
+
spin_data = epoch_data[energy_index, spin_sector_index, :]
|
|
152
|
+
despun_data[i][energy_index, despun_spin_sector, :] = (
|
|
153
|
+
spin_data
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# Replace original data
|
|
157
|
+
self.data = despun_data
|
|
158
|
+
|
|
90
159
|
def decompress_data(self, science_values: list[NDArray[str]] | list[str]) -> None:
|
|
91
160
|
"""
|
|
92
161
|
Perform decompression on the data.
|
|
@@ -122,7 +191,7 @@ class CoDICEL1aPipeline:
|
|
|
122
191
|
|
|
123
192
|
else:
|
|
124
193
|
for packet_data, byte_count in zip(
|
|
125
|
-
science_values, self.dataset.byte_count.data
|
|
194
|
+
science_values, self.dataset.byte_count.data, strict=False
|
|
126
195
|
):
|
|
127
196
|
# Convert from numpy array to byte object
|
|
128
197
|
values = packet_data[()]
|
|
@@ -134,11 +203,14 @@ class CoDICEL1aPipeline:
|
|
|
134
203
|
decompressed_values = decompress(values, compression_algorithm)
|
|
135
204
|
self.raw_data.append(decompressed_values)
|
|
136
205
|
|
|
137
|
-
def define_coordinates(self) -> None:
|
|
206
|
+
def define_coordinates(self) -> None: # noqa: PLR0912 (too many branches)
|
|
138
207
|
"""
|
|
139
208
|
Create ``xr.DataArrays`` for the coords needed in the final dataset.
|
|
140
209
|
|
|
141
210
|
The coordinates for the dataset depend on the data product being made.
|
|
211
|
+
|
|
212
|
+
# TODO: Split this function up or simplify it to avoid too many branches
|
|
213
|
+
# error.
|
|
142
214
|
"""
|
|
143
215
|
self.coords = {}
|
|
144
216
|
|
|
@@ -169,13 +241,18 @@ class CoDICEL1aPipeline:
|
|
|
169
241
|
if name in [
|
|
170
242
|
"esa_step",
|
|
171
243
|
"inst_az",
|
|
172
|
-
"spin_sector",
|
|
173
244
|
"spin_sector_pairs",
|
|
174
245
|
"spin_sector_index",
|
|
175
246
|
"ssd_index",
|
|
176
247
|
]:
|
|
177
248
|
values = np.arange(self.config["dims"][name])
|
|
178
249
|
dims = [name]
|
|
250
|
+
elif name == "spin_sector":
|
|
251
|
+
if self.config["dataset_name"] in constants.REQUIRES_DESPINNING:
|
|
252
|
+
values = np.arange(24)
|
|
253
|
+
else:
|
|
254
|
+
values = np.arange(self.config["dims"][name])
|
|
255
|
+
dims = [name]
|
|
179
256
|
elif name == "spin_sector_pairs_label":
|
|
180
257
|
values = np.array(
|
|
181
258
|
[
|
|
@@ -197,7 +274,6 @@ class CoDICEL1aPipeline:
|
|
|
197
274
|
values = np.arange(self.config["dims"]["inst_az"]).astype(str)
|
|
198
275
|
dims = ["inst_az"]
|
|
199
276
|
elif name in [
|
|
200
|
-
"spin_sector_label",
|
|
201
277
|
"esa_step_label",
|
|
202
278
|
"spin_sector_index_label",
|
|
203
279
|
"ssd_index_label",
|
|
@@ -205,6 +281,13 @@ class CoDICEL1aPipeline:
|
|
|
205
281
|
key = name.removesuffix("_label")
|
|
206
282
|
values = np.arange(self.config["dims"][key]).astype(str)
|
|
207
283
|
dims = [key]
|
|
284
|
+
elif name == "spin_sector_label":
|
|
285
|
+
key = name.removesuffix("_label")
|
|
286
|
+
dims = [key]
|
|
287
|
+
if self.config["dataset_name"] in constants.REQUIRES_DESPINNING:
|
|
288
|
+
values = np.arange(24).astype(str)
|
|
289
|
+
else:
|
|
290
|
+
values = np.arange(self.config["dims"][key]).astype(str)
|
|
208
291
|
|
|
209
292
|
coord = xr.DataArray(
|
|
210
293
|
values,
|
|
@@ -243,7 +326,7 @@ class CoDICEL1aPipeline:
|
|
|
243
326
|
# the num_counters dimension to isolate the data for each counter so
|
|
244
327
|
# each counter's data can be placed in a separate CDF data variable.
|
|
245
328
|
for counter, variable_name in zip(
|
|
246
|
-
range(all_data.shape[-1]), self.config["variable_names"]
|
|
329
|
+
range(all_data.shape[-1]), self.config["variable_names"], strict=False
|
|
247
330
|
):
|
|
248
331
|
# Extract the counter data
|
|
249
332
|
counter_data = all_data[..., counter]
|
|
@@ -262,7 +345,7 @@ class CoDICEL1aPipeline:
|
|
|
262
345
|
# energy dimension
|
|
263
346
|
# TODO: This bit of code may no longer be needed once I can figure
|
|
264
347
|
# out how to run hi-sectored product through the
|
|
265
|
-
# create_binned_dataset function
|
|
348
|
+
# create_binned_dataset function. See GitHub issue #2137.
|
|
266
349
|
if self.config["dataset_name"] == "imap_codice_l1a_hi-sectored":
|
|
267
350
|
dims = [
|
|
268
351
|
f"energy_{variable_name}" if item == "esa_step" else item
|
|
@@ -284,7 +367,7 @@ class CoDICEL1aPipeline:
|
|
|
284
367
|
# longer need the "esa_step" coordinate
|
|
285
368
|
# TODO: This bit of code may no longer be needed once I can figure
|
|
286
369
|
# out how to run hi-sectored product through the
|
|
287
|
-
# create_binned_dataset function
|
|
370
|
+
# create_binned_dataset function. See GitHub issue #2137.
|
|
288
371
|
if self.config["dataset_name"] == "imap_codice_l1a_hi-sectored":
|
|
289
372
|
for species in self.config["energy_table"]:
|
|
290
373
|
dataset = self.define_energy_bins(dataset, species)
|
|
@@ -313,7 +396,7 @@ class CoDICEL1aPipeline:
|
|
|
313
396
|
``xarray`` dataset for the data product, with added energy variables.
|
|
314
397
|
"""
|
|
315
398
|
energy_bin_name = f"energy_{species}"
|
|
316
|
-
centers,
|
|
399
|
+
centers, deltas_minus, deltas_plus = self.get_hi_energy_table_data(
|
|
317
400
|
energy_bin_name.split("energy_")[-1]
|
|
318
401
|
)
|
|
319
402
|
|
|
@@ -326,11 +409,19 @@ class CoDICEL1aPipeline:
|
|
|
326
409
|
check_schema=False,
|
|
327
410
|
),
|
|
328
411
|
)
|
|
329
|
-
dataset[f"{energy_bin_name}
|
|
330
|
-
|
|
331
|
-
dims=[f"{energy_bin_name}
|
|
412
|
+
dataset[f"{energy_bin_name}_minus"] = xr.DataArray(
|
|
413
|
+
deltas_minus,
|
|
414
|
+
dims=[f"{energy_bin_name}_minus"],
|
|
332
415
|
attrs=self.cdf_attrs.get_variable_attributes(
|
|
333
|
-
f"{self.config['dataset_name'].split('_')[-1]}-{energy_bin_name}
|
|
416
|
+
f"{self.config['dataset_name'].split('_')[-1]}-{energy_bin_name}_minus",
|
|
417
|
+
check_schema=False,
|
|
418
|
+
),
|
|
419
|
+
)
|
|
420
|
+
dataset[f"{energy_bin_name}_plus"] = xr.DataArray(
|
|
421
|
+
deltas_plus,
|
|
422
|
+
dims=[f"{energy_bin_name}_plus"],
|
|
423
|
+
attrs=self.cdf_attrs.get_variable_attributes(
|
|
424
|
+
f"{self.config['dataset_name'].split('_')[-1]}-{energy_bin_name}_plus",
|
|
334
425
|
check_schema=False,
|
|
335
426
|
),
|
|
336
427
|
)
|
|
@@ -488,7 +579,7 @@ class CoDICEL1aPipeline:
|
|
|
488
579
|
|
|
489
580
|
def get_hi_energy_table_data(
|
|
490
581
|
self, species: str
|
|
491
|
-
) -> tuple[NDArray[float], NDArray[float]]:
|
|
582
|
+
) -> tuple[NDArray[float], NDArray[float], NDArray[float]]:
|
|
492
583
|
"""
|
|
493
584
|
Retrieve energy table data for CoDICE-Hi products.
|
|
494
585
|
|
|
@@ -506,22 +597,25 @@ class CoDICEL1aPipeline:
|
|
|
506
597
|
-------
|
|
507
598
|
centers : NDArray[float]
|
|
508
599
|
An array whose values represent the centers of the energy bins.
|
|
509
|
-
|
|
510
|
-
An array whose values represent the deltas of the energy bins.
|
|
600
|
+
deltas_minus : NDArray[float]
|
|
601
|
+
An array whose values represent the minus deltas of the energy bins.
|
|
602
|
+
deltas_plus : NDArray[float]
|
|
603
|
+
An array whose values represent the plus deltas of the energy bins.
|
|
511
604
|
"""
|
|
512
605
|
data_product = self.config["dataset_name"].split("-")[-1].upper()
|
|
513
|
-
energy_table =
|
|
514
|
-
|
|
515
|
-
# Find the centers and deltas of the energy bins
|
|
516
|
-
centers = np.array(
|
|
517
|
-
[
|
|
518
|
-
(energy_table[i] + energy_table[i + 1]) / 2
|
|
519
|
-
for i in range(len(energy_table) - 1)
|
|
520
|
-
]
|
|
606
|
+
energy_table = np.array(
|
|
607
|
+
getattr(constants, f"{data_product}_ENERGY_TABLE")[species]
|
|
521
608
|
)
|
|
522
|
-
deltas = energy_table[1:] - centers
|
|
523
609
|
|
|
524
|
-
|
|
610
|
+
# Find the geometric centers and deltas of the energy bins
|
|
611
|
+
# The delta minus is the difference between the center of the bin
|
|
612
|
+
# and the 'left edge' of the bin. The delta plus is the difference
|
|
613
|
+
# between the 'right edge' of the bin and the center of the bin
|
|
614
|
+
centers = np.sqrt(energy_table[:-1] * energy_table[1:])
|
|
615
|
+
deltas_minus = centers - energy_table[:-1]
|
|
616
|
+
deltas_plus = energy_table[1:] - centers
|
|
617
|
+
|
|
618
|
+
return centers, deltas_minus, deltas_plus
|
|
525
619
|
|
|
526
620
|
def reshape_binned_data(self, dataset: xr.Dataset) -> dict[str, list]:
|
|
527
621
|
"""
|
|
@@ -624,6 +718,10 @@ class CoDICEL1aPipeline:
|
|
|
624
718
|
)
|
|
625
719
|
self.data.append(reshaped_packet_data)
|
|
626
720
|
|
|
721
|
+
# Apply despinning if necessary
|
|
722
|
+
if self.config["dataset_name"] in constants.REQUIRES_DESPINNING:
|
|
723
|
+
self.apply_despinning()
|
|
724
|
+
|
|
627
725
|
# No longer need to keep the raw data around
|
|
628
726
|
del self.raw_data
|
|
629
727
|
|
|
@@ -724,9 +822,6 @@ def group_ialirt_data(
|
|
|
724
822
|
|
|
725
823
|
# Workaround to get this function working for both I-ALiRT spacecraft
|
|
726
824
|
# data and CoDICE-specific I-ALiRT test data from Joey
|
|
727
|
-
# TODO: Once CoDICE I-ALiRT processing is more established, we can probably
|
|
728
|
-
# do away with processing the test data from Joey and just use the
|
|
729
|
-
# I-ALiRT data that is constructed closer to what we expect in-flight.
|
|
730
825
|
if hasattr(packets, "acquisition_time"):
|
|
731
826
|
time_key = "acquisition_time"
|
|
732
827
|
counter_key = "counter"
|
|
@@ -782,7 +877,7 @@ def create_binned_dataset(
|
|
|
782
877
|
Xarray dataset containing the final processed dataset.
|
|
783
878
|
"""
|
|
784
879
|
# TODO: hi-sectored data product should be processed similar to hi-omni,
|
|
785
|
-
# so I should be able to use this method.
|
|
880
|
+
# so I should be able to use this method. See GitHub issue #2137.
|
|
786
881
|
|
|
787
882
|
# Get the four "main" parameters for processing
|
|
788
883
|
table_id, plan_id, plan_step, view_id = get_params(dataset)
|
|
@@ -803,7 +898,7 @@ def create_binned_dataset(
|
|
|
803
898
|
attrs=pipeline.cdf_attrs.get_variable_attributes("epoch", check_schema=False),
|
|
804
899
|
)
|
|
805
900
|
# TODO: Figure out how to calculate epoch centers and deltas and store them
|
|
806
|
-
# in variables here
|
|
901
|
+
# in variables here. See GitHub issue #1501.
|
|
807
902
|
dataset = xr.Dataset(
|
|
808
903
|
coords={"epoch": coord},
|
|
809
904
|
attrs=pipeline.cdf_attrs.get_global_attributes(pipeline.config["dataset_name"]),
|
|
@@ -843,7 +938,7 @@ def create_binned_dataset(
|
|
|
843
938
|
return dataset
|
|
844
939
|
|
|
845
940
|
|
|
846
|
-
def create_direct_event_dataset(apid: int,
|
|
941
|
+
def create_direct_event_dataset(apid: int, unpacked_dataset: xr.Dataset) -> xr.Dataset:
|
|
847
942
|
"""
|
|
848
943
|
Create dataset for direct event data.
|
|
849
944
|
|
|
@@ -857,7 +952,7 @@ def create_direct_event_dataset(apid: int, packets: xr.Dataset) -> xr.Dataset:
|
|
|
857
952
|
dictionary. Padding is added to any fields that have less than 10000 events.
|
|
858
953
|
|
|
859
954
|
In order to process these data, we must take the decommed raw data, group
|
|
860
|
-
the
|
|
955
|
+
the unpacked_dataset appropriately based on their `seq_flgs`, decompress the data,
|
|
861
956
|
then arrange the data into CDF data variables for each priority and bit
|
|
862
957
|
field. For example, P2_SpinAngle represents the spin angles for the 2nd
|
|
863
958
|
priority data.
|
|
@@ -866,8 +961,8 @@ def create_direct_event_dataset(apid: int, packets: xr.Dataset) -> xr.Dataset:
|
|
|
866
961
|
----------
|
|
867
962
|
apid : int
|
|
868
963
|
The APID of the packet.
|
|
869
|
-
|
|
870
|
-
The
|
|
964
|
+
unpacked_dataset : xarray.Dataset
|
|
965
|
+
The unpacked dataset to process.
|
|
871
966
|
|
|
872
967
|
Returns
|
|
873
968
|
-------
|
|
@@ -875,13 +970,13 @@ def create_direct_event_dataset(apid: int, packets: xr.Dataset) -> xr.Dataset:
|
|
|
875
970
|
Xarray dataset containing the direct event data.
|
|
876
971
|
"""
|
|
877
972
|
# Group and decompress the data
|
|
878
|
-
grouped_data = group_data(
|
|
973
|
+
grouped_data = group_data(unpacked_dataset)
|
|
879
974
|
decompressed_data = [
|
|
880
975
|
decompress(group, CoDICECompression.LOSSLESS) for group in grouped_data
|
|
881
976
|
]
|
|
882
977
|
|
|
883
978
|
# Reshape the packet data into CDF-ready variables
|
|
884
|
-
|
|
979
|
+
reshaped_de_data = reshape_de_data(unpacked_dataset, decompressed_data, apid)
|
|
885
980
|
|
|
886
981
|
# Gather the CDF attributes
|
|
887
982
|
cdf_attrs = ImapCdfAttributes()
|
|
@@ -891,11 +986,11 @@ def create_direct_event_dataset(apid: int, packets: xr.Dataset) -> xr.Dataset:
|
|
|
891
986
|
# Determine the epochs to use in the dataset, which are the epochs whenever
|
|
892
987
|
# there is a start of a segment and the priority is 0
|
|
893
988
|
epoch_indices = np.where(
|
|
894
|
-
((
|
|
895
|
-
& (
|
|
989
|
+
((unpacked_dataset.seq_flgs.data == 3) | (unpacked_dataset.seq_flgs.data == 1))
|
|
990
|
+
& (unpacked_dataset.priority.data == 0)
|
|
896
991
|
)[0]
|
|
897
|
-
acq_start_seconds =
|
|
898
|
-
acq_start_subseconds =
|
|
992
|
+
acq_start_seconds = unpacked_dataset.acq_start_seconds[epoch_indices]
|
|
993
|
+
acq_start_subseconds = unpacked_dataset.acq_start_subseconds[epoch_indices]
|
|
899
994
|
|
|
900
995
|
# Calculate epoch variables
|
|
901
996
|
epochs, epochs_delta_minus, epochs_delta_plus = calculate_epoch_values(
|
|
@@ -953,20 +1048,19 @@ def create_direct_event_dataset(apid: int, packets: xr.Dataset) -> xr.Dataset:
|
|
|
953
1048
|
)
|
|
954
1049
|
|
|
955
1050
|
# Create the CDF data variables for each Priority and Field
|
|
956
|
-
for
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
)
|
|
1051
|
+
for field in constants.DE_DATA_PRODUCT_CONFIGURATIONS[apid]["cdf_fields"]:
|
|
1052
|
+
if field in ["num_events", "data_quality"]:
|
|
1053
|
+
attrs = cdf_attrs.get_variable_attributes("de_2d_attrs")
|
|
1054
|
+
dims = ["epoch", "priority"]
|
|
1055
|
+
else:
|
|
1056
|
+
attrs = cdf_attrs.get_variable_attributes("de_3d_attrs")
|
|
1057
|
+
dims = ["epoch", "priority", "event_num"]
|
|
1058
|
+
dataset[field] = xr.DataArray(
|
|
1059
|
+
np.array(reshaped_de_data[field]),
|
|
1060
|
+
name=field,
|
|
1061
|
+
dims=dims,
|
|
1062
|
+
attrs=attrs,
|
|
1063
|
+
)
|
|
970
1064
|
|
|
971
1065
|
return dataset
|
|
972
1066
|
|
|
@@ -1392,7 +1486,7 @@ def reshape_de_data(
|
|
|
1392
1486
|
CDF variable names, and the values represent the data.
|
|
1393
1487
|
"""
|
|
1394
1488
|
# Dictionary to hold all the (soon to be restructured) direct event data
|
|
1395
|
-
|
|
1489
|
+
de_data: dict[str, np.ndarray] = {}
|
|
1396
1490
|
|
|
1397
1491
|
# Extract some useful variables
|
|
1398
1492
|
num_priorities = constants.DE_DATA_PRODUCT_CONFIGURATIONS[apid]["num_priorities"]
|
|
@@ -1412,16 +1506,20 @@ def reshape_de_data(
|
|
|
1412
1506
|
|
|
1413
1507
|
# Initialize data arrays for each priority and field to store the data
|
|
1414
1508
|
# We also need arrays to hold number of events and data quality
|
|
1415
|
-
for
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1509
|
+
for field in bit_structure:
|
|
1510
|
+
# if these two, no need to store
|
|
1511
|
+
if field not in ["Priority", "Spare"]:
|
|
1512
|
+
de_data[f"{field}"] = np.full(
|
|
1513
|
+
(num_epochs, num_priorities, 10000),
|
|
1514
|
+
bit_structure[field]["fillval"],
|
|
1515
|
+
dtype=bit_structure[field]["dtype"],
|
|
1516
|
+
)
|
|
1517
|
+
# Add other additional fields of l1a
|
|
1518
|
+
de_data["num_events"] = np.full(
|
|
1519
|
+
(num_epochs, num_priorities), 65535, dtype=np.uint16
|
|
1520
|
+
)
|
|
1521
|
+
|
|
1522
|
+
de_data["data_quality"] = np.full((num_epochs, num_priorities), 255, dtype=np.uint8)
|
|
1425
1523
|
|
|
1426
1524
|
# decompressed_data is one large list of values of length
|
|
1427
1525
|
# (<number of epochs> * <number of priorities>)
|
|
@@ -1445,8 +1543,8 @@ def reshape_de_data(
|
|
|
1445
1543
|
|
|
1446
1544
|
# Number of events and data quality can be determined at this stage
|
|
1447
1545
|
num_events = num_events_arr[epoch_start:epoch_end][i]
|
|
1448
|
-
|
|
1449
|
-
|
|
1546
|
+
de_data["num_events"][epoch_index, priority_num] = num_events
|
|
1547
|
+
de_data["data_quality"][epoch_index, priority_num] = data_quality[i]
|
|
1450
1548
|
|
|
1451
1549
|
# Iterate over each event
|
|
1452
1550
|
for event_index in range(num_events):
|
|
@@ -1477,12 +1575,12 @@ def reshape_de_data(
|
|
|
1477
1575
|
)
|
|
1478
1576
|
|
|
1479
1577
|
# Set the value into the data array
|
|
1480
|
-
|
|
1578
|
+
de_data[f"{field_name}"][epoch_index, priority_num, event_index] = (
|
|
1481
1579
|
value
|
|
1482
1580
|
)
|
|
1483
1581
|
bit_position += field_components["bit_length"]
|
|
1484
1582
|
|
|
1485
|
-
return
|
|
1583
|
+
return de_data
|
|
1486
1584
|
|
|
1487
1585
|
|
|
1488
1586
|
def process_codice_l1a(file_path: Path) -> list[xr.Dataset]:
|
|
@@ -9,18 +9,18 @@ from imap_processing.codice.codice_l1b import process_codice_l1b
|
|
|
9
9
|
dataset = process_codice_l1b(l1a_filenanme)
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
# TODO: Figure out how to convert hi-priority data product. Need an updated
|
|
13
|
-
# algorithm document that describes this.
|
|
14
|
-
|
|
15
12
|
import logging
|
|
16
13
|
from pathlib import Path
|
|
17
14
|
|
|
18
15
|
import numpy as np
|
|
19
16
|
import xarray as xr
|
|
20
17
|
|
|
18
|
+
from imap_processing import imap_module_directory
|
|
21
19
|
from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
|
|
22
20
|
from imap_processing.cdf.utils import load_cdf
|
|
23
21
|
from imap_processing.codice import constants
|
|
22
|
+
from imap_processing.codice.utils import CODICEAPID
|
|
23
|
+
from imap_processing.utils import packet_file_to_datasets
|
|
24
24
|
|
|
25
25
|
logger = logging.getLogger(__name__)
|
|
26
26
|
logger.setLevel(logging.INFO)
|
|
@@ -49,9 +49,6 @@ def convert_to_rates(
|
|
|
49
49
|
rates_data : np.ndarray
|
|
50
50
|
The converted data array.
|
|
51
51
|
"""
|
|
52
|
-
# TODO: Temporary workaround to create CDFs for SIT-4. Revisit after SIT-4.
|
|
53
|
-
acq_times = 1
|
|
54
|
-
|
|
55
52
|
if descriptor in [
|
|
56
53
|
"lo-counters-aggregated",
|
|
57
54
|
"lo-counters-singles",
|
|
@@ -65,6 +62,13 @@ def convert_to_rates(
|
|
|
65
62
|
]:
|
|
66
63
|
# Applying rate calculation described in section 10.2 of the algorithm
|
|
67
64
|
# document
|
|
65
|
+
# In order to divide by acquisition times, we must reshape the acq
|
|
66
|
+
# time data array to match the data variable shape
|
|
67
|
+
dims = [1] * dataset[variable_name].data.ndim
|
|
68
|
+
dims[1] = 128
|
|
69
|
+
acq_times = dataset.acquisition_time_per_step.data.reshape(dims)
|
|
70
|
+
|
|
71
|
+
# Now perform the calculation
|
|
68
72
|
rates_data = dataset[variable_name].data / (
|
|
69
73
|
acq_times
|
|
70
74
|
* 1e-6 # Converting from microseconds to seconds
|
|
@@ -83,10 +87,8 @@ def convert_to_rates(
|
|
|
83
87
|
rates_data = dataset[variable_name].data / (
|
|
84
88
|
constants.L1B_DATA_PRODUCT_CONFIGURATIONS[descriptor]["num_spin_sectors"]
|
|
85
89
|
* constants.L1B_DATA_PRODUCT_CONFIGURATIONS[descriptor]["num_spins"]
|
|
86
|
-
*
|
|
90
|
+
* constants.HI_ACQUISITION_TIME
|
|
87
91
|
)
|
|
88
|
-
elif descriptor == "hskp":
|
|
89
|
-
rates_data = dataset[variable_name].data / acq_times
|
|
90
92
|
|
|
91
93
|
return rates_data
|
|
92
94
|
|
|
@@ -131,35 +133,43 @@ def process_codice_l1b(file_path: Path) -> xr.Dataset:
|
|
|
131
133
|
# Update the global attributes
|
|
132
134
|
l1b_dataset.attrs = cdf_attrs.get_global_attributes(dataset_name)
|
|
133
135
|
|
|
134
|
-
#
|
|
135
|
-
# TODO: Figure out exactly which hskp variables need to be converted
|
|
136
|
-
# Housekeeping and binned datasets are treated a bit differently since
|
|
137
|
-
# not all variables need to be converted
|
|
136
|
+
# TODO: This was thrown together quickly and should be double-checked
|
|
138
137
|
if descriptor == "hskp":
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
138
|
+
xtce_filename = "codice_packet_definition.xml"
|
|
139
|
+
xtce_packet_definition = Path(
|
|
140
|
+
f"{imap_module_directory}/codice/packet_definitions/{xtce_filename}"
|
|
141
|
+
)
|
|
142
|
+
packet_file = (
|
|
143
|
+
imap_module_directory
|
|
144
|
+
/ "tests"
|
|
145
|
+
/ "codice"
|
|
146
|
+
/ "data"
|
|
147
|
+
/ "imap_codice_l0_raw_20241110_v001.pkts"
|
|
148
|
+
)
|
|
149
|
+
datasets: dict[int, xr.Dataset] = packet_file_to_datasets(
|
|
150
|
+
packet_file, xtce_packet_definition, use_derived_value=True
|
|
151
|
+
)
|
|
152
|
+
l1b_dataset = datasets[CODICEAPID.COD_NHK]
|
|
153
|
+
|
|
154
|
+
# TODO: Drop the same variables as we do in L1a? (see line 1103 in
|
|
155
|
+
# codice_l1a.py
|
|
156
|
+
|
|
147
157
|
else:
|
|
148
158
|
variables_to_convert = getattr(
|
|
149
159
|
constants, f"{descriptor.upper().replace('-', '_')}_VARIABLE_NAMES"
|
|
150
160
|
)
|
|
151
161
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
162
|
+
# Apply the conversion to rates
|
|
163
|
+
for variable_name in variables_to_convert:
|
|
164
|
+
l1b_dataset[variable_name].data = convert_to_rates(
|
|
165
|
+
l1b_dataset, descriptor, variable_name
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
# Set the variable attributes
|
|
169
|
+
cdf_attrs_key = f"{descriptor}-{variable_name}"
|
|
170
|
+
l1b_dataset[variable_name].attrs = cdf_attrs.get_variable_attributes(
|
|
171
|
+
cdf_attrs_key, check_schema=False
|
|
172
|
+
)
|
|
163
173
|
|
|
164
174
|
logger.info(f"\nFinal data product:\n{l1b_dataset}\n")
|
|
165
175
|
|