imap-processing 0.7.0__py3-none-any.whl → 0.9.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 (172) hide show
  1. imap_processing/__init__.py +1 -1
  2. imap_processing/_version.py +2 -2
  3. imap_processing/ccsds/excel_to_xtce.py +36 -2
  4. imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +1 -1
  5. imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +145 -30
  6. imap_processing/cdf/config/imap_glows_l1b_variable_attrs.yaml +36 -36
  7. imap_processing/cdf/config/imap_hi_variable_attrs.yaml +136 -9
  8. imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +14 -0
  9. imap_processing/cdf/config/imap_hit_l1a_variable_attrs.yaml +63 -1
  10. imap_processing/cdf/config/imap_hit_l1b_variable_attrs.yaml +9 -0
  11. imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +14 -7
  12. imap_processing/cdf/config/imap_idex_l1a_variable_attrs.yaml +577 -235
  13. imap_processing/cdf/config/imap_idex_l1b_variable_attrs.yaml +326 -0
  14. imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +33 -23
  15. imap_processing/cdf/config/imap_mag_l1_variable_attrs.yaml +24 -28
  16. imap_processing/cdf/config/imap_ultra_l1a_variable_attrs.yaml +1 -0
  17. imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +137 -79
  18. imap_processing/cdf/config/imap_variable_schema.yaml +13 -0
  19. imap_processing/cdf/imap_cdf_manager.py +31 -27
  20. imap_processing/cdf/utils.py +3 -5
  21. imap_processing/cli.py +25 -14
  22. imap_processing/codice/codice_l1a.py +153 -63
  23. imap_processing/codice/constants.py +10 -10
  24. imap_processing/codice/decompress.py +10 -11
  25. imap_processing/codice/utils.py +1 -0
  26. imap_processing/glows/l1a/glows_l1a.py +1 -2
  27. imap_processing/glows/l1b/glows_l1b.py +3 -3
  28. imap_processing/glows/l1b/glows_l1b_data.py +59 -37
  29. imap_processing/glows/l2/glows_l2_data.py +123 -0
  30. imap_processing/hi/l1a/hi_l1a.py +4 -4
  31. imap_processing/hi/l1a/histogram.py +107 -109
  32. imap_processing/hi/l1a/science_direct_event.py +92 -225
  33. imap_processing/hi/l1b/hi_l1b.py +85 -11
  34. imap_processing/hi/l1c/hi_l1c.py +23 -1
  35. imap_processing/hi/packet_definitions/TLM_HI_COMBINED_SCI.xml +3994 -0
  36. imap_processing/hi/utils.py +1 -1
  37. imap_processing/hit/hit_utils.py +221 -0
  38. imap_processing/hit/l0/constants.py +118 -0
  39. imap_processing/hit/l0/decom_hit.py +100 -156
  40. imap_processing/hit/l1a/hit_l1a.py +170 -184
  41. imap_processing/hit/l1b/hit_l1b.py +33 -153
  42. imap_processing/ialirt/l0/process_codicelo.py +153 -0
  43. imap_processing/ialirt/l0/process_hit.py +5 -5
  44. imap_processing/ialirt/packet_definitions/ialirt_codicelo.xml +281 -0
  45. imap_processing/ialirt/process_ephemeris.py +212 -0
  46. imap_processing/idex/idex_l1a.py +65 -84
  47. imap_processing/idex/idex_l1b.py +192 -0
  48. imap_processing/idex/idex_variable_unpacking_and_eu_conversion.csv +33 -0
  49. imap_processing/idex/packet_definitions/idex_packet_definition.xml +97 -595
  50. imap_processing/lo/l0/decompression_tables/decompression_tables.py +17 -1
  51. imap_processing/lo/l0/lo_science.py +45 -13
  52. imap_processing/lo/l1a/lo_l1a.py +76 -8
  53. imap_processing/lo/packet_definitions/lo_xtce.xml +8344 -1849
  54. imap_processing/mag/l0/decom_mag.py +4 -3
  55. imap_processing/mag/l1a/mag_l1a.py +12 -13
  56. imap_processing/mag/l1a/mag_l1a_data.py +1 -2
  57. imap_processing/mag/l1b/mag_l1b.py +90 -7
  58. imap_processing/spice/geometry.py +156 -16
  59. imap_processing/spice/time.py +144 -2
  60. imap_processing/swapi/l1/swapi_l1.py +4 -4
  61. imap_processing/swapi/l2/swapi_l2.py +1 -1
  62. imap_processing/swapi/packet_definitions/swapi_packet_definition.xml +1535 -446
  63. imap_processing/swe/l1b/swe_l1b_science.py +8 -8
  64. imap_processing/swe/l2/swe_l2.py +134 -17
  65. imap_processing/tests/ccsds/test_data/expected_output.xml +2 -1
  66. imap_processing/tests/ccsds/test_excel_to_xtce.py +4 -4
  67. imap_processing/tests/cdf/test_imap_cdf_manager.py +0 -10
  68. imap_processing/tests/codice/conftest.py +1 -17
  69. imap_processing/tests/codice/data/imap_codice_l0_raw_20241110_v001.pkts +0 -0
  70. imap_processing/tests/codice/test_codice_l0.py +8 -2
  71. imap_processing/tests/codice/test_codice_l1a.py +127 -107
  72. imap_processing/tests/codice/test_codice_l1b.py +1 -0
  73. imap_processing/tests/codice/test_decompress.py +7 -7
  74. imap_processing/tests/conftest.py +100 -58
  75. imap_processing/tests/glows/conftest.py +6 -0
  76. imap_processing/tests/glows/test_glows_l1b.py +9 -9
  77. imap_processing/tests/glows/test_glows_l1b_data.py +9 -9
  78. imap_processing/tests/hi/test_data/l0/H90_NHK_20241104.bin +0 -0
  79. imap_processing/tests/hi/test_data/l0/H90_sci_cnt_20241104.bin +0 -0
  80. imap_processing/tests/hi/test_data/l0/H90_sci_de_20241104.bin +0 -0
  81. imap_processing/tests/hi/test_data/l1a/imap_hi_l1a_45sensor-de_20250415_v000.cdf +0 -0
  82. imap_processing/tests/hi/test_hi_l1b.py +73 -3
  83. imap_processing/tests/hi/test_hi_l1c.py +10 -2
  84. imap_processing/tests/hi/test_l1a.py +31 -58
  85. imap_processing/tests/hi/test_science_direct_event.py +58 -0
  86. imap_processing/tests/hi/test_utils.py +4 -3
  87. imap_processing/tests/hit/test_data/sci_sample1.ccsds +0 -0
  88. imap_processing/tests/hit/{test_hit_decom.py → test_decom_hit.py} +95 -36
  89. imap_processing/tests/hit/test_hit_l1a.py +299 -179
  90. imap_processing/tests/hit/test_hit_l1b.py +231 -24
  91. imap_processing/tests/hit/test_hit_utils.py +218 -0
  92. imap_processing/tests/hit/validation_data/hskp_sample_eu.csv +89 -0
  93. imap_processing/tests/hit/validation_data/sci_sample_raw1.csv +29 -0
  94. imap_processing/tests/ialirt/test_data/l0/apid01152.tlm +0 -0
  95. imap_processing/tests/ialirt/test_data/l0/imap_codice_l1a_lo-ialirt_20241110193700_v0.0.0.cdf +0 -0
  96. imap_processing/tests/ialirt/unit/test_process_codicelo.py +106 -0
  97. imap_processing/tests/ialirt/unit/test_process_ephemeris.py +109 -0
  98. imap_processing/tests/ialirt/unit/test_process_hit.py +9 -6
  99. imap_processing/tests/idex/conftest.py +2 -2
  100. imap_processing/tests/idex/imap_idex_l0_raw_20231214_v001.pkts +0 -0
  101. imap_processing/tests/idex/impact_14_tof_high_data.txt +4444 -4444
  102. imap_processing/tests/idex/test_idex_l0.py +4 -4
  103. imap_processing/tests/idex/test_idex_l1a.py +8 -2
  104. imap_processing/tests/idex/test_idex_l1b.py +126 -0
  105. imap_processing/tests/lo/test_lo_l1a.py +7 -16
  106. imap_processing/tests/lo/test_lo_science.py +69 -5
  107. imap_processing/tests/lo/test_pkts/imap_lo_l0_raw_20240803_v002.pkts +0 -0
  108. imap_processing/tests/lo/validation_data/Instrument_FM1_T104_R129_20240803_ILO_SCI_DE_dec_DN_with_fills.csv +1999 -0
  109. imap_processing/tests/mag/imap_mag_l1a_norm-magi_20251017_v001.cdf +0 -0
  110. imap_processing/tests/mag/test_mag_l1b.py +97 -7
  111. imap_processing/tests/spice/test_data/imap_ena_sim_metakernel.template +3 -1
  112. imap_processing/tests/spice/test_geometry.py +115 -9
  113. imap_processing/tests/spice/test_time.py +135 -6
  114. imap_processing/tests/swapi/test_swapi_decom.py +75 -69
  115. imap_processing/tests/swapi/test_swapi_l1.py +4 -4
  116. imap_processing/tests/swe/conftest.py +33 -0
  117. imap_processing/tests/swe/l1_validation/swe_l0_unpacked-data_20240510_v001_VALIDATION_L1B_v3.dat +4332 -0
  118. imap_processing/tests/swe/test_swe_l1b.py +29 -8
  119. imap_processing/tests/swe/test_swe_l2.py +64 -8
  120. imap_processing/tests/test_utils.py +2 -2
  121. imap_processing/tests/ultra/test_data/l0/ultra45_raw_sc_ultrarawimg_withFSWcalcs_FM45_40P_Phi28p5_BeamCal_LinearScan_phi2850_theta-000_20240207T102740.csv +3314 -3314
  122. imap_processing/tests/ultra/test_data/l1/dps_exposure_helio_45_E12.cdf +0 -0
  123. imap_processing/tests/ultra/test_data/l1/dps_exposure_helio_45_E24.cdf +0 -0
  124. imap_processing/tests/ultra/unit/test_de.py +113 -0
  125. imap_processing/tests/ultra/unit/test_spatial_utils.py +125 -0
  126. imap_processing/tests/ultra/unit/test_ultra_l1b.py +27 -3
  127. imap_processing/tests/ultra/unit/test_ultra_l1b_annotated.py +31 -10
  128. imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +55 -35
  129. imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +10 -68
  130. imap_processing/ultra/constants.py +12 -3
  131. imap_processing/ultra/l1b/de.py +168 -30
  132. imap_processing/ultra/l1b/ultra_l1b_annotated.py +24 -10
  133. imap_processing/ultra/l1b/ultra_l1b_extended.py +46 -80
  134. imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +60 -144
  135. imap_processing/ultra/utils/spatial_utils.py +221 -0
  136. {imap_processing-0.7.0.dist-info → imap_processing-0.9.0.dist-info}/METADATA +15 -14
  137. {imap_processing-0.7.0.dist-info → imap_processing-0.9.0.dist-info}/RECORD +142 -139
  138. imap_processing/cdf/cdf_attribute_manager.py +0 -322
  139. imap_processing/cdf/config/shared/default_global_cdf_attrs_schema.yaml +0 -246
  140. imap_processing/cdf/config/shared/default_variable_cdf_attrs_schema.yaml +0 -466
  141. imap_processing/hi/l0/decom_hi.py +0 -24
  142. imap_processing/hi/packet_definitions/hi_packet_definition.xml +0 -482
  143. imap_processing/hit/l0/data_classes/housekeeping.py +0 -240
  144. imap_processing/hit/l0/data_classes/science_packet.py +0 -259
  145. imap_processing/hit/l0/utils/hit_base.py +0 -57
  146. imap_processing/tests/cdf/shared/default_global_cdf_attrs_schema.yaml +0 -246
  147. imap_processing/tests/cdf/shared/default_variable_cdf_attrs_schema.yaml +0 -466
  148. imap_processing/tests/cdf/test_cdf_attribute_manager.py +0 -353
  149. imap_processing/tests/codice/data/imap_codice_l0_hi-counters-aggregated_20240429_v001.pkts +0 -0
  150. imap_processing/tests/codice/data/imap_codice_l0_hi-counters-singles_20240429_v001.pkts +0 -0
  151. imap_processing/tests/codice/data/imap_codice_l0_hi-omni_20240429_v001.pkts +0 -0
  152. imap_processing/tests/codice/data/imap_codice_l0_hi-pha_20240429_v001.pkts +0 -0
  153. imap_processing/tests/codice/data/imap_codice_l0_hi-sectored_20240429_v001.pkts +0 -0
  154. imap_processing/tests/codice/data/imap_codice_l0_hskp_20100101_v001.pkts +0 -0
  155. imap_processing/tests/codice/data/imap_codice_l0_lo-counters-aggregated_20240429_v001.pkts +0 -0
  156. imap_processing/tests/codice/data/imap_codice_l0_lo-counters-singles_20240429_v001.pkts +0 -0
  157. imap_processing/tests/codice/data/imap_codice_l0_lo-nsw-angular_20240429_v001.pkts +0 -0
  158. imap_processing/tests/codice/data/imap_codice_l0_lo-nsw-priority_20240429_v001.pkts +0 -0
  159. imap_processing/tests/codice/data/imap_codice_l0_lo-nsw-species_20240429_v001.pkts +0 -0
  160. imap_processing/tests/codice/data/imap_codice_l0_lo-pha_20240429_v001.pkts +0 -0
  161. imap_processing/tests/codice/data/imap_codice_l0_lo-sw-angular_20240429_v001.pkts +0 -0
  162. imap_processing/tests/codice/data/imap_codice_l0_lo-sw-priority_20240429_v001.pkts +0 -0
  163. imap_processing/tests/codice/data/imap_codice_l0_lo-sw-species_20240429_v001.pkts +0 -0
  164. imap_processing/tests/hi/test_decom.py +0 -55
  165. imap_processing/tests/hi/test_l1a_sci_de.py +0 -72
  166. imap_processing/tests/idex/imap_idex_l0_raw_20230725_v001.pkts +0 -0
  167. imap_processing/tests/mag/imap_mag_l1a_burst-magi_20231025_v001.cdf +0 -0
  168. /imap_processing/{hi/l0/__init__.py → tests/glows/test_glows_l2_data.py} +0 -0
  169. /imap_processing/tests/hit/test_data/{imap_hit_l0_hk_20100105_v001.pkts → imap_hit_l0_raw_20100105_v001.pkts} +0 -0
  170. {imap_processing-0.7.0.dist-info → imap_processing-0.9.0.dist-info}/LICENSE +0 -0
  171. {imap_processing-0.7.0.dist-info → imap_processing-0.9.0.dist-info}/WHEEL +0 -0
  172. {imap_processing-0.7.0.dist-info → imap_processing-0.9.0.dist-info}/entry_points.txt +0 -0
