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
@@ -26,15 +26,12 @@ from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
26
26
  from imap_processing.codice import constants
27
27
  from imap_processing.codice.codice_l0 import decom_packets
28
28
  from imap_processing.codice.decompress import decompress
29
- from imap_processing.codice.utils import CODICEAPID
29
+ from imap_processing.codice.utils import CODICEAPID, CoDICECompression
30
30
  from imap_processing.spice.time import met_to_ttj2000ns
31
31
 
32
32
  logger = logging.getLogger(__name__)
33
33
  logger.setLevel(logging.INFO)
34
34
 
35
- # TODO: Determine what should go in event data CDF and how it should be
36
- # structured.
37
-
38
35
 
39
36
  class CoDICEL1aPipeline:
40
37
  """
@@ -537,9 +534,7 @@ class CoDICEL1aPipeline:
537
534
  # No longer need to keep the raw data around
538
535
  del self.raw_data
539
536
 
540
- def set_data_product_config(
541
- self, apid: int, dataset: xr.Dataset, data_version: str
542
- ) -> None:
537
+ def set_data_product_config(self, apid: int, dataset: xr.Dataset) -> None:
543
538
  """
544
539
  Set the various settings for defining the data products.
545
540
 
@@ -549,79 +544,139 @@ class CoDICEL1aPipeline:
549
544
  The APID of interest.
550
545
  dataset : xarray.Dataset
551
546
  The dataset for the APID of interest.
552
- data_version : str
553
- Version of the data product being created.
554
547
  """
555
548
  # Set the packet dataset so that it can be easily called from various
556
549
  # methods
557
550
  self.dataset = dataset
558
551
 
559
552
  # Set various configurations of the data product
560
- self.config: dict[str, Any] = constants.DATA_PRODUCT_CONFIGURATIONS.get(apid) # type: ignore
553
+ self.config: dict[str, Any] = constants.DATA_PRODUCT_CONFIGURATIONS[apid]
561
554
 
562
555
  # Gather and set the CDF attributes
563
556
  self.cdf_attrs = ImapCdfAttributes()
564
557
  self.cdf_attrs.add_instrument_global_attrs("codice")
565
558
  self.cdf_attrs.add_instrument_variable_attrs("codice", "l1a")
566
- self.cdf_attrs.add_global_attribute("Data_version", data_version)
567
559
 
568
560
 
569
- def create_event_dataset(
570
- apid: int, packet: xr.Dataset, data_version: str
571
- ) -> xr.Dataset:
561
+ def create_direct_event_dataset(apid: int, packets: xr.Dataset) -> xr.Dataset:
572
562
  """
573
- Create dataset for event data.
563
+ Create dataset for direct event data.
564
+
565
+ For direct event data, the raw data from the spacecraft is organized first
566
+ by epoch, then by priority, then by events. For example, for a CoDICE-Lo
567
+ dataset with 10 epochs, we expect the length of the `event_data` field to
568
+ be (10 epochs * 8 priorities) = 80 items, with each item being a compressed
569
+ byte object representing a variable number of events (up to 10000 events).
570
+ Each compressed byte object is comprised of several fields with specific
571
+ bit lengths/positions, described by the constants.[LO|HI]_DE_BIT_STRUCTURE
572
+ dictionary. Padding is added to any fields that have less than 10000 events.
573
+
574
+ In order to process these data, we must take the decommed raw data, group
575
+ the packets appropriately based on their `seq_flgs`, decompress the data,
576
+ then arrange the data into CDF data variables for each priority and bit
577
+ field. For example, P2_SpinAngle represents the spin angles for the 2nd
578
+ priority data.
574
579
 
575
580
  Parameters
576
581
  ----------
577
582
  apid : int
578
583
  The APID of the packet.
579
- packet : xarray.Dataset
580
- The packet to process.
581
- data_version : str
582
- Version of the data product being created.
584
+ packets : xarray.Dataset
585
+ The packets to process..
583
586
 
584
587
  Returns
585
588
  -------
586
589
  dataset : xarray.Dataset
