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.

Files changed (141) hide show
  1. imap_processing/_version.py +2 -2
  2. imap_processing/ancillary/ancillary_dataset_combiner.py +161 -1
  3. imap_processing/ccsds/excel_to_xtce.py +12 -0
  4. imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +6 -6
  5. imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +312 -274
  6. imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +39 -28
  7. imap_processing/cdf/config/imap_codice_l2_variable_attrs.yaml +1048 -183
  8. imap_processing/cdf/config/imap_constant_attrs.yaml +4 -2
  9. imap_processing/cdf/config/imap_glows_l1b_variable_attrs.yaml +12 -0
  10. imap_processing/cdf/config/imap_hi_global_cdf_attrs.yaml +5 -0
  11. imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +10 -4
  12. imap_processing/cdf/config/imap_hit_l1a_variable_attrs.yaml +163 -100
  13. imap_processing/cdf/config/imap_hit_l2_variable_attrs.yaml +4 -4
  14. imap_processing/cdf/config/imap_ialirt_l1_variable_attrs.yaml +97 -54
  15. imap_processing/cdf/config/imap_idex_l2a_variable_attrs.yaml +33 -4
  16. imap_processing/cdf/config/imap_idex_l2b_variable_attrs.yaml +44 -44
  17. imap_processing/cdf/config/imap_idex_l2c_variable_attrs.yaml +77 -61
  18. imap_processing/cdf/config/imap_lo_global_cdf_attrs.yaml +30 -0
  19. imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +4 -15
  20. imap_processing/cdf/config/imap_lo_l1c_variable_attrs.yaml +189 -98
  21. imap_processing/cdf/config/imap_mag_global_cdf_attrs.yaml +99 -2
  22. imap_processing/cdf/config/imap_mag_l1c_variable_attrs.yaml +24 -1
  23. imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +60 -0
  24. imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +99 -11
  25. imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +50 -7
  26. imap_processing/cli.py +121 -44
  27. imap_processing/codice/codice_l1a.py +165 -77
  28. imap_processing/codice/codice_l1b.py +1 -1
  29. imap_processing/codice/codice_l2.py +118 -19
  30. imap_processing/codice/constants.py +1217 -1089
  31. imap_processing/decom.py +1 -4
  32. imap_processing/ena_maps/ena_maps.py +32 -25
  33. imap_processing/ena_maps/utils/naming.py +8 -2
  34. imap_processing/glows/ancillary/imap_glows_exclusions-by-instr-team_20250923_v002.dat +10 -0
  35. imap_processing/glows/ancillary/imap_glows_map-of-excluded-regions_20250923_v002.dat +393 -0
  36. imap_processing/glows/ancillary/imap_glows_map-of-uv-sources_20250923_v002.dat +593 -0
  37. imap_processing/glows/ancillary/imap_glows_pipeline_settings_20250923_v002.json +54 -0
  38. imap_processing/glows/ancillary/imap_glows_suspected-transients_20250923_v002.dat +10 -0
  39. imap_processing/glows/l1b/glows_l1b.py +99 -9
  40. imap_processing/glows/l1b/glows_l1b_data.py +350 -38
  41. imap_processing/glows/l2/glows_l2.py +11 -0
  42. imap_processing/hi/hi_l1a.py +124 -3
  43. imap_processing/hi/hi_l1b.py +154 -71
  44. imap_processing/hi/hi_l2.py +84 -51
  45. imap_processing/hi/utils.py +153 -8
  46. imap_processing/hit/l0/constants.py +3 -0
  47. imap_processing/hit/l0/decom_hit.py +5 -8
  48. imap_processing/hit/l1a/hit_l1a.py +375 -45
  49. imap_processing/hit/l1b/constants.py +5 -0
  50. imap_processing/hit/l1b/hit_l1b.py +61 -131
  51. imap_processing/hit/l2/constants.py +1 -1
  52. imap_processing/hit/l2/hit_l2.py +10 -11
  53. imap_processing/ialirt/calculate_ingest.py +219 -0
  54. imap_processing/ialirt/constants.py +32 -1
  55. imap_processing/ialirt/generate_coverage.py +201 -0
  56. imap_processing/ialirt/l0/ialirt_spice.py +5 -2
  57. imap_processing/ialirt/l0/parse_mag.py +337 -29
  58. imap_processing/ialirt/l0/process_hit.py +5 -3
  59. imap_processing/ialirt/l0/process_swapi.py +41 -25
  60. imap_processing/ialirt/l0/process_swe.py +23 -7
  61. imap_processing/ialirt/process_ephemeris.py +70 -14
  62. imap_processing/ialirt/utils/constants.py +22 -16
  63. imap_processing/ialirt/utils/create_xarray.py +42 -19
  64. imap_processing/idex/idex_constants.py +1 -5
  65. imap_processing/idex/idex_l0.py +2 -2
  66. imap_processing/idex/idex_l1a.py +2 -3
  67. imap_processing/idex/idex_l1b.py +2 -3
  68. imap_processing/idex/idex_l2a.py +130 -4
  69. imap_processing/idex/idex_l2b.py +313 -119
  70. imap_processing/idex/idex_utils.py +1 -3
  71. imap_processing/lo/l0/lo_apid.py +1 -0
  72. imap_processing/lo/l0/lo_science.py +25 -24
  73. imap_processing/lo/l1a/lo_l1a.py +44 -0
  74. imap_processing/lo/l1b/lo_l1b.py +3 -3
  75. imap_processing/lo/l1c/lo_l1c.py +116 -50
  76. imap_processing/lo/l2/lo_l2.py +29 -29
  77. imap_processing/lo/lo_ancillary.py +55 -0
  78. imap_processing/lo/packet_definitions/lo_xtce.xml +5359 -106
  79. imap_processing/mag/constants.py +1 -0
  80. imap_processing/mag/l1a/mag_l1a.py +1 -0
  81. imap_processing/mag/l1a/mag_l1a_data.py +26 -0
  82. imap_processing/mag/l1b/mag_l1b.py +3 -2
  83. imap_processing/mag/l1c/interpolation_methods.py +14 -15
  84. imap_processing/mag/l1c/mag_l1c.py +23 -6
  85. imap_processing/mag/l1d/__init__.py +0 -0
  86. imap_processing/mag/l1d/mag_l1d.py +176 -0
  87. imap_processing/mag/l1d/mag_l1d_data.py +725 -0
  88. imap_processing/mag/l2/__init__.py +0 -0
  89. imap_processing/mag/l2/mag_l2.py +25 -20
  90. imap_processing/mag/l2/mag_l2_data.py +199 -130
  91. imap_processing/quality_flags.py +28 -2
  92. imap_processing/spice/geometry.py +101 -36
  93. imap_processing/spice/pointing_frame.py +1 -7
  94. imap_processing/spice/repoint.py +29 -2
  95. imap_processing/spice/spin.py +32 -8
  96. imap_processing/spice/time.py +60 -19
  97. imap_processing/swapi/l1/swapi_l1.py +10 -4
  98. imap_processing/swapi/l2/swapi_l2.py +66 -24
  99. imap_processing/swapi/swapi_utils.py +1 -1
  100. imap_processing/swe/l1b/swe_l1b.py +3 -6
  101. imap_processing/ultra/constants.py +28 -3
  102. imap_processing/ultra/l0/decom_tools.py +15 -8
  103. imap_processing/ultra/l0/decom_ultra.py +35 -11
  104. imap_processing/ultra/l0/ultra_utils.py +102 -12
  105. imap_processing/ultra/l1a/ultra_l1a.py +26 -6
  106. imap_processing/ultra/l1b/cullingmask.py +6 -3
  107. imap_processing/ultra/l1b/de.py +122 -26
  108. imap_processing/ultra/l1b/extendedspin.py +29 -2
  109. imap_processing/ultra/l1b/lookup_utils.py +424 -50
  110. imap_processing/ultra/l1b/quality_flag_filters.py +23 -0
  111. imap_processing/ultra/l1b/ultra_l1b_culling.py +356 -5
  112. imap_processing/ultra/l1b/ultra_l1b_extended.py +534 -90
  113. imap_processing/ultra/l1c/helio_pset.py +127 -7
  114. imap_processing/ultra/l1c/l1c_lookup_utils.py +256 -0
  115. imap_processing/ultra/l1c/spacecraft_pset.py +90 -15
  116. imap_processing/ultra/l1c/ultra_l1c.py +6 -0
  117. imap_processing/ultra/l1c/ultra_l1c_culling.py +85 -0
  118. imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +446 -341
  119. imap_processing/ultra/l2/ultra_l2.py +0 -1
  120. imap_processing/ultra/utils/ultra_l1_utils.py +40 -3
  121. imap_processing/utils.py +3 -4
  122. {imap_processing-0.17.0.dist-info → imap_processing-0.19.0.dist-info}/METADATA +3 -3
  123. {imap_processing-0.17.0.dist-info → imap_processing-0.19.0.dist-info}/RECORD +126 -126
  124. imap_processing/idex/idex_l2c.py +0 -250
  125. imap_processing/spice/kernels.py +0 -187
  126. imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_LeftSlit.csv +0 -526
  127. imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_RightSlit.csv +0 -526
  128. imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_LeftSlit.csv +0 -526
  129. imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_RightSlit.csv +0 -524
  130. imap_processing/ultra/lookup_tables/EgyNorm.mem.csv +0 -32769
  131. imap_processing/ultra/lookup_tables/FM45_Startup1_ULTRA_IMGPARAMS_20240719.csv +0 -2
  132. imap_processing/ultra/lookup_tables/FM90_Startup1_ULTRA_IMGPARAMS_20240719.csv +0 -2
  133. imap_processing/ultra/lookup_tables/dps_grid45_compressed.cdf +0 -0
  134. imap_processing/ultra/lookup_tables/ultra45_back-pos-luts.csv +0 -4097
  135. imap_processing/ultra/lookup_tables/ultra45_tdc_norm.csv +0 -2050
  136. imap_processing/ultra/lookup_tables/ultra90_back-pos-luts.csv +0 -4097
  137. imap_processing/ultra/lookup_tables/ultra90_tdc_norm.csv +0 -2050
  138. imap_processing/ultra/lookup_tables/yadjust.csv +0 -257
  139. {imap_processing-0.17.0.dist-info → imap_processing-0.19.0.dist-info}/LICENSE +0 -0
  140. {imap_processing-0.17.0.dist-info → imap_processing-0.19.0.dist-info}/WHEEL +0 -0
  141. {imap_processing-0.17.0.dist-info → imap_processing-0.19.0.dist-info}/entry_points.txt +0 -0
