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
@@ -0,0 +1,230 @@
1
+ from unittest.mock import patch
2
+
3
+ import numpy as np
4
+ import pytest
5
+
6
+ from imap_processing.ena_maps import ena_maps
7
+ from imap_processing.ena_maps.utils.coordinates import CoordNames
8
+ from imap_processing.tests.ultra.data.mock_data import mock_l1c_pset_product_healpix
9
+ from imap_processing.ultra.l2 import ultra_l2
10
+
11
+
12
+ class TestUltraL2:
13
+ @pytest.fixture
14
+ def _setup_spice_kernels_list(self, spice_test_data_path, furnish_kernels):
15
+ self.required_kernel_names = [
16
+ "imap_science_0001.tf",
17
+ "imap_sclk_0000.tsc",
18
+ "sim_1yr_imap_attitude.bc",
19
+ "sim_1yr_imap_pointing_frame.bc",
20
+ ]
21
+
22
+ @pytest.fixture
23
+ def _mock_single_pset(self, _setup_spice_kernels_list, furnish_kernels):
24
+ with furnish_kernels(self.required_kernel_names):
25
+ self.ultra_pset = mock_l1c_pset_product_healpix(
26
+ nside=128, stripe_center_lat=0, timestr="2025-05-15T12:00:00"
27
+ )
28
+
29
+ @pytest.fixture
30
+ def _mock_multiple_psets(self, _setup_spice_kernels_list, furnish_kernels):
31
+ with furnish_kernels(self.required_kernel_names):
32
+ self.ultra_psets = [
33
+ mock_l1c_pset_product_healpix(
34
+ nside=128,
35
+ stripe_center_lat=mid_latitude,
36
+ width_scale=5,
37
+ counts_scaling_params=(50, 0.5),
38
+ peak_exposure=1000,
39
+ timestr=f"2025-05-{4 * i + 1:02d}T12:00:00",
40
+ head=("90"),
41
+ )
42
+ for i, mid_latitude in enumerate(
43
+ np.arange(
44
+ 22.5,
45
+ 180,
46
+ 45,
47
+ )
48
+ )
49
+ ]
50
+
51
+ self.psets_total_counts = np.sum(
52
+ [pset["counts"].values.sum() for pset in self.ultra_psets]
53
+ )
54
+
55
+ @pytest.fixture
56
+ def mock_data_dict(self, _mock_multiple_psets):
57
+ return {pset.attrs["Logical_file_id"]: pset for pset in self.ultra_psets}
58
+
59
+ @pytest.mark.parametrize(
60
+ ["map_frame", "rtol"],
61
+ [
62
+ # Tight tolerance when 'projecting' to the same frame
63
+ ("IMAP_DPS", 1e-8),
64
+ # Loose tolerance of 30% error vs naive flux estimate with real projection.
65
+ # TODO: Ideally this tolerance will tighten if we can fix the issue with
66
+ # the exposure time for uneven numbers of pixels from each PointingSet.
67
+ ("ECLIPJ2000", 3e-1),
68
+ ],
69
+ )
70
+ @pytest.mark.usefixtures("_mock_single_pset", "_setup_spice_kernels_list")
71
+ def test_generate_ultra_healpix_skymap_single_pset(
72
+ self, map_frame, rtol, furnish_kernels
73
+ ):
74
+ # Avoid modifying the original pset
75
+ pset = self.ultra_pset.copy(deep=True)
76
+
77
+ # Set the values in the single input PSET for easy calculation
78
+ # of the expected flux and flux uncertainty
79
+ pset["counts"].values = np.full_like(pset["counts"].values, 10)
80
+ pset["exposure_factor"].values = np.ones_like(pset["exposure_factor"].values)
81
+ pset["background_rates"].values = np.ones_like(pset["background_rates"].values)
82
+ pset["sensitivity"].values = np.ones_like(pset["sensitivity"].values)
83
+ pset["energy_bin_delta"].values = np.ones_like(pset["energy_bin_delta"].values)
84
+
85
+ # Create the Healpix skymap in the desired frame.
86
+ with furnish_kernels(self.required_kernel_names):
87
+ hp_skymap = ultra_l2.generate_ultra_healpix_skymap(
88
+ ultra_l1c_psets=[
89
+ pset,
90
+ ],
91
+ output_map_structure=ena_maps.AbstractSkyMap.from_dict(
92
+ {
93
+ "sky_tiling_type": "HEALPIX",
94
+ "spice_reference_frame": map_frame,
95
+ "projection_method_and_values": {
96
+ "PUSH": [
97
+ "counts",
98
+ "exposure_factor",
99
+ "sensitivity",
100
+ "background_rates",
101
+ ],
102
+ },
103
+ "nside": 32,
104
+ "nested": False,
105
+ }
106
+ ),
107
+ )
108
+
109
+ assert hp_skymap.nside == 32
110
+ assert hp_skymap.nested is False
111
+
112
+ # Check that required variables are present, and dropped variables are not
113
+ expected_vars = [
114
+ "flux",
115
+ "flux_uncertainty",
116
+ "exposure_factor",
117
+ "observation_time",
118
+ ]
119
+ for var in expected_vars:
120
+ assert var in hp_skymap.data_1d.data_vars
121
+ unexpected_vars = ultra_l2.VARIABLES_TO_DROP_AFTER_FLUX_CALCULATION
122
+ for var in unexpected_vars:
123
+ assert var not in hp_skymap.data_1d.data_vars
124
+
125
+ # The ratio of the solid angle of a map's pixel to the solid angle of a
126
+ # pointing set's pixel. Counts will scale up by this ratio.
127
+ solid_angle_ratio_map_to_pset = (
128
+ hp_skymap.solid_angle / ena_maps.UltraPointingSet(pset).solid_angle
129
+ )
130
+
131
+ # Estimate the expected flux and flux uncertainty
132
+ expected_flux = (
133
+ (10 * solid_angle_ratio_map_to_pset / 1) - 1 * solid_angle_ratio_map_to_pset
134
+ ) / (1 * hp_skymap.solid_angle * 1)
135
+ expected_flux_unc = ((10 * solid_angle_ratio_map_to_pset) ** 0.5 / 1) / (
136
+ 1 * hp_skymap.solid_angle * 1
137
+ )
138
+
139
+ np.testing.assert_allclose(
140
+ hp_skymap.data_1d["flux"].values,
141
+ expected_flux,
142
+ rtol=rtol,
143
+ )
144
+ np.testing.assert_allclose(
145
+ hp_skymap.data_1d["flux_uncertainty"].values,
146
+ expected_flux_unc,
147
+ rtol=rtol,
148
+ )
149
+
150
+ @pytest.mark.usefixtures("_mock_multiple_psets", "_setup_spice_kernels_list")
151
+ def test_generate_ultra_healpix_skymap_multiple_psets(self, furnish_kernels):
152
+ with patch(
153
+ "imap_processing.ultra.l2.ultra_l2.VARIABLES_TO_DROP_AFTER_FLUX_CALCULATION",
154
+ [],
155
+ ):
156
+ with furnish_kernels(self.required_kernel_names):
157
+ hp_skymap = ultra_l2.generate_ultra_healpix_skymap(
158
+ ultra_l1c_psets=self.ultra_psets,
159
+ output_map_structure=ena_maps.AbstractSkyMap.from_dict(
160
+ {
161
+ "sky_tiling_type": "RECTANGULAR",
162
+ "spice_reference_frame": "ECLIPJ2000",
163
+ "projection_method_and_values": {
164
+ "PUSH": [
165
+ "counts",
166
+ "exposure_factor",
167
+ "sensitivity",
168
+ "background_rates",
169
+ ],
170
+ },
171
+ "spacing_deg": 2.0,
172
+ }
173
+ ),
174
+ )
175
+
176
+ assert hp_skymap.nside == ultra_l2.DEFAULT_L2_HEALPIX_NSIDE
177
+ assert hp_skymap.nested == ultra_l2.DEFAULT_L2_HEALPIX_NESTED
178
+
179
+ # The total counts in the skymap should be equal to the sum of the counts
180
+ # in the individual psets
181
+ np.testing.assert_allclose(
182
+ hp_skymap.data_1d["counts"].sum(),
183
+ self.psets_total_counts,
184
+ )
185
+
186
+ # The map should contain the following variables,
187
+ # because we did not drop any variables
188
+ expected_vars = (
189
+ ultra_l2.REQUIRED_L1C_VARIABLES
190
+ + ultra_l2.VARIABLES_TO_DROP_AFTER_FLUX_CALCULATION
191
+ + ["flux", "flux_uncertainty"]
192
+ )
193
+ for var in expected_vars:
194
+ assert var in hp_skymap.data_1d.data_vars
195
+
196
+ # Check the dims of the key variables
197
+ counts_dims = (
198
+ CoordNames.TIME.value,
199
+ CoordNames.ENERGY_ULTRA.value,
200
+ CoordNames.GENERIC_PIXEL.value,
201
+ )
202
+ assert hp_skymap.data_1d["counts"].dims == counts_dims
203
+ assert hp_skymap.data_1d["flux"].dims == counts_dims
204
+ assert hp_skymap.data_1d["flux_uncertainty"].dims == counts_dims
205
+ assert hp_skymap.data_1d["exposure_factor"].dims == counts_dims[-1:]
206
+
207
+ @pytest.mark.usefixtures("_setup_spice_kernels_list")
208
+ def test_ultra_l2_output_unbinned_healpix(self, mock_data_dict, furnish_kernels):
209
+ map_structure = ena_maps.AbstractSkyMap.from_dict(
210
+ {
211
+ "sky_tiling_type": "HEALPIX",
212
+ "spice_reference_frame": "ECLIPJ2000",
213
+ "projection_method_and_values": {
214
+ "PUSH": ["counts", "exposure_factor", "sensitivity"],
215
+ },
216
+ "nside": 16,
217
+ "nested": True,
218
+ }
219
+ )
220
+ with furnish_kernels(self.required_kernel_names):
221
+ [
222
+ map_dataset,
223
+ ] = ultra_l2.ultra_l2(
224
+ data_dict=mock_data_dict,
225
+ data_version="001",
226
+ output_map_structure=map_structure,
227
+ )
228
+
229
+ assert map_dataset.attrs["HEALPix_nside"] == map_structure.nside
230
+ assert map_dataset.attrs["HEALPix_nest"] == map_structure.nested
@@ -80,4 +80,4 @@ class UltraConstants:
80
80
  CULLING_RPM_MAX = 6.0