@@ -1,188 +1,108 @@
1
1
  """Module to create pointing sets."""
2
2
 
3
- import typing
4
3
  from pathlib import Path
5
4
 
6
5
  import cdflib
7
6
  import numpy as np
8
- import spiceypy as spice
9
7
  from numpy.typing import NDArray
10
8
 
11
- from imap_processing.spice.kernels import ensure_spice
9
+ from imap_processing.spice.geometry import (
10
+ SpiceFrame,
11
+ cartesian_to_spherical,
12
+ imap_state,
13
+ spherical_to_cartesian,
14
+ )
12
15
  from imap_processing.ultra.constants import UltraConstants
16
+ from imap_processing.ultra.utils.spatial_utils import build_spatial_bins
13
17
 
14
18
  # TODO: add species binning.
15
19
 
16
20
 
17
- def build_energy_bins() -> tuple[np.ndarray, np.ndarray]:
21
+ def build_energy_bins() -> tuple[list[tuple[float, float]], np.ndarray]:
18
22
  """
19
23
  Build energy bin boundaries.
20
24
 
21
25
  Returns
22
26
  -------
23
- energy_bin_edges : np.ndarray
24
- Array of energy bin edges.
27
+ intervals : list[tuple[float, float]]
28
+ Energy bins.
25
29
  energy_midpoints : np.ndarray
26
30
  Array of energy bin midpoints.
27
31
  """
