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.

Files changed (110) hide show
  1. imap_processing/_version.py +2 -2
  2. imap_processing/ccsds/excel_to_xtce.py +12 -0
  3. imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +6 -6
  4. imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +35 -0
  5. imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +35 -0
  6. imap_processing/cdf/config/imap_codice_l2_variable_attrs.yaml +24 -0
  7. imap_processing/cdf/config/imap_hi_variable_attrs.yaml +8 -8
  8. imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +1 -1
  9. imap_processing/cdf/config/imap_hit_l1a_variable_attrs.yaml +163 -100
  10. imap_processing/cdf/config/imap_hit_l2_variable_attrs.yaml +398 -415
  11. imap_processing/cdf/config/imap_ialirt_l1_variable_attrs.yaml +97 -54
  12. imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +9 -9
  13. imap_processing/cdf/config/imap_idex_l2b_variable_attrs.yaml +233 -57
  14. imap_processing/cdf/config/imap_idex_l2c_variable_attrs.yaml +16 -90
  15. imap_processing/cdf/config/imap_lo_global_cdf_attrs.yaml +30 -0
  16. imap_processing/cdf/config/imap_mag_global_cdf_attrs.yaml +15 -1
  17. imap_processing/cdf/config/imap_swapi_variable_attrs.yaml +19 -0
  18. imap_processing/cdf/config/imap_swe_l1b_variable_attrs.yaml +20 -0
  19. imap_processing/cdf/config/imap_swe_l2_variable_attrs.yaml +39 -0
  20. imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +168 -0
  21. imap_processing/cdf/config/imap_ultra_l1a_variable_attrs.yaml +103 -2
  22. imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +91 -11
  23. imap_processing/cdf/utils.py +7 -1
  24. imap_processing/cli.py +42 -13
  25. imap_processing/codice/codice_l1a.py +125 -78
  26. imap_processing/codice/codice_l1b.py +1 -1
  27. imap_processing/codice/codice_l2.py +0 -9
  28. imap_processing/codice/constants.py +481 -498
  29. imap_processing/hi/hi_l1a.py +4 -4
  30. imap_processing/hi/hi_l1b.py +2 -2
  31. imap_processing/hi/packet_definitions/TLM_HI_COMBINED_SCI.xml +218 -38
  32. imap_processing/hit/hit_utils.py +2 -2
  33. imap_processing/hit/l0/decom_hit.py +4 -3
  34. imap_processing/hit/l1a/hit_l1a.py +64 -24
  35. imap_processing/hit/l1b/constants.py +5 -0
  36. imap_processing/hit/l1b/hit_l1b.py +18 -16
  37. imap_processing/hit/l2/constants.py +1 -1
  38. imap_processing/hit/l2/hit_l2.py +4 -4
  39. imap_processing/ialirt/constants.py +21 -0
  40. imap_processing/ialirt/generate_coverage.py +188 -0
  41. imap_processing/ialirt/l0/parse_mag.py +62 -5
  42. imap_processing/ialirt/l0/process_swapi.py +1 -1
  43. imap_processing/ialirt/l0/process_swe.py +23 -7
  44. imap_processing/ialirt/utils/constants.py +22 -16
  45. imap_processing/ialirt/utils/create_xarray.py +42 -19
  46. imap_processing/idex/idex_constants.py +8 -5
  47. imap_processing/idex/idex_l2b.py +554 -58
  48. imap_processing/idex/idex_l2c.py +30 -196
  49. imap_processing/lo/l0/lo_apid.py +1 -0
  50. imap_processing/lo/l0/lo_star_sensor.py +48 -0
  51. imap_processing/lo/l1a/lo_l1a.py +74 -30
  52. imap_processing/lo/packet_definitions/lo_xtce.xml +5359 -106
  53. imap_processing/mag/constants.py +1 -0
  54. imap_processing/mag/l0/decom_mag.py +9 -6
  55. imap_processing/mag/l0/mag_l0_data.py +46 -0
  56. imap_processing/mag/l1d/__init__.py +0 -0
  57. imap_processing/mag/l1d/mag_l1d.py +133 -0
  58. imap_processing/mag/l1d/mag_l1d_data.py +588 -0
  59. imap_processing/mag/l2/__init__.py +0 -0
  60. imap_processing/mag/l2/mag_l2.py +25 -20
  61. imap_processing/mag/l2/mag_l2_data.py +191 -130
  62. imap_processing/quality_flags.py +20 -2
  63. imap_processing/spice/geometry.py +25 -3
  64. imap_processing/spice/pointing_frame.py +1 -1
  65. imap_processing/spice/spin.py +4 -0
  66. imap_processing/spice/time.py +51 -0
  67. imap_processing/swapi/l1/swapi_l1.py +12 -2
  68. imap_processing/swapi/l2/swapi_l2.py +59 -14
  69. imap_processing/swapi/swapi_utils.py +1 -1
  70. imap_processing/swe/l1b/swe_l1b.py +11 -4
  71. imap_processing/swe/l2/swe_l2.py +111 -17
  72. imap_processing/ultra/constants.py +49 -1
  73. imap_processing/ultra/l0/decom_tools.py +28 -14
  74. imap_processing/ultra/l0/decom_ultra.py +225 -15
  75. imap_processing/ultra/l0/ultra_utils.py +281 -8
  76. imap_processing/ultra/l1a/ultra_l1a.py +77 -8
  77. imap_processing/ultra/l1b/cullingmask.py +3 -3
  78. imap_processing/ultra/l1b/de.py +53 -15
  79. imap_processing/ultra/l1b/extendedspin.py +26 -2
  80. imap_processing/ultra/l1b/lookup_utils.py +171 -50
  81. imap_processing/ultra/l1b/quality_flag_filters.py +14 -0
  82. imap_processing/ultra/l1b/ultra_l1b_culling.py +198 -5
  83. imap_processing/ultra/l1b/ultra_l1b_extended.py +304 -66
  84. imap_processing/ultra/l1c/helio_pset.py +54 -7
  85. imap_processing/ultra/l1c/spacecraft_pset.py +9 -1
  86. imap_processing/ultra/l1c/ultra_l1c.py +2 -0
  87. imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +106 -109
  88. imap_processing/ultra/packet_definitions/ULTRA_SCI_COMBINED.xml +3 -3
  89. imap_processing/ultra/utils/ultra_l1_utils.py +13 -1
  90. imap_processing/utils.py +20 -42
  91. {imap_processing-0.16.2.dist-info → imap_processing-0.18.0.dist-info}/METADATA +2 -2
  92. {imap_processing-0.16.2.dist-info → imap_processing-0.18.0.dist-info}/RECORD +95 -103
  93. imap_processing/lo/l0/data_classes/star_sensor.py +0 -98
  94. imap_processing/lo/l0/utils/lo_base.py +0 -57
  95. imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_LeftSlit.csv +0 -526
  96. imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_RightSlit.csv +0 -526
  97. imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_LeftSlit.csv +0 -526
  98. imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_RightSlit.csv +0 -524
  99. imap_processing/ultra/lookup_tables/EgyNorm.mem.csv +0 -32769
  100. imap_processing/ultra/lookup_tables/FM45_Startup1_ULTRA_IMGPARAMS_20240719.csv +0 -2
  101. imap_processing/ultra/lookup_tables/FM90_Startup1_ULTRA_IMGPARAMS_20240719.csv +0 -2
  102. imap_processing/ultra/lookup_tables/dps_grid45_compressed.cdf +0 -0
  103. imap_processing/ultra/lookup_tables/ultra45_back-pos-luts.csv +0 -4097
  104. imap_processing/ultra/lookup_tables/ultra45_tdc_norm.csv +0 -2050
  105. imap_processing/ultra/lookup_tables/ultra90_back-pos-luts.csv +0 -4097
  106. imap_processing/ultra/lookup_tables/ultra90_tdc_norm.csv +0 -2050
  107. imap_processing/ultra/lookup_tables/yadjust.csv +0 -257
  108. {imap_processing-0.16.2.dist-info → imap_processing-0.18.0.dist-info}/LICENSE +0 -0
  109. {imap_processing-0.16.2.dist-info → imap_processing-0.18.0.dist-info}/WHEEL +0 -0
  110. {imap_processing-0.16.2.dist-info → imap_processing-0.18.0.dist-info}/entry_points.txt +0 -0