81
81
 
82
82
  # Thresholds for culling based on counts.
83
- CULLING_ENERGY_BIN_EDGES: ClassVar[list] = [-1e5, 0, 10, 20, 1e5]
83
+ CULLING_ENERGY_BIN_EDGES: ClassVar[list] = [0, 10, 20, 1e5]
@@ -1,12 +1,9 @@
1
1
  """Ultra Decompression Tools."""
2
2
 
3
3
  import numpy as np
4
- import numpy.typing as npt
5
- import space_packet_parser
4
+ from numpy.typing import NDArray
6
5
 
7
6
  from imap_processing.ultra.l0.ultra_utils import (
8
- EVENT_FIELD_RANGES,
9
- append_fillval,
10
7
  parse_event,
11
8
  )
12
9
  from imap_processing.utils import convert_to_binary_string
@@ -157,7 +154,7 @@ def decompress_image(
157
154
  binary_data: str,
158
155
  width_bit: int,
159
156
  mantissa_bit_length: int,
160
- ) -> npt.NDArray:
157
+ ) -> NDArray:
161
158
  """
162
159
  Will decompress a binary string representing an image into a matrix of pixel values.
163
160
 
@@ -178,14 +175,14 @@ def decompress_image(
178
175
 
179
176
  Returns
180
177
  -------
181
- p_decom : numpy.ndarray
178
+ p_decom : NDArray
182
179
  A 2D numpy array representing pixel values.
183
180
  Each pixel is stored as an unsigned 16-bit integer (uint16).
184
181
 
185
182
  Notes
186
183
  -----
187
184
  This process is described starting on page 168 in IMAP-Ultra Flight
188
- Software Specification document (7523-9009_Rev_-.pdf).
185
+ Software Specification document.
189
186
  """