587
- Xarray dataset containing the event data.
590
+ Xarray dataset containing the direct event data.
588
591
  """
592
+ # Set some useful variables unique to CoDICE-Lo and CoDICE-Hi
589
593
  if apid == CODICEAPID.COD_LO_PHA:
590
- dataset_name = "imap_codice_l1a_lo-pha"
594
+ num_priorities = 8
595
+ cdf_fields = [
596
+ "NumEvents",
597
+ "DataQuality",
598
+ "APDGain",
599
+ "APD_ID",
600
+ "APDEnergy",
601
+ "TOF",
602
+ "MultiFlag",
603
+ "PHAType",
604
+ "SpinAngle",
605
+ "EnergyStep",
606
+ ]
591
607
  elif apid == CODICEAPID.COD_HI_PHA:
592
- dataset_name = "imap_codice_l1a_hi-pha"
608
+ num_priorities = 6
609
+ cdf_fields = [
610
+ "NumEvents",
611
+ "DataQuality",
612
+ "SSDEnergy0,TOF",
613
+ "SSD_ID",
614
+ "ERGE",
615
+ "MultiFlag",
616
+ "Type",
617
+ "SpinAngle",
618
+ "SpinNumber",
619
+ ]
620
+
621
+ # Group and decompress the data
622
+ grouped_data = group_data(packets)
623
+ decompressed_data = [
624
+ decompress(group, CoDICECompression.LOSSLESS) for group in grouped_data
625
+ ]
593
626
 
594
- # Extract the data
595
- # event_data = packet.event_data.data (Currently turned off, see TODO)
627
+ # Reshape the packet data into CDF-ready variables
628
+ data = reshape_de_data(packets, decompressed_data, num_priorities)
596
629
 
630
+ # Gather the CDF attributes
597
631
  cdf_attrs = ImapCdfAttributes()
598
632
  cdf_attrs.add_instrument_global_attrs("codice")
599
633
  cdf_attrs.add_instrument_variable_attrs("codice", "l1a")
600
- cdf_attrs.add_global_attribute("Data_version", data_version)
601
634
 
602
635
  # Define coordinates
636
+ # For epoch, we take the first epoch from each priority set
603
637
  epoch = xr.DataArray(
604
- packet.epoch,
638
+ packets.epoch[::num_priorities],
605
639
  name="epoch",
606
640
  dims=["epoch"],
607
641
  attrs=cdf_attrs.get_variable_attributes("epoch"),
608
642
  )
643
+ event_num = xr.DataArray(
644
+ np.arange(10000),
645
+ name="event_num",
646
+ dims=["event_num"],
647
+ attrs=cdf_attrs.get_variable_attributes("event_num"),
648
+ )
609
649
 
610
650
  # Create the dataset to hold the data variables
651
+ if apid == CODICEAPID.COD_LO_PHA:
652
+ attrs = cdf_attrs.get_global_attributes("imap_codice_l1a_lo-pha")
653
+ elif apid == CODICEAPID.COD_HI_PHA:
654
+ attrs = cdf_attrs.get_global_attributes("imap_codice_l1a_hi-pha")
611
655
  dataset = xr.Dataset(
612
- coords={
613
- "epoch": epoch,
614
- },
615
- attrs=cdf_attrs.get_global_attributes(dataset_name),
656
+ coords={"epoch": epoch, "event_num": event_num},
657
+ attrs=attrs,
616
658
  )
617
659
 
660
+ # Create the CDF data variables for each Priority and Field
661
+ for i in range(num_priorities):
662
+ for field in cdf_fields:
663
+ variable_name = f"P{i}_{field}"
664
+ attrs = cdf_attrs.get_variable_attributes(variable_name)
665
+ if field in ["NumEvents", "DataQuality"]:
666
+ dims = ["epoch"]
667
+ else:
668
+ dims = ["epoch", "event_num"]
669
+ dataset[variable_name] = xr.DataArray(
670
+ np.array(data[variable_name]),
671
+ name=variable_name,
672
+ dims=dims,
673
+ attrs=attrs,
674
+ )
675
+
618
676
  return dataset
619
677
 
620
678
 
621
- def create_hskp_dataset(
622
- packet: xr.Dataset,
623
- data_version: str,
624
- ) -> xr.Dataset:
679
+ def create_hskp_dataset(packet: xr.Dataset) -> xr.Dataset:
625
680
  """
626
681
  Create dataset for each metadata field for housekeeping data.
627
682
 
