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,248 +1,30 @@
1
1
  """Generate ULTRA L1a CDFs."""
2
2
 
3
- # TODO: Evaluate naming conventions for fields and variables
4
- # TODO: Improved short and long descriptions for each variable
5
- # TODO: Improved var_notes for each variable
6
3
  import logging
7
4
  from typing import Optional
8
5
 
9
- import numpy as np
10
6
  import xarray as xr
11
7
 
12
- from imap_processing import decom, imap_module_directory
8
+ from imap_processing import imap_module_directory
13
9
  from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
14
- from imap_processing.spice.time import met_to_ttj2000ns
15
- from imap_processing.ultra.l0.decom_ultra import process_ultra_apids
10
+ from imap_processing.ultra.l0.decom_ultra import (
11
+ process_ultra_events,
12
+ process_ultra_rates,
13
+ process_ultra_tof,
14
+ )
16
15
  from imap_processing.ultra.l0.ultra_utils import (
17
16
  ULTRA_AUX,
18
17
  ULTRA_EVENTS,
18
+ ULTRA_HK,
19
19
  ULTRA_RATES,
20
20
  ULTRA_TOF,
21
21
  )
22
- from imap_processing.utils import group_by_apid
22
+ from imap_processing.utils import packet_file_to_datasets
23
23
 
24
24
  logger = logging.getLogger(__name__)
25
25
 
26
26
 