28
- # TODO: these value will almost certainly change.
29
- alpha = 0.2 # deltaE/E
30
- energy_start = 3.385 # energy start for the Ultra grids
31
- n_bins = 23 # number of energy bins
32
-
33
32
  # Calculate energy step
34
- energy_step = (1 + alpha / 2) / (1 - alpha / 2)
33
+ energy_step = (1 + UltraConstants.ALPHA / 2) / (1 - UltraConstants.ALPHA / 2)
35
34
 
36
35
  # Create energy bins.
37
- energy_bin_edges = energy_start * energy_step ** np.arange(n_bins + 1)
36
+ energy_bin_edges = UltraConstants.ENERGY_START * energy_step ** np.arange(
37
+ UltraConstants.N_BINS + 1
38
+ )
38
39
  # Add a zero to the left side for outliers and round to nearest 3 decimal places.
39
40
  energy_bin_edges = np.around(np.insert(energy_bin_edges, 0, 0), 3)
40
41
  energy_midpoints = (energy_bin_edges[:-1] + energy_bin_edges[1:]) / 2
41
42
 
42
- return energy_bin_edges, energy_midpoints
43
-
44
-
45
- def build_spatial_bins(
46
- spacing: float = 0.5,
47
- ) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
48
- """
49
- Build spatial bin boundaries for azimuth and elevation.
50
-
51
- Parameters
52
- ----------
53
- spacing : float, optional
54
- The bin spacing in degrees (default is 0.5 degrees).
55
-
56
- Returns
57
- -------
58
- az_bin_edges : np.ndarray
59
- Array of azimuth bin boundary values.
60
- el_bin_edges : np.ndarray
61
- Array of elevation bin boundary values.
62
- az_bin_midpoints : np.ndarray
63
- Array of azimuth bin midpoint values.
64
- el_bin_midpoints : np.ndarray
65
- Array of elevation bin midpoint values.
66
- """
67
- # Azimuth bins from 0 to 360 degrees.
68
- az_bin_edges = np.arange(0, 360 + spacing, spacing)
69
- az_bin_midpoints = az_bin_edges[:-1] + spacing / 2 # Midpoints between edges
70
-
71
- # Elevation bins from -90 to 90 degrees.
72
- el_bin_edges = np.arange(-90, 90 + spacing, spacing)
73
- el_bin_midpoints = el_bin_edges[:-1] + spacing / 2 # Midpoints between edges
74
-
75
- return az_bin_edges, el_bin_edges, az_bin_midpoints, el_bin_midpoints
76
-
77
-
78
- def cartesian_to_spherical(
79
- v: NDArray,
80
- ) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
81
- """
82
- Convert cartesian coordinates to spherical coordinates.
83
-
84
- Parameters
85
- ----------
86
- v : np.ndarray
87
- A NumPy array with shape (n, 3) where each
88
- row represents a vector
89
- with x, y, z-components.
90
-
91
- Returns
92
- -------
93
- az : np.ndarray
94
- The azimuth angles in degrees.
95
- el : np.ndarray
96
- The elevation angles in degrees.
97
- r : np.ndarray
98
- The radii, or magnitudes, of the vectors.
99
- """
100
- vx = v[:, 0]
101
- vy = v[:, 1]
102
- vz = v[:, 2]
103
-
104
- # Magnitude of the velocity vector
105
- magnitude_v = np.sqrt(vx**2 + vy**2 + vz**2)
106
-
107
- vhat_x = -vx / magnitude_v
108
- vhat_y = -vy / magnitude_v
109
- vhat_z = -vz / magnitude_v
110
-
111
- # Elevation angle (angle from the z-axis, range: [-pi/2, pi/2])
112
- el = np.arcsin(vhat_z)
113
-
114
- # Azimuth angle (angle in the xy-plane, range: [0, 2*pi])
115
- az = np.arctan2(vhat_y, vhat_x)
116
-
117
- # Ensure azimuth is from 0 to 2PI
118
- az = az % (2 * np.pi)
43
+ intervals = [
44
+ (float(energy_bin_edges[i]), float(energy_bin_edges[i + 1]))
45
+ for i in range(len(energy_bin_edges) - 1)
46
+ ]
119
47
 