@@ -629,8 +684,6 @@ def create_hskp_dataset(
629
684
  ----------
630
685
  packet : xarray.Dataset
631
686
  The packet to process.
632
- data_version : str
633
- Version of the data product being created.
634
687
 
635
688
  Returns
636
689
  -------
@@ -640,7 +693,6 @@ def create_hskp_dataset(
640
693
  cdf_attrs = ImapCdfAttributes()
641
694
  cdf_attrs.add_instrument_global_attrs("codice")
642
695
  cdf_attrs.add_instrument_variable_attrs("codice", "l1a")
643
- cdf_attrs.add_global_attribute("Data_version", data_version)
644
696
 
645
697
  epoch = xr.DataArray(
646
698
  packet.epoch,
@@ -719,6 +771,69 @@ def get_params(dataset: xr.Dataset) -> tuple[int, int, int, int]:
719
771
  return table_id, plan_id, plan_step, view_id
720
772
 
721
773
 
774
+ def group_data(packets: xr.Dataset) -> list[bytes]:
775
+ """
776
+ Organize continuation packets into appropriate groups.
777
+
778
+ Some packets are continuation packets, as in, they are packets that are
779
+ part of a group of packets. These packets are marked by the `seq_flgs` field
780
+ in the CCSDS header of the packet. For CoDICE, the values are defined as
781
+ follows:
782
+
783
+ 3 = Packet is not part of a group
784
+ 1 = Packet is the first packet of the group
785
+ 0 = Packet is in the middle of the group
786
+ 2 = Packet is the last packet of the group
787
+
788
+ For packets that are part of a group, the byte count associated with the
789
+ first packet of the group signifies the byte count for the entire group.
790
+
791
+ Parameters
792
+ ----------
793
+ packets : xarray.Dataset
794
+ Dataset containing the packets to group.
795
+
796
+ Returns
797
+ -------
798
+ grouped_data : list[bytes]
799
+ The packet data, converted to bytes and grouped appropriately.
800
+ """
801
+ grouped_data = [] # Holds the properly grouped data to be decompressed
802
+ current_group = bytearray() # Temporary storage for current group
803
+ group_byte_count = None # Temporary storage for current group byte count
804
+
805
+ for packet_data, group_code, byte_count in zip(
806
+ packets.event_data.data, packets.seq_flgs.data, packets.byte_count.data
807
+ ):
808
+ # If the group code is 3, this means the data is not part of a group
809
+ # and can be decompressed as-is
810
+ if group_code == 3:
811
+ values_to_decompress = packet_data[:byte_count]
812
+ grouped_data.append(values_to_decompress)
813
+
814
+ # If the group code is 1, this means the data is the first data in a
815
+ # group. Also, set the byte count for the group
816
+ elif group_code == 1:
817
+ group_byte_count = byte_count
818
+ current_group = packet_data
819
+
820
+ # If the group code is 0, this means the data is part of the middle of
821
+ # the group
822
+ elif group_code == 0:
823
+ current_group += packet_data
824
+
825
+ # If the group code is 2, this means the data is the last data in the
826
+ # group
827
+ elif group_code == 2:
828
+ current_group += packet_data
829
+ values_to_decompress = current_group[:group_byte_count]
830
+ grouped_data.append(values_to_decompress)
831
+ current_group = bytearray()
832
+ group_byte_count = None
833
+
834
+ return grouped_data
835
+
836
+
722
837
  def log_dataset_info(datasets: dict[int, xr.Dataset]) -> None:
723
838
  """
724
839
  Log info about the input data to help with tracking and/or debugging.
@@ -730,9 +845,9 @@ def log_dataset_info(datasets: dict[int, xr.Dataset]) -> None:
730
845
  """
731
846
  launch_time = np.datetime64("2010-01-01T00:01:06.184", "ns")
732
847
  logger.info("\nThis input file contains the following APIDs:\n")
733
- for apid in datasets:
734
- num_packets = len(datasets[apid].epoch.data)
735
- time_deltas = [np.timedelta64(item, "ns") for item in datasets[apid].epoch.data]
848
+ for apid, ds in datasets.items():
849
+ num_packets = len(ds.epoch.data)
850
+ time_deltas = [np.timedelta64(item, "ns") for item in ds.epoch.data]
736
851
  times = [launch_time + delta for delta in time_deltas]
737
852
  start = np.datetime_as_string(times[0])
738
853
  end = np.datetime_as_string(times[-1])
@@ -741,7 +856,102 @@ def log_dataset_info(datasets: dict[int, xr.Dataset]) -> None:
741
856
  )
742
857
 
743
858
 
744
- def process_codice_l1a(file_path: Path, data_version: str) -> list[xr.Dataset]:
859
+ def reshape_de_data(
860
+ packets: xr.Dataset, decompressed_data: list[list[int]], num_priorities: int
861
+ ) -> dict[str, np.ndarray]:
862
+ """
863
+ Reshape the decompressed direct event data into CDF-ready arrays.
864
+
865
+ Parameters
866
+ ----------
867
+ packets : xarray.Dataset
868
+ Dataset containing the packets, needed to determine priority order
869
+ and data quality.
870
+ decompressed_data : list[list[int]]
871
+ The decompressed data to reshape, in the format <epoch>[<priority>[<event>]].
872
+ num_priorities : int
873
+ The number of priorities in the data product (differs between CoDICE-Lo
874
+ and CoDICE-Hi).
875
+
876
+ Returns
877
+ -------
878
+ data : dict[str, numpy.ndarray]
879
+ The reshaped, CDF-ready arrays. The keys of the dictionary represent the
880
+ CDF variable names, and the values represent the data.
881
+ """
882
+ # Dictionary to hold all the (soon to be restructured) direct event data
883
+ data: dict[str, np.ndarray] = {}
884
+
885
+ # Determine the number of epochs to help with data array initialization
886
+ # There is one epoch per set of priorities
887
+ num_epochs = len(packets.epoch.data) // num_priorities
888
+
889
+ # Initialize data arrays for each priority and field to store the data
890
+ # We also need arrays to hold number of events and data quality
891
+ for priority_num in range(num_priorities):
892
+ for field in constants.LO_DE_BIT_STRUCTURE:
893
+ if field not in ["Priority", "Spare"]:
894
+ data[f"P{priority_num}_{field}"] = np.full(
895
+ (num_epochs, 10000), 255, dtype=np.uint16
896
+ )
897
+ data[f"P{priority_num}_NumEvents"] = np.full(num_epochs, 255, dtype=np.uint16)
898
+ data[f"P{priority_num}_DataQuality"] = np.full(num_epochs, 255, dtype=np.uint16)
899
+
900
+ # decompressed_data is one large list of values of length
901
+ # (<number of epochs> * <8 priorities>)
902
+ # Chunk the data into each epoch
903
+ for epoch_index in range(num_epochs):
904
+ # Determine the starting and ending indices of the epoch
905
+ epoch_start = epoch_index * num_priorities
906
+ epoch_end = epoch_start + num_priorities
907
+
908
+ # Extract the data for the epoch
909
+ epoch_data = decompressed_data[epoch_start:epoch_end]
910
+
911
+ # The order of the priorities and data quality flags are unique to each
912
+ # epoch and can be gathered from the packet data
913
+ priority_order = packets.priority[epoch_start:epoch_end].data
914
+ data_quality = packets.suspect[epoch_start:epoch_end].data
915
+
916
+ # For each epoch/priority combo, iterate over each event
917
+ for i, priority_num in enumerate(priority_order):
918
+ priority_data = epoch_data[i]
919
+
920
+ # Number of events and data quality can be determined at this stage
921
+ num_events = len(priority_data) // num_priorities
922
+ data[f"P{priority_num}_NumEvents"][epoch_index] = num_events
923
+ data[f"P{priority_num}_DataQuality"][epoch_index] = data_quality[i]
924
+
925
+ # Iterate over each event
926
+ for event_index in range(num_events):
927
+ event_start = event_index * num_priorities
928
+ event_end = event_start + num_priorities
929
+ event = priority_data[event_start:event_end]
930
+ # Separate out each individual field from the bit string
931
+ # The fields are packed into the bit string in reverse order, so
932
+ # we need to back them out in reverse order
933
+ bit_string = (
934
+ f"{int.from_bytes(event, byteorder='big'):0{len(event) * 8}b}"
935
+ )
936
+ bit_position = 0
937
+ for field_name, bit_length in reversed(
938
+ constants.LO_DE_BIT_STRUCTURE.items()
939
+ ):
940
+ if field_name in ["Priority", "Spare"]:
941
+ bit_position += bit_length
942
+ continue
943
+ value = int(bit_string[bit_position : bit_position + bit_length], 2)
944
+ data[f"P{priority_num}_{field_name}"][epoch_index, event_index] = (
945
+ value
946
+ )
947
+ bit_position += bit_length
948
+
949
+ # TODO: Implement specific np.dtype and fill_val per field
950
+
951
+ return data
952
+
953
+
954
+ def process_codice_l1a(file_path: Path) -> list[xr.Dataset]:
745
955
  """
746
956
  Will process CoDICE l0 data to create l1a data products.
747
957
 
@@ -749,8 +959,6 @@ def process_codice_l1a(file_path: Path, data_version: str) -> list[xr.Dataset]:
749
959
  ----------
750
960
  file_path : pathlib.Path | str
751
961
  Path to the CoDICE L0 file to process.
752
- data_version : str
753
- Version of the data product being created.
754
962
 
755
963
  Returns
756
964
  -------
@@ -774,14 +982,19 @@ def process_codice_l1a(file_path: Path, data_version: str) -> list[xr.Dataset]:
774
982
 
775
983
  # Housekeeping data
776
984
  if apid == CODICEAPID.COD_NHK:
777
- processed_dataset = create_hskp_dataset(dataset, data_version)
985
+ processed_dataset = create_hskp_dataset(dataset)
778
986
  logger.info(f"\nFinal data product:\n{processed_dataset}\n")
779
987
 
780
988
  # Event data
781
- elif apid in [CODICEAPID.COD_LO_PHA, CODICEAPID.COD_HI_PHA]:
782
- processed_dataset = create_event_dataset(apid, dataset, data_version)
989
+ elif apid == CODICEAPID.COD_LO_PHA:
990
+ processed_dataset = create_direct_event_dataset(apid, dataset)
783
991
  logger.info(f"\nFinal data product:\n{processed_dataset}\n")
784
992
 
993
+ # TODO: Still need to implement
994
+ elif apid == CODICEAPID.COD_HI_PHA:
995
+ logger.info("\tStill need to properly implement")
996
+ processed_dataset = None
997
+
785
998
  # Everything else
786
999
  elif apid in constants.APIDS_FOR_SCIENCE_PROCESSING:
787
1000
  # Extract the data
@@ -792,7 +1005,7 @@ def process_codice_l1a(file_path: Path, data_version: str) -> list[xr.Dataset]:
792
1005
 
793
1006
  # Run the pipeline to create a dataset for the product
794
1007
  pipeline = CoDICEL1aPipeline(table_id, plan_id, plan_step, view_id)
795
- pipeline.set_data_product_config(apid, dataset, data_version)
1008
+ pipeline.set_data_product_config(apid, dataset)
796
1009
  pipeline.decompress_data(science_values)
797
1010
  pipeline.reshape_data()
798
1011
  pipeline.define_coordinates()
@@ -1,194 +1,93 @@
1
1
  """
2
2
  Perform CoDICE l1b processing.
3
3
 
4
- This module processes CoDICE l1a files and creates L1a data products.
4
+ This module processes CoDICE l1a files and creates L1b data products.
5
5
 
6
6
  Notes
7
7
  -----
8
- from imap_processing.codice.codice_l0 import decom_packets
9
8
  from imap_processing.codice.codice_l1b import process_codice_l1b
10
- dataset = process_codice_l1b(l1a_file)
9
+ dataset = process_codice_l1b(l1a_filenanme)
11
10
  """
12
11
 
13
12
  import logging
13
+ from pathlib import Path
14
14
 
15
15
  import xarray as xr
16
16
 
17
17
  from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
18
+ from imap_processing.cdf.utils import load_cdf
19
+ from imap_processing.codice import constants
18
20
 
19
21
  logger = logging.getLogger(__name__)
20
22
  logger.setLevel(logging.INFO)
21
23
 
22
- # TODO: Fix ISTP compliance issues (revealed in SKTEditor)
23
24
 
24
-
25
- def create_hskp_dataset(
26
- l1a_dataset: xr.Dataset, cdf_attrs: ImapCdfAttributes
27
- ) -> xr.Dataset:
28
- """
29
- Create an ``xarray`` dataset for the housekeeping data.
30
-
31
- The dataset can then be written to a CDF file.
32
-
33
- Parameters
34
- ----------
35
- l1a_dataset : xr.Dataset
36
- The L1a dataset that is being processed.
37
- cdf_attrs : ImapCdfAttributes
38
- The CDF attributes for the dataset.
39
-
40
- Returns
41
- -------
42
- l1b_dataset : xarray.Dataset
43
- The ``xarray`` dataset containing the science data and supporting metadata.
44
- """
45
- epoch = l1a_dataset.coords["epoch"]
46
- l1b_dataset = xr.Dataset(
47
- coords={"epoch": epoch},
48
- attrs=cdf_attrs.get_global_attributes("imap_codice_l1b_hskp"),
49
- )
50
- for variable_name in l1a_dataset:
51
- # Get the data array from the L1a data product
52
- values = l1a_dataset[variable_name].values
53
-
54
- # Convert data array to "rates"
55
- # TODO: For SIT-3, just convert value to float. Revisit after SIT-3.
56
- variable_data_arr = values.astype(float)
57
-
58
- # TODO: Change 'TBD' catdesc and fieldname
59
- # Once packet definition files are re-generated, can get this info from
60
- # something like this:
61
- # for key, value in (packet.header | packet.data).items():
62
- # fieldname = value.short_description
63
- # catdesc = value.short_description
64
- # I am holding off making this change until I acquire updated housekeeping
65
- # packets/validation data that match the latest telemetry definitions
66
- attrs = cdf_attrs.get_variable_attributes("codice_support_attrs")
67
- attrs["CATDESC"] = "TBD"
68
- attrs["DEPEND_0"] = "epoch"
69
- attrs["FIELDNAM"] = "TBD"
70
- attrs["LABLAXIS"] = variable_name
71
-
72
- # Put the new data array into the dataset
73
- l1b_dataset[variable_name] = xr.DataArray(
74
- variable_data_arr,
75
- name=variable_name,
76
- dims=["epoch"],
77
- attrs=attrs,
78
- )
79
-
80
- return l1b_dataset
81
-
82
-
83
- def create_science_dataset(
84
- l1a_dataset: xr.Dataset, cdf_attrs: ImapCdfAttributes, dataset_name: str
85
- ) -> xr.Dataset:
86
- """
87
- Create an ``xarray`` dataset for the science data.
88
-
89
- The dataset can then be written to a CDF file.
90
-
91
- Parameters
92
- ----------
93
- l1a_dataset : xr.Dataset
94
- The L1a dataset that is being processed.
95
- cdf_attrs : ImapCdfAttributes
96
- The CDF attributes for the dataset.
97
- dataset_name : str
98
- The name that is used to construct the data variable name and reference
99
- the CDF attributes (e.g. ``imap_codice_l1b_hi_omni``).
100
-
101
- Returns
102
- -------
103
- l1b_dataset : xarray.Dataset
104
- The ``xarray`` dataset containing the science data and supporting metadata.
105
- """
106
- # Retrieve the coordinates from the l1a dataset
107
- epoch = l1a_dataset.coords["epoch"]
108
- energy = l1a_dataset.coords["energy"]
109
- energy_label = l1a_dataset.coords["energy_label"]
110
-
111
- # Create empty l1b dataset
112
- l1b_dataset = xr.Dataset(
113
- coords={"epoch": epoch, "energy": energy, "energy_label": energy_label},
114
- attrs=cdf_attrs.get_global_attributes(dataset_name),
115
- )
116
-
117
- # Get the data variables from l1a dataset
118
- for variable_name in l1a_dataset:
119
- if variable_name == "esa_sweep_values":
120
- values = l1a_dataset["esa_sweep_values"]
121
- l1b_dataset["esa_sweep_values"] = xr.DataArray(
122
- values,
123
- dims=["energy"],
124
- attrs=cdf_attrs.get_variable_attributes("esa_sweep_attrs"),
125
- )
126
-
127
- elif variable_name == "acquisition_times":
128
- values = l1a_dataset["acquisition_times"]
129
- l1b_dataset["acquisition_times"] = xr.DataArray(
130
- values,
131
- dims=["energy"],
132
- attrs=cdf_attrs.get_variable_attributes("acquisition_times_attrs"),
133
- )
134
-
135
- else:
136
- # Get the data array from the L1a data product
137
- values = l1a_dataset[variable_name].values
138
-
139
- # Convert data array to "rates"
140
- # TODO: For SIT-3, just convert value to float. Revisit after SIT-3.
141
- variable_data_arr = values.astype(float)
142
-
143
- # Put the new data array into the dataset
144
- cdf_attrs_key = (
145
- f"{dataset_name.split('imap_codice_l1b_')[-1]}-{variable_name}"
146
- )
147
- l1b_dataset[variable_name] = xr.DataArray(
148
- variable_data_arr,
149
- name=variable_name,
150
- dims=["epoch", "energy"],
151
- attrs=cdf_attrs.get_variable_attributes(cdf_attrs_key),
152
- )
153
-
154
- return l1b_dataset
155
-
156
-
157
- def process_codice_l1b(l1a_dataset: xr.Dataset, data_version: str) -> xr.Dataset:
25
+ def process_codice_l1b(file_path: Path) -> xr.Dataset:
158
26
  """
159
27
  Will process CoDICE l1a data to create l1b data products.
160
28
 
161
29
  Parameters
162
30
  ----------
163
- l1a_dataset : xarray.Dataset
164
- CoDICE L1a dataset to process.
165
- data_version : str
166
- Version of the data product being created.
31
+ file_path : pathlib.Path
32
+ Path to the CoDICE L1a file to process.
167
33
 
168
34
  Returns
169
35
  -------
170
36
  l1b_dataset : xarray.Dataset
171
37
  The``xarray`` dataset containing the science data and supporting metadata.
172
38
  """
173
- logger.info(f"\nProcessing {l1a_dataset.attrs['Logical_source']}.")
39
+ logger.info(f"\nProcessing {file_path}")
174
40
 
175
- # Start constructing l1b dataset
41
+ # Open the l1a file
42
+ l1a_dataset = load_cdf(file_path)
43
+
44
+ # Use the logical source as a way to distinguish between data products and
45
+ # set some useful distinguishing variables
46
+ dataset_name = l1a_dataset.attrs["Logical_source"].replace("_l1a_", "_l1b_")
47
+ descriptor = dataset_name.removeprefix("imap_codice_l1b_")
48
+ apid = constants.CODICEAPID_MAPPING[descriptor]
49
+
50
+ # Get the L1b CDF attributes
176
51
  cdf_attrs = ImapCdfAttributes()
177
52
  cdf_attrs.add_instrument_global_attrs("codice")
178
53
  cdf_attrs.add_instrument_variable_attrs("codice", "l1b")
179
- cdf_attrs.add_global_attribute("Data_version", data_version)
180
54
 
181
- dataset_name = (
182
- l1a_dataset.attrs["Logical_source"].replace("-", "_").replace("l1a", "l1b")
183
- )
55
+ # Use the L1a data product as a starting point for L1b
56
+ l1b_dataset = l1a_dataset.copy()
184
57
 
185
- if "hskp" in dataset_name:
186
- l1b_dataset = create_hskp_dataset(l1a_dataset, cdf_attrs)
58
+ # Update the global attributes
59
+ l1b_dataset.attrs = cdf_attrs.get_global_attributes(dataset_name)
187
60
 
61
+ # Determine which variables need to be converted from counts to rates
62
+ # TODO: Figure out exactly which hskp variables need to be converted
63
+ if descriptor == "hskp":
64
+ data_variables = []
65
+ support_variables = ["cmdexe", "cmdrjct"]
66
+ variables_to_convert = support_variables
188
67
  else:
189
- l1b_dataset = create_science_dataset(l1a_dataset, cdf_attrs, dataset_name)
68
+ data_variables = getattr(
69
+ constants, f"{descriptor.upper().replace('-', '_')}_VARIABLE_NAMES"
70
+ )
71
+ support_variables = constants.DATA_PRODUCT_CONFIGURATIONS[apid][
72
+ "support_variables"
73
+ ]
74
+ variables_to_convert = data_variables + support_variables
75
+
76
+ for variable_name in variables_to_convert:
77
+ # Apply conversion of data from counts to rates
78
+ # TODO: Properly implement conversion factors on a per-data-product basis
79
+ # For now, just divide by 100 to get float values
80
+ l1b_dataset[variable_name].data = l1b_dataset[variable_name].data / 100
81
+
82
+ # Set the variable attributes
83
+ if variable_name in data_variables:
84
+ cdf_attrs_key = f"{descriptor}-{variable_name}"
85
+ elif variable_name in support_variables:
86
+ cdf_attrs_key = variable_name
87
+ l1b_dataset[variable_name].attrs = cdf_attrs.get_variable_attributes(
88
+ cdf_attrs_key
89
+ )
190
90
 
191
- # Write the dataset to CDF
192
91
  logger.info(f"\nFinal data product:\n{l1b_dataset}\n")
193
92
 
194
93
  return l1b_dataset