imap-processing 0.16.2__py3-none-any.whl → 0.18.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of imap-processing might be problematic. Click here for more details.
- imap_processing/_version.py +2 -2
- imap_processing/ccsds/excel_to_xtce.py +12 -0
- imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +6 -6
- imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +35 -0
- imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +35 -0
- imap_processing/cdf/config/imap_codice_l2_variable_attrs.yaml +24 -0
- imap_processing/cdf/config/imap_hi_variable_attrs.yaml +8 -8
- imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +1 -1
- imap_processing/cdf/config/imap_hit_l1a_variable_attrs.yaml +163 -100
- imap_processing/cdf/config/imap_hit_l2_variable_attrs.yaml +398 -415
- imap_processing/cdf/config/imap_ialirt_l1_variable_attrs.yaml +97 -54
- imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +9 -9
- imap_processing/cdf/config/imap_idex_l2b_variable_attrs.yaml +233 -57
- imap_processing/cdf/config/imap_idex_l2c_variable_attrs.yaml +16 -90
- imap_processing/cdf/config/imap_lo_global_cdf_attrs.yaml +30 -0
- imap_processing/cdf/config/imap_mag_global_cdf_attrs.yaml +15 -1
- imap_processing/cdf/config/imap_swapi_variable_attrs.yaml +19 -0
- imap_processing/cdf/config/imap_swe_l1b_variable_attrs.yaml +20 -0
- imap_processing/cdf/config/imap_swe_l2_variable_attrs.yaml +39 -0
- imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +168 -0
- imap_processing/cdf/config/imap_ultra_l1a_variable_attrs.yaml +103 -2
- imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +91 -11
- imap_processing/cdf/utils.py +7 -1
- imap_processing/cli.py +42 -13
- imap_processing/codice/codice_l1a.py +125 -78
- imap_processing/codice/codice_l1b.py +1 -1
- imap_processing/codice/codice_l2.py +0 -9
- imap_processing/codice/constants.py +481 -498
- imap_processing/hi/hi_l1a.py +4 -4
- imap_processing/hi/hi_l1b.py +2 -2
- imap_processing/hi/packet_definitions/TLM_HI_COMBINED_SCI.xml +218 -38
- imap_processing/hit/hit_utils.py +2 -2
- imap_processing/hit/l0/decom_hit.py +4 -3
- imap_processing/hit/l1a/hit_l1a.py +64 -24
- imap_processing/hit/l1b/constants.py +5 -0
- imap_processing/hit/l1b/hit_l1b.py +18 -16
- imap_processing/hit/l2/constants.py +1 -1
- imap_processing/hit/l2/hit_l2.py +4 -4
- imap_processing/ialirt/constants.py +21 -0
- imap_processing/ialirt/generate_coverage.py +188 -0
- imap_processing/ialirt/l0/parse_mag.py +62 -5
- imap_processing/ialirt/l0/process_swapi.py +1 -1
- imap_processing/ialirt/l0/process_swe.py +23 -7
- imap_processing/ialirt/utils/constants.py +22 -16
- imap_processing/ialirt/utils/create_xarray.py +42 -19
- imap_processing/idex/idex_constants.py +8 -5
- imap_processing/idex/idex_l2b.py +554 -58
- imap_processing/idex/idex_l2c.py +30 -196
- imap_processing/lo/l0/lo_apid.py +1 -0
- imap_processing/lo/l0/lo_star_sensor.py +48 -0
- imap_processing/lo/l1a/lo_l1a.py +74 -30
- imap_processing/lo/packet_definitions/lo_xtce.xml +5359 -106
- imap_processing/mag/constants.py +1 -0
- imap_processing/mag/l0/decom_mag.py +9 -6
- imap_processing/mag/l0/mag_l0_data.py +46 -0
- imap_processing/mag/l1d/__init__.py +0 -0
- imap_processing/mag/l1d/mag_l1d.py +133 -0
- imap_processing/mag/l1d/mag_l1d_data.py +588 -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 +191 -130
- imap_processing/quality_flags.py +20 -2
- imap_processing/spice/geometry.py +25 -3
- imap_processing/spice/pointing_frame.py +1 -1
- imap_processing/spice/spin.py +4 -0
- imap_processing/spice/time.py +51 -0
- imap_processing/swapi/l1/swapi_l1.py +12 -2
- imap_processing/swapi/l2/swapi_l2.py +59 -14
- imap_processing/swapi/swapi_utils.py +1 -1
- imap_processing/swe/l1b/swe_l1b.py +11 -4
- imap_processing/swe/l2/swe_l2.py +111 -17
- imap_processing/ultra/constants.py +49 -1
- imap_processing/ultra/l0/decom_tools.py +28 -14
- imap_processing/ultra/l0/decom_ultra.py +225 -15
- imap_processing/ultra/l0/ultra_utils.py +281 -8
- imap_processing/ultra/l1a/ultra_l1a.py +77 -8
- imap_processing/ultra/l1b/cullingmask.py +3 -3
- imap_processing/ultra/l1b/de.py +53 -15
- imap_processing/ultra/l1b/extendedspin.py +26 -2
- imap_processing/ultra/l1b/lookup_utils.py +171 -50
- imap_processing/ultra/l1b/quality_flag_filters.py +14 -0
- imap_processing/ultra/l1b/ultra_l1b_culling.py +198 -5
- imap_processing/ultra/l1b/ultra_l1b_extended.py +304 -66
- imap_processing/ultra/l1c/helio_pset.py +54 -7
- imap_processing/ultra/l1c/spacecraft_pset.py +9 -1
- imap_processing/ultra/l1c/ultra_l1c.py +2 -0
- imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +106 -109
- imap_processing/ultra/packet_definitions/ULTRA_SCI_COMBINED.xml +3 -3
- imap_processing/ultra/utils/ultra_l1_utils.py +13 -1
- imap_processing/utils.py +20 -42
- {imap_processing-0.16.2.dist-info → imap_processing-0.18.0.dist-info}/METADATA +2 -2
- {imap_processing-0.16.2.dist-info → imap_processing-0.18.0.dist-info}/RECORD +95 -103
- imap_processing/lo/l0/data_classes/star_sensor.py +0 -98
- imap_processing/lo/l0/utils/lo_base.py +0 -57
- 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.16.2.dist-info → imap_processing-0.18.0.dist-info}/LICENSE +0 -0
- {imap_processing-0.16.2.dist-info → imap_processing-0.18.0.dist-info}/WHEEL +0 -0
- {imap_processing-0.16.2.dist-info → imap_processing-0.18.0.dist-info}/entry_points.txt +0 -0
|
@@ -7,7 +7,12 @@ import pandas as pd
|
|
|
7
7
|
import xarray as xr
|
|
8
8
|
from numpy.typing import NDArray
|
|
9
9
|
|
|
10
|
-
from imap_processing.quality_flags import
|
|
10
|
+
from imap_processing.quality_flags import (
|
|
11
|
+
ImapAttitudeUltraFlags,
|
|
12
|
+
ImapHkUltraFlags,
|
|
13
|
+
ImapInstrumentUltraFlags,
|
|
14
|
+
ImapRatesUltraFlags,
|
|
15
|
+
)
|
|
11
16
|
from imap_processing.spice.spin import get_spin_data
|
|
12
17
|
from imap_processing.ultra.constants import UltraConstants
|
|
13
18
|
|
|
@@ -106,6 +111,10 @@ def flag_attitude(
|
|
|
106
111
|
|
|
107
112
|
spin_period = spin_df.loc[spin_df.spin_number.isin(spins), "spin_period_sec"]
|
|
108
113
|
spin_starttime = spin_df.loc[spin_df.spin_number.isin(spins), "spin_start_met"]
|
|
114
|
+
spin_phase_valid = spin_df.loc[spin_df.spin_number.isin(spins), "spin_phase_valid"]
|
|
115
|
+
spin_period_valid = spin_df.loc[
|
|
116
|
+
spin_df.spin_number.isin(spins), "spin_period_valid"
|
|
117
|
+
]
|
|
109
118
|
spin_rates = 60 / spin_period # 60 seconds in a minute
|
|
110
119
|
bad_spin_rate_indices = (spin_rates < UltraConstants.CULLING_RPM_MIN) | (
|
|
111
120
|
spin_rates > UltraConstants.CULLING_RPM_MAX
|
|
@@ -118,9 +127,59 @@ def flag_attitude(
|
|
|
118
127
|
mismatch_indices = compare_aux_univ_spin_table(aux_dataset, spins, spin_df)
|
|
119
128
|
quality_flags[mismatch_indices] |= ImapAttitudeUltraFlags.AUXMISMATCH.value
|
|
120
129
|
|
|
130
|
+
# Spin phase validity flag
|
|
131
|
+
phase_invalid_indices = spin_phase_valid == 0
|
|
132
|
+
quality_flags[phase_invalid_indices] |= ImapAttitudeUltraFlags.SPINPHASE.value
|
|
133
|
+
|
|
134
|
+
# Spin period validity flag
|
|
135
|
+
period_invalid_indices = ~spin_period_valid
|
|
136
|
+
quality_flags[period_invalid_indices] |= ImapAttitudeUltraFlags.SPINPERIOD.value
|
|
137
|
+
|
|
121
138
|
return quality_flags, spin_rates, spin_period, spin_starttime
|
|
122
139
|
|
|
123
140
|
|
|
141
|
+
def flag_hk(spin_number: NDArray) -> NDArray:
|
|
142
|
+
"""
|
|
143
|
+
Flag data based on hk.
|
|
144
|
+
|
|
145
|
+
Parameters
|
|
146
|
+
----------
|
|
147
|
+
spin_number : NDArray
|
|
148
|
+
Spin number at each direct event.
|
|
149
|
+
|
|
150
|
+
Returns
|
|
151
|
+
-------
|
|
152
|
+
quality_flags : NDArray
|
|
153
|
+
Quality flags..
|
|
154
|
+
"""
|
|
155
|
+
spins = np.unique(spin_number) # Get unique spins
|
|
156
|
+
quality_flags = np.full(spins.shape, ImapHkUltraFlags.NONE.value, dtype=np.uint16)
|
|
157
|
+
|
|
158
|
+
return quality_flags
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def flag_imap_instruments(spin_number: NDArray) -> NDArray:
|
|
162
|
+
"""
|
|
163
|
+
Flag data based on other IMAP instruments.
|
|
164
|
+
|
|
165
|
+
Parameters
|
|
166
|
+
----------
|
|
167
|
+
spin_number : NDArray
|
|
168
|
+
Spin number at each direct event.
|
|
169
|
+
|
|
170
|
+
Returns
|
|
171
|
+
-------
|
|
172
|
+
quality_flags : NDArray
|
|
173
|
+
Quality flags..
|
|
174
|
+
"""
|
|
175
|
+
spins = np.unique(spin_number) # Get unique spins
|
|
176
|
+
quality_flags = np.full(
|
|
177
|
+
spins.shape, ImapInstrumentUltraFlags.NONE.value, dtype=np.uint16
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
return quality_flags
|
|
181
|
+
|
|
182
|
+
|
|
124
183
|
def get_n_sigma(count_rates: NDArray, mean_duration: float, sigma: int = 6) -> NDArray:
|
|
125
184
|
"""
|
|
126
185
|
Calculate the threshold for the HIGHRATES flag.
|
|
@@ -140,7 +199,8 @@ def get_n_sigma(count_rates: NDArray, mean_duration: float, sigma: int = 6) -> N
|
|
|
140
199
|
threshold : NDArray
|
|
141
200
|
Threshold for applying HIGHRATES flag.
|
|
142
201
|
"""
|
|
143
|
-
|
|
202
|
+
# Take the Sample Standard Deviation.
|
|
203
|
+
sigma_per_energy = np.std(count_rates, axis=1, ddof=1)
|
|
144
204
|
n_sigma_per_energy = sigma * sigma_per_energy
|
|
145
205
|
mean_per_energy = np.mean(count_rates, axis=1)
|
|
146
206
|
# Must have a HIGHRATES threshold of at least 3 counts per spin.
|
|
@@ -149,7 +209,7 @@ def get_n_sigma(count_rates: NDArray, mean_duration: float, sigma: int = 6) -> N
|
|
|
149
209
|
return threshold
|
|
150
210
|
|
|
151
211
|
|
|
152
|
-
def
|
|
212
|
+
def flag_rates(
|
|
153
213
|
spin_number: NDArray, energy: NDArray, sigma: int = 6
|
|
154
214
|
) -> tuple[NDArray, NDArray, NDArray, NDArray]:
|
|
155
215
|
"""
|
|
@@ -182,8 +242,6 @@ def flag_spin(
|
|
|
182
242
|
count_rates.shape, ImapRatesUltraFlags.NONE.value, dtype=np.uint16
|
|
183
243
|
)
|
|
184
244
|
|
|
185
|
-
# Zero counts/spin/energy level
|
|
186
|
-
quality_flags[counts == 0] |= ImapRatesUltraFlags.ZEROCOUNTS.value
|
|
187
245
|
threshold = get_n_sigma(count_rates, duration, sigma=sigma)
|
|
188
246
|
|
|
189
247
|
bin_edges = np.array(UltraConstants.CULLING_ENERGY_BIN_EDGES)
|
|
@@ -194,6 +252,10 @@ def flag_spin(
|
|
|
194
252
|
indices_n_sigma = count_rates > threshold[:, np.newaxis]
|
|
195
253
|
quality_flags[indices_n_sigma] |= ImapRatesUltraFlags.HIGHRATES.value
|
|
196
254
|
|
|
255
|
+
# Flags the first and last spin
|
|
256
|
+
quality_flags[:, 0] |= ImapRatesUltraFlags.FIRSTSPIN.value
|
|
257
|
+
quality_flags[:, -1] |= ImapRatesUltraFlags.LASTSPIN.value
|
|
258
|
+
|
|
197
259
|
return quality_flags, spin, energy_midpoints, threshold
|
|
198
260
|
|
|
199
261
|
|
|
@@ -256,3 +318,134 @@ def compare_aux_univ_spin_table(
|
|
|
256
318
|
mismatch_indices[missing_spin_mask] = True
|
|
257
319
|
|
|
258
320
|
return mismatch_indices
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
# TODO: Make this a common util since it is being used for the de and rates packets.
|
|
324
|
+
def get_spin_and_duration(met: NDArray, spin: NDArray) -> tuple[NDArray, NDArray]:
|
|
325
|
+
"""
|
|
326
|
+
Get the spin number and duration.
|
|
327
|
+
|
|
328
|
+
Parameters
|
|
329
|
+
----------
|
|
330
|
+
met : NDArray
|
|
331
|
+
Mission elapsed time.
|
|
332
|
+
spin : NDArray
|
|
333
|
+
Spin number 0-255.
|
|
334
|
+
|
|
335
|
+
Returns
|
|
336
|
+
-------
|
|
337
|
+
assigned_spin_number : NDArray
|
|
338
|
+
Spin number for packet data product.
|
|
339
|
+
"""
|
|
340
|
+
# Packet data.
|
|
341
|
+
# Since the spin number in the direct events packet
|
|
342
|
+
# is only 8 bits it goes from 0-255.
|
|
343
|
+
# Within a pointing that means we will always have duplicate spin numbers.
|
|
344
|
+
# In other words, different spins will be represented by the same spin number.
|
|
345
|
+
# Just to make certain that we won't accidentally combine
|
|
346
|
+
# multiple spins we need to sort by time here.
|
|
347
|
+
sort_idx = np.argsort(met)
|
|
348
|
+
packet_met_sorted = met[sort_idx]
|
|
349
|
+
packet_spin_sorted = spin[sort_idx]
|
|
350
|
+
# Here we are finding the start and end indices of each spin in the sorted array.
|
|
351
|
+
is_new_spin = np.concatenate(
|
|
352
|
+
[[True], packet_spin_sorted.values[1:] != packet_spin_sorted.values[:-1]]
|
|
353
|
+
)
|
|
354
|
+
spin_start_indices = np.where(is_new_spin)[0]
|
|
355
|
+
spin_end_indices = np.append(spin_start_indices[1:], len(packet_met_sorted))
|
|
356
|
+
|
|
357
|
+
# Universal Spin Table.
|
|
358
|
+
spin_df = get_spin_data()
|
|
359
|
+
# Retrieve the met values of the start of the spin.
|
|
360
|
+
spin_start_mets = spin_df["spin_start_met"].values
|
|
361
|
+
# Retrieve the corresponding spin numbers.
|
|
362
|
+
spin_numbers = spin_df["spin_number"].values
|
|
363
|
+
spin_period_sec = spin_df["spin_period_sec"].values
|
|
364
|
+
assigned_spin_number_sorted = np.empty(packet_spin_sorted.shape, dtype=np.uint32)
|
|
365
|
+
assigned_spin_duration_sorted = np.empty(packet_spin_sorted.shape, dtype=np.float32)
|
|
366
|
+
# These last 8 bits are the same as the spin number in the DE packet.
|
|
367
|
+
# So this will give us choices of which spins are
|
|
368
|
+
# available to assign to the packet data.
|
|
369
|
+
possible_spins = spin_numbers & 0xFF
|
|
370
|
+
|
|
371
|
+
# Assign each group based on time.
|
|
372
|
+
for start, end in zip(spin_start_indices, spin_end_indices):
|
|
373
|
+
# Now that we have the possible spins from the Universal Spin Table,
|
|
374
|
+
# we match the times of those spins to the nearest times in the DE data.
|
|
375
|
+
possible_times = spin_start_mets[
|
|
376
|
+
possible_spins == packet_spin_sorted.values[start]
|
|
377
|
+
]
|
|
378
|
+
# Get nearest time for matching spins.
|
|
379
|
+
nearest_idx = np.abs(possible_times - packet_met_sorted.values[start]).argmin()
|
|
380
|
+
nearest_value = possible_times[nearest_idx]
|
|
381
|
+
assigned_spin_number_sorted[start:end] = spin_numbers[
|
|
382
|
+
spin_start_mets == nearest_value
|
|
383
|
+
]
|
|
384
|
+
assigned_spin_duration_sorted[start:end] = spin_period_sec[
|
|
385
|
+
spin_start_mets == nearest_value
|
|
386
|
+
]
|
|
387
|
+
|
|
388
|
+
# Undo the sort to match original order.
|
|
389
|
+
assigned_spin_number = np.empty_like(assigned_spin_number_sorted)
|
|
390
|
+
assigned_spin_number[sort_idx] = assigned_spin_number_sorted
|
|
391
|
+
assigned_duration = np.empty_like(assigned_spin_duration_sorted)
|
|
392
|
+
assigned_duration[sort_idx] = assigned_spin_duration_sorted
|
|
393
|
+
|
|
394
|
+
return assigned_spin_number, assigned_duration
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def get_pulses_per_spin(rates: xr.Dataset) -> tuple[NDArray, NDArray, NDArray]:
|
|
398
|
+
"""
|
|
399
|
+
Get the total number of pulses per spin.
|
|
400
|
+
|
|
401
|
+
Parameters
|
|
402
|
+
----------
|
|
403
|
+
rates : xr.Dataset
|
|
404
|
+
Rates dataset.
|
|
405
|
+
|
|
406
|
+
Returns
|
|
407
|
+
-------
|
|
408
|
+
start_per_spin : NDArray
|
|
409
|
+
Total start pulses per spin.
|
|
410
|
+
stop_per_spin : NDArray
|
|
411
|
+
Total stop pulses per spin.
|
|
412
|
+
coin_per_spin : NDArray
|
|
413
|
+
Total coincidence pulses per spin.
|
|
414
|
+
"""
|
|
415
|
+
spin_number, duration = get_spin_and_duration(rates["shcoarse"], rates["spin"])
|
|
416
|
+
|
|
417
|
+
# Top coin pulses
|
|
418
|
+
top_coin_pulses = np.stack(
|
|
419
|
+
[v for k, v in rates.items() if k.startswith("coin_t")], axis=1
|
|
420
|
+
)
|
|
421
|
+
max_top_coin_pulse = np.max(top_coin_pulses, axis=1)
|
|
422
|
+
|
|
423
|
+
# Bottom coin pulses
|
|
424
|
+
bottom_coin_pulses = np.stack(
|
|
425
|
+
[v for k, v in rates.items() if k.startswith("coin_b")], axis=1
|
|
426
|
+
)
|
|
427
|
+
max_bottom_coin_pulse = np.max(bottom_coin_pulses, axis=1)
|
|
428
|
+
|
|
429
|
+
# Top stop pulses
|
|
430
|
+
top_stop_pulses = np.stack(
|
|
431
|
+
[v for k, v in rates.items() if k.startswith("stop_t")], axis=1
|
|
432
|
+
)
|
|
433
|
+
max_top_stop_pulse = np.max(top_stop_pulses, axis=1)
|
|
434
|
+
|
|
435
|
+
# Bottom stop pulses
|
|
436
|
+
bottom_stop_pulses = np.stack(
|
|
437
|
+
[v for k, v in rates.items() if k.startswith("stop_b")], axis=1
|
|
438
|
+
)
|
|
439
|
+
max_bottom_stop_pulse = np.max(bottom_stop_pulses, axis=1)
|
|
440
|
+
|
|
441
|
+
stop_pulses = max_top_stop_pulse + max_bottom_stop_pulse
|
|
442
|
+
start_pulses = rates["start_rf"] + rates["start_lf"]
|
|
443
|
+
coin_pulses = max_top_coin_pulse + max_bottom_coin_pulse
|
|
444
|
+
|
|
445
|
+
unique_spins, spin_idx = np.unique(spin_number, return_inverse=True)
|
|
446
|
+
|
|
447
|
+
start_per_spin = np.bincount(spin_idx, weights=start_pulses)
|
|
448
|
+
stop_per_spin = np.bincount(spin_idx, weights=stop_pulses)
|
|
449
|
+
coin_per_spin = np.bincount(spin_idx, weights=coin_pulses)
|
|
450
|
+
|
|
451
|
+
return start_per_spin, stop_per_spin, coin_per_spin
|