120
- return np.degrees(az), np.degrees(el), magnitude_v
121
-
122
-
123
- def spherical_to_cartesian(
124
- r: np.ndarray, theta: np.ndarray, phi: np.ndarray
125
- ) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
126
- """
127
- Convert spherical coordinates to Cartesian coordinates.
128
-
129
- Parameters
130
- ----------
131
- r : np.ndarray
132
- Radius.
133
- theta : np.ndarray
134
- Azimuth angle in radians.
135
- phi : array-like or float
136
- Elevation angle in radians.
137
-
138
- Returns
139
- -------
140
- x, y, z : tuple
141
- Cartesian coordinates.
142
- """
143
- x = r * np.cos(phi) * np.cos(theta)
144
- y = r * np.cos(phi) * np.sin(theta)
145
- z = r * np.sin(phi)
146
-
147
- return x, y, z
48
+ return intervals, energy_midpoints
148
49
 
149
50
 
150
51
  def get_histogram(
151
- v: tuple[np.ndarray, np.ndarray, np.ndarray],
52
+ vhat: tuple[np.ndarray, np.ndarray, np.ndarray],
152
53
  energy: np.ndarray,
153
54
  az_bin_edges: np.ndarray,
154
55
  el_bin_edges: np.ndarray,
155
- energy_bin_edges: np.ndarray,
56
+ energy_bin_edges: list[tuple[float, float]],
156
57
  ) -> NDArray:
157
58
  """
158
59
  Compute a 3D histogram of the particle data.
159
60
 
160
61
  Parameters
161
62
  ----------
162
- v : tuple[np.ndarray, np.ndarray, np.ndarray]
163
- The x,y,z-components of the velocity vector.
63
+ vhat : tuple[np.ndarray, np.ndarray, np.ndarray]
64
+ The x,y,z-components of the unit velocity vector.
164
65
  energy : np.ndarray
165
66
  The particle energy.
166
67
  az_bin_edges : np.ndarray
167
68
  Array of azimuth bin boundary values.
168
69
  el_bin_edges : np.ndarray
169
70
  Array of elevation bin boundary values.
170
- energy_bin_edges : np.ndarray
71
+ energy_bin_edges : list[tuple[float, float]]
171
72
  Array of energy bin edges.
172
73
 
173
74
  Returns
174
75
  -------
175
76
  hist : np.ndarray
176
77
  A 3D histogram array.
78
+
79
+ Notes
80
+ -----
81
+ The histogram will now work properly for overlapping energy bins, i.e.
82
+ the same energy value can fall into multiple bins if the intervals overlap.
177
83
  """
178
- az, el, _ = cartesian_to_spherical(v)
84
+ spherical_coords = cartesian_to_spherical(vhat)
85
+ az, el = (
86
+ spherical_coords[..., 1],
87
+ spherical_coords[..., 2],
88
+ )
179
89
 