@@ -1,12 +1,12 @@
1
1
  """IMAP-Hi utils functions."""
2
2
 
3
3
  import re
4
- from collections.abc import Sequence
4
+ from collections.abc import Iterable, Sequence
5
5
  from dataclasses import dataclass
6
6
  from enum import IntEnum
7
- from typing import Optional, Union
8
7
 
9
8
  import numpy as np
9
+ import pandas as pd
10
10
  import xarray as xr
11
11
 
12
12
  from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
@@ -15,11 +15,13 @@ from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
15
15
  class HIAPID(IntEnum):
16
16
  """Create ENUM for apid."""
17
17
 
18
+ H45_MEMDMP = 740
18
19
  H45_APP_NHK = 754
19
20
  H45_SCI_CNT = 769
20
21
  H45_SCI_DE = 770
21
22
  H45_DIAG_FEE = 772
22
23
 
24
+ H90_MEMDMP = 804
23
25
  H90_APP_NHK = 818
24
26
  H90_SCI_CNT = 833
25
27
  H90_SCI_DE = 834
@@ -100,9 +102,9 @@ def parse_sensor_number(full_string: str) -> int:
100
102
  def full_dataarray(
101
103
  name: str,
102
104
  attrs: dict,
103
- coords: Optional[dict[str, xr.DataArray]] = None,
104
- shape: Optional[Union[int, Sequence[int]]] = None,
105
- fill_value: Optional[float] = None,
105
+ coords: dict[str, xr.DataArray] | None = None,
106
+ shape: int | Sequence[int] | None = None,
107
+ fill_value: float | None = None,
106
108
  ) -> xr.DataArray:
