imap-processing 0.17.0__py3-none-any.whl → 0.19.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/ancillary/ancillary_dataset_combiner.py +161 -1
- imap_processing/ccsds/excel_to_xtce.py +12 -0
- imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +6 -6
- imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +312 -274
- imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +39 -28
- imap_processing/cdf/config/imap_codice_l2_variable_attrs.yaml +1048 -183
- imap_processing/cdf/config/imap_constant_attrs.yaml +4 -2
- imap_processing/cdf/config/imap_glows_l1b_variable_attrs.yaml +12 -0
- 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_hit_l1a_variable_attrs.yaml +163 -100
- imap_processing/cdf/config/imap_hit_l2_variable_attrs.yaml +4 -4
- imap_processing/cdf/config/imap_ialirt_l1_variable_attrs.yaml +97 -54
- imap_processing/cdf/config/imap_idex_l2a_variable_attrs.yaml +33 -4
- imap_processing/cdf/config/imap_idex_l2b_variable_attrs.yaml +44 -44
- imap_processing/cdf/config/imap_idex_l2c_variable_attrs.yaml +77 -61
- imap_processing/cdf/config/imap_lo_global_cdf_attrs.yaml +30 -0
- 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 +99 -2
- imap_processing/cdf/config/imap_mag_l1c_variable_attrs.yaml +24 -1
- imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +60 -0
- imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +99 -11
- imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +50 -7
- imap_processing/cli.py +121 -44
- imap_processing/codice/codice_l1a.py +165 -77
- imap_processing/codice/codice_l1b.py +1 -1
- imap_processing/codice/codice_l2.py +118 -19
- imap_processing/codice/constants.py +1217 -1089
- imap_processing/decom.py +1 -4
- imap_processing/ena_maps/ena_maps.py +32 -25
- 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 +99 -9
- imap_processing/glows/l1b/glows_l1b_data.py +350 -38
- 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_l2.py +84 -51
- imap_processing/hi/utils.py +153 -8
- imap_processing/hit/l0/constants.py +3 -0
- imap_processing/hit/l0/decom_hit.py +5 -8
- imap_processing/hit/l1a/hit_l1a.py +375 -45
- imap_processing/hit/l1b/constants.py +5 -0
- imap_processing/hit/l1b/hit_l1b.py +61 -131
- imap_processing/hit/l2/constants.py +1 -1
- imap_processing/hit/l2/hit_l2.py +10 -11
- imap_processing/ialirt/calculate_ingest.py +219 -0
- imap_processing/ialirt/constants.py +32 -1
- imap_processing/ialirt/generate_coverage.py +201 -0
- imap_processing/ialirt/l0/ialirt_spice.py +5 -2
- imap_processing/ialirt/l0/parse_mag.py +337 -29
- imap_processing/ialirt/l0/process_hit.py +5 -3
- imap_processing/ialirt/l0/process_swapi.py +41 -25
- imap_processing/ialirt/l0/process_swe.py +23 -7
- imap_processing/ialirt/process_ephemeris.py +70 -14
- imap_processing/ialirt/utils/constants.py +22 -16
- imap_processing/ialirt/utils/create_xarray.py +42 -19
- imap_processing/idex/idex_constants.py +1 -5
- 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 +313 -119
- imap_processing/idex/idex_utils.py +1 -3
- imap_processing/lo/l0/lo_apid.py +1 -0
- imap_processing/lo/l0/lo_science.py +25 -24
- imap_processing/lo/l1a/lo_l1a.py +44 -0
- imap_processing/lo/l1b/lo_l1b.py +3 -3
- imap_processing/lo/l1c/lo_l1c.py +116 -50
- imap_processing/lo/l2/lo_l2.py +29 -29
- imap_processing/lo/lo_ancillary.py +55 -0
- imap_processing/lo/packet_definitions/lo_xtce.xml +5359 -106
- imap_processing/mag/constants.py +1 -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/__init__.py +0 -0
- imap_processing/mag/l1d/mag_l1d.py +176 -0
- imap_processing/mag/l1d/mag_l1d_data.py +725 -0
- imap_processing/mag/l2/__init__.py +0 -0
- imap_processing/mag/l2/mag_l2.py +25 -20
- imap_processing/mag/l2/mag_l2_data.py +199 -130
- imap_processing/quality_flags.py +28 -2
- imap_processing/spice/geometry.py +101 -36
- imap_processing/spice/pointing_frame.py +1 -7
- imap_processing/spice/repoint.py +29 -2
- imap_processing/spice/spin.py +32 -8
- imap_processing/spice/time.py +60 -19
- imap_processing/swapi/l1/swapi_l1.py +10 -4
- imap_processing/swapi/l2/swapi_l2.py +66 -24
- imap_processing/swapi/swapi_utils.py +1 -1
- imap_processing/swe/l1b/swe_l1b.py +3 -6
- imap_processing/ultra/constants.py +28 -3
- imap_processing/ultra/l0/decom_tools.py +15 -8
- imap_processing/ultra/l0/decom_ultra.py +35 -11
- imap_processing/ultra/l0/ultra_utils.py +102 -12
- imap_processing/ultra/l1a/ultra_l1a.py +26 -6
- imap_processing/ultra/l1b/cullingmask.py +6 -3
- imap_processing/ultra/l1b/de.py +122 -26
- imap_processing/ultra/l1b/extendedspin.py +29 -2
- imap_processing/ultra/l1b/lookup_utils.py +424 -50
- imap_processing/ultra/l1b/quality_flag_filters.py +23 -0
- imap_processing/ultra/l1b/ultra_l1b_culling.py +356 -5
- imap_processing/ultra/l1b/ultra_l1b_extended.py +534 -90
- imap_processing/ultra/l1c/helio_pset.py +127 -7
- imap_processing/ultra/l1c/l1c_lookup_utils.py +256 -0
- imap_processing/ultra/l1c/spacecraft_pset.py +90 -15
- imap_processing/ultra/l1c/ultra_l1c.py +6 -0
- imap_processing/ultra/l1c/ultra_l1c_culling.py +85 -0
- imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +446 -341
- imap_processing/ultra/l2/ultra_l2.py +0 -1
- imap_processing/ultra/utils/ultra_l1_utils.py +40 -3
- imap_processing/utils.py +3 -4
- {imap_processing-0.17.0.dist-info → imap_processing-0.19.0.dist-info}/METADATA +3 -3
- {imap_processing-0.17.0.dist-info → imap_processing-0.19.0.dist-info}/RECORD +126 -126
- imap_processing/idex/idex_l2c.py +0 -250
- imap_processing/spice/kernels.py +0 -187
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_LeftSlit.csv +0 -526
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_RightSlit.csv +0 -526
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_LeftSlit.csv +0 -526
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_RightSlit.csv +0 -524
- imap_processing/ultra/lookup_tables/EgyNorm.mem.csv +0 -32769
- imap_processing/ultra/lookup_tables/FM45_Startup1_ULTRA_IMGPARAMS_20240719.csv +0 -2
- imap_processing/ultra/lookup_tables/FM90_Startup1_ULTRA_IMGPARAMS_20240719.csv +0 -2
- imap_processing/ultra/lookup_tables/dps_grid45_compressed.cdf +0 -0
- imap_processing/ultra/lookup_tables/ultra45_back-pos-luts.csv +0 -4097
- imap_processing/ultra/lookup_tables/ultra45_tdc_norm.csv +0 -2050
- imap_processing/ultra/lookup_tables/ultra90_back-pos-luts.csv +0 -4097
- imap_processing/ultra/lookup_tables/ultra90_tdc_norm.csv +0 -2050
- imap_processing/ultra/lookup_tables/yadjust.csv +0 -257
- {imap_processing-0.17.0.dist-info → imap_processing-0.19.0.dist-info}/LICENSE +0 -0
- {imap_processing-0.17.0.dist-info → imap_processing-0.19.0.dist-info}/WHEEL +0 -0
- {imap_processing-0.17.0.dist-info → imap_processing-0.19.0.dist-info}/entry_points.txt +0 -0
|
@@ -12,31 +12,77 @@ from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
|
|
|
12
12
|
logger = logging.getLogger(__name__)
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
SWAPI_LIVETIME = 0.145 # seconds
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
def solve_full_sweep_energy(
|
|
19
|
-
esa_lvl5_data: np.ndarray,
|
|
19
|
+
esa_lvl5_data: np.ndarray,
|
|
20
|
+
sweep_table: np.ndarray,
|
|
21
|
+
esa_table_df: pd.DataFrame,
|
|
22
|
+
lut_notes_df: pd.DataFrame,
|
|
23
|
+
data_time: npt.NDArray[np.datetime64],
|
|
20
24
|
) -> npt.NDArray:
|
|
21
25
|
"""
|
|
22
26
|
Calculate the energy of each full sweep data.
|
|
23
27
|
|
|
28
|
+
Get the fixed energy values for steps 0-62 using the
|
|
29
|
+
esa_table_df information. It's important to ensure
|
|
30
|
+
that the correct fixed energy values are selected for
|
|
31
|
+
the specified time, as the sweep table can contain
|
|
32
|
+
different values depending on the operational phase
|
|
33
|
+
(e.g., I+T, pre-launch, post-launch). There may be
|
|
34
|
+
more fixed energy added in the future. TODO: add
|
|
35
|
+
document section once SWAPI document is updated.
|
|
36
|
+
|
|
37
|
+
Now, find the last 9 fine energy values using steps
|
|
38
|
+
noted in the section x in the algorithm document.
|
|
39
|
+
|
|
24
40
|
Parameters
|
|
25
41
|
----------
|
|
26
42
|
esa_lvl5_data : numpy.ndarray
|
|
27
43
|
The L1 data input.
|
|
44
|
+
sweep_table : numpy.ndarray
|
|
45
|
+
Sweep table information.
|
|
28
46
|
esa_table_df : pandas.DataFrame
|
|
29
47
|
The ESA unit conversion table that contains first 63 energies.
|
|
30
48
|
lut_notes_df : pandas.DataFrame
|
|
31
49
|
The LUT notes table that contains the last 9 fine energies.
|
|
50
|
+
data_time : numpy.ndarray
|
|
51
|
+
The collection time of the data.
|
|
32
52
|
|
|
33
53
|
Returns
|
|
34
54
|
-------
|
|
35
55
|
energy : numpy.ndarray
|
|
36
56
|
The energy of each full sweep data.
|
|
37
57
|
"""
|
|
38
|
-
#
|
|
39
|
-
|
|
58
|
+
# Convert timestamp from string to datetime
|
|
59
|
+
# and to the same format as data_time
|
|
60
|
+
esa_table_df["timestamp"] = pd.to_datetime(
|
|
61
|
+
esa_table_df["timestamp"], format="%m/%d/%Y %H:%M"
|
|
62
|
+
)
|
|
63
|
+
esa_table_df["timestamp"] = esa_table_df["timestamp"].to_numpy(
|
|
64
|
+
dtype="datetime64[ns]"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
first_63_energies = []
|
|
68
|
+
|
|
69
|
+
for time, sweep_id in zip(data_time, sweep_table, strict=False):
|
|
70
|
+
# Find the sweep's ESA data for the given time and sweep_id
|
|
71
|
+
subset = esa_table_df[
|
|
72
|
+
(esa_table_df["timestamp"] <= time) & (esa_table_df["Sweep #"] == sweep_id)
|
|
73
|
+
]
|
|
74
|
+
if subset.empty:
|
|
75
|
+
first_63_energies.append(np.full(63, np.nan, dtype=np.float64))
|
|
76
|
+
continue
|
|
77
|
+
|
|
78
|
+
# Subset data can contain multiple 72 energy values with last 9 fine energies
|
|
79
|
+
# with 'Solve' value. We need to sort by time and ESA step to maintain correct
|
|
80
|
+
# order. Then take the last group of 72 steps values and select first 63
|
|
81
|
+
# values only.
|
|
82
|
+
subset = subset.sort_values(["timestamp", "ESA Step #"])
|
|
83
|
+
grouped = subset["Energy"].values.reshape(-1, 72)
|
|
84
|
+
first_63 = grouped[-1, :63]
|
|
85
|
+
first_63_energies.append(first_63)
|
|
40
86
|
|
|
41
87
|
# Find last 9 fine energy values of all sweeps data
|
|
42
88
|
# -------------------------------------------------
|
|
@@ -96,13 +142,9 @@ def solve_full_sweep_energy(
|
|
|
96
142
|
# order it should be in:
|
|
97
143
|
# [64, 65, 66, 67, 68, 69, 70, 71, 72]
|
|
98
144
|
energy_values = np.flip(energy_values, axis=1)
|
|
99
|
-
# Expand to match the number of rows in energy_values
|
|
100
|
-
first_63_values = np.tile(
|
|
101
|
-
fixed_energy_values, (energy_values.shape[0], 1)
|
|
102
|
-
) # (epoch, 63)
|
|
103
145
|
|
|
104
146
|
# Append the first_63_values in front of energy_values
|
|
105
|
-
sweeps_energy_value = np.hstack(
|
|
147
|
+
sweeps_energy_value = np.hstack([first_63_energies, energy_values])
|
|
106
148
|
|
|
107
149
|
return sweeps_energy_value
|
|
108
150
|
|
|
@@ -117,14 +159,12 @@ def swapi_l2(
|
|
|
117
159
|
|
|
118
160
|
To process science data to L2, we need to:
|
|
119
161
|
- convert counts to rates. This is done by dividing the counts by the
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
(12 s, coarse + fine sweep) by the total energy steps (72),
|
|
123
|
-
i.e., TIME_PER_BIN = 12/72 = 0.167 s. This will be constant.
|
|
162
|
+
SWAPI_LIVETIME time. LIVETIME is data acquisition time. It will
|
|
163
|
+
be constant, SWAPI_LIVETIME = 0.145 s.
|
|
124
164
|
|
|
125
165
|
- update uncertainty. Calculate new uncertainty value using
|
|
126
|
-
SWP_PCEM_ERR data from level one and divide by
|
|
127
|
-
SWP_PCEM_UNC = SWP_PCEM_ERR /
|
|
166
|
+
SWP_PCEM_ERR data from level one and divide by SWAPI_LIVETIME. Eg.
|
|
167
|
+
SWP_PCEM_UNC = SWP_PCEM_ERR / SWAPI_LIVETIME
|
|
128
168
|
Do the same for SCEM and COIN data.
|
|
129
169
|
|
|
130
170
|
Parameters
|
|
@@ -169,8 +209,10 @@ def swapi_l2(
|
|
|
169
209
|
esa_lvl5_hex = np.vectorize(lambda x: format(x, "X"))(l1_dataset["esa_lvl5"].values)
|
|
170
210
|
esa_energy = solve_full_sweep_energy(
|
|
171
211
|
esa_lvl5_hex,
|
|
212
|
+
l1_dataset["sweep_table"].data,
|
|
172
213
|
esa_table_df=esa_table_df,
|
|
173
214
|
lut_notes_df=lut_notes_df,
|
|
215
|
+
data_time=np.array(l1_dataset["epoch"].data, dtype="datetime64[ns]"),
|
|
174
216
|
)
|
|
175
217
|
|
|
176
218
|
l2_dataset["swp_esa_energy"] = xr.DataArray(
|
|
@@ -189,9 +231,9 @@ def swapi_l2(
|
|
|
189
231
|
]
|
|
190
232
|
|
|
191
233
|
# convert counts to rate
|
|
192
|
-
l2_dataset["swp_pcem_rate"] = l1_dataset["swp_pcem_counts"] /
|
|
193
|
-
l2_dataset["swp_scem_rate"] = l1_dataset["swp_scem_counts"] /
|
|
194
|
-
l2_dataset["swp_coin_rate"] = l1_dataset["swp_coin_counts"] /
|
|
234
|
+
l2_dataset["swp_pcem_rate"] = l1_dataset["swp_pcem_counts"] / SWAPI_LIVETIME
|
|
235
|
+
l2_dataset["swp_scem_rate"] = l1_dataset["swp_scem_counts"] / SWAPI_LIVETIME
|
|
236
|
+
l2_dataset["swp_coin_rate"] = l1_dataset["swp_coin_counts"] / SWAPI_LIVETIME
|
|
195
237
|
# update attrs
|
|
196
238
|
l2_dataset["swp_pcem_rate"].attrs = cdf_manager.get_variable_attributes("pcem_rate")
|
|
197
239
|
l2_dataset["swp_scem_rate"].attrs = cdf_manager.get_variable_attributes("scem_rate")
|
|
@@ -199,22 +241,22 @@ def swapi_l2(
|
|
|
199
241
|
|
|
200
242
|
# update uncertainty
|
|
201
243
|
l2_dataset["swp_pcem_rate_stat_uncert_plus"] = (
|
|
202
|
-
l1_dataset["swp_pcem_counts_stat_uncert_plus"] /
|
|
244
|
+
l1_dataset["swp_pcem_counts_stat_uncert_plus"] / SWAPI_LIVETIME
|
|
203
245
|
)
|
|
204
246
|
l2_dataset["swp_pcem_rate_stat_uncert_minus"] = (
|
|
205
|
-
l1_dataset["swp_pcem_counts_stat_uncert_minus"] /
|
|
247
|
+
l1_dataset["swp_pcem_counts_stat_uncert_minus"] / SWAPI_LIVETIME
|
|
206
248
|
)
|
|
207
249
|
l2_dataset["swp_scem_rate_stat_uncert_plus"] = (
|
|
208
|
-
l1_dataset["swp_scem_counts_stat_uncert_plus"] /
|
|
250
|
+
l1_dataset["swp_scem_counts_stat_uncert_plus"] / SWAPI_LIVETIME
|
|
209
251
|
)
|
|
210
252
|
l2_dataset["swp_scem_rate_stat_uncert_minus"] = (
|
|
211
|
-
l1_dataset["swp_scem_counts_stat_uncert_minus"] /
|
|
253
|
+
l1_dataset["swp_scem_counts_stat_uncert_minus"] / SWAPI_LIVETIME
|
|
212
254
|
)
|
|
213
255
|
l2_dataset["swp_coin_rate_stat_uncert_plus"] = (
|
|
214
|
-
l1_dataset["swp_coin_counts_stat_uncert_plus"] /
|
|
256
|
+
l1_dataset["swp_coin_counts_stat_uncert_plus"] / SWAPI_LIVETIME
|
|
215
257
|
)
|
|
216
258
|
l2_dataset["swp_coin_rate_stat_uncert_minus"] = (
|
|
217
|
-
l1_dataset["swp_coin_counts_stat_uncert_minus"] /
|
|
259
|
+
l1_dataset["swp_coin_counts_stat_uncert_minus"] / SWAPI_LIVETIME
|
|
218
260
|
)
|
|
219
261
|
# update attrs
|
|
220
262
|
l2_dataset[
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import Union
|
|
6
5
|
|
|
7
6
|
import numpy as np
|
|
8
7
|
import numpy.typing as npt
|
|
@@ -51,7 +50,7 @@ def get_esa_dataframe(esa_table_number: int) -> pd.DataFrame:
|
|
|
51
50
|
|
|
52
51
|
|
|
53
52
|
def deadtime_correction(
|
|
54
|
-
counts: np.ndarray, acq_duration:
|
|
53
|
+
counts: np.ndarray, acq_duration: int | npt.NDArray
|
|
55
54
|
) -> npt.NDArray:
|
|
56
55
|
"""
|
|
57
56
|
Calculate deadtime correction.
|
|
@@ -767,10 +766,8 @@ def swe_l1b_science(dependencies: ProcessingInputCollection) -> xr.Dataset:
|
|
|
767
766
|
|
|
768
767
|
# Store ESA energies of full cycle for L2 purposes.
|
|
769
768
|
esa_energies = get_esa_energy_pattern(esa_lut_files[0])
|
|
770
|
-
# Repeat
|
|
771
|
-
esa_energies = np.repeat(esa_energies, total_packets // 4)
|
|
772
|
-
-1, swe_constants.N_ESA_STEPS, swe_constants.N_ANGLE_SECTORS
|
|
773
|
-
)
|
|
769
|
+
# Repeat the (24, 30) energy pattern n_cycles times along a new first axis
|
|
770
|
+
esa_energies = np.repeat(esa_energies[np.newaxis, :, :], total_packets // 4, axis=0)
|
|
774
771
|
# Convert voltage to electron energy in eV by apply conversion factor
|
|
775
772
|
esa_energies = esa_energies * swe_constants.ENERGY_CONVERSION_FACTOR
|
|
776
773
|
# ------------------------------------------------------------------
|
|
@@ -51,7 +51,6 @@ class UltraConstants:
|
|
|
51
51
|
Z_DSTOP: float = 2.6 / 2 # Position of stop foil on Z axis [mm]
|
|
52
52
|
Z_DS: float = 46.19 - (2.6 / 2) # Position of slit on Z axis [mm]
|
|
53
53
|
DF: float = 3.39 # Distance from slit to foil [mm]
|
|
54
|
-
|
|
55
54
|
# Derived constants
|
|
56
55
|
DMIN_PH_CTOF: float = (
|
|
57
56
|
Z_DS - (2**0.5) * DF
|
|
@@ -79,5 +78,31 @@ class UltraConstants:
|
|
|
79
78
|
CULLING_RPM_MIN = 2.0
|
|
80
79
|
CULLING_RPM_MAX = 6.0
|
|
81
80
|
|
|
82
|
-
# Thresholds for culling based on counts.
|
|
83
|
-
CULLING_ENERGY_BIN_EDGES: ClassVar[list] = [
|
|
81
|
+
# Thresholds for culling based on counts (keV).
|
|
82
|
+
CULLING_ENERGY_BIN_EDGES: ClassVar[list] = [
|
|
83
|
+
3.385,
|
|
84
|
+
4.13722222222222,
|
|
85
|
+
5.05660493827161,
|
|
86
|
+
6.18029492455419,
|
|
87
|
+
7.55369379667734,
|
|
88
|
+
9.23229241816119,
|
|
89
|
+
11.2839129555303,
|
|
90
|
+
13.7914491678704,
|
|
91
|
+
16.8562156496194,
|
|
92
|
+
20.6020413495348,
|
|
93
|
+
25.1802727605426,
|
|
94
|
+
30.775888929552,
|
|
95
|
+
37.6149753583414,
|
|
96
|
+
45.9738587713061,
|
|
97
|
+
56.1902718315964,
|
|
98
|
+
68.6769989052845,
|
|
99
|
+
83.93855421757,
|
|
100
|
+
102.591566265919,
|
|
101
|
+
125.38969210279,
|
|
102
|
+
153.254068125632,
|
|
103
|
+
187.310527709106,
|
|
104
|
+
228.93508942224,
|
|
105
|
+
279.809553738294,
|
|
106
|
+
341.989454569026,
|
|
107
|
+
1e5,
|
|
108
|
+
]
|
|
@@ -4,6 +4,7 @@ import numpy as np
|
|
|
4
4
|
from numpy.typing import NDArray
|
|
5
5
|
|
|
6
6
|
from imap_processing.ultra.l0.ultra_utils import (
|
|
7
|
+
PacketProperties,
|
|
7
8
|
parse_event,
|
|
8
9
|
)
|
|
9
10
|
from imap_processing.utils import convert_to_binary_string
|
|
@@ -155,8 +156,7 @@ def decompress_binary(
|
|
|
155
156
|
def decompress_image(
|
|
156
157
|
pixel0: int,
|
|
157
158
|
binary_data: str,
|
|
158
|
-
|
|
159
|
-
mantissa_bit_length: int,
|
|
159
|
+
packet_props: PacketProperties,
|
|
160
160
|
) -> NDArray:
|
|
161
161
|
"""
|
|
162
162
|
Will decompress a binary string representing an image into a matrix of pixel values.
|
|
@@ -171,10 +171,9 @@ def decompress_image(
|
|
|
171
171
|
The first, unmodified pixel p0,0.
|
|
172
172
|
binary_data : str
|
|
173
173
|
Binary string.
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
The bit length of the mantissa.
|
|
174
|
+
packet_props : PacketProperties
|
|
175
|
+
Properties of the packet, including width bit, mantissa bit length and pixel
|
|
176
|
+
window dimensions.
|
|
178
177
|
|
|
179
178
|
Returns
|
|
180
179
|
-------
|
|
@@ -187,10 +186,18 @@ def decompress_image(
|
|
|
187
186
|
This process is described starting on page 168 in IMAP-Ultra Flight
|
|
188
187
|
Software Specification document.
|
|
189
188
|
"""
|
|
190
|
-
rows =
|
|
191
|
-
cols =
|
|
189
|
+
rows = packet_props.pixel_window_rows
|
|
190
|
+
cols = packet_props.pixel_window_columns
|
|
191
|
+
width_bit = packet_props.width
|
|
192
|
+
mantissa_bit_length = packet_props.mantissa_bit_length
|
|
192
193
|
pixels_per_block = 15
|
|
193
194
|
|
|
195
|
+
if width_bit is None or rows is None or cols is None or mantissa_bit_length is None:
|
|
196
|
+
raise ValueError(
|
|
197
|
+
"Packet properties must specify pixel window dimensions, "
|
|
198
|
+
"width bit, and mantissa bit length for this packet type."
|
|
199
|
+
)
|
|
200
|
+
|
|
194
201
|
blocks_per_row = cols // pixels_per_block
|
|
195
202
|
|
|
196
203
|
# Compressed pixel matrix
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Decommutates Ultra CCSDS packets."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
+
import math
|
|
4
5
|
from collections import defaultdict
|
|
5
6
|
from typing import cast
|
|
6
7
|
|
|
@@ -29,7 +30,7 @@ from imap_processing.ultra.l0.ultra_utils import (
|
|
|
29
30
|
ULTRA_PRI_3_EVENTS,
|
|
30
31
|
ULTRA_PRI_4_EVENTS,
|
|
31
32
|
ULTRA_RATES,
|
|
32
|
-
|
|
33
|
+
PacketProperties,
|
|
33
34
|
)
|
|
34
35
|
from imap_processing.utils import convert_to_binary_string
|
|
35
36
|
|
|
@@ -37,7 +38,7 @@ logging.basicConfig(level=logging.INFO)
|
|
|
37
38
|
logger = logging.getLogger(__name__)
|
|
38
39
|
|
|
39
40
|
|
|
40
|
-
def process_ultra_tof(ds: xr.Dataset) -> xr.Dataset:
|
|
41
|
+
def process_ultra_tof(ds: xr.Dataset, packet_props: PacketProperties) -> xr.Dataset:
|
|
41
42
|
"""
|
|
42
43
|
Unpack and decode Ultra TOF packets.
|
|
43
44
|
|
|
@@ -45,6 +46,9 @@ def process_ultra_tof(ds: xr.Dataset) -> xr.Dataset:
|
|
|
45
46
|
----------
|
|
46
47
|
ds : xarray.Dataset
|
|
47
48
|
TOF dataset.
|
|
49
|
+
packet_props : PacketProperties
|
|
50
|
+
Information that defines properties of the packet including the pixel window
|
|
51
|
+
dimensions of images and number of image panes.
|
|
48
52
|
|
|
49
53
|
Returns
|
|
50
54
|
-------
|
|
@@ -53,14 +57,35 @@ def process_ultra_tof(ds: xr.Dataset) -> xr.Dataset:
|
|
|
53
57
|
"""
|
|
54
58
|
scalar_keys = [key for key in ds.data_vars if key not in ("packetdata", "sid")]
|
|
55
59
|
|
|
60
|
+
image_planes = packet_props.image_planes
|
|
61
|
+
rows = packet_props.pixel_window_rows
|
|
62
|
+
cols = packet_props.pixel_window_columns
|
|
63
|
+
planes_per_packet = packet_props.image_planes_per_packet
|
|
64
|
+
|
|
65
|
+
if (
|
|
66
|
+
image_planes is None
|
|
67
|
+
or rows is None
|
|
68
|
+
or cols is None
|
|
69
|
+
or planes_per_packet is None
|
|
70
|
+
):
|
|
71
|
+
raise ValueError(
|
|
72
|
+
"Packet properties must specify pixel window dimensions, "
|
|
73
|
+
"width bit, image planes, and image planes per packet for this packet type."
|
|
74
|
+
)
|
|
75
|
+
# Calculate the number of image packets based on the number of image panes and
|
|
76
|
+
# planes per packet.
|
|
77
|
+
# There may be cases where the last packet has fewer planes than the
|
|
78
|
+
# planes_per_packet, to account for this, we use ceiling division.
|
|
79
|
+
num_image_packets = math.ceil(image_planes / planes_per_packet)
|
|
80
|
+
|
|
56
81
|
decom_data: defaultdict[str, list[np.ndarray]] = defaultdict(list)
|
|
57
82
|
decom_data["packetdata"] = []
|
|
58
83
|
valid_epoch = []
|
|
59
|
-
width = cast(int, ULTRA_TOF.width)
|
|
60
|
-
mantissa_bit_length = cast(int, ULTRA_TOF.mantissa_bit_length)
|
|
61
84
|
|
|
62
85
|
for val, group in ds.groupby("epoch"):
|
|
63
|
-
if set(group["sid"].values) >= set(
|
|
86
|
+
if set(group["sid"].values) >= set(
|
|
87
|
+
np.arange(0, image_planes, planes_per_packet)
|
|
88
|
+
):
|
|
64
89
|
valid_epoch.append(val)
|
|
65
90
|
group.sortby("sid")
|
|
66
91
|
|
|
@@ -68,13 +93,12 @@ def process_ultra_tof(ds: xr.Dataset) -> xr.Dataset:
|
|
|
68
93
|
decom_data[key].append(group[key].values)
|
|
69
94
|
|
|
70
95
|
image = []
|
|
71
|
-
for i in range(
|
|
96
|
+
for i in range(num_image_packets):
|
|
72
97
|
binary = convert_to_binary_string(group["packetdata"].values[i])
|
|
73
98
|
decompressed = decompress_image(
|
|
74
99
|
group["p00"].values[i],
|
|
75
100
|
binary,
|
|
76
|
-
|
|
77
|
-
mantissa_bit_length,
|
|
101
|
+
packet_props,
|
|
78
102
|
)
|
|
79
103
|
image.append(decompressed)
|
|
80
104
|
|
|
@@ -87,9 +111,9 @@ def process_ultra_tof(ds: xr.Dataset) -> xr.Dataset:
|
|
|
87
111
|
|
|
88
112
|
coords = {
|
|
89
113
|
"epoch": np.array(valid_epoch, dtype=np.uint64),
|
|
90
|
-
"sid": xr.DataArray(np.arange(
|
|
91
|
-
"row": xr.DataArray(np.arange(
|
|
92
|
-
"column": xr.DataArray(np.arange(
|
|
114
|
+
"sid": xr.DataArray(np.arange(num_image_packets), dims=["sid"], name="sid"),
|
|
115
|
+
"row": xr.DataArray(np.arange(rows), dims=["row"], name="row"),
|
|
116
|
+
"column": xr.DataArray(np.arange(cols), dims=["column"], name="column"),
|
|
93
117
|
}
|
|
94
118
|
|
|
95
119
|
dataset = xr.Dataset(coords=coords)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Contains data classes to support Ultra L0 processing."""
|
|
2
2
|
|
|
3
|
-
from typing import NamedTuple
|
|
3
|
+
from typing import NamedTuple
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class PacketProperties(NamedTuple):
|
|
@@ -9,16 +9,22 @@ class PacketProperties(NamedTuple):
|
|
|
9
9
|
apid: list # List of APIDs
|
|
10
10
|
logical_source: list # List of logical sources
|
|
11
11
|
addition_to_logical_desc: str # Description of the logical source
|
|
12
|
-
width:
|
|
13
|
-
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
len_array:
|
|
18
|
-
|
|
19
|
-
] # Length of the array to be decompressed (could be None).
|
|
20
|
-
mantissa_bit_length: Union[int, None] # used to determine the level of
|
|
12
|
+
width: int | None # Width of binary data (could be None).
|
|
13
|
+
# Block, image_planes, pixel_window_rows, and pixel_window_columns are important for
|
|
14
|
+
# decompressing the images and a description is available on page 171 of IMAP-Ultra
|
|
15
|
+
# Flight Software Specification document (7523-9009_Rev_-.pdf).
|
|
16
|
+
block: int | None # Number of values in each block (could be None).
|
|
17
|
+
len_array: int | None # Length of the array to be decompressed (could be None).
|
|
18
|
+
mantissa_bit_length: int | None # used to determine the level of
|
|
21
19
|
# precision that can be recovered from compressed data (could be None).
|
|
20
|
+
image_planes: int | None = None
|
|
21
|
+
# number of images. See table 11 in the FSSD.
|
|
22
|
+
pixel_window_rows: int | None = None
|
|
23
|
+
# number of rows in each image. See table 49 in the FSSD.
|
|
24
|
+
pixel_window_columns: int | None = None
|
|
25
|
+
# number of columns in each image. See table 49 in the FSSD.
|
|
26
|
+
image_planes_per_packet: int | None = None
|
|
27
|
+
# number of image planes in each packet. See table 52 in the FSSD.
|
|
22
28
|
|
|
23
29
|
|
|
24
30
|
# Define PacketProperties instances directly in the module namespace
|
|
@@ -64,15 +70,99 @@ ULTRA_ENERGY_SPECTRA = PacketProperties(
|
|
|
64
70
|
len_array=1,
|
|
65
71
|
mantissa_bit_length=5,
|
|
66
72
|
)
|
|
67
|
-
|
|
73
|
+
ULTRA_PHXTOF_HIGH_ANGULAR = PacketProperties(
|
|
68
74
|
apid=[883, 947],
|
|
69
75
|
logical_source=[
|
|
70
76
|
"imap_ultra_l1a_45sensor-histogram-ena-phxtof-hi-ang",
|
|
71
77
|
"imap_ultra_l1a_90sensor-histogram-ena-phxtof-hi-ang",
|
|
72
78
|
],
|
|
73
|
-
addition_to_logical_desc="Time of Flight Images",
|
|
79
|
+
addition_to_logical_desc="Pulse Height Time of Flight High Angular Images",
|
|
74
80
|
width=4,
|
|
75
81
|
block=15,
|
|
82
|
+
image_planes=8,
|
|
83
|
+
pixel_window_rows=54,
|
|
84
|
+
pixel_window_columns=180,
|
|
85
|
+
image_planes_per_packet=1,
|
|
86
|
+
len_array=None,
|
|
87
|
+
mantissa_bit_length=4,
|
|
88
|
+
)
|
|
89
|
+
ULTRA_PHXTOF_HIGH_ENERGY = PacketProperties(
|
|
90
|
+
apid=[884, 948],
|
|
91
|
+
logical_source=[
|
|
92
|
+
"imap_ultra_l1a_45sensor-histogram-ena-phxtof-hi-nrg",
|
|
93
|
+
"imap_ultra_l1a_90sensor-histogram-ena-phxtof-hi-nrg",
|
|
94
|
+
],
|
|
95
|
+
addition_to_logical_desc="Pulse Height By Time of Flight High Energy Images",
|
|
96
|
+
width=4,
|
|
97
|
+
block=15,
|
|
98
|
+
image_planes=28,
|
|
99
|
+
pixel_window_rows=27,
|
|
100
|
+
pixel_window_columns=90,
|
|
101
|
+
image_planes_per_packet=1,
|
|
102
|
+
len_array=None,
|
|
103
|
+
mantissa_bit_length=4,
|
|
104
|
+
)
|
|
105
|
+
ULTRA_PHXTOF_HIGH_TIME = PacketProperties(
|
|
106
|
+
apid=[885, 949],
|
|
107
|
+
logical_source=[
|
|
108
|
+
"imap_ultra_l1a_45sensor-histogram-ena-phxtof-hi-time",
|
|
109
|
+
"imap_ultra_l1a_90sensor-histogram-ena-phxtof-hi-time",
|
|
110
|
+
],
|
|
111
|
+
addition_to_logical_desc="Time of Flight High Time Images",
|
|
112
|
+
width=4,
|
|
113
|
+
block=15,
|
|
114
|
+
image_planes=8,
|
|
115
|
+
pixel_window_rows=18,
|
|
116
|
+
pixel_window_columns=60,
|
|
117
|
+
image_planes_per_packet=2,
|
|
118
|
+
len_array=None,
|
|
119
|
+
mantissa_bit_length=4,
|
|
120
|
+
)
|
|
121
|
+
ULTRA_EXTOF_HIGH_ANGULAR = PacketProperties(
|
|
122
|
+
apid=[886, 950],
|
|
123
|
+
logical_source=[
|
|
124
|
+
"imap_ultra_l1a_45sensor-histogram-ena-extof-hi-ang",
|
|
125
|
+
"imap_ultra_l1a_90sensor-histogram-ena-extof-hi-ang",
|
|
126
|
+
],
|
|
127
|
+
addition_to_logical_desc="Energy By Time of Flight High Angular Images",
|
|
128
|
+
width=4,
|
|
129
|
+
block=15,
|
|
130
|
+
image_planes=12,
|
|
131
|
+
pixel_window_rows=18,
|
|
132
|
+
pixel_window_columns=60,
|
|
133
|
+
image_planes_per_packet=2,
|
|
134
|
+
len_array=None,
|
|
135
|
+
mantissa_bit_length=4,
|
|
136
|
+
)
|
|
137
|
+
ULTRA_EXTOF_HIGH_TIME = PacketProperties(
|
|
138
|
+
apid=[888, 952],
|
|
139
|
+
logical_source=[
|
|
140
|
+
"imap_ultra_l1a_45sensor-histogram-ena-extof-hi-time",
|
|
141
|
+
"imap_ultra_l1a_90sensor-histogram-ena-extof-hi-time",
|
|
142
|
+
],
|
|
143
|
+
addition_to_logical_desc="Energy By Time of Flight High Time Images",
|
|
144
|
+
width=4,
|
|
145
|
+
block=15,
|
|
146
|
+
image_planes=4,
|
|
147
|
+
pixel_window_rows=9,
|
|
148
|
+
pixel_window_columns=30,
|
|
149
|
+
image_planes_per_packet=8,
|
|
150
|
+
len_array=None,
|
|
151
|
+
mantissa_bit_length=4,
|
|
152
|
+
)
|
|
153
|
+
ULTRA_EXTOF_HIGH_ENERGY = PacketProperties(
|
|
154
|
+
apid=[887, 951],
|
|
155
|
+
logical_source=[
|
|
156
|
+
"imap_ultra_l1a_45sensor-histogram-ena-extof-hi-nrg",
|
|
157
|
+
"imap_ultra_l1a_90sensor-histogram-ena-extof-hi-nrg",
|
|
158
|
+
],
|
|
159
|
+
addition_to_logical_desc="Energy By Time of Flight High Energy Images",
|
|
160
|
+
width=4,
|
|
161
|
+
block=15,
|
|
162
|
+
image_planes=44,
|
|
163
|
+
pixel_window_rows=9,
|
|
164
|
+
pixel_window_columns=30,
|
|
165
|
+
image_planes_per_packet=8,
|
|
76
166
|
len_array=None,
|
|
77
167
|
mantissa_bit_length=4,
|
|
78
168
|
)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""Generate ULTRA L1a CDFs."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
from typing import Optional
|
|
5
4
|
|
|
6
5
|
import xarray as xr
|
|
7
6
|
|
|
@@ -24,14 +23,19 @@ from imap_processing.ultra.l0.ultra_utils import (
|
|
|
24
23
|
ULTRA_ENERGY_RATES,
|
|
25
24
|
ULTRA_ENERGY_SPECTRA,
|
|
26
25
|
ULTRA_EVENTS,
|
|
26
|
+
ULTRA_EXTOF_HIGH_ANGULAR,
|
|
27
|
+
ULTRA_EXTOF_HIGH_ENERGY,
|
|
28
|
+
ULTRA_EXTOF_HIGH_TIME,
|
|
27
29
|
ULTRA_HK,
|
|
28
30
|
ULTRA_MACROS_CHECKSUM,
|
|
31
|
+
ULTRA_PHXTOF_HIGH_ANGULAR,
|
|
32
|
+
ULTRA_PHXTOF_HIGH_ENERGY,
|
|
33
|
+
ULTRA_PHXTOF_HIGH_TIME,
|
|
29
34
|
ULTRA_PRI_1_EVENTS,
|
|
30
35
|
ULTRA_PRI_2_EVENTS,
|
|
31
36
|
ULTRA_PRI_3_EVENTS,
|
|
32
37
|
ULTRA_PRI_4_EVENTS,
|
|
33
38
|
ULTRA_RATES,
|
|
34
|
-
ULTRA_TOF,
|
|
35
39
|
)
|
|
36
40
|
from imap_processing.utils import packet_file_to_datasets
|
|
37
41
|
|
|
@@ -39,7 +43,7 @@ logger = logging.getLogger(__name__)
|
|
|
39
43
|
|
|
40
44
|
|
|
41
45
|
def ultra_l1a( # noqa: PLR0912
|
|
42
|
-
packet_file: str, apid_input:
|
|
46
|
+
packet_file: str, apid_input: int | None = None
|
|
43
47
|
) -> list[xr.Dataset]:
|
|
44
48
|
"""
|
|
45
49
|
Will process ULTRA L0 data into L1A CDF files at output_filepath.
|
|
@@ -87,6 +91,19 @@ def ultra_l1a( # noqa: PLR0912
|
|
|
87
91
|
for i, apid in enumerate(group.apid)
|
|
88
92
|
}
|
|
89
93
|
|
|
94
|
+
all_l1a_image_apids = {
|
|
95
|
+
apid: group
|
|
96
|
+
for group in [
|
|
97
|
+
ULTRA_PHXTOF_HIGH_ANGULAR,
|
|
98
|
+
ULTRA_PHXTOF_HIGH_ENERGY,
|
|
99
|
+
ULTRA_PHXTOF_HIGH_TIME,
|
|
100
|
+
ULTRA_EXTOF_HIGH_ANGULAR,
|
|
101
|
+
ULTRA_EXTOF_HIGH_TIME,
|
|
102
|
+
ULTRA_EXTOF_HIGH_ENERGY,
|
|
103
|
+
]
|
|
104
|
+
for apid in group.apid
|
|
105
|
+
}
|
|
106
|
+
|
|
90
107
|
# Update dataset global attributes
|
|
91
108
|
attr_mgr = ImapCdfAttributes()
|
|
92
109
|
attr_mgr.add_instrument_global_attrs("ultra")
|
|
@@ -96,9 +113,12 @@ def ultra_l1a( # noqa: PLR0912
|
|
|
96
113
|
if apid in ULTRA_AUX.apid:
|
|
97
114
|
decom_ultra_dataset = datasets_by_apid[apid]
|
|
98
115
|
gattr_key = ULTRA_AUX.logical_source[ULTRA_AUX.apid.index(apid)]
|
|
99
|
-
elif apid in
|
|
100
|
-
|
|
101
|
-
|
|
116
|
+
elif apid in all_l1a_image_apids:
|
|
117
|
+
packet_props = all_l1a_image_apids[apid]
|
|
118
|
+
decom_ultra_dataset = process_ultra_tof(
|
|
119
|
+
datasets_by_apid[apid], packet_props
|
|
120
|
+
)
|
|
121
|
+
gattr_key = packet_props.logical_source[packet_props.apid.index(apid)]
|
|
102
122
|
elif apid in ULTRA_RATES.apid:
|
|
103
123
|
decom_ultra_dataset = process_ultra_rates(datasets_by_apid[apid])
|
|
104
124
|
decom_ultra_dataset = decom_ultra_dataset.drop_vars("fastdata_00")
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import numpy as np
|
|
4
4
|
import xarray as xr
|
|
5
5
|
|
|
6
|
-
from imap_processing.
|
|
6
|
+
from imap_processing.ultra.l1b.quality_flag_filters import SPIN_QUALITY_FLAG_FILTERS
|
|
7
7
|
from imap_processing.ultra.utils.ultra_l1_utils import create_dataset, extract_data_dict
|
|
8
8
|
|
|
9
9
|
FILLVAL_UINT16 = 65535
|
|
@@ -32,14 +32,17 @@ def calculate_cullingmask(extendedspin_dataset: xr.Dataset, name: str) -> xr.Dat
|
|
|
32
32
|
good_mask = (
|
|
33
33
|
(
|
|
34
34
|
extendedspin_dataset["quality_attitude"]
|
|
35
|
-
&
|
|
35
|
+
& sum(flag.value for flag in SPIN_QUALITY_FLAG_FILTERS["quality_attitude"])
|
|
36
36
|
)
|
|
37
37
|
== 0
|
|
38
38
|
) & (
|
|
39
39
|
(
|
|
40
40
|
(
|
|
41
41
|
extendedspin_dataset["quality_ena_rates"]
|
|
42
|
-
&
|
|
42
|
+
& sum(
|
|
43
|
+
flag.value
|
|
44
|
+
for flag in SPIN_QUALITY_FLAG_FILTERS["quality_ena_rates"]
|
|
45
|
+
)
|
|
43
46
|
)
|
|
44
47
|
== 0
|
|
45
48
|
).all(dim="energy_bin_geometric_mean")
|