@@ -1,10 +1,17 @@
1
1
  """Calculate Pointing Set Grids."""
2
2
 
3
- import astropy_healpix.healpy as hp
4
3
  import numpy as np
4
+ import pandas as pd
5
5
  import xarray as xr
6
6
 
7
- from imap_processing.ultra.l1c.ultra_l1c_pset_bins import build_energy_bins
7
+ from imap_processing.spice.time import sct_to_et
8
+ from imap_processing.ultra.l1c.ultra_l1c_pset_bins import (
9
+ build_energy_bins,
10
+ get_helio_background_rates,
11
+ get_helio_exposure_times,
12
+ get_helio_sensitivity,
13
+ get_spacecraft_histogram,
14
+ )
8
15
  from imap_processing.ultra.utils.ultra_l1_utils import create_dataset
9
16
 
10
17
 
@@ -36,17 +43,57 @@ def calculate_helio_pset(
36
43
  dataset : xarray.Dataset
37
44
  Dataset containing the data.
38
45
  """
39
- # TODO: Fill in the rest of this later.
40
46
  pset_dict: dict[str, np.ndarray] = {}
41
- healpix = np.arange(hp.nside2npix(128))
42
- _, _, energy_bin_geometric_means = build_energy_bins()
43
47
 
48
+ v_mag_helio_spacecraft = np.linalg.norm(
49
+ de_dataset["velocity_dps_helio"].values, axis=1
50
+ )
51
+ vhat_dps_helio = (
52
+ de_dataset["velocity_dps_helio"].values / v_mag_helio_spacecraft[:, np.newaxis]
53
+ )
54
+ intervals, _, energy_bin_geometric_means = build_energy_bins()
55
+ counts, latitude, longitude, n_pix = get_spacecraft_histogram(
56
+ vhat_dps_helio,
57
+ de_dataset["energy_heliosphere"].values,
58
+ intervals,
59
+ nside=128,
60
+ )
61
+
62
+ healpix = np.arange(n_pix)
63
+
64
+ # calculate background rates
65
+ background_rates = get_helio_background_rates()
66
+
67
+ efficiencies = ancillary_files["l1c-90sensor-efficiencies"]
68
+ geometric_function = ancillary_files["l1c-90sensor-gf"]
69
+
70
+ df_efficiencies = pd.read_csv(efficiencies)
71
+ df_geometric_function = pd.read_csv(geometric_function)
72
+ mid_time = sct_to_et(np.median(de_dataset["event_times"].data))
73
+ sensitivity = get_helio_sensitivity(
74
+ mid_time,
75
+ df_efficiencies,
76
+ df_geometric_function,
77
+ )
78
+
79
+ # Calculate exposure
80
+ constant_exposure = ancillary_files["l1c-90sensor-dps-exposure"]
81
+ df_exposure = pd.read_csv(constant_exposure)
82
+ exposure_pointing = get_helio_exposure_times(mid_time, df_exposure)
83
+
84
+ # For ISTP, epoch should be the center of the time bin.
44
85
  pset_dict["epoch"] = de_dataset.epoch.data[:1].astype(np.int64)
45
- pset_dict["pixel_index"] = healpix
86
+ pset_dict["counts"] = counts[np.newaxis, ...]
87
+ pset_dict["latitude"] = latitude[np.newaxis, ...]
88
+ pset_dict["longitude"] = longitude[np.newaxis, ...]
46
89
  pset_dict["energy_bin_geometric_mean"] = energy_bin_geometric_means
47
- pset_dict["exposure_factor"] = np.zeros(len(healpix), dtype=np.uint8)[
90
+ pset_dict["background_rates"] = background_rates[np.newaxis, ...]
91
+ pset_dict["helio_exposure_factor"] = exposure_pointing[np.newaxis, ...]
92
+ pset_dict["pixel_index"] = healpix
93
+ pset_dict["energy_bin_delta"] = np.diff(intervals, axis=1).squeeze()[
48
94
  np.newaxis, ...
49
95
  ]
96
+ pset_dict["sensitivity"] = sensitivity[np.newaxis, ...]
50
97
 
51
98
  dataset = create_dataset(pset_dict, name, "l1c")
52
99
 
@@ -18,6 +18,8 @@ def calculate_spacecraft_pset(
18
18
  de_dataset: xr.Dataset,
19
19
  extendedspin_dataset: xr.Dataset,
20
20
  cullingmask_dataset: xr.Dataset,
21
+ rates_dataset: xr.Dataset,
22
+ params_dataset: xr.Dataset,
21
23
  name: str,
22
24
  ancillary_files: dict,
23
25
  ) -> xr.Dataset:
@@ -32,6 +34,10 @@ def calculate_spacecraft_pset(
32
34
  Dataset containing extendedspin data.
33
35
  cullingmask_dataset : xarray.Dataset
34
36
  Dataset containing cullingmask data.
37
+ rates_dataset : xarray.Dataset
38
+ Dataset containing image rates data.
39
+ params_dataset : xarray.Dataset
40
+ Dataset containing image parameters data.
35
41
  name : str
36
42
  Name of the dataset.
37
43
  ancillary_files : dict
@@ -71,7 +77,9 @@ def calculate_spacecraft_pset(
71
77
  # Calculate exposure
72
78
  constant_exposure = ancillary_files["l1c-90sensor-dps-exposure"]
73
79
  df_exposure = pd.read_csv(constant_exposure)
74
- exposure_pointing = get_spacecraft_exposure_times(df_exposure)
80
+ exposure_pointing = get_spacecraft_exposure_times(
81
+ df_exposure, rates_dataset, params_dataset
82
+ )
75
83
 
76
84
  # For ISTP, epoch should be the center of the time bin.
77
85
  pset_dict["epoch"] = de_dataset.epoch.data[:1].astype(np.int64)
@@ -63,6 +63,8 @@ def ultra_l1c(
63
63
  data_dict[f"imap_ultra_l1b_{instrument_id}sensor-de"],
64
64
  data_dict[f"imap_ultra_l1b_{instrument_id}sensor-extendedspin"],
65
65
  data_dict[f"imap_ultra_l1b_{instrument_id}sensor-cullingmask"],
66
+ data_dict[f"imap_ultra_{instrument_id}sensor-rates"],
67
+ data_dict[f"imap_ultra_{instrument_id}sensor-params"],
66
68
  f"imap_ultra_l1c_{instrument_id}sensor-spacecraftpset",
67
69
  ancillary_files,
68
70
  )
@@ -4,6 +4,7 @@ import astropy_healpix.healpy as hp
4
4
  import numpy as np
5
5
  import pandas
6
6
  import pandas as pd
7
+ import xarray as xr
7
8
  from numpy.typing import NDArray
8
9
  from scipy.interpolate import interp1d
9
10
 
@@ -151,110 +152,6 @@ def get_spacecraft_histogram(
151
152
  return hist, latitude, longitude, n_pix
152
153
 
153
154
 
154
- def get_helio_histogram(
155
- time: NDArray,
156
- vhat: NDArray,
157
- energy: NDArray,
158
- energy_bin_edges: list[tuple[float, float]],
159
- nside: int = 128,
160
- nested: bool = False,
161
- ) -> tuple[NDArray, NDArray, NDArray, NDArray]:
162
- """
163
- Compute a 3D histogram of the particle data using HEALPix binning.
164
-
165
- Parameters
166
- ----------
167
- time : np.ndarray
168
- Median time of pointing in et.
169
- vhat : tuple[np.ndarray, np.ndarray, np.ndarray]
170
- The x,y,z-components of the unit velocity vector.
171
- energy : np.ndarray
172
- The particle energy.
173
- energy_bin_edges : list[tuple[float, float]]
174
- Array of energy bin edges.
175
- nside : int, optional
176
- The nside parameter of the Healpix tessellation.
177
- Default is 128.
178
- nested : bool, optional
179
- Whether the Healpix tessellation is nested. Default is False.
180
-
181
- Returns
182
- -------
183
- hist : np.ndarray
184
- A 3D histogram array with shape (n_pix, n_energy_bins).
185
- latitude : np.ndarray
186
- Array of latitude values.
187
- longitude : np.ndarray
188
- Array of longitude values.
189
- n_pix : int
190
- Number of healpix pixels.
191
-
192
- Notes
193
- -----
194
- The histogram will work properly for overlapping energy bins, i.e.
195
- the same energy value can fall into multiple bins if the intervals overlap.
196
-
197
- azimuthal angle [0, 360], elevation angle [-90, 90]
198
- """
199
- # Compute number of HEALPix pixels that cover the sphere
200
- n_pix = hp.nside2npix(nside)
201
-
202
- # Calculate the corresponding longitude (az) latitude (el)
203
- # center coordinates
204
- longitude, latitude = hp.pix2ang(nside, np.arange(n_pix), lonlat=True)
205
-
206
- # The Cartesian state vector representing the position and velocity of the
207
- # IMAP spacecraft.
208
- state = imap_state(time, ref_frame=SpiceFrame.IMAP_DPS)
209
-
210
- # Extract the velocity part of the state vector
211
- spacecraft_velocity = state[3:6]
212
-
213
- # Initialize histogram: (n_energy_bins, n_HEALPix pixels)
214
- hist = np.zeros((len(energy_bin_edges), n_pix))
215
-
216
- # Bin data in energy & HEALPix space
217
- for i, (e_min, e_max) in enumerate(energy_bin_edges):
218
- # Convert the midpoint energy to a velocity (km/s).
219
- # Based on kinetic energy equation: E = 1/2 * m * v^2.
220
- energy_midpoint = (e_min + e_max) / 2
221
- energy_velocity = (
222
- np.sqrt(2 * energy_midpoint * UltraConstants.KEV_J / UltraConstants.MASS_H)
223
- / 1e3
224
- )
225
-
226
- # Use Galilean Transform to transform the velocity wrt spacecraft
227
- # to the velocity wrt heliosphere.
228
- # energy_velocity * cartesian -> apply the magnitude of the velocity
229
- # to every position on the grid in the despun grid.
230
- mask = (energy >= e_min) & (energy < e_max)
231
- vx, vy, vz = vhat.T
232
-
233
- # Select only the particles that fall within the energy bin.
234
- vx_bin, vy_bin, vz_bin = vx[mask], vy[mask], vz[mask]
235
- vhat_bin = np.stack((vx_bin, vy_bin, vz_bin), axis=1)
236
- helio_velocity = spacecraft_velocity.reshape(1, 3) + energy_velocity * vhat_bin
237
-
238
- # Normalized vectors representing the direction of the heliocentric velocity.
239
- helio_normalized = -helio_velocity / np.linalg.norm(
240
- helio_velocity, axis=1, keepdims=True
241
- )
242
-
243
- # Convert Cartesian heliocentric vectors into spherical coordinates.
244
- # Result: azimuth (longitude) and elevation (latitude) in degrees.
245
- helio_spherical = cartesian_to_spherical(np.squeeze(helio_normalized))
246
- helio_spherical = np.atleast_2d(helio_spherical)
247
- az, el = helio_spherical[:, 1], helio_spherical[:, 2]
248
-
249
- # Convert azimuth/elevation directions to HEALPix pixel indices.
250
- hpix_idx = hp.ang2pix(nside, az, el, nest=nested, lonlat=True)
251
-
252
- # Only count the events that fall within the energy bin
253
- hist[i, :] += np.bincount(hpix_idx, minlength=n_pix).astype(np.float64)
254
-
255
- return hist, latitude, longitude, n_pix
256
-
257
-
258
155
  def get_spacecraft_background_rates(
259
156
  nside: int = 128,
260
157
  ) -> NDArray:
@@ -309,7 +206,98 @@ def get_helio_background_rates(
309
206
  return background
310
207
 
311
208
 
312
- def get_spacecraft_exposure_times(constant_exposure: pandas.DataFrame) -> NDArray:
209
+ def get_deadtime_correction_factors(sectored_rates_ds: xr.Dataset) -> xr.DataArray:
210
+ """
211
+ Compute the dead time correction factor at each sector.
212
+
213
+ Further description is available in section 3.4.3 of the IMAP-Ultra Algorithm
214
+ Document.
215
+
216
+ Parameters
217
+ ----------
218
+ sectored_rates_ds : xarray.Dataset
219
+ Dataset containing sector mode image rates data.
220
+
221
+ Returns
222
+ -------
223
+ dead_time_ratio : xarray.DataArray
224
+ Dead time correction factor for each sector.
225
+ """
226
+ # Compute the correction factor at each sector
227
+ a = sectored_rates_ds.fifo_valid_events / (
228
+ 1
229
+ - (sectored_rates_ds.event_active_time + 2 * sectored_rates_ds.start_pos) * 1e-7
230
+ )
231
+
232
+ start_full = sectored_rates_ds.start_rf + sectored_rates_ds.start_lf
233
+ b = a * np.exp(start_full * 1e-7 * 5)
234
+
235
+ coin_stop_nd = (
236
+ sectored_rates_ds.coin_tn
237
+ + sectored_rates_ds.coin_bn
238
+ - sectored_rates_ds.stop_tn
239
+ - sectored_rates_ds.stop_bn
240
+ )
241
+
242
+ corrected_valid_events = b * np.exp(1e-7 * 8 * coin_stop_nd)
243
+
244
+ # Compute dead time ratio
245
+ dead_time_ratios = sectored_rates_ds.fifo_valid_events / corrected_valid_events
246
+
247
+ return dead_time_ratios
248
+
249
+
250
+ def get_sectored_rates(rates_ds: xr.Dataset, params_ds: xr.Dataset) -> xr.Dataset:
251
+ """
252
+ Filter rates dataset to only include sector mode data.
253
+
254
+ Parameters
255
+ ----------
256
+ rates_ds : xarray.Dataset
257
+ Dataset containing image rates data.
258
+ params_ds : xarray.Dataset
259
+ Dataset containing image parameters data.
260
+
261
+ Returns
262
+ -------
263
+ rates : xarray.Dataset
264
+ Rates dataset with only the sector mode data.
265
+ """
266
+ # Find indices in which the parameters dataset, indicates that ULTRA was in
267
+ # sector mode. At the normal 15-second spin period, each 24° sector takes ~1 second.
268
+
269
+ # This means that data was collected as a function of spin allowing for fine grained
270
+ # rate analysis.
271
+ sector_mode_start_inds = np.where(params_ds["imageratescadence"] == 3)[0]
272
+ # get the sector mode start and stop indices
273
+ sector_mode_stop_inds = sector_mode_start_inds + 1
274
+ # get the sector mode start and stop times
275
+ mode_3_start = params_ds["epoch"].values[sector_mode_start_inds]
276
+
277
+ # if the last mode is a sector mode, we can assume that the sector data goes through
278
+ # the end of the dataset, so we append np.inf to the end of the last time range.
279
+ if sector_mode_stop_inds[-1] == len(params_ds["epoch"]):
280
+ mode_3_end = np.append(
281
+ params_ds["epoch"].values[sector_mode_stop_inds[:-1]], np.inf
282
+ )
283
+ else:
284
+ mode_3_end = params_ds["epoch"].values[sector_mode_stop_inds]
285
+
286
+ # Build a list of conditions for each sector mode time range
287
+ conditions = [
288
+ (rates_ds["epoch"] >= start) & (rates_ds["epoch"] < end)
289
+ for start, end in zip(mode_3_start, mode_3_end)
290
+ ]
291
+
292
+ sector_mode_mask = np.logical_or.reduce(conditions)
293
+ return rates_ds.isel(epoch=sector_mode_mask)
294
+
295
+
296
+ def get_spacecraft_exposure_times(
297
+ constant_exposure: pandas.DataFrame,
298
+ rates_dataset: xr.Dataset,
299
+ params_dataset: xr.Dataset,
300
+ ) -> NDArray:
313
301
  """
314
302
  Compute exposure times for HEALPix pixels.
315
303
 
@@ -317,6 +305,10 @@ def get_spacecraft_exposure_times(constant_exposure: pandas.DataFrame) -> NDArra
317
305
  ----------
318
306
  constant_exposure : pandas.DataFrame
319
307
  Exposure data.
308
+ rates_dataset : xarray.Dataset
309
+ Dataset containing image rates data.
310
+ params_dataset : xarray.Dataset
311
+ Dataset containing image parameters data.
320
312
 
321
313
  Returns
322
314
  -------
@@ -325,6 +317,11 @@ def get_spacecraft_exposure_times(constant_exposure: pandas.DataFrame) -> NDArra
325
317
  Healpix tessellation of the sky
326
318
  in the pointing (dps) frame.
327
319
  """
320
+ # TODO: uncomment these lines when the deadtime correction is implemented
321
+ # sectored_rates = get_sectored_rates(rates_dataset, params_dataset)
322
+ # get_deadtime_correction_factors(sectored_rates)
323
+ # TODO: calculate the deadtime correction function
324
+ # TODO: Apply the deadtime correction to the exposure times
328
325
  # TODO: use the universal spin table and
329
326
  # universal pointing table here to determine actual number of spins
330
327
  exposure_pointing = (
@@ -384,7 +381,7 @@ def get_helio_exposure_times(
384
381
  # Initialize output array.
385
382
  # Each row corresponds to a HEALPix pixel, and each column to an energy bin.
386
383
  npix = hp.nside2npix(nside)
387
- helio_exposure = np.zeros((npix, len(energy_midpoints)))
384
+ helio_exposure = np.zeros((len(energy_midpoints), npix))
388
385
 
389
386
  # Loop through energy bins and compute transformed exposure.
390
387
  for i, energy_midpoint in enumerate(energy_midpoints):
@@ -415,7 +412,7 @@ def get_helio_exposure_times(
415
412
  hpix_idx = hp.ang2pix(nside, az, el, nest=nested, lonlat=True)
416
413
 
417
414
  # Accumulate exposure values into HEALPix pixels for this energy bin.
418
- helio_exposure[:, i] = np.bincount(
415
+ helio_exposure[i, :] = np.bincount(
419
416
  hpix_idx, weights=exposure_flat, minlength=npix
420
417
  )
421
418
 
@@ -597,7 +594,7 @@ def get_helio_sensitivity(
597
594
  # Initialize output array.
598
595
  # Each row corresponds to a HEALPix pixel, and each column to an energy bin.
599
596
  npix = hp.nside2npix(nside)
600
- helio_sensitivity = np.zeros((npix, len(energy_midpoints)))
597
+ helio_sensitivity = np.zeros((len(energy_midpoints), npix))
601
598
 
602
599
  # Loop through energy bins and compute transformed sensitivity.
603
600
  for i, energy in enumerate(energy_midpoints):
@@ -628,7 +625,7 @@ def get_helio_sensitivity(
628
625
  gridded_sensitivity = grid_sensitivity(efficiencies, geometric_function, energy)
629
626
 
630
627
  # Accumulate sensitivity values into HEALPix pixels for this energy bin.
631
- helio_sensitivity[:, i] = np.bincount(
628
+ helio_sensitivity[i, :] = np.bincount(
632
629
  hpix_idx, weights=gridded_sensitivity, minlength=npix
633
630
  )
634
631
 
@@ -4726,15 +4726,15 @@
4726
4726
  <xtce:IntegerParameterType name="U45_IMG_ENA_PHXTOF_HI_TIME.SPIN" signed="false">
4727
4727
  <xtce:IntegerDataEncoding sizeInBits="8" encoding="unsigned" />
4728
4728
  </xtce:IntegerParameterType>
4729
+ <xtce:IntegerParameterType name="U45_IMG_ENA_PHXTOF_HI_TIME.ABORTFLAG" signed="false">
4730
+ <xtce:IntegerDataEncoding sizeInBits="1" encoding="unsigned" />
4731
+ </xtce:IntegerParameterType>
4729
4732
  <xtce:IntegerParameterType name="U45_IMG_ENA_PHXTOF_HI_TIME.STARTDELAY" signed="false">
4730
4733
  <xtce:IntegerDataEncoding sizeInBits="15" encoding="unsigned" />
4731
4734
  </xtce:IntegerParameterType>
4732
4735
  <xtce:IntegerParameterType name="U45_IMG_ENA_PHXTOF_HI_TIME.P00" signed="false">
4733
4736
  <xtce:IntegerDataEncoding sizeInBits="8" encoding="unsigned" />
4734
4737
  </xtce:IntegerParameterType>
4735
- <xtce:IntegerParameterType name="U45_IMG_ENA_PHXTOF_HI_TIME.ABORTFLAG" signed="false">
4736
- <xtce:IntegerDataEncoding sizeInBits="8" encoding="unsigned" />
4737
- </xtce:IntegerParameterType>
4738
4738
  <xtce:BinaryParameterType name="U45_IMG_ENA_PHXTOF_HI_TIME.PACKETDATA">
4739
4739
  <xtce:BinaryDataEncoding bitOrder="mostSignificantBitFirst">
4740
4740
  <xtce:SizeInBits>
@@ -91,6 +91,7 @@ def create_dataset( # noqa: PLR0912
91
91
  "ena_rates_threshold",
92
92
  "quality_ena_rates",
93
93
  }
94
+ rates_pulse_keys = {"start_per_spin", "stop_per_spin", "coin_per_spin"}
94
95
 
95
96
  for key, data in data_dict.items():
96
97
  # Skip keys that are coordinates.
@@ -114,6 +115,12 @@ def create_dataset( # noqa: PLR0912
114
115
  dims=["epoch", "energy_bin_geometric_mean"],
115
116
  attrs=cdf_manager.get_variable_attributes(key, check_schema=False),
116
117
  )
118
+ elif key in rates_pulse_keys:
119
+ dataset[key] = xr.DataArray(
120
+ data,
121
+ dims=["spin_number"],
122
+ attrs=cdf_manager.get_variable_attributes(key, check_schema=False),
123
+ )
117
124
  elif key in rates_keys:
118
125
  dataset[key] = xr.DataArray(
119
126
  data,
@@ -126,7 +133,12 @@ def create_dataset( # noqa: PLR0912
126
133
  dims=["epoch", "pixel_index"],
127
134
  attrs=cdf_manager.get_variable_attributes(key, check_schema=False),
128
135
  )
129
- elif key in {"counts", "background_rates", "sensitivity"}:
136
+ elif key in {
137
+ "counts",
138
+ "background_rates",
139
+ "helio_exposure_factor",
140
+ "sensitivity",
141
+ }:
130
142
  dataset[key] = xr.DataArray(
131
143
  data,
132
144
  dims=["epoch", "energy_bin_geometric_mean", "pixel_index"],
imap_processing/utils.py CHANGED
@@ -15,48 +15,6 @@ from imap_processing.spice.time import met_to_ttj2000ns
15
15
  logger = logging.getLogger(__name__)
16
16
 
17
17
 
18
- def sort_by_time(packets: list, time_key: str) -> list:
19
- """
20
- Sort packets by specified key.
21
-
22
- Parameters
23
- ----------
24
- packets : list
25
- Decom data packets.
26
- time_key : str
27
- Key to sort by. Must be a key in the packets data dictionary.
28
- e.g. "SHCOARSE" or "MET_TIME" or "ACQ_START_COARSE".
29
-
30
- Returns
31
- -------
32
- sorted_packets : list
33
- Sorted packets.
34
- """
35
- sorted_packets = sorted(packets, key=lambda x: x[time_key])
36
- return sorted_packets
37
-
38
-
39
- def group_by_apid(packets: list) -> dict:
40
- """
41
- Group data by apid.
42
-
43
- Parameters
44
- ----------
45
- packets : list
46
- Packet list.
47
-
48
- Returns
49
- -------
50
- grouped_packets : dict
51
- Grouped data by apid.
52
- """
53
- grouped_packets: dict[list] = collections.defaultdict(list)
54
- for packet in packets:
55
- apid = packet["PKT_APID"]
56
- grouped_packets.setdefault(apid, []).append(packet)
57
- return grouped_packets
58
-
59
-
60
18
  def convert_raw_to_eu(
61
19
  dataset: xr.Dataset,
62
20
  conversion_table_path: str,
@@ -347,6 +305,26 @@ def packet_file_to_datasets(
347
305
  coords={"epoch": time_data},
348
306
  )
349
307
  ds = ds.sortby("epoch")
308
+ # We may get duplicate packets within the packet file if packets were
309
+ # ingested multiple times by the POC. We want to drop packets where
310
+ # apid, epoch, and src_seq_ctr are the same.
311
+
312
+ # xarray only supports dropping duplicates by index, so we instead go
313
+ # to pandas multi-index dataframe to identify the unique positions
314
+ unique_indices = (
315
+ ds[["src_seq_ctr"]]
316
+ .to_dataframe()
317
+ .reset_index()
318
+ .drop_duplicates()
319
+ .index.values
320
+ )
321
+ nduplicates = len(ds["epoch"]) - len(unique_indices)
322
+ if nduplicates != 0:
323
+ logger.warning(
324
+ f"Found [{nduplicates}] duplicate packets for APID {apid}. "
325
+ "Dropping duplicate packets and continuing processing."
326
+ )
327
+ ds = ds.isel(epoch=unique_indices)
350
328
 
351
329
  # Strip any leading characters before "." from the field names which was due
352
330
  # to the packet_name being a part of the variable name in the XTCE definition
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: imap-processing
3
- Version: 0.16.2
3
+ Version: 0.18.0
4
4
  Summary: IMAP Science Operations Center Processing
5
5
  License: MIT
6
6
  Keywords: IMAP,SDC,SOC,Science Operations
@@ -30,7 +30,7 @@ Provides-Extra: tools
30
30
  Requires-Dist: astropy-healpix (>=1.0)
31
31
  Requires-Dist: cdflib (>=1.3.1,<2.0.0)
32
32
  Requires-Dist: healpy (>=1.18.0,<2.0.0) ; extra == "map-visualization"
33
- Requires-Dist: imap-data-access (>=0.31.0)
33
+ Requires-Dist: imap-data-access (>=0.32.0)
34
34
  Requires-Dist: mypy (==1.10.1) ; extra == "dev"
35
35
  Requires-Dist: netcdf4 (>=1.7.2,<2.0.0) ; extra == "test"
36
36
  Requires-Dist: numpy (<=3)