27
- def initiate_data_arrays(decom_ultra: dict, apid: int) -> xr.Dataset:
28
- """
29
- Initiate xarray data arrays.
30
-
31
- Parameters
32
- ----------
33
- decom_ultra : dict
34
- Parsed data.
35
- apid : int
36
- Packet APID.
37
-
38
- Returns
39
- -------
40
- dataset : xarray.Dataset
41
- Data in xarray format.
42
- """
43
- if apid in ULTRA_EVENTS.apid:
44
- index = ULTRA_EVENTS.apid.index(apid)
45
- logical_source = ULTRA_EVENTS.logical_source[index]
46
- addition_to_logical_desc = ULTRA_EVENTS.addition_to_logical_desc
47
- raw_time = decom_ultra["SHCOARSE"]
48
- elif apid in ULTRA_TOF.apid:
49
- index = ULTRA_TOF.apid.index(apid)
50
- logical_source = ULTRA_TOF.logical_source[index]
51
- addition_to_logical_desc = ULTRA_TOF.addition_to_logical_desc
52
- raw_time = np.unique(decom_ultra["SHCOARSE"])
53
- elif apid in ULTRA_AUX.apid:
54
- index = ULTRA_AUX.apid.index(apid)
55
- logical_source = ULTRA_AUX.logical_source[index]
56
- addition_to_logical_desc = ULTRA_AUX.addition_to_logical_desc
57
- raw_time = decom_ultra["SHCOARSE"]
58
- elif apid in ULTRA_RATES.apid:
59
- index = ULTRA_RATES.apid.index(apid)
60
- logical_source = ULTRA_RATES.logical_source[index]
61
- addition_to_logical_desc = ULTRA_RATES.addition_to_logical_desc
62
- raw_time = decom_ultra["SHCOARSE"]
63
- else:
64
- raise ValueError(f"APID {apid} not recognized.")
65
-
66
- # Load the CDF attributes
67
- cdf_manager = ImapCdfAttributes()
68
- cdf_manager.add_instrument_global_attrs("ultra")
69
- cdf_manager.add_instrument_variable_attrs("ultra", "l1a")
70
-
71
- epoch_time = xr.DataArray(
72
- met_to_ttj2000ns(raw_time),
73
- name="epoch",
74
- dims=["epoch"],
75
- attrs=cdf_manager.get_variable_attributes("epoch"),
76
- )
77
-
78
- sci_cdf_attrs = cdf_manager.get_global_attributes("imap_ultra_l1a_sci")
79
- # replace the logical source and logical source description
80
- sci_cdf_attrs["Logical_source"] = logical_source
81
- sci_cdf_attrs["Logical_source_desc"] = (
82
- f"IMAP Mission ULTRA Instrument Level-1A {addition_to_logical_desc} Data"
83
- )
84
-
85
- if apid not in (ULTRA_TOF.apid[0], ULTRA_TOF.apid[1]):
86
- dataset = xr.Dataset(
87
- coords={"epoch": epoch_time},
88
- attrs=sci_cdf_attrs,
89
- )
90
- else:
91
- row = xr.DataArray(
92
- # Number of pixel rows
93
- np.arange(54),
94
- name="row",
95
- dims=["row"],
96
- attrs=cdf_manager.get_variable_attributes("ultra_metadata_attrs"),
97
- )
98
-
99
- column = xr.DataArray(
100
- # Number of pixel columns
101
- np.arange(180),
102
- name="column",
103
- dims=["column"],
104
- attrs=cdf_manager.get_variable_attributes("ultra_metadata_attrs"),
105
- )
106
-
107
- sid = xr.DataArray(
108
- # Number of pixel columns
109
- np.arange(8),
110
- name="sid",
111
- dims=["sid"],
112
- attrs=cdf_manager.get_variable_attributes("ultra_metadata_attrs"),
113
- )
114
-
115
- dataset = xr.Dataset(
116
- coords={"epoch": epoch_time, "sid": sid, "row": row, "column": column},
117
- attrs=sci_cdf_attrs,
118
- )
119
-
120
- return dataset
121
-
122
-
123
- def get_event_id(decom_ultra_dict: dict) -> dict:
124
- """
125
- Get unique event IDs using data from events packets.
126
-
127
- Parameters
128
- ----------
129
- decom_ultra_dict : dict
130
- Events data.
131
-
132
- Returns
133
- -------
134
- decom_events : dict
135
- Ultra events data with calculated unique event IDs as 64-bit integers.
136
- """
137
- decom_events: dict = decom_ultra_dict[ULTRA_EVENTS.apid[0]]
138
-
139
- event_ids = []
140
- packet_counters = {}
141
-
142
- for met in decom_events["SHCOARSE"]:
143
- # Initialize the counter for a new packet (MET value)
144
- if met not in packet_counters:
145
- packet_counters[met] = 0
146
- else:
147
- packet_counters[met] += 1
148
-
149
- # Left shift SHCOARSE (u32) by 31 bits, to make room for our event counters
150
- # (31 rather than 32 to keep it positive in the int64 representation)
151
- # Append the current number of events in this packet to the right-most bits
152
- # This makes each event a unique value including the MET and event number
153
- # in the packet
154
- # NOTE: CDF does not allow for uint64 values,
155
- # so we use int64 representation here
156
- event_id = (np.int64(met) << np.int64(31)) | np.int64(packet_counters[met])
157
- event_ids.append(event_id)
158
-
159
- decom_events["EVENTID"] = event_ids
160
-
161
- return decom_events
162
-
163
-
164
- def create_dataset(decom_ultra_dict: dict) -> xr.Dataset:
165
- """
166
- Create xarray for packet.
167
-
168
- Parameters
169
- ----------
170
- decom_ultra_dict : dict
171
- Dictionary of parsed data.
172
-
173
- Returns
174
- -------
175
- dataset : xarray.Dataset
176
- Data in xarray format.
177
- """
178
- # Combine events and aux datasets so we can have proper event timestamps
179
- if ULTRA_EVENTS.apid[0] in decom_ultra_dict.keys():
180
- apid = ULTRA_EVENTS.apid[0]
181
- decom_ultra = get_event_id(decom_ultra_dict)
182
- else:
183
- apid = next(iter(decom_ultra_dict.keys()))
184
- decom_ultra = decom_ultra_dict[apid]
185
-
186
- # Load the CDF attributes
187
- # TODO: call this once and pass the object to the function
188
- cdf_manager = ImapCdfAttributes()
189
- cdf_manager.add_instrument_global_attrs("ultra")
190
- cdf_manager.add_instrument_variable_attrs("ultra", "l1a")
191
-
192
- dataset = initiate_data_arrays(decom_ultra, apid)
193
-
194
- for key, value in decom_ultra.items():
195
- # EVENT DATA and FASTDATA_00 have been broken down further
196
- # (see ultra_utils.py) and are therefore not needed.
197
- # SID is also not needed as it is used as a dimension.
198
- if key in {"EVENTDATA", "FASTDATA_00", "SID"}:
199
- continue
200
- # Everything in the TOF packet has dimensions of (time, sid) except
201
- # for PACKETDATA which has dimensions of (time, sid, row, column) and
202
- # SHCOARSE with has dimensions of (time)
203
- elif apid == ULTRA_TOF.apid[0] and key != "PACKETDATA" and key != "SHCOARSE":
204
- # TODO: fix this to use the correct attributes
205
- attrs = cdf_manager.get_variable_attributes("ultra_support_attrs")
206
- dims = ["epoch", "sid"]
207
- # AUX enums require string attributes
208
- elif key in [
209
- "SPINPERIODVALID",
210
- "SPINPHASEVALID",
211
- "SPINPERIODSOURCE",
212
- "CATBEDHEATERFLAG",
213
- "HWMODE",
214
- "IMCENB",
215
- "LEFTDEFLECTIONCHARGE",
216
- "RIGHTDEFLECTIONCHARGE",
217
- ]:
218
- # TODO: fix this to use the correct attributes
219
- attrs = cdf_manager.get_variable_attributes("string_base_attrs")
220
- dims = ["epoch"]
221
- # TOF packetdata has multiple dimensions
222
- elif key == "PACKETDATA":
223
- # TODO: fix this to use the correct attributes
224
- attrs = cdf_manager.get_variable_attributes("packet_data_attrs")
225
- dims = ["epoch", "sid", "row", "column"]
226
- # Use metadata with a single dimension for
227
- # all other data products
228
- else:
229
- # TODO: fix this to use the correct attributes
230
- attrs = cdf_manager.get_variable_attributes("ultra_support_attrs")
231
- dims = ["epoch"]
232
-
233
- dataset[key] = xr.DataArray(
234
- value,
235
- name=key if key == "epoch" else key.lower(),
236
- dims=dims,
237
- attrs=attrs,
238
- )
239
-
240
- return dataset
241
-
242
-
243
- def ultra_l1a(
244
- packet_file: str, data_version: str, apid: Optional[int] = None
245
- ) -> list[xr.Dataset]:
27
+ def ultra_l1a(packet_file: str, apid_input: Optional[int] = None) -> list[xr.Dataset]:
246
28
  """
247
29
  Will process ULTRA L0 data into L1A CDF files at output_filepath.
248
30
 
@@ -250,9 +32,7 @@ def ultra_l1a(
250
32
  ----------
251
33
  packet_file : str
252
34
  Path to the CCSDS data packet file.
253
- data_version : str
254
- Version of the data product being created.
255
- apid : Optional[int]
35
+ apid_input : Optional[int]
256
36
  Optional apid.
257
37
 
258
38
  Returns
@@ -261,11 +41,10 @@ def ultra_l1a(
261
41
  List of xarray.Dataset.
262
42
  """
