imap-processing 0.11.0__py3-none-any.whl → 0.13.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 (415) hide show
  1. imap_processing/__init__.py +11 -11
  2. imap_processing/_version.py +2 -2
  3. imap_processing/ccsds/ccsds_data.py +1 -2
  4. imap_processing/ccsds/excel_to_xtce.py +66 -18
  5. imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +24 -40
  6. imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +934 -42
  7. imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +1846 -128
  8. imap_processing/cdf/config/imap_glows_global_cdf_attrs.yaml +0 -5
  9. imap_processing/cdf/config/imap_hi_global_cdf_attrs.yaml +10 -11
  10. imap_processing/cdf/config/imap_hi_variable_attrs.yaml +17 -19
  11. imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +27 -14
  12. imap_processing/cdf/config/imap_hit_l1a_variable_attrs.yaml +106 -116
  13. imap_processing/cdf/config/imap_hit_l1b_variable_attrs.yaml +120 -145
  14. imap_processing/cdf/config/imap_hit_l2_variable_attrs.yaml +14 -0
  15. imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +25 -9
  16. imap_processing/cdf/config/imap_idex_l1a_variable_attrs.yaml +6 -4
  17. imap_processing/cdf/config/imap_idex_l1b_variable_attrs.yaml +3 -3
  18. imap_processing/cdf/config/imap_lo_global_cdf_attrs.yaml +0 -12
  19. imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +1 -1
  20. imap_processing/cdf/config/imap_mag_global_cdf_attrs.yaml +23 -20
  21. imap_processing/cdf/config/imap_mag_l1a_variable_attrs.yaml +361 -0
  22. imap_processing/cdf/config/imap_mag_l1b_variable_attrs.yaml +160 -0
  23. imap_processing/cdf/config/imap_mag_l1c_variable_attrs.yaml +160 -0
  24. imap_processing/cdf/config/imap_spacecraft_global_cdf_attrs.yaml +18 -0
  25. imap_processing/cdf/config/imap_spacecraft_variable_attrs.yaml +40 -0
  26. imap_processing/cdf/config/imap_swapi_global_cdf_attrs.yaml +1 -5
  27. imap_processing/cdf/config/imap_swapi_variable_attrs.yaml +22 -0
  28. imap_processing/cdf/config/imap_swe_global_cdf_attrs.yaml +12 -4
  29. imap_processing/cdf/config/imap_swe_l1a_variable_attrs.yaml +16 -2
  30. imap_processing/cdf/config/imap_swe_l1b_variable_attrs.yaml +64 -52
  31. imap_processing/cdf/config/imap_swe_l2_variable_attrs.yaml +71 -47
  32. imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +180 -19
  33. imap_processing/cdf/config/imap_ultra_l1a_variable_attrs.yaml +5045 -41
  34. imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +80 -17
  35. imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +32 -57
  36. imap_processing/cdf/utils.py +52 -38
  37. imap_processing/cli.py +477 -233
  38. imap_processing/codice/codice_l1a.py +466 -131
  39. imap_processing/codice/codice_l1b.py +51 -152
  40. imap_processing/codice/constants.py +1360 -569
  41. imap_processing/codice/decompress.py +2 -6
  42. imap_processing/ena_maps/ena_maps.py +1103 -146
  43. imap_processing/ena_maps/utils/coordinates.py +19 -0
  44. imap_processing/ena_maps/utils/map_utils.py +14 -17
  45. imap_processing/ena_maps/utils/spatial_utils.py +55 -52
  46. imap_processing/glows/l1a/glows_l1a.py +28 -99
  47. imap_processing/glows/l1a/glows_l1a_data.py +2 -2
  48. imap_processing/glows/l1b/glows_l1b.py +1 -4
  49. imap_processing/glows/l1b/glows_l1b_data.py +1 -3
  50. imap_processing/glows/l2/glows_l2.py +2 -5
  51. imap_processing/hi/l1a/hi_l1a.py +54 -29
  52. imap_processing/hi/l1a/histogram.py +0 -1
  53. imap_processing/hi/l1a/science_direct_event.py +6 -8
  54. imap_processing/hi/l1b/hi_l1b.py +111 -82
  55. imap_processing/hi/l1c/hi_l1c.py +416 -32
  56. imap_processing/hi/utils.py +58 -12
  57. imap_processing/hit/ancillary/imap_hit_l1b-to-l2-sector-dt0-factors_20250219_v002.csv +81 -0
  58. imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt0-factors_20250219_v002.csv +205 -0
  59. imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt1-factors_20250219_v002.csv +205 -0
  60. imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt2-factors_20250219_v002.csv +205 -0
  61. imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt3-factors_20250219_v002.csv +205 -0
  62. imap_processing/hit/ancillary/imap_hit_l1b-to-l2-summed-dt0-factors_20250219_v002.csv +68 -0
  63. imap_processing/hit/hit_utils.py +235 -5
  64. imap_processing/hit/l0/constants.py +20 -11
  65. imap_processing/hit/l0/decom_hit.py +21 -5
  66. imap_processing/hit/l1a/hit_l1a.py +71 -75
  67. imap_processing/hit/l1b/constants.py +321 -0
  68. imap_processing/hit/l1b/hit_l1b.py +377 -67
  69. imap_processing/hit/l2/constants.py +318 -0
  70. imap_processing/hit/l2/hit_l2.py +723 -0
  71. imap_processing/hit/packet_definitions/hit_packet_definitions.xml +1323 -71
  72. imap_processing/ialirt/l0/mag_l0_ialirt_data.py +155 -0
  73. imap_processing/ialirt/l0/parse_mag.py +374 -0
  74. imap_processing/ialirt/l0/process_swapi.py +69 -0
  75. imap_processing/ialirt/l0/process_swe.py +548 -0
  76. imap_processing/ialirt/packet_definitions/ialirt.xml +216 -208
  77. imap_processing/ialirt/packet_definitions/ialirt_codicehi.xml +1 -1
  78. imap_processing/ialirt/packet_definitions/ialirt_codicelo.xml +1 -1
  79. imap_processing/ialirt/packet_definitions/ialirt_mag.xml +115 -0
  80. imap_processing/ialirt/packet_definitions/ialirt_swapi.xml +14 -14
  81. imap_processing/ialirt/utils/grouping.py +114 -0
  82. imap_processing/ialirt/utils/time.py +29 -0
  83. imap_processing/idex/atomic_masses.csv +22 -0
  84. imap_processing/idex/decode.py +2 -2
  85. imap_processing/idex/idex_constants.py +33 -0
  86. imap_processing/idex/idex_l0.py +22 -8
  87. imap_processing/idex/idex_l1a.py +81 -51
  88. imap_processing/idex/idex_l1b.py +13 -39
  89. imap_processing/idex/idex_l2a.py +823 -0
  90. imap_processing/idex/idex_l2b.py +120 -0
  91. imap_processing/idex/idex_variable_unpacking_and_eu_conversion.csv +11 -11
  92. imap_processing/idex/packet_definitions/idex_housekeeping_packet_definition.xml +9130 -0
  93. imap_processing/lo/l0/lo_science.py +7 -2
  94. imap_processing/lo/l1a/lo_l1a.py +1 -5
  95. imap_processing/lo/l1b/lo_l1b.py +702 -29
  96. imap_processing/lo/l1b/tof_conversions.py +11 -0
  97. imap_processing/lo/l1c/lo_l1c.py +1 -4
  98. imap_processing/mag/constants.py +51 -0
  99. imap_processing/mag/imap_mag_sdc_configuration_v001.py +8 -0
  100. imap_processing/mag/l0/decom_mag.py +10 -3
  101. imap_processing/mag/l1a/mag_l1a.py +23 -19
  102. imap_processing/mag/l1a/mag_l1a_data.py +35 -10
  103. imap_processing/mag/l1b/mag_l1b.py +259 -50
  104. imap_processing/mag/l1c/interpolation_methods.py +388 -0
  105. imap_processing/mag/l1c/mag_l1c.py +621 -17
  106. imap_processing/mag/l2/mag_l2.py +140 -0
  107. imap_processing/mag/l2/mag_l2_data.py +288 -0
  108. imap_processing/quality_flags.py +1 -0
  109. imap_processing/spacecraft/packet_definitions/scid_x252.xml +538 -0
  110. imap_processing/spacecraft/quaternions.py +121 -0
  111. imap_processing/spice/geometry.py +19 -22
  112. imap_processing/spice/kernels.py +0 -276
  113. imap_processing/spice/pointing_frame.py +257 -0
  114. imap_processing/spice/repoint.py +149 -0
  115. imap_processing/spice/spin.py +38 -33
  116. imap_processing/spice/time.py +24 -0
  117. imap_processing/swapi/l1/swapi_l1.py +20 -12
  118. imap_processing/swapi/l2/swapi_l2.py +116 -5
  119. imap_processing/swapi/swapi_utils.py +32 -0
  120. imap_processing/swe/l1a/swe_l1a.py +44 -12
  121. imap_processing/swe/l1a/swe_science.py +13 -13
  122. imap_processing/swe/l1b/swe_l1b.py +898 -23
  123. imap_processing/swe/l2/swe_l2.py +75 -136
  124. imap_processing/swe/packet_definitions/swe_packet_definition.xml +1121 -1
  125. imap_processing/swe/utils/swe_constants.py +64 -0
  126. imap_processing/swe/utils/swe_utils.py +85 -28
  127. imap_processing/tests/ccsds/test_data/expected_output.xml +40 -1
  128. imap_processing/tests/ccsds/test_excel_to_xtce.py +24 -21
  129. imap_processing/tests/cdf/test_data/imap_instrument2_global_cdf_attrs.yaml +0 -2
  130. imap_processing/tests/cdf/test_utils.py +14 -16
  131. imap_processing/tests/codice/conftest.py +44 -33
  132. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-counters-aggregated_20241110193700_v0.0.0.cdf +0 -0
  133. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-counters-singles_20241110193700_v0.0.0.cdf +0 -0
  134. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-ialirt_20241110193700_v0.0.0.cdf +0 -0
  135. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-omni_20241110193700_v0.0.0.cdf +0 -0
  136. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-pha_20241110193700_v0.0.0.cdf +0 -0
  137. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-priorities_20241110193700_v0.0.0.cdf +0 -0
  138. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-sectored_20241110193700_v0.0.0.cdf +0 -0
  139. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-counters-aggregated_20241110193700_v0.0.0.cdf +0 -0
  140. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-counters-singles_20241110193700_v0.0.0.cdf +0 -0
  141. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-ialirt_20241110193700_v0.0.0.cdf +0 -0
  142. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-angular_20241110193700_v0.0.0.cdf +0 -0
  143. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-priority_20241110193700_v0.0.0.cdf +0 -0
  144. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-species_20241110193700_v0.0.0.cdf +0 -0
  145. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-pha_20241110193700_v0.0.0.cdf +0 -0
  146. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-angular_20241110193700_v0.0.0.cdf +0 -0
  147. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-priority_20241110193700_v0.0.0.cdf +0 -0
  148. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-species_20241110193700_v0.0.0.cdf +0 -0
  149. imap_processing/tests/codice/test_codice_l1a.py +126 -53
  150. imap_processing/tests/codice/test_codice_l1b.py +6 -7
  151. imap_processing/tests/codice/test_decompress.py +4 -4
  152. imap_processing/tests/conftest.py +239 -27
  153. imap_processing/tests/ena_maps/conftest.py +51 -0
  154. imap_processing/tests/ena_maps/test_ena_maps.py +1068 -110
  155. imap_processing/tests/ena_maps/test_map_utils.py +66 -43
  156. imap_processing/tests/ena_maps/test_spatial_utils.py +17 -21
  157. imap_processing/tests/glows/conftest.py +10 -14
  158. imap_processing/tests/glows/test_glows_decom.py +4 -4
  159. imap_processing/tests/glows/test_glows_l1a_cdf.py +6 -27
  160. imap_processing/tests/glows/test_glows_l1a_data.py +6 -8
  161. imap_processing/tests/glows/test_glows_l1b.py +11 -11
  162. imap_processing/tests/glows/test_glows_l1b_data.py +5 -5
  163. imap_processing/tests/glows/test_glows_l2.py +2 -8
  164. imap_processing/tests/hi/conftest.py +1 -1
  165. imap_processing/tests/hi/data/l0/H45_diag_fee_20250208.bin +0 -0
  166. imap_processing/tests/hi/data/l0/H45_diag_fee_20250208_verify.csv +205 -0
  167. imap_processing/tests/hi/test_hi_l1b.py +22 -27
  168. imap_processing/tests/hi/test_hi_l1c.py +249 -18
  169. imap_processing/tests/hi/test_l1a.py +35 -7
  170. imap_processing/tests/hi/test_science_direct_event.py +3 -3
  171. imap_processing/tests/hi/test_utils.py +24 -2
  172. imap_processing/tests/hit/helpers/l1_validation.py +74 -73
  173. imap_processing/tests/hit/test_data/hskp_sample.ccsds +0 -0
  174. imap_processing/tests/hit/test_data/imap_hit_l0_raw_20100105_v001.pkts +0 -0
  175. imap_processing/tests/hit/test_decom_hit.py +5 -1
  176. imap_processing/tests/hit/test_hit_l1a.py +32 -36
  177. imap_processing/tests/hit/test_hit_l1b.py +300 -81
  178. imap_processing/tests/hit/test_hit_l2.py +716 -0
  179. imap_processing/tests/hit/test_hit_utils.py +184 -7
  180. imap_processing/tests/hit/validation_data/hit_l1b_standard_sample2_nsrl_v4_3decimals.csv +62 -62
  181. imap_processing/tests/hit/validation_data/hskp_sample_eu_3_6_2025.csv +89 -0
  182. imap_processing/tests/hit/validation_data/hskp_sample_raw.csv +89 -88
  183. imap_processing/tests/hit/validation_data/sci_sample_raw.csv +1 -1
  184. imap_processing/tests/ialirt/data/l0/461971383-404.bin +0 -0
  185. imap_processing/tests/ialirt/data/l0/461971384-405.bin +0 -0
  186. imap_processing/tests/ialirt/data/l0/461971385-406.bin +0 -0
  187. imap_processing/tests/ialirt/data/l0/461971386-407.bin +0 -0
  188. imap_processing/tests/ialirt/data/l0/461971387-408.bin +0 -0
  189. imap_processing/tests/ialirt/data/l0/461971388-409.bin +0 -0
  190. imap_processing/tests/ialirt/data/l0/461971389-410.bin +0 -0
  191. imap_processing/tests/ialirt/data/l0/461971390-411.bin +0 -0
  192. imap_processing/tests/ialirt/data/l0/461971391-412.bin +0 -0
  193. imap_processing/tests/ialirt/data/l0/sample_decoded_i-alirt_data.csv +383 -0
  194. imap_processing/tests/ialirt/unit/test_decom_ialirt.py +16 -81
  195. imap_processing/tests/ialirt/unit/test_grouping.py +81 -0
  196. imap_processing/tests/ialirt/unit/test_parse_mag.py +223 -0
  197. imap_processing/tests/ialirt/unit/test_process_codicehi.py +3 -3
  198. imap_processing/tests/ialirt/unit/test_process_codicelo.py +3 -10
  199. imap_processing/tests/ialirt/unit/test_process_ephemeris.py +4 -4
  200. imap_processing/tests/ialirt/unit/test_process_hit.py +3 -3
  201. imap_processing/tests/ialirt/unit/test_process_swapi.py +24 -16
  202. imap_processing/tests/ialirt/unit/test_process_swe.py +319 -6
  203. imap_processing/tests/ialirt/unit/test_time.py +16 -0
  204. imap_processing/tests/idex/conftest.py +127 -6
  205. imap_processing/tests/idex/test_data/imap_idex_l0_raw_20231218_v001.pkts +0 -0
  206. imap_processing/tests/idex/test_data/imap_idex_l0_raw_20241206_v001.pkts +0 -0
  207. imap_processing/tests/idex/test_data/imap_idex_l0_raw_20250108_v001.pkts +0 -0
  208. imap_processing/tests/idex/test_data/impact_14_tof_high_data.txt +4508 -4508
  209. imap_processing/tests/idex/test_idex_l0.py +33 -11
  210. imap_processing/tests/idex/test_idex_l1a.py +92 -21
  211. imap_processing/tests/idex/test_idex_l1b.py +106 -27
  212. imap_processing/tests/idex/test_idex_l2a.py +399 -0
  213. imap_processing/tests/idex/test_idex_l2b.py +93 -0
  214. imap_processing/tests/lo/test_cdfs/imap_lo_l1a_de_20241022_v002.cdf +0 -0
  215. imap_processing/tests/lo/test_cdfs/imap_lo_l1a_spin_20241022_v002.cdf +0 -0
  216. imap_processing/tests/lo/test_lo_l1a.py +3 -3
  217. imap_processing/tests/lo/test_lo_l1b.py +515 -6
  218. imap_processing/tests/lo/test_lo_l1c.py +1 -1
  219. imap_processing/tests/lo/test_lo_science.py +7 -7
  220. imap_processing/tests/lo/test_star_sensor.py +1 -1
  221. imap_processing/tests/mag/conftest.py +120 -2
  222. imap_processing/tests/mag/test_mag_decom.py +5 -4
  223. imap_processing/tests/mag/test_mag_l1a.py +51 -7
  224. imap_processing/tests/mag/test_mag_l1b.py +40 -59
  225. imap_processing/tests/mag/test_mag_l1c.py +354 -19
  226. imap_processing/tests/mag/test_mag_l2.py +130 -0
  227. imap_processing/tests/mag/test_mag_validation.py +247 -26
  228. imap_processing/tests/mag/validation/L1b/T009/MAGScience-normal-(2,2)-8s-20250204-16h39.csv +17 -0
  229. imap_processing/tests/mag/validation/L1b/T009/mag-l1a-l1b-t009-magi-out.csv +16 -16
  230. imap_processing/tests/mag/validation/L1b/T009/mag-l1a-l1b-t009-mago-out.csv +16 -16
  231. imap_processing/tests/mag/validation/L1b/T010/MAGScience-normal-(2,2)-8s-20250206-12h05.csv +17 -0
  232. imap_processing/tests/mag/validation/L1b/T011/MAGScience-normal-(2,2)-8s-20250204-16h08.csv +17 -0
  233. imap_processing/tests/mag/validation/L1b/T011/mag-l1a-l1b-t011-magi-out.csv +16 -16
  234. imap_processing/tests/mag/validation/L1b/T011/mag-l1a-l1b-t011-mago-out.csv +16 -16
  235. imap_processing/tests/mag/validation/L1b/T012/MAGScience-normal-(2,2)-8s-20250204-16h08.csv +17 -0
  236. imap_processing/tests/mag/validation/L1b/T012/data.bin +0 -0
  237. imap_processing/tests/mag/validation/L1b/T012/field_like_all_ranges.txt +19200 -0
  238. imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-cal.cdf +0 -0
  239. imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-in.csv +17 -0
  240. imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-magi-out.csv +17 -0
  241. imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-mago-out.csv +17 -0
  242. imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-magi-normal-in.csv +1217 -0
  243. imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-magi-normal-out.csv +1857 -0
  244. imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-mago-normal-in.csv +1217 -0
  245. imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-mago-normal-out.csv +1857 -0
  246. imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-magi-normal-in.csv +1217 -0
  247. imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-magi-normal-out.csv +1793 -0
  248. imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-mago-normal-in.csv +1217 -0
  249. imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-mago-normal-out.csv +1793 -0
  250. imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-magi-burst-in.csv +2561 -0
  251. imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-magi-normal-in.csv +961 -0
  252. imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-magi-normal-out.csv +1539 -0
  253. imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-mago-normal-in.csv +1921 -0
  254. imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-mago-normal-out.csv +2499 -0
  255. imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-magi-normal-in.csv +865 -0
  256. imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-magi-normal-out.csv +1196 -0
  257. imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-mago-normal-in.csv +1729 -0
  258. imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-mago-normal-out.csv +3053 -0
  259. imap_processing/tests/mag/validation/L2/imap_mag_l1b_norm-mago_20251017_v002.cdf +0 -0
  260. imap_processing/tests/mag/validation/calibration/imap_mag_l1b-calibration_20240229_v001.cdf +0 -0
  261. imap_processing/tests/mag/validation/calibration/imap_mag_l2-calibration-matrices_20251017_v004.cdf +0 -0
  262. imap_processing/tests/mag/validation/calibration/imap_mag_l2-offsets-norm_20251017_20251017_v001.cdf +0 -0
  263. imap_processing/tests/spacecraft/data/SSR_2024_190_20_08_12_0483851794_2_DA_apid0594_1packet.pkts +0 -0
  264. imap_processing/tests/spacecraft/test_quaternions.py +71 -0
  265. imap_processing/tests/spice/test_data/fake_repoint_data.csv +5 -0
  266. imap_processing/tests/spice/test_data/fake_spin_data.csv +11 -11
  267. imap_processing/tests/spice/test_geometry.py +9 -12
  268. imap_processing/tests/spice/test_kernels.py +1 -200
  269. imap_processing/tests/spice/test_pointing_frame.py +185 -0
  270. imap_processing/tests/spice/test_repoint.py +121 -0
  271. imap_processing/tests/spice/test_spin.py +50 -9
  272. imap_processing/tests/spice/test_time.py +14 -0
  273. imap_processing/tests/swapi/lut/imap_swapi_esa-unit-conversion_20250211_v000.csv +73 -0
  274. imap_processing/tests/swapi/lut/imap_swapi_lut-notes_20250211_v000.csv +1025 -0
  275. imap_processing/tests/swapi/test_swapi_l1.py +13 -11
  276. imap_processing/tests/swapi/test_swapi_l2.py +180 -8
  277. imap_processing/tests/swe/l0_data/2024051010_SWE_HK_packet.bin +0 -0
  278. imap_processing/tests/swe/l0_data/2024051011_SWE_CEM_RAW_packet.bin +0 -0
  279. imap_processing/tests/swe/l0_validation_data/idle_export_eu.SWE_APP_HK_20240510_092742.csv +49 -0
  280. imap_processing/tests/swe/l0_validation_data/idle_export_eu.SWE_CEM_RAW_20240510_092742.csv +593 -0
  281. imap_processing/tests/swe/lut/checker-board-indices.csv +24 -0
  282. imap_processing/tests/swe/lut/imap_swe_esa-lut_20250301_v000.csv +385 -0
  283. imap_processing/tests/swe/lut/imap_swe_l1b-in-flight-cal_20240510_20260716_v000.csv +3 -0
  284. imap_processing/tests/swe/test_swe_l1a.py +20 -2
  285. imap_processing/tests/swe/test_swe_l1a_cem_raw.py +52 -0
  286. imap_processing/tests/swe/test_swe_l1a_hk.py +68 -0
  287. imap_processing/tests/swe/test_swe_l1a_science.py +3 -3
  288. imap_processing/tests/swe/test_swe_l1b.py +162 -24
  289. imap_processing/tests/swe/test_swe_l2.py +153 -91
  290. imap_processing/tests/test_cli.py +171 -88
  291. imap_processing/tests/test_utils.py +140 -17
  292. imap_processing/tests/ultra/data/l0/FM45_UltraFM45_Functional_2024-01-22T0105_20240122T010548.CCSDS +0 -0
  293. imap_processing/tests/ultra/data/l0/ultra45_raw_sc_ultraimgrates_20220530_00.csv +164 -0
  294. imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_ultrarawimg_withFSWcalcs_FM45_40P_Phi28p5_BeamCal_LinearScan_phi2850_theta-000_20240207T102740.csv +3243 -3243
  295. imap_processing/tests/ultra/data/mock_data.py +369 -0
  296. imap_processing/tests/ultra/unit/conftest.py +115 -89
  297. imap_processing/tests/ultra/unit/test_badtimes.py +4 -4
  298. imap_processing/tests/ultra/unit/test_cullingmask.py +8 -6
  299. imap_processing/tests/ultra/unit/test_de.py +14 -13
  300. imap_processing/tests/ultra/unit/test_decom_apid_880.py +27 -76
  301. imap_processing/tests/ultra/unit/test_decom_apid_881.py +54 -11
  302. imap_processing/tests/ultra/unit/test_decom_apid_883.py +12 -10
  303. imap_processing/tests/ultra/unit/test_decom_apid_896.py +202 -55
  304. imap_processing/tests/ultra/unit/test_lookup_utils.py +23 -1
  305. imap_processing/tests/ultra/unit/test_spacecraft_pset.py +77 -0
  306. imap_processing/tests/ultra/unit/test_ultra_l1a.py +98 -305
  307. imap_processing/tests/ultra/unit/test_ultra_l1b.py +60 -14
  308. imap_processing/tests/ultra/unit/test_ultra_l1b_annotated.py +2 -2
  309. imap_processing/tests/ultra/unit/test_ultra_l1b_culling.py +26 -27
  310. imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +239 -70
  311. imap_processing/tests/ultra/unit/test_ultra_l1c.py +5 -5
  312. imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +114 -83
  313. imap_processing/tests/ultra/unit/test_ultra_l2.py +230 -0
  314. imap_processing/ultra/constants.py +1 -1
  315. imap_processing/ultra/l0/decom_tools.py +27 -39
  316. imap_processing/ultra/l0/decom_ultra.py +168 -204
  317. imap_processing/ultra/l0/ultra_utils.py +152 -136
  318. imap_processing/ultra/l1a/ultra_l1a.py +55 -271
  319. imap_processing/ultra/l1b/badtimes.py +1 -4
  320. imap_processing/ultra/l1b/cullingmask.py +2 -6
  321. imap_processing/ultra/l1b/de.py +116 -57
  322. imap_processing/ultra/l1b/extendedspin.py +20 -18
  323. imap_processing/ultra/l1b/lookup_utils.py +72 -9
  324. imap_processing/ultra/l1b/ultra_l1b.py +36 -16
  325. imap_processing/ultra/l1b/ultra_l1b_culling.py +66 -30
  326. imap_processing/ultra/l1b/ultra_l1b_extended.py +297 -94
  327. imap_processing/ultra/l1c/histogram.py +2 -6
  328. imap_processing/ultra/l1c/spacecraft_pset.py +84 -0
  329. imap_processing/ultra/l1c/ultra_l1c.py +8 -9
  330. imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +206 -108
  331. imap_processing/ultra/l2/ultra_l2.py +299 -0
  332. imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_LeftSlit.csv +526 -0
  333. imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_RightSlit.csv +526 -0
  334. imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_LeftSlit.csv +526 -0
  335. imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_RightSlit.csv +526 -0
  336. imap_processing/ultra/lookup_tables/FM45_Startup1_ULTRA_IMGPARAMS_20240719.csv +2 -2
  337. imap_processing/ultra/lookup_tables/FM90_Startup1_ULTRA_IMGPARAMS_20240719.csv +2 -0
  338. imap_processing/ultra/packet_definitions/README.md +38 -0
  339. imap_processing/ultra/packet_definitions/ULTRA_SCI_COMBINED.xml +15302 -482
  340. imap_processing/ultra/utils/ultra_l1_utils.py +31 -12
  341. imap_processing/utils.py +69 -29
  342. {imap_processing-0.11.0.dist-info → imap_processing-0.13.0.dist-info}/METADATA +10 -6
  343. imap_processing-0.13.0.dist-info/RECORD +578 -0
  344. imap_processing/cdf/config/imap_mag_l1_variable_attrs.yaml +0 -237
  345. imap_processing/hi/l1a/housekeeping.py +0 -27
  346. imap_processing/hi/l1b/hi_eng_unit_convert_table.csv +0 -154
  347. imap_processing/swe/l1b/swe_esa_lookup_table.csv +0 -1441
  348. imap_processing/swe/l1b/swe_l1b_science.py +0 -652
  349. imap_processing/tests/codice/data/imap_codice_l1a_hi-counters-aggregated_20240429_v001.cdf +0 -0
  350. imap_processing/tests/codice/data/imap_codice_l1a_hi-counters-singles_20240429_v001.cdf +0 -0
  351. imap_processing/tests/codice/data/imap_codice_l1a_hi-omni_20240429_v001.cdf +0 -0
  352. imap_processing/tests/codice/data/imap_codice_l1a_hi-sectored_20240429_v001.cdf +0 -0
  353. imap_processing/tests/codice/data/imap_codice_l1a_hskp_20100101_v001.cdf +0 -0
  354. imap_processing/tests/codice/data/imap_codice_l1a_lo-counters-aggregated_20240429_v001.cdf +0 -0
  355. imap_processing/tests/codice/data/imap_codice_l1a_lo-counters-singles_20240429_v001.cdf +0 -0
  356. imap_processing/tests/codice/data/imap_codice_l1a_lo-nsw-angular_20240429_v001.cdf +0 -0
  357. imap_processing/tests/codice/data/imap_codice_l1a_lo-nsw-priority_20240429_v001.cdf +0 -0
  358. imap_processing/tests/codice/data/imap_codice_l1a_lo-nsw-species_20240429_v001.cdf +0 -0
  359. imap_processing/tests/codice/data/imap_codice_l1a_lo-sw-angular_20240429_v001.cdf +0 -0
  360. imap_processing/tests/codice/data/imap_codice_l1a_lo-sw-priority_20240429_v001.cdf +0 -0
  361. imap_processing/tests/codice/data/imap_codice_l1a_lo-sw-species_20240429_v001.cdf +0 -0
  362. imap_processing/tests/codice/data/imap_codice_l1b_hi-counters-aggregated_20240429_v001.cdf +0 -0
  363. imap_processing/tests/codice/data/imap_codice_l1b_hi-counters-singles_20240429_v001.cdf +0 -0
  364. imap_processing/tests/codice/data/imap_codice_l1b_hi-omni_20240429_v001.cdf +0 -0
  365. imap_processing/tests/codice/data/imap_codice_l1b_hi-sectored_20240429_v001.cdf +0 -0
  366. imap_processing/tests/codice/data/imap_codice_l1b_hskp_20100101_v001.cdf +0 -0
  367. imap_processing/tests/codice/data/imap_codice_l1b_lo-counters-aggregated_20240429_v001.cdf +0 -0
  368. imap_processing/tests/codice/data/imap_codice_l1b_lo-counters-singles_20240429_v001.cdf +0 -0
  369. imap_processing/tests/codice/data/imap_codice_l1b_lo-nsw-angular_20240429_v001.cdf +0 -0
  370. imap_processing/tests/codice/data/imap_codice_l1b_lo-nsw-priority_20240429_v001.cdf +0 -0
  371. imap_processing/tests/codice/data/imap_codice_l1b_lo-nsw-species_20240429_v001.cdf +0 -0
  372. imap_processing/tests/codice/data/imap_codice_l1b_lo-sw-angular_20240429_v001.cdf +0 -0
  373. imap_processing/tests/codice/data/imap_codice_l1b_lo-sw-priority_20240429_v001.cdf +0 -0
  374. imap_processing/tests/codice/data/imap_codice_l1b_lo-sw-species_20240429_v001.cdf +0 -0
  375. imap_processing/tests/hi/data/l1/imap_hi_l1b_45sensor-de_20250415_v999.cdf +0 -0
  376. imap_processing/tests/hit/PREFLIGHT_raw_record_2023_256_15_59_04_apid1251.pkts +0 -0
  377. imap_processing/tests/hit/PREFLIGHT_raw_record_2023_256_15_59_04_apid1252.pkts +0 -0
  378. imap_processing/tests/hit/validation_data/hskp_sample_eu.csv +0 -89
  379. imap_processing/tests/hit/validation_data/sci_sample_raw1.csv +0 -29
  380. imap_processing/tests/idex/test_data/imap_idex_l0_raw_20231214_v001.pkts +0 -0
  381. imap_processing/tests/lo/test_cdfs/imap_lo_l1a_de_20100101_v001.cdf +0 -0
  382. imap_processing/tests/lo/test_cdfs/imap_lo_l1a_spin_20100101_v001.cdf +0 -0
  383. imap_processing/tests/swe/test_swe_l1b_science.py +0 -84
  384. imap_processing/tests/ultra/test_data/mock_data.py +0 -161
  385. imap_processing/ultra/l1c/pset.py +0 -40
  386. imap_processing/ultra/lookup_tables/dps_sensitivity45.cdf +0 -0
  387. imap_processing-0.11.0.dist-info/RECORD +0 -488
  388. /imap_processing/idex/packet_definitions/{idex_packet_definition.xml → idex_science_packet_definition.xml} +0 -0
  389. /imap_processing/tests/ialirt/{test_data → data}/l0/20240827095047_SWE_IALIRT_packet.bin +0 -0
  390. /imap_processing/tests/ialirt/{test_data → data}/l0/BinLog CCSDS_FRAG_TLM_20240826_152323Z_IALIRT_data_for_SDC.bin +0 -0
  391. /imap_processing/tests/ialirt/{test_data → data}/l0/IALiRT Raw Packet Telemetry.txt +0 -0
  392. /imap_processing/tests/ialirt/{test_data → data}/l0/apid01152.tlm +0 -0
  393. /imap_processing/tests/ialirt/{test_data → data}/l0/eu_SWP_IAL_20240826_152033.csv +0 -0
  394. /imap_processing/tests/ialirt/{test_data → data}/l0/hi_fsw_view_1_ccsds.bin +0 -0
  395. /imap_processing/tests/ialirt/{test_data → data}/l0/hit_ialirt_sample.ccsds +0 -0
  396. /imap_processing/tests/ialirt/{test_data → data}/l0/hit_ialirt_sample.csv +0 -0
  397. /imap_processing/tests/ialirt/{test_data → data}/l0/idle_export_eu.SWE_IALIRT_20240827_093852.csv +0 -0
  398. /imap_processing/tests/ialirt/{test_data → data}/l0/imap_codice_l1a_hi-ialirt_20240523200000_v0.0.0.cdf +0 -0
  399. /imap_processing/tests/ialirt/{test_data → data}/l0/imap_codice_l1a_lo-ialirt_20241110193700_v0.0.0.cdf +0 -0
  400. /imap_processing/{mag/l1b → tests/spacecraft}/__init__.py +0 -0
  401. /imap_processing/{swe/l1b/engineering_unit_convert_table.csv → tests/swe/lut/imap_swe_eu-conversion_20240510_v000.csv} +0 -0
  402. /imap_processing/tests/ultra/{test_data → data}/l0/FM45_40P_Phi28p5_BeamCal_LinearScan_phi28.50_theta-0.00_20240207T102740.CCSDS +0 -0
  403. /imap_processing/tests/ultra/{test_data → data}/l0/FM45_7P_Phi0.0_BeamCal_LinearScan_phi0.04_theta-0.01_20230821T121304.CCSDS +0 -0
  404. /imap_processing/tests/ultra/{test_data → data}/l0/FM45_TV_Cycle6_Hot_Ops_Front212_20240124T063837.CCSDS +0 -0
  405. /imap_processing/tests/ultra/{test_data → data}/l0/Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.CCSDS +0 -0
  406. /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_auxdata_Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.csv +0 -0
  407. /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_enaphxtofhangimg_FM45_TV_Cycle6_Hot_Ops_Front212_20240124T063837.csv +0 -0
  408. /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_ultraimgrates_Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.csv +0 -0
  409. /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_ultrarawimgevent_FM45_7P_Phi00_BeamCal_LinearScan_phi004_theta-001_20230821T121304.csv +0 -0
  410. /imap_processing/tests/ultra/{test_data → data}/l1/dps_exposure_helio_45_E1.cdf +0 -0
  411. /imap_processing/tests/ultra/{test_data → data}/l1/dps_exposure_helio_45_E12.cdf +0 -0
  412. /imap_processing/tests/ultra/{test_data → data}/l1/dps_exposure_helio_45_E24.cdf +0 -0
  413. {imap_processing-0.11.0.dist-info → imap_processing-0.13.0.dist-info}/LICENSE +0 -0
  414. {imap_processing-0.11.0.dist-info → imap_processing-0.13.0.dist-info}/WHEEL +0 -0
  415. {imap_processing-0.11.0.dist-info → imap_processing-0.13.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,548 @@
1
+ """Functions to support I-ALiRT SWE processing."""
2
+
3
+ import logging
4
+
5
+ import numpy as np
6
+ import pandas as pd
7
+ import xarray as xr
8
+ from numpy.typing import NDArray
9
+
10
+ from imap_processing.ialirt.utils.grouping import find_groups
11
+ from imap_processing.swe.l1a.swe_science import decompressed_counts
12
+ from imap_processing.swe.l1b.swe_l1b import (
13
+ deadtime_correction,
14
+ read_in_flight_cal_data,
15
+ )
16
+ from imap_processing.swe.utils.swe_constants import (
17
+ ESA_VOLTAGE_ROW_INDEX_DICT,
18
+ GEOMETRIC_FACTORS,
19
+ N_CEMS,
20
+ )
21
+ from imap_processing.swe.utils.swe_utils import combine_acquisition_time
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+ # Energy bin lookup table (indexed by quarter cycle)
26
+ ENERGY_BINS = np.array(
27
+ [
28
+ [1, 5, 7, 3], # 0 to 14 (Q1)
29
+ [2, 6, 4, 0], # 15 to 29 (Q2)
30
+ [3, 7, 5, 1], # 30 to 44 (Q3)
31
+ [0, 4, 6, 2], # 45 to 59 (Q4)
32
+ ]
33
+ )
34
+
35
+
36
+ def decompress_counts(raw_counts: NDArray) -> NDArray:
37
+ """
38
+ Perform decompression of raw counts using a predefined decompression table.
39
+
40
+ Parameters
41
+ ----------
42
+ raw_counts : np.ndarray
43
+ Array of raw compressed counts with shape (n_energy, n_cem, n_phi).
44
+
45
+ Returns
46
+ -------
47
+ counts : np.ndarray
48
+ Array of decompressed counts with the same shape as raw_counts.
49
+
50
+ Notes
51
+ -----
52
+ CEM is channel electron multiplier.
53
+ """
54
+ decompression_table = np.array([decompressed_counts(i) for i in range(256)])
55
+
56
+ # Decompress using the precomputed table
57
+ counts = decompression_table[raw_counts]
58
+
59
+ return counts
60
+
61
+
62
+ def phi_to_bin(phi_values: NDArray) -> NDArray:
63
+ """
64
+ Convert phi values to corresponding bin indices.
65
+
66
+ Parameters
67
+ ----------
68
+ phi_values : NDArray
69
+ Array of phi values.
70
+
71
+ Returns
72
+ -------
73
+ bin_indices : NDArray
74
+ Array of bin indices.
75
+ """
76
+ # Ensure it wraps correctly within 0-29 bins
77
+ return ((phi_values - 12) // 12) % 30
78
+
79
+
80
+ def prepare_raw_counts(grouped: xr.Dataset, cem_number: int = N_CEMS) -> NDArray:
81
+ """
82
+ Reformat raw counts into a 3D array binned by phi.
83
+
84
+ Parameters
85
+ ----------
86
+ grouped : xr.Dataset
87
+ Dataset containing grouped i-ALiRT packet data for 30 seconds.
88
+ cem_number : int
89
+ Number of CEMs (default 7).
90
+
91
+ Returns
92
+ -------
93
+ raw_counts : NDArray
94
+ Raw counts with shape (8, 7, 30).
95
+
96
+ Notes
97
+ -----
98
+ Array of raw counts with shape (n_energy, n_cem, n_phi), where:
99
+ - 8 corresponds to the 8 energy steps.
100
+ - 7 corresponds to the 7 CEM detectors.
101
+ - 30 corresponds to the 30 phi bins.
102
+ """
103
+ raw_counts = np.zeros((8, cem_number, 30), dtype=np.uint8)
104
+
105
+ # Compute phi values and their corresponding bins
106
+ # Example: energy steps 0-1 have the same phi;
107
+ # energy steps 2-3 have the same phi, etc.
108
+ # A depiction of this is shown in Figure 7 of the SWE Algorithm Document.
109
+ phi_values = np.array(
110
+ [
111
+ (12 + 24 * grouped["swe_seq"].values) % 360, # Energy steps 0 and 1
112
+ (24 + 24 * grouped["swe_seq"].values) % 360, # Energy steps 2 and 3
113
+ ]
114
+ )
115
+ phi_bins = phi_to_bin(phi_values).astype(int) # Get phi bin indices
116
+
117
+ # The first 15 seconds is the first quarter cycle, etc.
118
+ quarter_cycles = np.floor(grouped["swe_seq"] / 15).astype(int)
119
+ e_bins = ENERGY_BINS[quarter_cycles]
120
+
121
+ # Populate raw_counts
122
+ for cem in range(1, cem_number + 1): # 7 CEMs
123
+ e1 = grouped[f"swe_cem{cem}_e1"].values
124
+ e2 = grouped[f"swe_cem{cem}_e2"].values
125
+ e3 = grouped[f"swe_cem{cem}_e3"].values
126
+ e4 = grouped[f"swe_cem{cem}_e4"].values
127
+
128
+ # Phi bins 0, 2...(12, 36, ...)
129
+ raw_counts[e_bins[:, 0], cem - 1, phi_bins[0]] = e1
130
+ raw_counts[e_bins[:, 1], cem - 1, phi_bins[0]] = e2
131
+ # Phi bins 1, 3...(24, 48, ...)
132
+ raw_counts[e_bins[:, 2], cem - 1, phi_bins[1]] = e3
133
+ raw_counts[e_bins[:, 3], cem - 1, phi_bins[1]] = e4
134
+
135
+ return raw_counts
136
+
137
+
138
+ def get_ialirt_energies() -> list:
139
+ """
140
+ Get the ESA voltages for I-ALiRT.
141
+
142
+ Returns
143
+ -------
144
+ energy : list
145
+ List of ESA voltage for I-ALiRT.
146
+
147
+ Notes
148
+ -----
149
+ This is a subset of the ESA_VOLTAGE_ROW_INDEX_DICT.
150
+ """
151
+ energy = [k for k, v in ESA_VOLTAGE_ROW_INDEX_DICT.items() if 11 <= v <= 18]
152
+
153
+ return energy
154
+
155
+
156
+ def normalize_counts(counts: NDArray, latest_cal: pd.Series) -> NDArray:
157
+ """
158
+ Normalize the counts using the latest calibration factor.
159
+
160
+ Parameters
161
+ ----------
162
+ counts : np.ndarray
163
+ Array of counts.
164
+ latest_cal : pd.Series
165
+ Array of latest calibration factors.
166
+
167
+ Returns
168
+ -------
169
+ norm_counts : np.ndarray
170
+ Array of normalized counts.
171
+ """
172
+ latest_cal = latest_cal.to_numpy()
173
+
174
+ # Norm counts where counts are non-negative
175
+ norm_counts = counts * (latest_cal / GEOMETRIC_FACTORS)[:, np.newaxis]
176
+ norm_counts[norm_counts < 0] = 0
177
+
178
+ return norm_counts
179
+
180
+
181
+ def find_bin_offsets(
182
+ peak_bins: NDArray, offsets: tuple[int, int]
183
+ ) -> tuple[NDArray, NDArray]:
184
+ """
185
+ Find the bins with offsets from the peak bins.
186
+
187
+ Parameters
188
+ ----------
189
+ peak_bins : np.ndarray
190
+ Bins that correspond to the maximum counts at each energy.
191
+ offsets : tuple[int, int]
192
+ Offset values for the bins.
193
+
194
+ Returns
195
+ -------
196
+ bin_0 : np.ndarray
197
+ First bin used for the average.
198
+ bin_1 : np.ndarray
199
+ Second bin used for the average.
200
+ """
201
+ # Azimuth has 30 values.
202
+ # Therefore, anything greater than 30 should be wrapped around.
203
+ bin_0, bin_1 = (peak_bins + offsets[0]) % 30, (peak_bins + offsets[1]) % 30
204
+
205
+ return bin_0, bin_1
206
+
207
+
208
+ def average_counts(
209
+ peak_bins: NDArray, summed_half_cycle: NDArray, offsets: tuple[int, int]
210
+ ) -> NDArray:
211
+ """
212
+ Get the counts value for the offset bins at each energy level and average them.
213
+
214
+ Parameters
215
+ ----------
216
+ peak_bins : np.ndarray
217
+ Bins that corresponds to the maximum counts at each energy.
218
+ summed_half_cycle : np.ndarray
219
+ Counts summed over the 7 CEM detectors.
220
+ offsets : tuple
221
+ Offset values for the bins.
222
+ Offsets +6 and +8 correspond to +90 degrees.
223
+ Offsets +14 and +16 correspond to 180 degrees.
224
+ Offsets -6 and -8 correspond to -90 degrees.
225
+
226
+ Returns
227
+ -------
228
+ avg_counts : np.ndarray
229
+ Average counts of offset bin.
230
+ """
231
+ # Find the bins with offsets from the peak bins.
232
+ bin_0, bin_1 = find_bin_offsets(peak_bins, offsets)
233
+
234
+ # Get the counts value for the offset bins at each energy level and average them.
235
+ row_idx = np.arange(len(peak_bins))
236
+ avg_counts = (
237
+ summed_half_cycle[row_idx, bin_0] + summed_half_cycle[row_idx, bin_1]
238
+ ) / 2
239
+
240
+ return avg_counts
241
+
242
+
243
+ def find_min_counts(
244
+ summed_half_cycle: NDArray,
245
+ ) -> tuple[NDArray, NDArray, tuple[NDArray, NDArray, NDArray]]:
246
+ """
247
+ Find min counts (cmin), defined as the minimum of counts_180 and counts_90.
248
+
249
+ Parameters
250
+ ----------
251
+ summed_half_cycle : np.ndarray
252
+ Counts summed over the 7 CEM detectors.
253
+
254
+ Returns
255
+ -------
256
+ cpeak : np.ndarray
257
+ Maximum counts for each energy level.
258
+ cmin : np.ndarray
259
+ Minimum of counts_neg_90, counts_90, counts_180.
260
+ counts : tuple[np.ndarray, np.ndarray, np.ndarray]
261
+ Counts at +/- 90 and 180 degrees from peak.
262
+ """
263
+ # Find the maximum counts for each energy level
264
+ cpeak = np.max(summed_half_cycle, axis=1)
265
+
266
+ # Find the bin that corresponds to the maximum counts at each energy
267
+ peak_bin = np.argmax(summed_half_cycle, axis=1)
268
+
269
+ # Find the counts in each offset bins.
270
+ # Offsets +6 and +8 correspond to +90 degrees.
271
+ counts_90 = average_counts(peak_bin, summed_half_cycle, (6, 8))
272
+
273
+ # Find the counts in each offset bins.
274
+ # Offsets +14 and +16 correspond to 180 degrees.
275
+ counts_180 = average_counts(peak_bin, summed_half_cycle, (14, 16))
276
+
277
+ # Find the counts in each offset bins.
278
+ # Offsets -6 and -8 correspond to -90 degrees.
279
+ counts_neg_90 = average_counts(peak_bin, summed_half_cycle, (-6, -8))
280
+
281
+ counts_stacked = np.hstack(
282
+ [
283
+ counts_90[:, np.newaxis],
284
+ counts_180[:, np.newaxis],
285
+ counts_neg_90[:, np.newaxis],
286
+ ]
287
+ )
288
+
289
+ # Find the minimum value for each energy level
290
+ cmin = np.min(counts_stacked, axis=1)
291
+
292
+ return cpeak, cmin, (counts_neg_90, counts_90, counts_180)
293
+
294
+
295
+ def determine_streaming(
296
+ numerator_1: NDArray,
297
+ numerator_2: NDArray,
298
+ denominator: NDArray,
299
+ threshold: float = 1.75,
300
+ ) -> NDArray:
301
+ """
302
+ Determine if any energy level satisfies the bidirectional streaming condition.
303
+
304
+ Parameters
305
+ ----------
306
+ numerator_1 : np.ndarray
307
+ For the first streaming check : cpeak.
308
+ For the second streaming check : ccem_1.
309
+ numerator_2 : np.ndarray
310
+ For the first streaming check : counts_180.
311
+ For the second streaming check : ccem_7.
312
+ denominator : np.ndarray
313
+ For both streaming checks: cmin.
314
+ threshold : float (optional)
315
+ Threshold value for the streaming condition.
316
+
317
+ Returns
318
+ -------
319
+ streaming_flag : np.ndarray
320
+ Array of 1s and 0s indicating if the condition is satisfied.
321
+ """
322
+ ratio_1 = numerator_1 / denominator
323
+ ratio_2 = numerator_2 / denominator
324
+
325
+ return ((ratio_1 > threshold) & (ratio_2 > threshold)).astype(int)
326
+
327
+
328
+ def compute_bidirectional(
329
+ streaming_first_half: NDArray,
330
+ streaming_second_half: NDArray,
331
+ min_esa_steps: int = 3,
332
+ ) -> tuple[int, int]:
333
+ """
334
+ Compute the Bidirectional Electron parameter (BDE).
335
+
336
+ Parameters
337
+ ----------
338
+ streaming_first_half : np.ndarray
339
+ Array of 1s and 0s indicating bidirectional streaming for first half-cycle.
340
+ streaming_second_half : np.ndarray
341
+ Array of 1s and 0s indicating bidirectional streaming for second half-cycle.
342
+ min_esa_steps : int (optional)
343
+ Minimum number of ESA steps for bidirectional streaming.
344
+ If either of the half cycles has bidirectional streaming for
345
+ 3/8 energies then bde = 1.
346
+
347
+ Returns
348
+ -------
349
+ bde : tuple
350
+ Indicator for counter-streaming.
351
+ """
352
+ count_first: int = int(np.sum(streaming_first_half))
353
+ count_second: int = int(np.sum(streaming_second_half))
354
+
355
+ return int(count_first >= min_esa_steps), int(count_second >= min_esa_steps)
356
+
357
+
358
+ def azimuthal_check_counterstreaming(
359
+ summed_first_half: NDArray, summed_second_half: NDArray
360
+ ) -> tuple[int, int]:
361
+ """
362
+ Check if counterstreaming is observed in azimuthal angle direction.
363
+
364
+ Parameters
365
+ ----------
366
+ summed_first_half : np.ndarray
367
+ Counts summed over the 7 CEM detectors for first half-cycle.
368
+ summed_second_half : np.ndarray
369
+ Counts summed over the 7 CEM detectors for second half-cycle.
370
+
371
+ Returns
372
+ -------
373
+ bde : tuple
374
+ Indicator for counter-streaming.
375
+ """
376
+ # Find peaks, cmin, counts (-90, 90, 180)
377
+ cpeak_first_half, cmin_first_half, counts_first_half = find_min_counts(
378
+ summed_first_half
379
+ )
380
+ cpeak_second_half, cmin_second_half, counts_second_half = find_min_counts(
381
+ summed_second_half
382
+ )
383
+
384
+ # First search for counter-streaming
385
+ streaming_first_half = determine_streaming(
386
+ cpeak_first_half, counts_first_half[2], cmin_first_half
387
+ )
388
+ streaming_second_half = determine_streaming(
389
+ cpeak_second_half, counts_second_half[2], cmin_second_half
390
+ )
391
+
392
+ # If either of the half cycles has bidirectional streaming
393
+ # for 3/8 energies then bde = 1
394
+ bde_first_search = compute_bidirectional(
395
+ streaming_first_half, streaming_second_half
396
+ )
397
+
398
+ return bde_first_search
399
+
400
+
401
+ def polar_check_counterstreaming(
402
+ summed_first_half: NDArray, summed_second_half: NDArray
403
+ ) -> tuple[int, int]:
404
+ """
405
+ Check if counterstreaming is observed in the polar angle direction.
406
+
407
+ Parameters
408
+ ----------
409
+ summed_first_half : np.ndarray
410
+ Counts summed over the azimuth for first half-cycle.
411
+ summed_second_half : np.ndarray
412
+ Counts summed over the azimuth for second half-cycle.
413
+
414
+ Returns
415
+ -------
416
+ bde : tuple
417
+ Indicator for counter-streaming.
418
+ """
419
+ # Cmin is the average of the counts in CEMs 3, 4, and 5
420
+ cmin_first_half = summed_first_half[:, 2:5].mean(axis=1)
421
+ cmin_second_half = summed_second_half[:, 2:5].mean(axis=1)
422
+
423
+ # Determine if streaming is observed.
424
+ # Note: Bidirectional electron streaming at a given ESA step
425
+ # is identified if both c_cem1/cmin and c_cem7/cmin are > 1.75.
426
+ streaming_first_half = determine_streaming(
427
+ summed_first_half[:, 0], summed_first_half[:, 6], cmin_first_half
428
+ )
429
+ streaming_second_half = determine_streaming(
430
+ summed_second_half[:, 0], summed_second_half[:, 6], cmin_second_half
431
+ )
432
+
433
+ # If either of the half cycles has bidirectional streaming
434
+ # for 3/8 energies then bde = 1
435
+ bde_second_search = compute_bidirectional(
436
+ streaming_first_half, streaming_second_half
437
+ )
438
+
439
+ return bde_second_search
440
+
441
+
442
+ def process_swe(accumulated_data: xr.Dataset, in_flight_cal_files: list) -> list[dict]:
443
+ """
444
+ Create L1 data dictionary.
445
+
446
+ Parameters
447
+ ----------
448
+ accumulated_data : xr.Dataset
449
+ Packets dataset accumulated over 1 min.
450
+ in_flight_cal_files : list
451
+ List of path to the in-flight calibration files.
452
+
453
+ Returns
454
+ -------
455
+ swe_data : list[dict]
456
+ Dictionaries of the parsed data product.
457
+ """
458
+ logger.info("Processing SWE.")
459
+
460
+ # Calculate time in seconds
461
+ time_seconds = combine_acquisition_time(
462
+ accumulated_data["swe_acq_sec"], accumulated_data["swe_acq_sub"]
463
+ )
464
+ accumulated_data["time_seconds"] = time_seconds
465
+
466
+ # Get total full cycle data available for processing.
467
+ # There are 60 packets in a set so (0, 59) is the range.
468
+ grouped_data = find_groups(accumulated_data, (0, 59), "swe_seq", "time_seconds")
469
+ unique_groups = np.unique(grouped_data["group"])
470
+ swe_data: list[dict] = []
471
+
472
+ for group in unique_groups:
473
+ # Sequence values for the group should be 0-59 with no duplicates.
474
+ seq_values = grouped_data["swe_seq"][(grouped_data["group"] == group).values]
475
+
476
+ # Ensure no duplicates and all values from 0 to 59 are present
477
+ if not np.array_equal(seq_values, np.arange(60)):
478
+ logger.info(
479
+ f"Group {group} does not contain all values from 0 to "
480
+ f"59 without duplicates."
481
+ )
482
+ continue
483
+ # Prepare raw counts array just for this group
484
+ # (8 energy steps, 7 CEMs, 30 phi bins)
485
+ group_mask = grouped_data["group"] == group
486
+ grouped = grouped_data.sel(epoch=group_mask)
487
+
488
+ # Split into Q1 & Q2 (swe_seq 0-29) and Q3 & Q4 (swe_seq 30-59)
489
+ first_half = grouped.where(grouped["swe_seq"] < 30, drop=True)
490
+ second_half = grouped.where(grouped["swe_seq"] >= 30, drop=True)
491
+
492
+ # Prepare raw counts separately for both halves
493
+ raw_counts_first_half = prepare_raw_counts(first_half)
494
+ raw_counts_second_half = prepare_raw_counts(second_half)
495
+
496
+ # Decompress the raw counts
497
+ counts_first_half = decompress_counts(raw_counts_first_half)
498
+ counts_second_half = decompress_counts(raw_counts_second_half)
499
+
500
+ # Apply the deadtime correction
501
+ # acq_duration = 80 milliseconds
502
+ corrected_first_half = deadtime_correction(counts_first_half, 80 * 10**3)
503
+ corrected_second_half = deadtime_correction(counts_second_half, 80 * 10**3)
504
+
505
+ # Grab the latest calibration factor
506
+ in_flight_cal_df = read_in_flight_cal_data(in_flight_cal_files)
507
+ latest_cal = in_flight_cal_df.sort_values("met_time").iloc[-1][1::]
508
+
509
+ normalized_first_half = normalize_counts(corrected_first_half, latest_cal)
510
+ normalized_second_half = normalize_counts(corrected_second_half, latest_cal)
511
+
512
+ # Sum over the 7 detectors
513
+ summed_first_half_cem = np.sum(normalized_first_half, axis=1)
514
+ summed_second_half_cem = np.sum(normalized_second_half, axis=1)
515
+ bde_first_search = azimuthal_check_counterstreaming(
516
+ summed_first_half_cem, summed_second_half_cem
517
+ )
518
+ # Sum over azimuth.
519
+ summed_first_half_az = np.sum(normalized_first_half, axis=2)
520
+ summed_second_half_az = np.sum(normalized_second_half, axis=2)
521
+ bde_second_search = polar_check_counterstreaming(
522
+ summed_first_half_az, summed_second_half_az
523
+ )
524
+
525
+ # BDE value
526
+ bde_first_half = max(bde_first_search[0], bde_second_search[0])
527
+ bde_second_half = max(bde_first_search[1], bde_second_search[1])
528
+
529
+ # For normalized counts each ESA step is summed
530
+ # over both azimuthal and polar angles
531
+ # Sum over CEMs (axis=1) and azimuths (axis=2)
532
+ summed_first = normalized_first_half.sum(axis=(1, 2))
533
+ summed_second = normalized_second_half.sum(axis=(1, 2))
534
+ times = np.unique(grouped["time_seconds"].values)
535
+
536
+ swe_data.append(
537
+ {
538
+ # Select times corresponding to energy level.
539
+ "met_first_half_cycle": times[[1, 0] * 4],
540
+ "met_second_half_cycle": times[[3, 2] * 4],
541
+ "normalized_counts_first_half_cycle": summed_first,
542
+ "normalized_counts_second_half_cycle": summed_second,
543
+ "bde_first_half_cycle": np.full(summed_first.shape, bde_first_half),
544
+ "bde_second_half_cycle": np.full(summed_second.shape, bde_second_half),
545
+ }
546
+ )
547
+
548
+ return swe_data