180
- # 3D binning.
181
- hist, _ = np.histogramdd(
182
- sample=(az, el, energy), bins=[az_bin_edges, el_bin_edges, energy_bin_edges]
90
+ # Initialize histogram
91
+ hist_total = np.zeros(
92
+ (len(az_bin_edges) - 1, len(el_bin_edges) - 1, len(energy_bin_edges))
183
93
  )
184
94
 
185
- return hist
95
+ for i, (e_min, e_max) in enumerate(energy_bin_edges):
96
+ # Filter data for current energy bin.
97
+ mask = (energy >= e_min) & (energy < e_max)
98
+ hist, _ = np.histogramdd(
99
+ sample=(az[mask], el[mask], energy[mask]),
100
+ bins=[az_bin_edges, el_bin_edges, [e_min, e_max]],
101
+ )
102
+ # Assign 2D histogram to current energy bin.
103
+ hist_total[:, :, i] = hist[:, :, 0]
104
+
105
+ return hist_total
186
106
 
187
107
 
188
108
  def get_pointing_frame_exposure_times(
@@ -211,19 +131,17 @@ def get_pointing_frame_exposure_times(
211
131
  return exposure
212
132
 
213
133
 
214
- @ensure_spice
215
- @typing.no_type_check
216
134
  def get_helio_exposure_times(
217
135
  time: np.ndarray,
218
136
  sc_exposure: np.ndarray,
219
- ) -> np.ndarray:
137
+ ) -> NDArray:
220
138
  """
221
139
  Compute a 3D array of the exposure in the helio frame.
222
140
 
223
141
  Parameters
224
142
  ----------
225
143
  time : np.ndarray
226
- Median time of pointing.
144
+ Median time of pointing in J2000 seconds.
227
145
  sc_exposure : np.ndarray
228
146
  Spacecraft exposure.
229
147
 
@@ -237,7 +155,7 @@ def get_helio_exposure_times(
237
155
  These calculations are performed once per pointing.
238
156
  """
239
157
  # Get bins and midpoints.
240
- energy_bin_edges, energy_midpoints = build_energy_bins()
158
+ _, energy_midpoints = build_energy_bins()
241
159
  az_bin_edges, el_bin_edges, az_bin_midpoints, el_bin_midpoints = (
242
160
  build_spatial_bins()
243
161
  )
@@ -252,15 +170,12 @@ def get_helio_exposure_times(
252
170
 
253
171
  # Radial distance.
254
172
  r = np.ones(el_grid.shape)
255
- x, y, z = spherical_to_cartesian(r, np.radians(az_grid), np.radians(el_grid))
256
-
257
- # Reshape and combine the Cartesian coordinates into a 2D array.
258
- cartesian = np.vstack(
259
- [x.flatten(order="F"), y.flatten(order="F"), z.flatten(order="F")]
260
- )
173
+ spherical_coords = np.stack((r, np.radians(az_grid), np.radians(el_grid)), axis=-1)
174
+ cartesian_coords = spherical_to_cartesian(spherical_coords)
175
+ cartesian = cartesian_coords.reshape(-1, 3, order="F").T
261
176
 
262
177
  # Spacecraft velocity in the pointing (DPS) frame wrt heliosphere.
263
- state, lt = spice.spkezr("IMAP", time, "IMAP_DPS", "NONE", "SUN")
178
+ state = imap_state(time, ref_frame=SpiceFrame.IMAP_DPS)
264
179
 
265
180
  # Extract the velocity part of the state vector
266
181
  spacecraft_velocity = state[3:6]
@@ -273,7 +188,7 @@ def get_helio_exposure_times(
273
188
  / 1e3
274
189
  )
275
190
 
276
- # Use Compton-Getting to transform the velocity wrt spacecraft
191
+ # Use Galilean Transform to transform the velocity wrt spacecraft
277
192
  # to the velocity wrt heliosphere.
278
193
  # energy_velocity * cartesian -> apply the magnitude of the velocity
279
194
  # to every position on the grid in the despun grid.
@@ -284,10 +199,11 @@ def get_helio_exposure_times(
284
199
  helio_velocity.T, axis=1, keepdims=True
285
200
  )
286
201
  # Converts vectors from Cartesian coordinates (x, y, z)
287
- # into spherical coordinates
288
- az, el, _ = cartesian_to_spherical(-helio_normalized)
202
+ # into spherical coordinates.
203
+ spherical_coords = cartesian_to_spherical(helio_normalized)
204
+ az, el = spherical_coords[..., 1], spherical_coords[..., 2]
289
205
 
290
- # Bin the coordinates.
206
+ # Assign values from sc_exposure directly to bins.
291
207
  az_idx = np.digitize(az, az_bin_edges) - 1
292
208
  el_idx = np.digitize(el, el_bin_edges[::-1]) - 1
293
209
 
@@ -0,0 +1,221 @@
1
+ """IMAP Ultra utils for spatial binning and grid creation."""
2
+
3
+ import typing
4
+
5
+ import numpy as np
6
+ from numpy.typing import NDArray
7
+
8
+
9
+ def build_spatial_bins(
10
+ az_spacing: float = 0.5,
11
+ el_spacing: float = 0.5,
12
+ ) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
13
+ """
14
+ Build spatial bin boundaries for azimuth and elevation.
15
+
16
+ Parameters
17
+ ----------
18
+ az_spacing : float, optional
19
+ The azimuth bin spacing in degrees (default is 0.5 degrees).
20
+ el_spacing : float, optional
21
+ The elevation bin spacing in degrees (default is 0.5 degrees).
22
+
23
+ Returns
24
+ -------
25
+ az_bin_edges : np.ndarray
26
+ Array of azimuth bin boundary values.
27
+ el_bin_edges : np.ndarray
28
+ Array of elevation bin boundary values.
29
+ az_bin_midpoints : np.ndarray
30
+ Array of azimuth bin midpoint values.
31
+ el_bin_midpoints : np.ndarray
32
+ Array of elevation bin midpoint values.
33
+ """
34
+ # Azimuth bins from 0 to 360 degrees.
35
+ az_bin_edges = np.arange(0, 360 + az_spacing, az_spacing)
36
+ az_bin_midpoints = az_bin_edges[:-1] + az_spacing / 2 # Midpoints between edges
37
+
38
+ # Elevation bins from -90 to 90 degrees.
39
+ el_bin_edges = np.arange(-90, 90 + el_spacing, el_spacing)
40
+ el_bin_midpoints = el_bin_edges[:-1] + el_spacing / 2 # Midpoints between edges
41
+
42
+ return az_bin_edges, el_bin_edges, az_bin_midpoints, el_bin_midpoints
43
+
44
+
45
+ def build_solid_angle_map(
46
+ spacing: float, input_degrees: bool = True, output_degrees: bool = False
47
+ ) -> NDArray:
48
+ """
49
+ Build a solid angle map for a given spacing in degrees.
50
+
51
+ Parameters
52
+ ----------
53
+ spacing : float
54
+ The bin spacing in the specified units.
55
+ input_degrees : bool, optional
56
+ If True, the input spacing is in degrees
57
+ (default is True for radians).
58
+ output_degrees : bool, optional
59
+ If True, the output solid angle map is in square degrees
60
+ (default is False for steradians).
61
+
62
+ Returns
63
+ -------
64
+ solid_angle_grid : np.ndarray
65
+ The solid angle map grid in steradians (default) or square degrees.
66
+ First index is latitude/el, second index is longitude/az.
67
+ """
68
+ if input_degrees:
69
+ spacing = np.deg2rad(spacing)
70
+
71
+ if spacing <= 0:
72
+ raise ValueError("Spacing must be positive valued, non-zero.")
73
+
74
+ if not np.isclose((np.pi / spacing) % 1, 0):
75
+ raise ValueError("Spacing must divide evenly into pi radians.")
76
+
77
+ latitudes = np.arange(-np.pi / 2, np.pi / 2 + spacing, step=spacing)
78
+ sine_latitudes = np.sin(latitudes)
79
+ delta_sine_latitudes = np.diff(sine_latitudes)
80
+ solid_angle_by_latitude = np.abs(spacing * delta_sine_latitudes)
81
+
82
+ solid_angle_grid = np.repeat(
83
+ solid_angle_by_latitude[:, np.newaxis], (2 * np.pi) / spacing, axis=1
84
+ )
85
+
86
+ if output_degrees:
87
+ solid_angle_grid *= (180 / np.pi) ** 2
88
+
89
+ return solid_angle_grid
90
+
91
+
92
+ def build_az_el_grid(
93
+ spacing: float,
94
+ input_degrees: bool = False,
95
+ output_degrees: bool = False,
96
+ centered_azimuth: bool = False,
97
+ centered_elevation: bool = True,
98
+ ) -> tuple[NDArray, NDArray, NDArray, NDArray]:
99
+ """
100
+ Build a 2D grid of azimuth and elevation angles.
101
+
102
+ Azimuth and Elevation values represent the center of each grid cell,
103
+ so the grid is offset by half the spacing.
104
+
105
+ Parameters
106
+ ----------
107
+ spacing : float
108
+ Spacing of the grid in degrees if `input_degrees` is True, else radians.
109
+ input_degrees : bool, optional
110
+ Whether the spacing is specified in degrees and must be converted to radians,
111
+ by default False (indicating radians).
112
+ output_degrees : bool, optional
113
+ Whether the output azimuth and elevation angles should be in degrees,
114
+ by default False (indicating radians).
115
+ centered_azimuth : bool, optional
116
+ Whether the azimuth grid should be centered around 0 degrees/0 radians,
117
+ i.e. from -pi to pi radians, by default False, indicating 0 to 2pi radians.
118
+ If True, the azimuth grid will be from -pi to pi radians.
119
+ centered_elevation : bool, optional
120
+ Whether the elevation grid should be centered around 0 degrees/0 radians,
121
+ i.e. from -pi/2 to pi/2 radians, by default True.
122
+ If False, the elevation grid will be from 0 to pi radians.
123
+
124
+ Returns
125
+ -------
126
+ tuple[NDArray, NDArray, NDArray, NDArray]
127
+ - The evenly spaced, 1D range of azimuth angles
128
+ e.g.(0, 0.5, 1, ..., 359.5) deg.
129
+ - The evenly spaced, 1D range of elevation angles
130
+ e.g.(-90, -89.5, ..., 89.5) deg.
131
+ - The 2D grid of azimuth angles (azimuths for each elevation).
132
+ This grid will be constant along the elevation (0th) axis.
133
+ - The 2D grid of elevation angles (elevations for each azimuth).
134
+ This grid will be constant along the azimuth (1st) axis.
135
+
136
+ Raises
137
+ ------
138
+ ValueError
139
+ If the spacing is not positive or does not divide evenly into pi radians.
140
+ """
141
+ if input_degrees:
142
+ spacing = np.deg2rad(spacing)
143
+
144
+ if spacing <= 0:
145
+ raise ValueError("Spacing must be positive valued, non-zero.")
146
+
147
+ if not np.isclose((np.pi / spacing) % 1, 0):
148
+ raise ValueError("Spacing must divide evenly into pi radians.")
149
+
150
+ el_range = np.arange(spacing / 2, np.pi, spacing)
151
+ az_range = np.arange(spacing / 2, 2 * np.pi, spacing)
152
+ if centered_azimuth:
153
+ az_range = az_range - np.pi
154
+ if centered_elevation:
155
+ el_range = el_range - np.pi / 2
156
+
157
+ # Reverse the elevation range so that the grid is in the order
158
+ # defined by the Ultra prototype code (`build_dps_grid.m`).
159
+ el_range = el_range[::-1]
160
+
161
+ az_grid, el_grid = np.meshgrid(az_range, el_range)
162
+
163
+ if output_degrees:
164
+ az_range = np.rad2deg(az_range)
165
+ el_range = np.rad2deg(el_range)
166
+ az_grid = np.rad2deg(az_grid)
167
+ el_grid = np.rad2deg(el_grid)
168
+
169
+ return az_range, el_range, az_grid, el_grid
170
+
171
+
172
+ @typing.no_type_check
173
+ def rewrap_even_spaced_el_az_grid(
174
+ raveled_values: NDArray,
175
+ shape: typing.Optional[tuple[int]] = None,
176
+ extra_axis: bool = False,
177
+ ) -> NDArray:
178
+ """
179
+ Take an unwrapped (raveled) 1D array and reshapes it into a 2D el/az grid.
180
+
181
+ Assumes the following must be true of the original grid:
182
+ 1. Grid was evenly spaced in angular space,
183
+ 2. Grid had the same spacing in both azimuth and elevation.
184
+ 3. Elevation is the 0th axis (and extends a total of 180 degrees),
185
+ 4. Azimuth is the 1st axis (and extends a total of 360 degrees).
186
+ 5. The grid was raveled in Fortran (F) order.
187
+
188
+ Parameters
189
+ ----------
190
+ raveled_values : NDArray
191
+ 1D array of values to be reshaped into a 2D grid.
192
+ shape : tuple[int], optional
193
+ The shape of the original grid, if known, by default None.
194
+ If None, the shape will be inferred from the size of the input array.
195
+ extra_axis : bool, optional
196
+ If True, input is a 2D array with latter axis being 'extra', non-spatial axis.
197
+ This axis (e.g. energy bins) will be preserved in the reshaped grid.
198
+
199
+ Returns
200
+ -------
201
+ NDArray
202
+ The reshaped 2D grid of values.
203
+
204
+ Raises
205
+ ------
206
+ ValueError
207
+ If the input is not a 1D array or 2D array with an extra axis.
208
+ """
209
+ if raveled_values.ndim not in (1, 2) or (
210
+ raveled_values.ndim == 2 and not extra_axis
211
+ ):
212
+ raise ValueError("Input must be a 1D array or 2D array with extra axis.")
213
+
214
+ # We can infer the shape if its evenly spaced and 2D
215
+ if not shape:
216
+ spacing_deg = 1 / np.sqrt(raveled_values.shape[0] / (360 * 180))
217
+ shape = (int(180 // spacing_deg), int(360 // spacing_deg))
218
+
219
+ if extra_axis:
220
+ shape = (shape[0], shape[1], raveled_values.shape[1])
221
+ return raveled_values.reshape(shape, order="F")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: imap-processing
3
- Version: 0.7.0
3
+ Version: 0.9.0
4
4
  Summary: IMAP Science Operations Center Processing
5
5
  License: MIT
6
6
  Keywords: IMAP,SDC,SOC,Science Operations
@@ -41,6 +41,7 @@ Requires-Dist: pytest-cov (>=4.0.0,<5.0.0) ; extra == "test"
41
41
  Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
42
42
  Requires-Dist: requests (>=2.32.3,<3.0.0) ; extra == "test"
43
43
  Requires-Dist: ruff (==0.2.1) ; extra == "dev"
44
+ Requires-Dist: sammi-cdf (>=0.1.2,<0.2.0)
44
45
  Requires-Dist: space_packet_parser (>=5.0.1,<6.0.0)
45
46
  Requires-Dist: sphinx ; extra == "doc"
46
47
  Requires-Dist: sphinxcontrib-openapi (>=0.8.3,<0.9.0) ; extra == "doc"
@@ -82,21 +83,21 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
82
83
  <table>
83
84
  <tbody>
84
85
  <tr>
85
- <td align="center" valign="top" width="14.28%"><a href="http://greglucas.github.io"><img src="https://avatars.githubusercontent.com/u/12417828?v=4?s=100" width="100px;" alt="Greg Lucas"/><br /><sub><b>Greg Lucas</b></sub></a><br /><a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/commits?author=greglucas" title="Code">💻</a> <a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/commits?author=greglucas" title="Documentation">📖</a> <a href="#ideas-greglucas" title="Ideas, Planning, & Feedback">🤔</a> <a href="#infra-greglucas" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#maintenance-greglucas" title="Maintenance">🚧</a> <a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/pulls?q=is%3Apr+reviewed-by%3Agreglucas" title="Reviewed Pull Requests">👀</a></td>
86
- <td align="center" valign="top" width="14.28%"><a href="https://github.com/tech3371"><img src="https://avatars.githubusercontent.com/u/36522642?v=4?s=100" width="100px;" alt="Tenzin Choedon"/><br /><sub><b>Tenzin Choedon</b></sub></a><br /><a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/commits?author=tech3371" title="Code">💻</a> <a href="#ideas-tech3371" title="Ideas, Planning, & Feedback">🤔</a> <a href="#infra-tech3371" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#example-tech3371" title="Examples">💡</a> <a href="#maintenance-tech3371" title="Maintenance">🚧</a> <a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/pulls?q=is%3Apr+reviewed-by%3Atech3371" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/commits?author=tech3371" title="Documentation">📖</a></td>
87
- <td align="center" valign="top" width="14.28%"><a href="https://github.com/laspsandoval"><img src="https://avatars.githubusercontent.com/u/46567335?v=4?s=100" width="100px;" alt="Laura Sandoval"/><br /><sub><b>Laura Sandoval</b></sub></a><br /><a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/pulls?q=is%3Apr+reviewed-by%3Alaspsandoval" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/commits?author=laspsandoval" title="Code">💻</a> <a href="#infra-laspsandoval" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#ideas-laspsandoval" title="Ideas, Planning, & Feedback">🤔</a></td>
88
- <td align="center" valign="top" width="14.28%"><a href="https://github.com/sdhoyt"><img src="https://avatars.githubusercontent.com/u/7146374?v=4?s=100" width="100px;" alt="Sean Hoyt"/><br /><sub><b>Sean Hoyt</b></sub></a><br /><a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/commits?author=sdhoyt" title="Code">💻</a> <a href="#ideas-sdhoyt" title="Ideas, Planning, & Feedback">🤔</a> <a href="#infra-sdhoyt" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/pulls?q=is%3Apr+reviewed-by%3Asdhoyt" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/commits?author=sdhoyt" title="Documentation">📖</a> <a href="#maintenance-sdhoyt" title="Maintenance">🚧</a></td>
89
- <td align="center" valign="top" width="14.28%"><a href="https://github.com/GFMoraga"><img src="https://avatars.githubusercontent.com/u/104743000?v=4?s=100" width="100px;" alt="Gabriel M."/><br /><sub><b>Gabriel M.</b></sub></a><br /><a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/commits?author=GFMoraga" title="Code">💻</a> <a href="#ideas-GFMoraga" title="Ideas, Planning, & Feedback">🤔</a> <a href="#infra-GFMoraga" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/pulls?q=is%3Apr+reviewed-by%3AGFMoraga" title="Reviewed Pull Requests">👀</a> <a href="#maintenance-GFMoraga" title="Maintenance">🚧</a> <a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/commits?author=GFMoraga" title="Documentation">📖</a></td>
90
- <td align="center" valign="top" width="14.28%"><a href="https://github.com/bourque"><img src="https://avatars.githubusercontent.com/u/2250769?v=4?s=100" width="100px;" alt="Matthew Bourque"/><br /><sub><b>Matthew Bourque</b></sub></a><br /><a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/commits?author=bourque" title="Code">💻</a> <a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/commits?author=bourque" title="Documentation">📖</a> <a href="#ideas-bourque" title="Ideas, Planning, & Feedback">🤔</a> <a href="#infra-bourque" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/pulls?q=is%3Apr+reviewed-by%3Abourque" title="Reviewed Pull Requests">👀</a> <a href="#maintenance-bourque" title="Maintenance">🚧</a></td>
91
- <td align="center" valign="top" width="14.28%"><a href="https://github.com/maxinelasp"><img src="https://avatars.githubusercontent.com/u/117409426?v=4?s=100" width="100px;" alt="Maxine Hartnett"/><br /><sub><b>Maxine Hartnett</b></sub></a><br /><a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/commits?author=maxinelasp" title="Code">💻</a> <a href="#ideas-maxinelasp" title="Ideas, Planning, & Feedback">🤔</a> <a href="#infra-maxinelasp" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/pulls?q=is%3Apr+reviewed-by%3Amaxinelasp" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/commits?author=maxinelasp" title="Documentation">📖</a> <a href="#maintenance-maxinelasp" title="Maintenance">🚧</a></td>
86
+ <td align="center" valign="top" width="14.28%"><a href="http://greglucas.github.io"><img src="https://avatars.githubusercontent.com/u/12417828?v=4?s=100" width="100px;" alt="Greg Lucas"/><br /><sub><b>Greg Lucas</b></sub></a><br /></td>
87
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/tech3371"><img src="https://avatars.githubusercontent.com/u/36522642?v=4?s=100" width="100px;" alt="Tenzin Choedon"/><br /><sub><b>Tenzin Choedon</b></sub></a><br /></td>
88
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/laspsandoval"><img src="https://avatars.githubusercontent.com/u/46567335?v=4?s=100" width="100px;" alt="Laura Sandoval"/><br /><sub><b>Laura Sandoval</b></sub></a><br /></td>
89
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/sdhoyt"><img src="https://avatars.githubusercontent.com/u/7146374?v=4?s=100" width="100px;" alt="Sean Hoyt"/><br /><sub><b>Sean Hoyt</b></sub></a><br /></td>
90
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/GFMoraga"><img src="https://avatars.githubusercontent.com/u/104743000?v=4?s=100" width="100px;" alt="Gabriel Moraga"/><br /><sub><b>Gabriel Moraga</b></sub></a><br /></td>
91
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/bourque"><img src="https://avatars.githubusercontent.com/u/2250769?v=4?s=100" width="100px;" alt="Matthew Bourque"/><br /><sub><b>Matthew Bourque</b></sub></a><br /></td>
92
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/maxinelasp"><img src="https://avatars.githubusercontent.com/u/117409426?v=4?s=100" width="100px;" alt="Maxine Hartnett"/><br /><sub><b>Maxine Hartnett</b></sub></a><br /></td>
92
93
  </tr>
93
94
  <tr>
94
- <td align="center" valign="top" width="14.28%"><a href="https://github.com/bryan-harter"><img src="https://avatars.githubusercontent.com/u/41062454?v=4?s=100" width="100px;" alt="Bryan Harter"/><br /><sub><b>Bryan Harter</b></sub></a><br /><a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/commits?author=bryan-harter" title="Code">💻</a> <a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/commits?author=bryan-harter" title="Documentation">📖</a></td>
95
- <td align="center" valign="top" width="14.28%"><a href="https://github.com/mstrumik"><img src="https://avatars.githubusercontent.com/u/142874888?v=4?s=100" width="100px;" alt="mstrumik"/><br /><sub><b>mstrumik</b></sub></a><br /><a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/pulls?q=is%3Apr+reviewed-by%3Amstrumik" title="Reviewed Pull Requests">👀</a></td>
96
- <td align="center" valign="top" width="14.28%"><a href="https://github.com/vmartinez-cu"><img src="https://avatars.githubusercontent.com/u/39746325?v=4?s=100" width="100px;" alt="vmartinez-cu"/><br /><sub><b>vmartinez-cu</b></sub></a><br /><a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/pulls?q=is%3Apr+reviewed-by%3Avmartinez-cu" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/commits?author=vmartinez-cu" title="Code">💻</a></td>
97
- <td align="center" valign="top" width="14.28%"><a href="https://github.com/subagonsouth"><img src="https://avatars.githubusercontent.com/u/16110870?v=4?s=100" width="100px;" alt="Tim Plummer"/><br /><sub><b>Tim Plummer</b></sub></a><br /><a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/pulls?q=is%3Apr+reviewed-by%3Asubagonsouth" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/commits?author=subagonsouth" title="Code">💻</a></td>
98
- <td align="center" valign="top" width="14.28%"><a href="https://github.com/daralynnrhode"><img src="https://avatars.githubusercontent.com/u/143308810?v=4?s=100" width="100px;" alt="Daralynn Rhode"/><br /><sub><b>Daralynn Rhode</b></sub></a><br /><a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/commits?author=daralynnrhode" title="Code">💻</a> <a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/commits?author=daralynnrhode" title="Documentation">📖</a></td>
99
- <td align="center" valign="top" width="14.28%"><a href="https://github.com/anamanica"><img src="https://avatars.githubusercontent.com/u/171708990?v=4?s=100" width="100px;" alt="anamanica"/><br /><sub><b>anamanica</b></sub></a><br /><a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/commits?author=anamanica" title="Code">💻</a> <a href="https://github.com/IMAP-Science-Operations-Center/imap_processing/commits?author=anamanica" title="Documentation">📖</a></td>
95
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/bryan-harter"><img src="https://avatars.githubusercontent.com/u/41062454?v=4?s=100" width="100px;" alt="Bryan Harter"/><br /><sub><b>Bryan Harter</b></sub></a><br /></td>
96
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/mstrumik"><img src="https://avatars.githubusercontent.com/u/142874888?v=4?s=100" width="100px;" alt="Marek Strumik"/><br /><sub><b>Marek Strumik</b></sub></a><br /></td>
97
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/vmartinez-cu"><img src="https://avatars.githubusercontent.com/u/39746325?v=4?s=100" width="100px;" alt="Veronica Martinez"/><br /><sub><b>Veronica Martinez</b></sub></a><br /></td>
98
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/subagonsouth"><img src="https://avatars.githubusercontent.com/u/16110870?v=4?s=100" width="100px;" alt="Tim Plummer"/><br /><sub><b>Tim Plummer</b></sub></a><br /></td>
99
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/daralynnrhode"><img src="https://avatars.githubusercontent.com/u/143308810?v=4?s=100" width="100px;" alt="Daralynn Rhode"/><br /><sub><b>Daralynn Rhode</b></sub></a><br /></td>
100
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/anamanica"><img src="https://avatars.githubusercontent.com/u/171708990?v=4?s=100" width="100px;" alt="Ana Manica"/><br /><sub><b>Ana Manica</b></sub></a><br /></td>
100
101
  </tr>
101
102
  </tbody>
102
103
  </table>