190
187
  rows = 54
191
188
  cols = 180
@@ -240,44 +237,34 @@ def decompress_image(
240
237
 
241
238
 
242
239
  def read_image_raw_events_binary(
243
- packet: space_packet_parser.packets.CCSDSPacket, decom_data: dict
244
- ) -> dict:
240
+ event_data: bytes,
241
+ count: int,
242
+ ) -> NDArray:
245
243
  """
246
244
  Convert contents of binary string 'EVENTDATA' into values.
247
245
 
248
246
  Parameters
249
247
  ----------
250
- packet : space_packet_parser.packets.CCSDSPacket
251
- Packet.
252
- decom_data : dict
253
- Parsed data.
248
+ event_data : bytes
249
+ Event data.
250
+ count : int
251
+ Number of events.
254
252
 
255
253
  Returns
256
254
  -------
257
- decom_data : dict
258
- Each for loop appends to the existing dictionary.
255
+ event_data : NDArray
256
+ Event data.
259
257
  """
260
- binary = convert_to_binary_string(packet["EVENTDATA"])
261
- count = packet["COUNT"]
258
+ binary = convert_to_binary_string(event_data)
262
259
  # 166 bits per event
263
260
  event_length = 166 if count else 0
264
-
265
- # Uses fill value for all packets that do not contain event data.
266
- if count == 0:
267
- # if decom_data is empty, append fill values to all fields
268
- if not decom_data:
269
- for field in EVENT_FIELD_RANGES.keys():
270
- decom_data[field] = []
271
- append_fillval(decom_data, packet)
261
+ event_data_list = []
272
262
 
273
263
  # For all packets with event data, parses the binary string
274
- else:
275
- for i in range(count):
276
- start_index = i * event_length
277
- event_binary = binary[start_index : start_index + event_length]
278
- event_data = parse_event(event_binary)
279
-
280
- for key, value in event_data.items():
281
- decom_data[key].append(value)
264
+ for i in range(count):
265
+ start_index = i * event_length
266
+ event_binary = binary[start_index : start_index + event_length]
267
+ parsed_event = parse_event(event_binary)
268
+ event_data_list.append(parsed_event)
282
269
 
283
- return decom_data
270
+ return np.array(event_data_list)