107
109
  """
108
110
  Generate an empty xarray.DataArray with appropriate attributes.
@@ -158,9 +160,9 @@ def full_dataarray(
158
160
 
159
161
  def create_dataset_variables(
160
162
  variable_names: list[str],
161
- variable_shape: Optional[Union[int, Sequence[int]]] = None,
162
- coords: Optional[dict[str, xr.DataArray]] = None,
163
- fill_value: Optional[float] = None,
163
+ variable_shape: int | Sequence[int] | None = None,
164
+ coords: dict[str, xr.DataArray] | None = None,
165
+ fill_value: float | None = None,
164
166
  att_manager_lookup_str: str = "{0}",
165
167
  ) -> dict[str, xr.DataArray]:
166
168
  """
@@ -247,3 +249,146 @@ class CoincidenceBitmap(IntEnum):
247
249
  matches = re.findall(pattern, detector_hit_str)
248
250
  # Sum the integer value assigned to the detector name for each match
249
251
  return sum(CoincidenceBitmap[m] for m in matches)
252
+
253
+
254
+ class EsaEnergyStepLookupTable:
255
+ """Class for holding a esa_step to esa_energy lookup table."""
256
+
257
+ def __init__(self) -> None:
258
+ self.df = pd.DataFrame(
259
+ columns=["start_met", "end_met", "esa_step", "esa_energy_step"]
260
+ )
261
+ self._indexed = False
262
+
263
+ # Get the FILLVAL from the CDF attribute manager that will be returned
264
+ # for queries without matches
265
+ attr_mgr = ImapCdfAttributes()
266
+ attr_mgr.add_instrument_global_attrs("hi")
267
+ attr_mgr.add_instrument_variable_attrs(instrument="hi", level=None)
268
+ var_attrs = attr_mgr.get_variable_attributes(
269
+ "hi_de_esa_energy_step", check_schema=False
270
+ )
271
+ self._fillval = var_attrs["FILLVAL"]
272
+ self._esa_energy_step_dtype = var_attrs["dtype"]
273
+
274
+ def add_entry(
275
+ self, start_met: float, end_met: float, esa_step: int, esa_energy_step: int
276
+ ) -> None:
277
+ """
278
+ Add a single entry to the lookup table.
279
+
280
+ Parameters
281
+ ----------
282
+ start_met : float
283
+ Start mission elapsed time of the time range.
284
+ end_met : float
285
+ End mission elapsed time of the time range.
286
+ esa_step : int
287
+ ESA step value.
288
+ esa_energy_step : int
289
+ ESA energy step value to be stored.
290
+ """
291
+ new_row = pd.DataFrame(
292
+ {
293
+ "start_met": [start_met],
294
+ "end_met": [end_met],
295
+ "esa_step": [esa_step],
296
+ "esa_energy_step": [esa_energy_step],
297
+ }
298
+ )
299
+ self.df = pd.concat([self.df, new_row], ignore_index=True)
300
+ self._indexed = False
301
+
302
+ def _ensure_indexed(self) -> None:
303
+ """
304
+ Create index for faster queries if not already done.
305
+
306
+ Notes
307
+ -----
308
+ This method sorts the internal DataFrame by start_met and esa_step
309
+ for improved query performance.
310
+ """
311
+ if not self._indexed:
312
+ # Sort by start_met and esa_step for better query performance
313
+ self.df = self.df.sort_values(["start_met", "esa_step"]).reset_index(
314
+ drop=True
315
+ )
316
+ self._indexed = True
317
+
318
+ def query(
319
+ self,
320
+ query_met: float | Iterable[float],
321
+ esa_step: int | Iterable[float],
322
+ ) -> float | np.ndarray:
323
+ """
324
+ Query MET(s) and esa_step(s) to retrieve esa_energy_step(s).
325
+
326
+ Parameters
327
+ ----------
328
+ query_met : float or array_like
329
+ Mission elapsed time value(s) to query.
330
+ Can be a single float or array-like of floats.
331
+ esa_step : int or array_like
332
+ ESA step value(s) to match. Can be a single int or array-like of ints.
333
+ Must be same type (scalar or array-like) as query_met.
334
+
335
+ Returns
336
+ -------
337
+ float or numpy.ndarray
338
+ - If inputs are scalars: returns float (esa_energy_step)
339
+ - If inputs are array-like: returns numpy array of esa_energy_steps
340
+ with same length as inputs.
341
+ Contains FILLVAL for queries with no matches.
342
+
343
+ Raises
344
+ ------
345
+ ValueError
346
+ If one input is scalar and the other is array-like, or if both are
347
+ array-like but have different lengths.
348
+
349
+ Notes
350
+ -----
351
+ If multiple entries match a query, returns the first match found.
352
+ """
353
+ self._ensure_indexed()
354
+
355
+ # Check if inputs are scalars
356
+ is_scalar_met = np.isscalar(query_met)
357
+ is_scalar_step = np.isscalar(esa_step)
358
+
359
+ # Check for mismatched input types
360
+ if is_scalar_met != is_scalar_step:
361
+ raise ValueError(
362
+ "query_met and esa_step must both be scalars or both be array-like"
363
+ )
364
+
365
+ # Convert to arrays for uniform processing
366
+ query_mets = np.atleast_1d(query_met)
367
+ esa_steps = np.atleast_1d(esa_step)
368
+
369
+ # Ensure both arrays have the same shape
370
+ if query_mets.shape != esa_steps.shape:
371
+ raise ValueError(
372
+ "query_met and esa_step must have the same "
373
+ "length when both are array-like"
374
+ )
375
+
376
+ results = np.full_like(query_mets, self._fillval)
377
+
378
+ # Lookup esa_energy_steps for queries
379
+ for i, (qm, es) in enumerate(zip(query_mets, esa_steps, strict=False)):
380
+ mask = (
381
+ (self.df["start_met"] <= qm)
382
+ & (self.df["end_met"] >= qm)
383
+ & (self.df["esa_step"] == es)
384
+ )
385
+
386
+ matches = self.df[mask]
387
+ if not matches.empty:
388
+ results[i] = matches["esa_energy_step"].iloc[0]
389
+
390
+ # Return scalar for scalar inputs, array for array inputs
391
+ if is_scalar_met and is_scalar_step:
392
+ return results.astype(self._esa_energy_step_dtype)[0]
393
+ else:
394
+ return results.astype(self._esa_energy_step_dtype)
@@ -114,6 +114,9 @@ FLAG_PATTERN = np.array([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
114
114
  # Define size of science frame (num of packets)
115
115
  FRAME_SIZE = len(FLAG_PATTERN)
116
116
 
117
+ # Mod 10 pattern
118
+ MOD_10_PATTERN = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
119
+
117
120
  # Define the number of bits in the mantissa and exponent for
118
121
  # decompressing data
119
122
  MANTISSA_BITS = 12
@@ -124,9 +124,9 @@ def parse_count_rates(sci_dataset: xr.Dataset) -> None:
124
124
  for dim in dims:
125
125
  if dim not in sci_dataset.coords:
126
126
  sci_dataset.coords[dim] = xr.DataArray(
127
- np.arange(sci_dataset.sizes[dim], dtype=np.int16)
127
+ np.arange(sci_dataset.sizes[dim], dtype=np.uint16)
128
128
  if dim == "gain"
129
- else np.arange(sci_dataset.sizes[dim], dtype=np.int32),
129
+ else np.arange(sci_dataset.sizes[dim], dtype=np.uint32),
130
130
  dims=[dim],
131
131
  name=dim,
132
132
  )
@@ -260,12 +260,9 @@ def assemble_science_frames(sci_dataset: xr.Dataset) -> xr.Dataset:
260
260
  height event data per valid science frame added as new
261
261
  data variables.
262
262
  """
263
- # TODO: Figure out how to handle partial science frames at the
264
- # beginning and end of CCSDS files. These science frames are split
265
- # across CCSDS files and still need to be processed with packets
266
- # from the previous file. Only discard incomplete science frames
267
- # in the middle of the CCSDS file. The code currently skips all
268
- # incomplete science frames.
263
+ # TODO: The code currently skips all incomplete science frames.
264
+ # Only discard incomplete science frames in the middle of the CCSDS file or
265
+ # use fill values?
269
266
 
270
267
  # Convert sequence flags and counters to NumPy arrays for vectorized operations
271
268
  seq_flgs = sci_dataset.seq_flgs.values