imap-processing 0.12.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 (272) hide show
  1. imap_processing/__init__.py +1 -0
  2. imap_processing/_version.py +2 -2
  3. imap_processing/ccsds/ccsds_data.py +1 -2
  4. imap_processing/ccsds/excel_to_xtce.py +1 -2
  5. imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +18 -12
  6. imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +569 -0
  7. imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +1846 -128
  8. imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +5 -5
  9. imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +20 -1
  10. imap_processing/cdf/config/imap_idex_l1a_variable_attrs.yaml +6 -4
  11. imap_processing/cdf/config/imap_idex_l1b_variable_attrs.yaml +3 -3
  12. imap_processing/cdf/config/imap_mag_global_cdf_attrs.yaml +15 -0
  13. imap_processing/cdf/config/imap_swapi_variable_attrs.yaml +22 -0
  14. imap_processing/cdf/config/imap_swe_l1b_variable_attrs.yaml +16 -0
  15. imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +178 -5
  16. imap_processing/cdf/config/imap_ultra_l1a_variable_attrs.yaml +5045 -41
  17. imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +33 -19
  18. imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +8 -48
  19. imap_processing/cdf/utils.py +41 -33
  20. imap_processing/cli.py +463 -234
  21. imap_processing/codice/codice_l1a.py +260 -47
  22. imap_processing/codice/codice_l1b.py +51 -152
  23. imap_processing/codice/constants.py +38 -1
  24. imap_processing/ena_maps/ena_maps.py +658 -65
  25. imap_processing/ena_maps/utils/coordinates.py +1 -1
  26. imap_processing/ena_maps/utils/spatial_utils.py +10 -5
  27. imap_processing/glows/l1a/glows_l1a.py +28 -99
  28. imap_processing/glows/l1a/glows_l1a_data.py +2 -2
  29. imap_processing/glows/l1b/glows_l1b.py +1 -4
  30. imap_processing/glows/l1b/glows_l1b_data.py +1 -3
  31. imap_processing/glows/l2/glows_l2.py +2 -5
  32. imap_processing/hi/l1a/hi_l1a.py +31 -12
  33. imap_processing/hi/l1b/hi_l1b.py +80 -43
  34. imap_processing/hi/l1c/hi_l1c.py +12 -16
  35. imap_processing/hit/ancillary/imap_hit_l1b-to-l2-sector-dt0-factors_20250219_v002.csv +81 -0
  36. imap_processing/hit/hit_utils.py +93 -35
  37. imap_processing/hit/l0/decom_hit.py +3 -1
  38. imap_processing/hit/l1a/hit_l1a.py +30 -25
  39. imap_processing/hit/l1b/constants.py +6 -2
  40. imap_processing/hit/l1b/hit_l1b.py +279 -318
  41. imap_processing/hit/l2/constants.py +37 -0
  42. imap_processing/hit/l2/hit_l2.py +373 -264
  43. imap_processing/ialirt/l0/parse_mag.py +138 -10
  44. imap_processing/ialirt/l0/process_swapi.py +69 -0
  45. imap_processing/ialirt/l0/process_swe.py +318 -22
  46. imap_processing/ialirt/packet_definitions/ialirt.xml +216 -212
  47. imap_processing/ialirt/packet_definitions/ialirt_codicehi.xml +1 -1
  48. imap_processing/ialirt/packet_definitions/ialirt_codicelo.xml +1 -1
  49. imap_processing/ialirt/packet_definitions/ialirt_swapi.xml +14 -14
  50. imap_processing/ialirt/utils/grouping.py +1 -1
  51. imap_processing/idex/idex_constants.py +9 -1
  52. imap_processing/idex/idex_l0.py +22 -8
  53. imap_processing/idex/idex_l1a.py +75 -44
  54. imap_processing/idex/idex_l1b.py +9 -8
  55. imap_processing/idex/idex_l2a.py +79 -45
  56. imap_processing/idex/idex_l2b.py +120 -0
  57. imap_processing/idex/idex_variable_unpacking_and_eu_conversion.csv +33 -39
  58. imap_processing/idex/packet_definitions/idex_housekeeping_packet_definition.xml +9130 -0
  59. imap_processing/lo/l0/lo_science.py +1 -2
  60. imap_processing/lo/l1a/lo_l1a.py +1 -4
  61. imap_processing/lo/l1b/lo_l1b.py +527 -6
  62. imap_processing/lo/l1b/tof_conversions.py +11 -0
  63. imap_processing/lo/l1c/lo_l1c.py +1 -4
  64. imap_processing/mag/constants.py +43 -0
  65. imap_processing/mag/imap_mag_sdc_configuration_v001.py +8 -0
  66. imap_processing/mag/l1a/mag_l1a.py +2 -9
  67. imap_processing/mag/l1a/mag_l1a_data.py +10 -10
  68. imap_processing/mag/l1b/mag_l1b.py +84 -17
  69. imap_processing/mag/l1c/interpolation_methods.py +180 -3
  70. imap_processing/mag/l1c/mag_l1c.py +236 -70
  71. imap_processing/mag/l2/mag_l2.py +140 -0
  72. imap_processing/mag/l2/mag_l2_data.py +288 -0
  73. imap_processing/spacecraft/quaternions.py +1 -3
  74. imap_processing/spice/geometry.py +3 -3
  75. imap_processing/spice/kernels.py +0 -276
  76. imap_processing/spice/pointing_frame.py +257 -0
  77. imap_processing/spice/repoint.py +48 -19
  78. imap_processing/spice/spin.py +38 -33
  79. imap_processing/spice/time.py +24 -0
  80. imap_processing/swapi/l1/swapi_l1.py +16 -12
  81. imap_processing/swapi/l2/swapi_l2.py +116 -4
  82. imap_processing/swapi/swapi_utils.py +32 -0
  83. imap_processing/swe/l1a/swe_l1a.py +2 -9
  84. imap_processing/swe/l1a/swe_science.py +8 -11
  85. imap_processing/swe/l1b/swe_l1b.py +898 -23
  86. imap_processing/swe/l2/swe_l2.py +21 -77
  87. imap_processing/swe/utils/swe_constants.py +1 -0
  88. imap_processing/tests/ccsds/test_excel_to_xtce.py +1 -1
  89. imap_processing/tests/cdf/test_utils.py +14 -16
  90. imap_processing/tests/codice/conftest.py +44 -33
  91. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-pha_20241110193700_v0.0.0.cdf +0 -0
  92. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-pha_20241110193700_v0.0.0.cdf +0 -0
  93. imap_processing/tests/codice/test_codice_l1a.py +20 -11
  94. imap_processing/tests/codice/test_codice_l1b.py +6 -7
  95. imap_processing/tests/conftest.py +78 -22
  96. imap_processing/tests/ena_maps/test_ena_maps.py +462 -33
  97. imap_processing/tests/ena_maps/test_spatial_utils.py +1 -1
  98. imap_processing/tests/glows/conftest.py +10 -14
  99. imap_processing/tests/glows/test_glows_decom.py +4 -4
  100. imap_processing/tests/glows/test_glows_l1a_cdf.py +6 -27
  101. imap_processing/tests/glows/test_glows_l1a_data.py +6 -8
  102. imap_processing/tests/glows/test_glows_l1b.py +11 -11
  103. imap_processing/tests/glows/test_glows_l1b_data.py +5 -5
  104. imap_processing/tests/glows/test_glows_l2.py +2 -8
  105. imap_processing/tests/hi/conftest.py +1 -1
  106. imap_processing/tests/hi/test_hi_l1b.py +10 -12
  107. imap_processing/tests/hi/test_hi_l1c.py +27 -24
  108. imap_processing/tests/hi/test_l1a.py +7 -9
  109. imap_processing/tests/hi/test_science_direct_event.py +2 -2
  110. imap_processing/tests/hit/helpers/l1_validation.py +44 -43
  111. imap_processing/tests/hit/test_decom_hit.py +1 -1
  112. imap_processing/tests/hit/test_hit_l1a.py +9 -9
  113. imap_processing/tests/hit/test_hit_l1b.py +172 -217
  114. imap_processing/tests/hit/test_hit_l2.py +380 -118
  115. imap_processing/tests/hit/test_hit_utils.py +122 -55
  116. imap_processing/tests/hit/validation_data/hit_l1b_standard_sample2_nsrl_v4_3decimals.csv +62 -62
  117. imap_processing/tests/hit/validation_data/sci_sample_raw.csv +1 -1
  118. imap_processing/tests/ialirt/unit/test_decom_ialirt.py +16 -81
  119. imap_processing/tests/ialirt/unit/test_grouping.py +2 -2
  120. imap_processing/tests/ialirt/unit/test_parse_mag.py +71 -16
  121. imap_processing/tests/ialirt/unit/test_process_codicehi.py +3 -3
  122. imap_processing/tests/ialirt/unit/test_process_codicelo.py +3 -10
  123. imap_processing/tests/ialirt/unit/test_process_ephemeris.py +4 -4
  124. imap_processing/tests/ialirt/unit/test_process_hit.py +3 -3
  125. imap_processing/tests/ialirt/unit/test_process_swapi.py +24 -16
  126. imap_processing/tests/ialirt/unit/test_process_swe.py +115 -7
  127. imap_processing/tests/idex/conftest.py +72 -7
  128. imap_processing/tests/idex/test_data/imap_idex_l0_raw_20241206_v001.pkts +0 -0
  129. imap_processing/tests/idex/test_data/imap_idex_l0_raw_20250108_v001.pkts +0 -0
  130. imap_processing/tests/idex/test_idex_l0.py +33 -11
  131. imap_processing/tests/idex/test_idex_l1a.py +50 -23
  132. imap_processing/tests/idex/test_idex_l1b.py +104 -25
  133. imap_processing/tests/idex/test_idex_l2a.py +48 -32
  134. imap_processing/tests/idex/test_idex_l2b.py +93 -0
  135. imap_processing/tests/lo/test_lo_l1a.py +3 -3
  136. imap_processing/tests/lo/test_lo_l1b.py +371 -6
  137. imap_processing/tests/lo/test_lo_l1c.py +1 -1
  138. imap_processing/tests/lo/test_lo_science.py +6 -7
  139. imap_processing/tests/lo/test_star_sensor.py +1 -1
  140. imap_processing/tests/mag/conftest.py +58 -9
  141. imap_processing/tests/mag/test_mag_decom.py +4 -3
  142. imap_processing/tests/mag/test_mag_l1a.py +13 -7
  143. imap_processing/tests/mag/test_mag_l1b.py +9 -9
  144. imap_processing/tests/mag/test_mag_l1c.py +151 -47
  145. imap_processing/tests/mag/test_mag_l2.py +130 -0
  146. imap_processing/tests/mag/test_mag_validation.py +144 -7
  147. imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-magi-normal-in.csv +1217 -0
  148. imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-magi-normal-out.csv +1857 -0
  149. imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-mago-normal-in.csv +1217 -0
  150. imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-mago-normal-out.csv +1857 -0
  151. imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-magi-normal-in.csv +1217 -0
  152. imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-magi-normal-out.csv +1793 -0
  153. imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-mago-normal-in.csv +1217 -0
  154. imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-mago-normal-out.csv +1793 -0
  155. imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-magi-burst-in.csv +2561 -0
  156. imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-magi-normal-in.csv +961 -0
  157. imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-magi-normal-out.csv +1539 -0
  158. imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-mago-normal-in.csv +1921 -0
  159. imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-mago-normal-out.csv +2499 -0
  160. imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-magi-normal-in.csv +865 -0
  161. imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-magi-normal-out.csv +1196 -0
  162. imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-mago-normal-in.csv +1729 -0
  163. imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-mago-normal-out.csv +3053 -0
  164. imap_processing/tests/mag/validation/L2/imap_mag_l1b_norm-mago_20251017_v002.cdf +0 -0
  165. imap_processing/tests/mag/validation/calibration/imap_mag_l2-calibration-matrices_20251017_v004.cdf +0 -0
  166. imap_processing/tests/mag/validation/calibration/imap_mag_l2-offsets-norm_20251017_20251017_v001.cdf +0 -0
  167. imap_processing/tests/spacecraft/test_quaternions.py +1 -1
  168. imap_processing/tests/spice/test_data/fake_repoint_data.csv +4 -4
  169. imap_processing/tests/spice/test_data/fake_spin_data.csv +11 -11
  170. imap_processing/tests/spice/test_geometry.py +3 -3
  171. imap_processing/tests/spice/test_kernels.py +1 -200
  172. imap_processing/tests/spice/test_pointing_frame.py +185 -0
  173. imap_processing/tests/spice/test_repoint.py +20 -10
  174. imap_processing/tests/spice/test_spin.py +50 -9
  175. imap_processing/tests/spice/test_time.py +14 -0
  176. imap_processing/tests/swapi/lut/imap_swapi_esa-unit-conversion_20250211_v000.csv +73 -0
  177. imap_processing/tests/swapi/lut/imap_swapi_lut-notes_20250211_v000.csv +1025 -0
  178. imap_processing/tests/swapi/test_swapi_l1.py +7 -9
  179. imap_processing/tests/swapi/test_swapi_l2.py +180 -8
  180. imap_processing/tests/swe/lut/checker-board-indices.csv +24 -0
  181. imap_processing/tests/swe/lut/imap_swe_esa-lut_20250301_v000.csv +385 -0
  182. imap_processing/tests/swe/lut/imap_swe_l1b-in-flight-cal_20240510_20260716_v000.csv +3 -0
  183. imap_processing/tests/swe/test_swe_l1a.py +6 -6
  184. imap_processing/tests/swe/test_swe_l1a_science.py +3 -3
  185. imap_processing/tests/swe/test_swe_l1b.py +162 -24
  186. imap_processing/tests/swe/test_swe_l2.py +82 -102
  187. imap_processing/tests/test_cli.py +171 -88
  188. imap_processing/tests/test_utils.py +2 -1
  189. imap_processing/tests/ultra/data/mock_data.py +49 -21
  190. imap_processing/tests/ultra/unit/conftest.py +53 -70
  191. imap_processing/tests/ultra/unit/test_badtimes.py +2 -4
  192. imap_processing/tests/ultra/unit/test_cullingmask.py +4 -6
  193. imap_processing/tests/ultra/unit/test_de.py +3 -10
  194. imap_processing/tests/ultra/unit/test_decom_apid_880.py +27 -76
  195. imap_processing/tests/ultra/unit/test_decom_apid_881.py +15 -16
  196. imap_processing/tests/ultra/unit/test_decom_apid_883.py +12 -10
  197. imap_processing/tests/ultra/unit/test_decom_apid_896.py +202 -55
  198. imap_processing/tests/ultra/unit/test_lookup_utils.py +23 -1
  199. imap_processing/tests/ultra/unit/test_spacecraft_pset.py +3 -4
  200. imap_processing/tests/ultra/unit/test_ultra_l1a.py +84 -307
  201. imap_processing/tests/ultra/unit/test_ultra_l1b.py +30 -12
  202. imap_processing/tests/ultra/unit/test_ultra_l1b_annotated.py +2 -2
  203. imap_processing/tests/ultra/unit/test_ultra_l1b_culling.py +4 -1
  204. imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +163 -29
  205. imap_processing/tests/ultra/unit/test_ultra_l1c.py +5 -5
  206. imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +32 -43
  207. imap_processing/tests/ultra/unit/test_ultra_l2.py +230 -0
  208. imap_processing/ultra/constants.py +1 -1
  209. imap_processing/ultra/l0/decom_tools.py +21 -34
  210. imap_processing/ultra/l0/decom_ultra.py +168 -204
  211. imap_processing/ultra/l0/ultra_utils.py +152 -136
  212. imap_processing/ultra/l1a/ultra_l1a.py +55 -243
  213. imap_processing/ultra/l1b/badtimes.py +1 -4
  214. imap_processing/ultra/l1b/cullingmask.py +2 -6
  215. imap_processing/ultra/l1b/de.py +62 -47
  216. imap_processing/ultra/l1b/extendedspin.py +8 -4
  217. imap_processing/ultra/l1b/lookup_utils.py +72 -9
  218. imap_processing/ultra/l1b/ultra_l1b.py +3 -8
  219. imap_processing/ultra/l1b/ultra_l1b_culling.py +4 -4
  220. imap_processing/ultra/l1b/ultra_l1b_extended.py +236 -78
  221. imap_processing/ultra/l1c/histogram.py +2 -6
  222. imap_processing/ultra/l1c/spacecraft_pset.py +2 -4
  223. imap_processing/ultra/l1c/ultra_l1c.py +1 -5
  224. imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +107 -60
  225. imap_processing/ultra/l2/ultra_l2.py +299 -0
  226. imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_LeftSlit.csv +526 -0
  227. imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_RightSlit.csv +526 -0
  228. imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_LeftSlit.csv +526 -0
  229. imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_RightSlit.csv +526 -0
  230. imap_processing/ultra/lookup_tables/FM45_Startup1_ULTRA_IMGPARAMS_20240719.csv +2 -2
  231. imap_processing/ultra/lookup_tables/FM90_Startup1_ULTRA_IMGPARAMS_20240719.csv +2 -0
  232. imap_processing/ultra/packet_definitions/README.md +38 -0
  233. imap_processing/ultra/packet_definitions/ULTRA_SCI_COMBINED.xml +15302 -482
  234. imap_processing/ultra/utils/ultra_l1_utils.py +13 -12
  235. imap_processing/utils.py +1 -1
  236. {imap_processing-0.12.0.dist-info → imap_processing-0.13.0.dist-info}/METADATA +3 -2
  237. {imap_processing-0.12.0.dist-info → imap_processing-0.13.0.dist-info}/RECORD +264 -225
  238. imap_processing/hi/l1b/hi_eng_unit_convert_table.csv +0 -154
  239. imap_processing/mag/imap_mag_sdc-configuration_v001.yaml +0 -6
  240. imap_processing/mag/l1b/__init__.py +0 -0
  241. imap_processing/swe/l1b/swe_esa_lookup_table.csv +0 -1441
  242. imap_processing/swe/l1b/swe_l1b_science.py +0 -699
  243. imap_processing/tests/swe/test_swe_l1b_science.py +0 -103
  244. imap_processing/ultra/lookup_tables/dps_sensitivity45.cdf +0 -0
  245. imap_processing/ultra/lookup_tables/ultra_90_dps_exposure_compressed.cdf +0 -0
  246. /imap_processing/idex/packet_definitions/{idex_packet_definition.xml → idex_science_packet_definition.xml} +0 -0
  247. /imap_processing/tests/ialirt/{test_data → data}/l0/20240827095047_SWE_IALIRT_packet.bin +0 -0
  248. /imap_processing/tests/ialirt/{test_data → data}/l0/461971383-404.bin +0 -0
  249. /imap_processing/tests/ialirt/{test_data → data}/l0/461971384-405.bin +0 -0
  250. /imap_processing/tests/ialirt/{test_data → data}/l0/461971385-406.bin +0 -0
  251. /imap_processing/tests/ialirt/{test_data → data}/l0/461971386-407.bin +0 -0
  252. /imap_processing/tests/ialirt/{test_data → data}/l0/461971387-408.bin +0 -0
  253. /imap_processing/tests/ialirt/{test_data → data}/l0/461971388-409.bin +0 -0
  254. /imap_processing/tests/ialirt/{test_data → data}/l0/461971389-410.bin +0 -0
  255. /imap_processing/tests/ialirt/{test_data → data}/l0/461971390-411.bin +0 -0
  256. /imap_processing/tests/ialirt/{test_data → data}/l0/461971391-412.bin +0 -0
  257. /imap_processing/tests/ialirt/{test_data → data}/l0/BinLog CCSDS_FRAG_TLM_20240826_152323Z_IALIRT_data_for_SDC.bin +0 -0
  258. /imap_processing/tests/ialirt/{test_data → data}/l0/IALiRT Raw Packet Telemetry.txt +0 -0
  259. /imap_processing/tests/ialirt/{test_data → data}/l0/apid01152.tlm +0 -0
  260. /imap_processing/tests/ialirt/{test_data → data}/l0/eu_SWP_IAL_20240826_152033.csv +0 -0
  261. /imap_processing/tests/ialirt/{test_data → data}/l0/hi_fsw_view_1_ccsds.bin +0 -0
  262. /imap_processing/tests/ialirt/{test_data → data}/l0/hit_ialirt_sample.ccsds +0 -0
  263. /imap_processing/tests/ialirt/{test_data → data}/l0/hit_ialirt_sample.csv +0 -0
  264. /imap_processing/tests/ialirt/{test_data → data}/l0/idle_export_eu.SWE_IALIRT_20240827_093852.csv +0 -0
  265. /imap_processing/tests/ialirt/{test_data → data}/l0/imap_codice_l1a_hi-ialirt_20240523200000_v0.0.0.cdf +0 -0
  266. /imap_processing/tests/ialirt/{test_data → data}/l0/imap_codice_l1a_lo-ialirt_20241110193700_v0.0.0.cdf +0 -0
  267. /imap_processing/tests/ialirt/{test_data → data}/l0/sample_decoded_i-alirt_data.csv +0 -0
  268. /imap_processing/tests/mag/validation/{imap_calibration_mag_20240229_v01.cdf → calibration/imap_mag_l1b-calibration_20240229_v001.cdf} +0 -0
  269. /imap_processing/{swe/l1b/engineering_unit_convert_table.csv → tests/swe/lut/imap_swe_eu-conversion_20240510_v000.csv} +0 -0
  270. {imap_processing-0.12.0.dist-info → imap_processing-0.13.0.dist-info}/LICENSE +0 -0
  271. {imap_processing-0.12.0.dist-info → imap_processing-0.13.0.dist-info}/WHEEL +0 -0
  272. {imap_processing-0.12.0.dist-info → imap_processing-0.13.0.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,7 @@
1
1
  """Functions to support I-ALiRT MAG packet parsing."""
2
2
 
3
3
  import logging
4
+ from typing import Union
4
5
 
5
6
  import numpy as np
6
7
  import xarray as xr
@@ -13,6 +14,13 @@ from imap_processing.ialirt.l0.mag_l0_ialirt_data import (
13
14
  )
14
15
  from imap_processing.ialirt.utils.grouping import find_groups
15
16
  from imap_processing.ialirt.utils.time import calculate_time
17
+ from imap_processing.mag.l1a.mag_l1a_data import TimeTuple
18
+ from imap_processing.mag.l1b.mag_l1b import (
19
+ calibrate_vector,
20
+ retrieve_matrix_from_l1b_calibration,
21
+ shift_time,
22
+ )
23
+ from imap_processing.spice.time import met_to_ttj2000ns
16
24
 
17
25
  logger = logging.getLogger(__name__)
18
26
 
@@ -132,7 +140,13 @@ def extract_magnetic_vectors(science_values: xr.DataArray) -> dict:
132
140
  return vectors
133
141
 
134
142
 
135
- def get_time(grouped_data: xr.Dataset, group: int, pkt_counter: xr.DataArray) -> dict:
143
+ def get_time(
144
+ grouped_data: xr.Dataset,
145
+ group: int,
146
+ pkt_counter: xr.DataArray,
147
+ time_shift_mago: xr.DataArray,
148
+ time_shift_magi: xr.DataArray,
149
+ ) -> dict:
136
150
  """
137
151
  Get the time for the grouped data.
138
152
 
@@ -144,12 +158,22 @@ def get_time(grouped_data: xr.Dataset, group: int, pkt_counter: xr.DataArray) ->
144
158
  Group number.
145
159
  pkt_counter : xr.DataArray
146
160
  Packet counter.
161
+ time_shift_mago : xr.DataArray
162
+ Time shift value mago.
163
+ time_shift_magi : xr.DataArray
164
+ Time shift value magi.
147
165
 
148
166
  Returns
149
167
  -------
150
168
  time_data : dict
151
169
  Coarse and fine time for Primary and Secondary Sensors.
170
+
171
+ Notes
172
+ -----
173
+ Packet id 0 is course and fine time for the primary sensor PRI.
174
+ Packet id 2 is the course time for the secondary sensor SEC.
152
175
  """
176
+ # Get the coarse and fine time for the primary and secondary sensors.
153
177
  pri_coarsetm = grouped_data["mag_acq_tm_coarse"][
154
178
  (grouped_data["group"] == group).values
155
179
  ][pkt_counter == 0]
@@ -166,17 +190,102 @@ def get_time(grouped_data: xr.Dataset, group: int, pkt_counter: xr.DataArray) ->
166
190
  (grouped_data["group"] == group).values
167
191
  ][pkt_counter == 2]