263
43
  xtce = str(
264
- f"{imap_module_directory}/ultra/packet_definitions/" f"ULTRA_SCI_COMBINED.xml"
44
+ f"{imap_module_directory}/ultra/packet_definitions/ULTRA_SCI_COMBINED.xml"
265
45
  )
266
46
 
267
- packets = decom.decom_packets(packet_file, xtce)
268
- grouped_data = group_by_apid(packets)
47
+ datasets_by_apid = packet_file_to_datasets(packet_file, xtce)
269
48
 
270
49
  output_datasets = []
271
50
 
@@ -274,18 +53,51 @@ def ultra_l1a(
274
53
  # Each test dataset is only for a single apid while the rest of the apids
275
54
  # contain zeros. Ideally we would have
276
55
  # test data for all apids and remove this parameter.
277
- if apid is not None:
278
- apids = [apid]
56
+ if apid_input is not None:
57
+ apids = [apid_input]
279
58
  else:
280
- apids = list(grouped_data.keys())
59
+ apids = list(datasets_by_apid.keys())
60
+
61
+ # Update dataset global attributes
62
+ attr_mgr = ImapCdfAttributes()
63
+ attr_mgr.add_instrument_global_attrs("ultra")
64
+ attr_mgr.add_instrument_variable_attrs("ultra", "l1a")
281
65
 
282
66
  for apid in apids:
283
- decom_ultra_dict = {
284
- apid: process_ultra_apids(grouped_data[apid], apid),
285
- }
286
- dataset = create_dataset(decom_ultra_dict)
287
- # TODO: move this to use ImapCdfAttributes().add_global_attribute()
288
- dataset.attrs["Data_version"] = data_version
289
- output_datasets.append(dataset)
67
+ if apid in ULTRA_AUX.apid:
68
+ decom_ultra_dataset = datasets_by_apid[apid]
69
+ gattr_key = ULTRA_AUX.logical_source[ULTRA_AUX.apid.index(apid)]
70
+ elif apid in ULTRA_TOF.apid:
71
+ decom_ultra_dataset = process_ultra_tof(datasets_by_apid[apid])
72
+ gattr_key = ULTRA_TOF.logical_source[ULTRA_TOF.apid.index(apid)]
73
+ elif apid in ULTRA_RATES.apid:
74
+ decom_ultra_dataset = process_ultra_rates(datasets_by_apid[apid])
75
+ gattr_key = ULTRA_RATES.logical_source[ULTRA_RATES.apid.index(apid)]
76
+ elif apid in ULTRA_EVENTS.apid:
77
+ decom_ultra_dataset = process_ultra_events(datasets_by_apid[apid])
78
+ gattr_key = ULTRA_EVENTS.logical_source[ULTRA_EVENTS.apid.index(apid)]
79
+ # Add coordinate attributes
80
+ attrs = attr_mgr.get_variable_attributes("event_id")
81
+ decom_ultra_dataset.coords["event_id"].attrs.update(attrs)
82
+ elif apid in ULTRA_HK.apid:
83
+ decom_ultra_dataset = datasets_by_apid[apid]
84
+ gattr_key = ULTRA_HK.logical_source[ULTRA_HK.apid.index(apid)]
85
+ else:
86
+ logger.error(f"APID {apid} not recognized.")
87
+ # TODO: here we can put other apids
88
+ continue
89
+
90
+ decom_ultra_dataset.attrs.update(attr_mgr.get_global_attributes(gattr_key))
91
+
92
+ # Add data variable attributes
93
+ for key in decom_ultra_dataset.data_vars:
94
+ attrs = attr_mgr.get_variable_attributes(key.lower())
95
+ decom_ultra_dataset.data_vars[key].attrs.update(attrs)
96
+
97
+ # Add coordinate attributes
98
+ attrs = attr_mgr.get_variable_attributes("epoch", check_schema=False)
99
+ decom_ultra_dataset.coords["epoch"].attrs.update(attrs)
100
+
101
+ output_datasets.append(decom_ultra_dataset)
290
102
 
291
103
  return output_datasets
@@ -11,7 +11,6 @@ def calculate_badtimes(
11
11
  extendedspin_dataset: xr.Dataset,
12
12
  cullingmask_spins: NDArray,
13
13
  name: str,
14
- data_version: str,
15
14
  ) -> xr.Dataset:
16
15
  """
17
16
  Create dataset with defined datatypes for Badtimes Data.
@@ -24,8 +23,6 @@ def calculate_badtimes(
24
23
  Dataset containing the culled data.
25
24
  name : str
26
25
  Name of the dataset.
27
- data_version : str
28
- Version of the data.
29
26
 
30
27
  Returns
31
28
  -------
@@ -38,6 +35,6 @@ def calculate_badtimes(
38
35
 
39
36
  filtered_dataset = extendedspin_dataset.sel(spin_number=culled_spins)
40
37
 
41
- badtimes_dataset = create_dataset(filtered_dataset, name, "l1b", data_version)
38
+ badtimes_dataset = create_dataset(filtered_dataset, name, "l1b")
42
39
 
43
40
  return badtimes_dataset
@@ -6,9 +6,7 @@ from imap_processing.quality_flags import ImapAttitudeUltraFlags, ImapRatesUltra
6
6
  from imap_processing.ultra.utils.ultra_l1_utils import create_dataset
7
7
 
8
8
 
9
- def calculate_cullingmask(
10
- extendedspin_dataset: xr.Dataset, name: str, data_version: str
11
- ) -> xr.Dataset:
9
+ def calculate_cullingmask(extendedspin_dataset: xr.Dataset, name: str) -> xr.Dataset:
12
10
  """
13
11
  Create dataset with defined datatype for Culling Mask Data.
14
12
 
@@ -18,8 +16,6 @@ def calculate_cullingmask(
18
16
  Dataset containing the data.
19
17
  name : str
20
18
  Name of the dataset.
21
- data_version : str
22
- Version of the data.
23
19
 
24
20
  Returns
25
21
  -------
@@ -47,6 +43,6 @@ def calculate_cullingmask(
47
43
  spin_number=extendedspin_dataset["spin_number"][good_mask]
48
44
  )
49
45
 
50
- cullingmask_dataset = create_dataset(filtered_dataset, name, "l1b", data_version)
46
+ cullingmask_dataset = create_dataset(filtered_dataset, name, "l1b")
51
47
 
52
48
  return cullingmask_dataset
@@ -20,6 +20,7 @@ from imap_processing.ultra.l1b.ultra_l1b_extended import (
20
20
  get_eventtimes,
21
21
  get_front_x_position,
22
22
  get_front_y_position,
23
+ get_fwhm,
23
24
  get_path_length,
24
25
  get_ph_tof_and_back_positions,
25
26
  get_phi_theta,
@@ -28,8 +29,11 @@ from imap_processing.ultra.l1b.ultra_l1b_extended import (
28
29
  )
29
30
  from imap_processing.ultra.utils.ultra_l1_utils import create_dataset
30
31
 
32
+ FILLVAL_UINT8 = 255
33
+ FILLVAL_FLOAT32 = -1.0e31
31
34
 
32
- def calculate_de(de_dataset: xr.Dataset, name: str, data_version: str) -> xr.Dataset:
35
+
36
+ def calculate_de(de_dataset: xr.Dataset, name: str) -> xr.Dataset:
33
37
  """
34
38
  Create dataset with defined datatypes for Direct Event Data.
35
39
 
@@ -39,8 +43,6 @@ def calculate_de(de_dataset: xr.Dataset, name: str, data_version: str) -> xr.Dat
39
43
  L1a dataset containing direct event data.
40
44
  name : str
41
45
  Name of the l1a dataset.
42
- data_version : str
43
- Version of the data.
44
46
 
45
47
  Returns
46
48
  -------
@@ -52,7 +54,7 @@ def calculate_de(de_dataset: xr.Dataset, name: str, data_version: str) -> xr.Dat
52
54
 
53
55
  # Define epoch and spin.
54
56
  de_dict["epoch"] = de_dataset["epoch"].data
55
- de_dict["spin"] = de_dataset["SPIN"].data
57
+ de_dict["spin"] = de_dataset["spin"].data
56
58
 
57
59
  # Add already populated fields.
58
60
  keys = [
@@ -64,61 +66,66 @@ def calculate_de(de_dataset: xr.Dataset, name: str, data_version: str) -> xr.Dat
64
66
  "spin",
65
67
  ]
66
68
  dataset_keys = [
67
- "COIN_TYPE",
68
- "START_TYPE",
69
- "STOP_TYPE",
70
- "SHCOARSE",
71
- "PHASE_ANGLE",
72
- "SPIN",
69
+ "coin_type",
70
+ "start_type",
71
+ "stop_type",
72
+ "shcoarse",
73
+ "phase_angle",
74
+ "spin",
73
75
  ]
74
76
 
75
77
  de_dict.update(
76
78
  {key: de_dataset[dataset_key] for key, dataset_key in zip(keys, dataset_keys)}
77
79
  )
78
80
 
79
- valid_mask = de_dataset["START_TYPE"].data != np.iinfo(np.int64).min
81
+ valid_mask = de_dataset["start_type"].data != FILLVAL_UINT8
80
82
  ph_mask = np.isin(
81
- de_dataset["STOP_TYPE"].data, [StopType.Top.value, StopType.Bottom.value]
83
+ de_dataset["stop_type"].data, [StopType.Top.value, StopType.Bottom.value]
82
84
  )
83
- ssd_mask = np.isin(de_dataset["STOP_TYPE"].data, [StopType.SSD.value])
85
+ ssd_mask = np.isin(de_dataset["stop_type"].data, [StopType.SSD.value])
84
86
 
85
87
  valid_indices = np.nonzero(valid_mask)[0]
86
88
  ph_indices = np.nonzero(valid_mask & ph_mask)[0]
87
89
  ssd_indices = np.nonzero(valid_mask & ssd_mask)[0]
88
90
 
89
91
  # Instantiate arrays
90
- xf = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
91
- yf = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
92
- xb = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
93
- yb = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
94
- xc = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
95
- d = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float64)
96
- r = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
97
- phi = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
98
- theta = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
99
- tof = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
100
- etof = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
101
- ctof = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
102
- magnitude_v = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
103
- energy = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
104
- species_bin = np.full(len(de_dataset["epoch"]), "UNKNOWN", dtype="U10")
105
- t2 = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
106
- event_times = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float64)
107
- spin_starts = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float64)
108
- spin_period_sec = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float64)
92
+ xf = np.full(len(de_dataset["epoch"]), FILLVAL_FLOAT32, dtype=np.float32)
93
+ yf = np.full(len(de_dataset["epoch"]), FILLVAL_FLOAT32, dtype=np.float32)
94
+ xb = np.full(len(de_dataset["epoch"]), FILLVAL_FLOAT32, dtype=np.float32)
95
+ yb = np.full(len(de_dataset["epoch"]), FILLVAL_FLOAT32, dtype=np.float32)
96
+ xc = np.full(len(de_dataset["epoch"]), FILLVAL_FLOAT32, dtype=np.float32)
97
+ d = np.full(len(de_dataset["epoch"]), FILLVAL_FLOAT32, dtype=np.float64)
98
+ r = np.full(len(de_dataset["epoch"]), FILLVAL_FLOAT32, dtype=np.float32)
99
+ phi = np.full(len(de_dataset["epoch"]), FILLVAL_FLOAT32, dtype=np.float32)
100
+ theta = np.full(len(de_dataset["epoch"]), FILLVAL_FLOAT32, dtype=np.float32)
101
+ tof = np.full(len(de_dataset["epoch"]), FILLVAL_FLOAT32, dtype=np.float32)
102
+ etof = np.full(len(de_dataset["epoch"]), FILLVAL_FLOAT32, dtype=np.float32)
103
+ ctof = np.full(len(de_dataset["epoch"]), FILLVAL_FLOAT32, dtype=np.float32)
104
+ magnitude_v = np.full(len(de_dataset["epoch"]), FILLVAL_FLOAT32, dtype=np.float32)
105
+ energy = np.full(len(de_dataset["epoch"]), FILLVAL_FLOAT32, dtype=np.float32)
106
+ species_bin = np.full(len(de_dataset["epoch"]), FILLVAL_UINT8, dtype=np.uint8)
107
+ t2 = np.full(len(de_dataset["epoch"]), FILLVAL_FLOAT32, dtype=np.float32)
108
+ event_times = np.full(len(de_dataset["epoch"]), FILLVAL_FLOAT32, dtype=np.float64)
109
+ spin_starts = np.full(len(de_dataset["epoch"]), FILLVAL_FLOAT32, dtype=np.float64)
110
+ spin_period_sec = np.full(
111
+ len(de_dataset["epoch"]), FILLVAL_FLOAT32, dtype=np.float64
112
+ )
113
+ start_type = np.full(len(de_dataset["epoch"]), FILLVAL_UINT8, dtype=np.uint8)
109
114
 
110
115
  xf[valid_indices] = get_front_x_position(
111
- de_dataset["START_TYPE"].data[valid_indices],
112
- de_dataset["START_POS_TDC"].data[valid_indices],
116
+ de_dataset["start_type"].data[valid_indices],
117
+ de_dataset["start_pos_tdc"].data[valid_indices],
118
+ f"ultra{sensor}",
113
119
  )
120
+ start_type[valid_indices] = de_dataset["start_type"].data[valid_indices]
114
121
 
115
122
  (
116
123
  event_times[valid_indices],
117
124
  spin_starts[valid_indices],
118
125
  spin_period_sec[valid_indices],
119
126
  ) = get_eventtimes(
120
- de_dataset["SPIN"].data[valid_indices],
121
- de_dataset["PHASE_ANGLE"].data[valid_indices],
127
+ de_dataset["spin"].data[valid_indices],
128
+ de_dataset["phase_angle"].data[valid_indices],
122
129
  )
123
130
 
124
131
  # Pulse height
@@ -126,13 +133,14 @@ def calculate_de(de_dataset: xr.Dataset, name: str, data_version: str) -> xr.Dat
126
133
  get_ph_tof_and_back_positions(de_dataset, xf, f"ultra{sensor}")
127
134
  )
128
135
  d[ph_indices], yf[ph_indices] = get_front_y_position(
129
- de_dataset["START_TYPE"].data[ph_indices], yb[ph_indices]
136
+ de_dataset["start_type"].data[ph_indices], yb[ph_indices]
130
137
  )
131
138
  energy[ph_indices] = get_energy_pulse_height(
132
- de_dataset["STOP_TYPE"].data[ph_indices],
133
- de_dataset["ENERGY_PH"].data[ph_indices],
139
+ de_dataset["stop_type"].data[ph_indices],
140
+ de_dataset["energy_ph"].data[ph_indices],
134
141
  xb[ph_indices],
135
142
  yb[ph_indices],
143
+ f"ultra{sensor}",
136
144
  )
137
145
  r[ph_indices] = get_path_length(
138
146
  (xf[ph_indices], yf[ph_indices]),
@@ -153,13 +161,15 @@ def calculate_de(de_dataset: xr.Dataset, name: str, data_version: str) -> xr.Dat
153
161
  )
154
162
 
155
163
  # SSD
156
- tof[ssd_indices] = get_ssd_tof(de_dataset, xf)
157
- yb[ssd_indices], _, ssd_number = get_ssd_back_position_and_tof_offset(de_dataset)
164
+ tof[ssd_indices] = get_ssd_tof(de_dataset, xf, f"ultra{sensor}")
165
+ yb[ssd_indices], _, ssd_number = get_ssd_back_position_and_tof_offset(
166
+ de_dataset, f"ultra{sensor}"
167
+ )
158
168
  xc[ssd_indices] = np.zeros(len(ssd_indices))
159
169
  xb[ssd_indices] = np.zeros(len(ssd_indices))
160
170
  etof[ssd_indices] = np.zeros(len(ssd_indices))
161
171
  d[ssd_indices], yf[ssd_indices] = get_front_y_position(
162
- de_dataset["START_TYPE"].data[ssd_indices], yb[ssd_indices]
172
+ de_dataset["start_type"].data[ssd_indices], yb[ssd_indices]
163
173
  )
164
174
  energy[ssd_indices] = get_energy_ssd(de_dataset, ssd_number)
165
175
  r[ssd_indices] = get_path_length(
@@ -197,13 +207,15 @@ def calculate_de(de_dataset: xr.Dataset, name: str, data_version: str) -> xr.Dat
197
207
  de_dict["phi"] = phi
198
208
  de_dict["theta"] = theta
199
209
 
200
- v = get_de_velocity(
210
+ v, vhat, r = get_de_velocity(
201
211
  (de_dict["x_front"], de_dict["y_front"]),
202
212
  (de_dict["x_back"], de_dict["y_back"]),
203
213
  de_dict["front_back_distance"],
204
214
  de_dict["tof_start_stop"],
205
215
  )
206
216
  de_dict["direct_event_velocity"] = v.astype(np.float32)
217
+ de_dict["direct_event_unit_velocity"] = vhat.astype(np.float32)
218
+ de_dict["direct_event_unit_position"] = r.astype(np.float32)
207
219
 
208
220
  de_dict["tof_energy"] = get_de_energy_kev(v, species_bin)
209
221
  de_dict["energy"] = energy
@@ -226,11 +238,14 @@ def calculate_de(de_dataset: xr.Dataset, name: str, data_version: str) -> xr.Dat
226
238
  de_dict["energy_spacecraft"] = get_de_energy_kev(sc_dps_velocity, species_bin)
227
239
  de_dict["energy_heliosphere"] = get_de_energy_kev(helio_velocity, species_bin)
228
240
 
229
- # TODO: TBD.
230
- de_dict["event_efficiency"] = np.full(
231
- len(de_dataset["epoch"]), np.nan, dtype=np.float32
241
+ de_dict["phi_fwhm"], de_dict["theta_fwhm"] = get_fwhm(
242
+ start_type,
243
+ f"ultra{sensor}",
244
+ de_dict["tof_energy"],
245
+ de_dict["phi"],
246
+ de_dict["theta"],
232
247
  )
233
248
 
234
- dataset = create_dataset(de_dict, name, "l1b", data_version)
249
+ dataset = create_dataset(de_dict, name, "l1b")
235
250
 
236
251
  return dataset
@@ -1,5 +1,6 @@
1
1
  """Calculate Extended Spin."""
2
2
 
3
+ import numpy as np
3
4
  import xarray as xr
4
5
 
5
6
  from imap_processing.ultra.l1b.ultra_l1b_culling import (
@@ -13,7 +14,6 @@ from imap_processing.ultra.utils.ultra_l1_utils import create_dataset
13
14
  def calculate_extendedspin(
14
15
  dict_datasets: dict[str, xr.Dataset],
15
16
  name: str,
16
- data_version: str,
17
17
  instrument_id: int,
18
18
  ) -> xr.Dataset:
19
19
  """
@@ -25,8 +25,6 @@ def calculate_extendedspin(
25
25
  Dictionary containing all the datasets.
26
26
  name : str
27
27
  Name of the dataset.
28
- data_version : str
29
- Version of the data.
30
28
  instrument_id : int
31
29
  Instrument ID.
32
30
 
@@ -49,8 +47,14 @@ def calculate_extendedspin(
49
47
  attitude_qf, spin_rates, spin_period, spin_starttime = flag_attitude(
50
48
  de_dataset["spin"].values, aux_dataset
51
49
  )
50
+ # Get the first epoch for each spin.
51
+ mask = xr.DataArray(np.isin(de_dataset["spin"], spin), dims="epoch")
52
+ filtered_dataset = de_dataset.where(mask, drop=True)
53
+ _, first_indices = np.unique(filtered_dataset["spin"].values, return_index=True)
54
+ first_epochs = filtered_dataset["epoch"].values[first_indices]
52
55
 
53
56
  # These will be the coordinates.
57
+ extendedspin_dict["epoch"] = first_epochs
54
58
  extendedspin_dict["spin_number"] = spin
55
59
  extendedspin_dict["energy_bin_geometric_mean"] = energy_midpoints
56
60
 
@@ -62,6 +66,6 @@ def calculate_extendedspin(
62
66
  extendedspin_dict["quality_attitude"] = attitude_qf
63
67
  extendedspin_dict["quality_ena_rates"] = rates_qf
64
68
 
65
- extendedspin_dataset = create_dataset(extendedspin_dict, name, "l1b", data_version)
69
+ extendedspin_dataset = create_dataset(extendedspin_dict, name, "l1b")
66
70
 
67
71
  return extendedspin_dataset