168
192
 
169
- time_data = {
170
- "pri_coarsetm": int(pri_coarsetm),
171
- "pri_fintm": int(pri_fintm),
172
- "sec_coarsetm": int(sec_coarsetm),
173
- "sec_fintm": int(sec_fintm),
193
+ time_data: dict[str, Union[int, float]] = {
194
+ "pri_coarsetm": int(pri_coarsetm.item()),
195
+ "pri_fintm": int(pri_fintm.item()),
196
+ "sec_coarsetm": int(sec_coarsetm.item()),
197
+ "sec_fintm": int(sec_fintm.item()),
174
198
  }
175
199
 
200
+ primary_time = TimeTuple(int(pri_coarsetm.item()), int(pri_fintm.item()))
201
+ secondary_time = TimeTuple(int(sec_coarsetm.item()), int(sec_fintm.item()))
202
+ time_data_pri_met = primary_time.to_seconds()
203
+ time_data_primary_ttj2000ns = met_to_ttj2000ns(time_data_pri_met)
204
+ time_data["primary_epoch"] = shift_time(
205
+ time_data_primary_ttj2000ns, time_shift_mago
206
+ )
207
+ time_data_sec_met = secondary_time.to_seconds()
208
+ time_data_secondary_ttj2000ns = met_to_ttj2000ns(time_data_sec_met)
209
+ time_data["secondary_epoch"] = shift_time(
210
+ time_data_secondary_ttj2000ns, time_shift_magi
211
+ )
212
+
176
213
  return time_data
177
214
 
178
215
 
179
- def parse_packet(accumulated_data: xr.Dataset) -> list[dict]:
216
+ def calculate_l1b(
217
+ grouped_data: xr.Dataset,
218
+ group: int,
219
+ pkt_counter: xr.DataArray,
220
+ science_data: dict,
221
+ status_data: dict,
222
+ calibration_dataset: xr.Dataset,
223
+ ) -> tuple[np.ndarray, np.ndarray, dict]:
224
+ """
225
+ Calculate equivalent of l1b data product.
226
+
227
+ Parameters
228
+ ----------
229
+ grouped_data : xr.Dataset
230
+ Grouped data.
231
+ group : int
232
+ Group number.
233
+ pkt_counter : xr.DataArray
234
+ Packet counter.
235
+ science_data : dict
236
+ Science data.
237
+ status_data : dict
238
+ Status data.
239
+ calibration_dataset : xr.Dataset
240
+ Calibration dataset.
241
+
242
+ Returns
243
+ -------
244
+ updated_vector_mago : numpy.ndarray
245
+ Calibrated mago vector.
246
+ updated_vector_magi : numpy.ndarray
247
+ Calibrated magi vector.
248
+ time_data : dict
249
+ Time data.
250
+ """
251
+ calibration_matrix_mago, time_shift_mago = retrieve_matrix_from_l1b_calibration(
252
+ calibration_dataset, is_mago=True
253
+ )
254
+ calibration_matrix_magi, time_shift_magi = retrieve_matrix_from_l1b_calibration(
255
+ calibration_dataset, is_mago=False
256
+ )
257
+
258
+ # Get time values for each group.
259
+ time_data = get_time(
260
+ grouped_data, group, pkt_counter, time_shift_mago, time_shift_magi
261
+ )
262
+
263
+ input_vector_mago = np.array(
264
+ [
265
+ science_data["pri_x"],
266
+ science_data["pri_y"],
267
+ science_data["pri_z"],
268
+ status_data["fob_range"],
269
+ ]
270
+ )
271
+ input_vector_magi = np.array(
272
+ [
273
+ science_data["sec_x"],
274
+ science_data["sec_y"],
275
+ science_data["sec_z"],
276
+ status_data["fib_range"],
277
+ ]
278
+ )
279
+
280
+ updated_vector_mago = calibrate_vector(input_vector_mago, calibration_matrix_mago)
281
+ updated_vector_magi = calibrate_vector(input_vector_magi, calibration_matrix_magi)
282
+
283
+ return updated_vector_mago, updated_vector_magi, time_data
284
+
285
+
286
+ def process_packet(
287
+ accumulated_data: xr.Dataset, calibration_dataset: xr.Dataset
288
+ ) -> list[dict]:
180
289
  """
181
290
  Parse the MAG packets.
182
291
 
@@ -184,6 +293,8 @@ def parse_packet(accumulated_data: xr.Dataset) -> list[dict]:
184
293
  ----------
185
294
  accumulated_data : xr.Dataset
186
295
  Packets dataset accumulated over 1 min.
296
+ calibration_dataset : xr.Dataset
297
+ Calibration dataset.
187
298
 
188
299
  Returns
189
300
  -------
@@ -237,9 +348,26 @@ def parse_packet(accumulated_data: xr.Dataset) -> list[dict]:
237
348
  (grouped_data["group"] == group).values
238
349
  ]
239
350
  science_data = extract_magnetic_vectors(science_values)
240
-
241
- # Get time values for each group.
242
- time_data = get_time(grouped_data, group, pkt_counter)
351
+ updated_vector_mago, updated_vector_magi, time_data = calculate_l1b(
352
+ grouped_data,
353
+ group,
354
+ pkt_counter,
355
+ science_data,
356
+ status_data,
357
+ calibration_dataset,
358
+ )
359
+
360
+ # Note: primary = MAGo, secondary = MAGi.
361
+ science_data.update(
362
+ {
363
+ "calibrated_pri_x": updated_vector_mago[0],
364
+ "calibrated_pri_y": updated_vector_mago[1],
365
+ "calibrated_pri_z": updated_vector_mago[2],
366
+ "calibrated_sec_x": updated_vector_magi[0],
367
+ "calibrated_sec_y": updated_vector_magi[1],
368
+ "calibrated_sec_z": updated_vector_magi[2],
369
+ }
370
+ )
243
371
 
244
372
  mag_data.append({**status_data, **science_data, **time_data})
245
373
 
@@ -0,0 +1,69 @@
1
+ """Functions to support I-ALiRT SWAPI processing."""
2
+
3
+ import logging
4
+
5
+ import numpy as np
6
+ import xarray as xr
7
+ from xarray import DataArray
8
+
9
+ from imap_processing.ialirt.utils.grouping import find_groups
10
+
11
+ # from imap_processing.swapi.l1.swapi_l1 import process_sweep_data
12
+ # from imap_processing.swapi.l2.swapi_l2 import TIME_PER_BIN
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ def process_swapi_ialirt(unpacked_data: xr.Dataset) -> dict[str, DataArray]:
18
+ """
19
+ Extract I-ALiRT variables and calculate coincidence count rate.
20
+
21
+ Parameters
22
+ ----------
23
+ unpacked_data : xr.Dataset
24
+ SWAPI I-ALiRT data that has been parsed from the spacecraft packet.
25
+
26
+ Returns
27
+ -------
28
+ swapi_data : dict
29
+ Dictionary containing all data variables for SWAPI I-ALiRT product.
30
+ """
31
+ logger.info("Processing SWAPI.")
32
+
33
+ sci_dataset = unpacked_data.sortby("epoch", ascending=True)
34
+
35
+ grouped_dataset = find_groups(sci_dataset, (0, 11), "swapi_seq_number", "swapi_acq")
36
+
37
+ for group in np.unique(grouped_dataset["group"]):
38
+ # Sequence values for the group should be 0-11 with no duplicates.
39
+ seq_values = grouped_dataset["swapi_seq_number"][
40
+ (grouped_dataset["group"] == group)
41
+ ]
42
+
43
+ # Ensure no duplicates and all values from 0 to 11 are present
44
+ if not np.array_equal(seq_values.astype(int), np.arange(12)):
45
+ logger.info(
46
+ f"SWAPI group {group} does not contain all sequence values from 0 to "
47
+ f"11 without duplicates."
48
+ )
49
+ continue
50
+
51
+ total_packets = len(grouped_dataset["swapi_seq_number"].data)
52
+
53
+ # It takes 12 sequence data to make one full SWAPI sweep
54
+ total_sequence = 12
55
+ total_full_sweeps = total_packets // total_sequence
56
+
57
+ met_values = grouped_dataset["swapi_shcoarse"].data.reshape(total_full_sweeps, 12)[
58
+ :, 0
59
+ ]
60
+
61
+ # raw_coin_count = process_sweep_data(grouped_dataset, "coin_cnt")
62
+ # raw_coin_rate = raw_coin_count / TIME_PER_BIN
63
+
64
+ swapi_data = {
65
+ "met": met_values
66
+ # more variables to go here
67
+ }
68
+
69
+ return swapi_data
@@ -1,4 +1,4 @@
1
- """Functions to support I-ALiRT SWE packet parsing."""
1
+ """Functions to support I-ALiRT SWE processing."""
2
2
 
3
3
  import logging
4
4
 
@@ -9,7 +9,7 @@ from numpy.typing import NDArray
9
9
 
10
10
  from imap_processing.ialirt.utils.grouping import find_groups
11
11
  from imap_processing.swe.l1a.swe_science import decompressed_counts
12
- from imap_processing.swe.l1b.swe_l1b_science import (
12
+ from imap_processing.swe.l1b.swe_l1b import (
13
13
  deadtime_correction,
14
14
  read_in_flight_cal_data,
15
15
  )
@@ -22,6 +22,16 @@ from imap_processing.swe.utils.swe_utils import combine_acquisition_time
22
22
 
23
23
  logger = logging.getLogger(__name__)
24
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
+
25
35
 
26
36
  def decompress_counts(raw_counts: NDArray) -> NDArray:
27
37
  """
@@ -36,6 +46,10 @@ def decompress_counts(raw_counts: NDArray) -> NDArray:
36
46
  -------
37
47
  counts : np.ndarray
38
48
  Array of decompressed counts with the same shape as raw_counts.
49
+
50
+ Notes
51
+ -----
52
+ CEM is channel electron multiplier.
39
53
  """
40
54
  decompression_table = np.array([decompressed_counts(i) for i in range(256)])
41
55
 
@@ -100,19 +114,9 @@ def prepare_raw_counts(grouped: xr.Dataset, cem_number: int = N_CEMS) -> NDArray
100
114
  )
101
115
  phi_bins = phi_to_bin(phi_values).astype(int) # Get phi bin indices
102
116
 
103
- # Energy bin lookup table (indexed by quarter cycle)
104
- energy_bins = np.array(
105
- [
106
- [1, 5, 7, 3], # 0-14 (first quarter cycle)
107
- [2, 6, 4, 0], # 15-29 (second quarter cycle)
108
- [3, 7, 5, 1], # 30-44 (third quarter cycle)
109
- [0, 4, 6, 2], # 45-59 (fourth quarter cycle)
110
- ]
111
- )
112
-
113
117
  # The first 15 seconds is the first quarter cycle, etc.
114
118
  quarter_cycles = np.floor(grouped["swe_seq"] / 15).astype(int)
115
- e_bins = energy_bins[quarter_cycles]
119
+ e_bins = ENERGY_BINS[quarter_cycles]
116
120
 
117
121
  # Populate raw_counts
118
122
  for cem in range(1, cem_number + 1): # 7 CEMs
@@ -168,21 +172,283 @@ def normalize_counts(counts: NDArray, latest_cal: pd.Series) -> NDArray:
168
172
  latest_cal = latest_cal.to_numpy()
169
173
 
170
174
  # Norm counts where counts are non-negative
171
- # TODO: confirm fv is counts with Ruth
172
175
  norm_counts = counts * (latest_cal / GEOMETRIC_FACTORS)[:, np.newaxis]
173
176
  norm_counts[norm_counts < 0] = 0
174
177
 
175
178
  return norm_counts
176
179
 
177
180
 
178
- def process_swe(accumulated_data: xr.Dataset) -> list[dict]:
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]:
179
404
  """
180
- Create L1 data dictionary..
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.
181
445
 
182
446
  Parameters
183
447
  ----------
184
448
  accumulated_data : xr.Dataset
185
449
  Packets dataset accumulated over 1 min.
450
+ in_flight_cal_files : list
451
+ List of path to the in-flight calibration files.
186
452
 
187
453
  Returns
188
454
  -------
@@ -209,7 +475,7 @@ def process_swe(accumulated_data: xr.Dataset) -> list[dict]:
209
475
 
210
476
  # Ensure no duplicates and all values from 0 to 59 are present
211
477
  if not np.array_equal(seq_values, np.arange(60)):
212
- logger.warning(
478
+ logger.info(
213
479
  f"Group {group} does not contain all values from 0 to "
214
480
  f"59 without duplicates."
215
481
  )
@@ -237,16 +503,46 @@ def process_swe(accumulated_data: xr.Dataset) -> list[dict]:
237
503
  corrected_second_half = deadtime_correction(counts_second_half, 80 * 10**3)
238
504
 
239
505
  # Grab the latest calibration factor
240
- in_flight_cal_df = read_in_flight_cal_data()
506
+ in_flight_cal_df = read_in_flight_cal_data(in_flight_cal_files)
241
507
  latest_cal = in_flight_cal_df.sort_values("met_time").iloc[-1][1::]
242
508
 
243
509
  normalized_first_half = normalize_counts(corrected_first_half, latest_cal)
244
510
  normalized_second_half = normalize_counts(corrected_second_half, latest_cal)
245
511
 
246
512
  # Sum over the 7 detectors
247
- summed_first_half = np.sum(normalized_first_half, axis=1) # noqa: F841
248
- summed_second_half = np.sum(normalized_second_half, axis=1) # noqa: F841
249
-
250
- # TODO: will continue here
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
+ )
251
547
 
252
548
  return swe_data