imap-processing 0.11.0__py3-none-any.whl → 0.13.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of imap-processing might be problematic. Click here for more details.

Files changed (415) hide show
  1. imap_processing/__init__.py +11 -11
  2. imap_processing/_version.py +2 -2
  3. imap_processing/ccsds/ccsds_data.py +1 -2
  4. imap_processing/ccsds/excel_to_xtce.py +66 -18
  5. imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +24 -40
  6. imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +934 -42
  7. imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +1846 -128
  8. imap_processing/cdf/config/imap_glows_global_cdf_attrs.yaml +0 -5
  9. imap_processing/cdf/config/imap_hi_global_cdf_attrs.yaml +10 -11
  10. imap_processing/cdf/config/imap_hi_variable_attrs.yaml +17 -19
  11. imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +27 -14
  12. imap_processing/cdf/config/imap_hit_l1a_variable_attrs.yaml +106 -116
  13. imap_processing/cdf/config/imap_hit_l1b_variable_attrs.yaml +120 -145
  14. imap_processing/cdf/config/imap_hit_l2_variable_attrs.yaml +14 -0
  15. imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +25 -9
  16. imap_processing/cdf/config/imap_idex_l1a_variable_attrs.yaml +6 -4
  17. imap_processing/cdf/config/imap_idex_l1b_variable_attrs.yaml +3 -3
  18. imap_processing/cdf/config/imap_lo_global_cdf_attrs.yaml +0 -12
  19. imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +1 -1
  20. imap_processing/cdf/config/imap_mag_global_cdf_attrs.yaml +23 -20
  21. imap_processing/cdf/config/imap_mag_l1a_variable_attrs.yaml +361 -0
  22. imap_processing/cdf/config/imap_mag_l1b_variable_attrs.yaml +160 -0
  23. imap_processing/cdf/config/imap_mag_l1c_variable_attrs.yaml +160 -0
  24. imap_processing/cdf/config/imap_spacecraft_global_cdf_attrs.yaml +18 -0
  25. imap_processing/cdf/config/imap_spacecraft_variable_attrs.yaml +40 -0
  26. imap_processing/cdf/config/imap_swapi_global_cdf_attrs.yaml +1 -5
  27. imap_processing/cdf/config/imap_swapi_variable_attrs.yaml +22 -0
  28. imap_processing/cdf/config/imap_swe_global_cdf_attrs.yaml +12 -4
  29. imap_processing/cdf/config/imap_swe_l1a_variable_attrs.yaml +16 -2
  30. imap_processing/cdf/config/imap_swe_l1b_variable_attrs.yaml +64 -52
  31. imap_processing/cdf/config/imap_swe_l2_variable_attrs.yaml +71 -47
  32. imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +180 -19
  33. imap_processing/cdf/config/imap_ultra_l1a_variable_attrs.yaml +5045 -41
  34. imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +80 -17
  35. imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +32 -57
  36. imap_processing/cdf/utils.py +52 -38
  37. imap_processing/cli.py +477 -233
  38. imap_processing/codice/codice_l1a.py +466 -131
  39. imap_processing/codice/codice_l1b.py +51 -152
  40. imap_processing/codice/constants.py +1360 -569
  41. imap_processing/codice/decompress.py +2 -6
  42. imap_processing/ena_maps/ena_maps.py +1103 -146
  43. imap_processing/ena_maps/utils/coordinates.py +19 -0
  44. imap_processing/ena_maps/utils/map_utils.py +14 -17
  45. imap_processing/ena_maps/utils/spatial_utils.py +55 -52
  46. imap_processing/glows/l1a/glows_l1a.py +28 -99
  47. imap_processing/glows/l1a/glows_l1a_data.py +2 -2
  48. imap_processing/glows/l1b/glows_l1b.py +1 -4
  49. imap_processing/glows/l1b/glows_l1b_data.py +1 -3
  50. imap_processing/glows/l2/glows_l2.py +2 -5
  51. imap_processing/hi/l1a/hi_l1a.py +54 -29
  52. imap_processing/hi/l1a/histogram.py +0 -1
  53. imap_processing/hi/l1a/science_direct_event.py +6 -8
  54. imap_processing/hi/l1b/hi_l1b.py +111 -82
  55. imap_processing/hi/l1c/hi_l1c.py +416 -32
  56. imap_processing/hi/utils.py +58 -12
  57. imap_processing/hit/ancillary/imap_hit_l1b-to-l2-sector-dt0-factors_20250219_v002.csv +81 -0
  58. imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt0-factors_20250219_v002.csv +205 -0
  59. imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt1-factors_20250219_v002.csv +205 -0
  60. imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt2-factors_20250219_v002.csv +205 -0
  61. imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt3-factors_20250219_v002.csv +205 -0
  62. imap_processing/hit/ancillary/imap_hit_l1b-to-l2-summed-dt0-factors_20250219_v002.csv +68 -0
  63. imap_processing/hit/hit_utils.py +235 -5
  64. imap_processing/hit/l0/constants.py +20 -11
  65. imap_processing/hit/l0/decom_hit.py +21 -5
  66. imap_processing/hit/l1a/hit_l1a.py +71 -75
  67. imap_processing/hit/l1b/constants.py +321 -0
  68. imap_processing/hit/l1b/hit_l1b.py +377 -67
  69. imap_processing/hit/l2/constants.py +318 -0
  70. imap_processing/hit/l2/hit_l2.py +723 -0
  71. imap_processing/hit/packet_definitions/hit_packet_definitions.xml +1323 -71
  72. imap_processing/ialirt/l0/mag_l0_ialirt_data.py +155 -0
  73. imap_processing/ialirt/l0/parse_mag.py +374 -0
  74. imap_processing/ialirt/l0/process_swapi.py +69 -0
  75. imap_processing/ialirt/l0/process_swe.py +548 -0
  76. imap_processing/ialirt/packet_definitions/ialirt.xml +216 -208
  77. imap_processing/ialirt/packet_definitions/ialirt_codicehi.xml +1 -1
  78. imap_processing/ialirt/packet_definitions/ialirt_codicelo.xml +1 -1
  79. imap_processing/ialirt/packet_definitions/ialirt_mag.xml +115 -0
  80. imap_processing/ialirt/packet_definitions/ialirt_swapi.xml +14 -14
  81. imap_processing/ialirt/utils/grouping.py +114 -0
  82. imap_processing/ialirt/utils/time.py +29 -0
  83. imap_processing/idex/atomic_masses.csv +22 -0
  84. imap_processing/idex/decode.py +2 -2
  85. imap_processing/idex/idex_constants.py +33 -0
  86. imap_processing/idex/idex_l0.py +22 -8
  87. imap_processing/idex/idex_l1a.py +81 -51
  88. imap_processing/idex/idex_l1b.py +13 -39
  89. imap_processing/idex/idex_l2a.py +823 -0
  90. imap_processing/idex/idex_l2b.py +120 -0
  91. imap_processing/idex/idex_variable_unpacking_and_eu_conversion.csv +11 -11
  92. imap_processing/idex/packet_definitions/idex_housekeeping_packet_definition.xml +9130 -0
  93. imap_processing/lo/l0/lo_science.py +7 -2
  94. imap_processing/lo/l1a/lo_l1a.py +1 -5
  95. imap_processing/lo/l1b/lo_l1b.py +702 -29
  96. imap_processing/lo/l1b/tof_conversions.py +11 -0
  97. imap_processing/lo/l1c/lo_l1c.py +1 -4
  98. imap_processing/mag/constants.py +51 -0
  99. imap_processing/mag/imap_mag_sdc_configuration_v001.py +8 -0
  100. imap_processing/mag/l0/decom_mag.py +10 -3
  101. imap_processing/mag/l1a/mag_l1a.py +23 -19
  102. imap_processing/mag/l1a/mag_l1a_data.py +35 -10
  103. imap_processing/mag/l1b/mag_l1b.py +259 -50
  104. imap_processing/mag/l1c/interpolation_methods.py +388 -0
  105. imap_processing/mag/l1c/mag_l1c.py +621 -17
  106. imap_processing/mag/l2/mag_l2.py +140 -0
  107. imap_processing/mag/l2/mag_l2_data.py +288 -0
  108. imap_processing/quality_flags.py +1 -0
  109. imap_processing/spacecraft/packet_definitions/scid_x252.xml +538 -0
  110. imap_processing/spacecraft/quaternions.py +121 -0
  111. imap_processing/spice/geometry.py +19 -22
  112. imap_processing/spice/kernels.py +0 -276
  113. imap_processing/spice/pointing_frame.py +257 -0
  114. imap_processing/spice/repoint.py +149 -0
  115. imap_processing/spice/spin.py +38 -33
  116. imap_processing/spice/time.py +24 -0
  117. imap_processing/swapi/l1/swapi_l1.py +20 -12
  118. imap_processing/swapi/l2/swapi_l2.py +116 -5
  119. imap_processing/swapi/swapi_utils.py +32 -0
  120. imap_processing/swe/l1a/swe_l1a.py +44 -12
  121. imap_processing/swe/l1a/swe_science.py +13 -13
  122. imap_processing/swe/l1b/swe_l1b.py +898 -23
  123. imap_processing/swe/l2/swe_l2.py +75 -136
  124. imap_processing/swe/packet_definitions/swe_packet_definition.xml +1121 -1
  125. imap_processing/swe/utils/swe_constants.py +64 -0
  126. imap_processing/swe/utils/swe_utils.py +85 -28
  127. imap_processing/tests/ccsds/test_data/expected_output.xml +40 -1
  128. imap_processing/tests/ccsds/test_excel_to_xtce.py +24 -21
  129. imap_processing/tests/cdf/test_data/imap_instrument2_global_cdf_attrs.yaml +0 -2
  130. imap_processing/tests/cdf/test_utils.py +14 -16
  131. imap_processing/tests/codice/conftest.py +44 -33
  132. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-counters-aggregated_20241110193700_v0.0.0.cdf +0 -0
  133. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-counters-singles_20241110193700_v0.0.0.cdf +0 -0
  134. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-ialirt_20241110193700_v0.0.0.cdf +0 -0
  135. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-omni_20241110193700_v0.0.0.cdf +0 -0
  136. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-pha_20241110193700_v0.0.0.cdf +0 -0
  137. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-priorities_20241110193700_v0.0.0.cdf +0 -0
  138. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-sectored_20241110193700_v0.0.0.cdf +0 -0
  139. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-counters-aggregated_20241110193700_v0.0.0.cdf +0 -0
  140. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-counters-singles_20241110193700_v0.0.0.cdf +0 -0
  141. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-ialirt_20241110193700_v0.0.0.cdf +0 -0
  142. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-angular_20241110193700_v0.0.0.cdf +0 -0
  143. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-priority_20241110193700_v0.0.0.cdf +0 -0
  144. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-species_20241110193700_v0.0.0.cdf +0 -0
  145. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-pha_20241110193700_v0.0.0.cdf +0 -0
  146. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-angular_20241110193700_v0.0.0.cdf +0 -0
  147. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-priority_20241110193700_v0.0.0.cdf +0 -0
  148. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-species_20241110193700_v0.0.0.cdf +0 -0
  149. imap_processing/tests/codice/test_codice_l1a.py +126 -53
  150. imap_processing/tests/codice/test_codice_l1b.py +6 -7
  151. imap_processing/tests/codice/test_decompress.py +4 -4
  152. imap_processing/tests/conftest.py +239 -27
  153. imap_processing/tests/ena_maps/conftest.py +51 -0
  154. imap_processing/tests/ena_maps/test_ena_maps.py +1068 -110
  155. imap_processing/tests/ena_maps/test_map_utils.py +66 -43
  156. imap_processing/tests/ena_maps/test_spatial_utils.py +17 -21
  157. imap_processing/tests/glows/conftest.py +10 -14
  158. imap_processing/tests/glows/test_glows_decom.py +4 -4
  159. imap_processing/tests/glows/test_glows_l1a_cdf.py +6 -27
  160. imap_processing/tests/glows/test_glows_l1a_data.py +6 -8
  161. imap_processing/tests/glows/test_glows_l1b.py +11 -11
  162. imap_processing/tests/glows/test_glows_l1b_data.py +5 -5
  163. imap_processing/tests/glows/test_glows_l2.py +2 -8
  164. imap_processing/tests/hi/conftest.py +1 -1
  165. imap_processing/tests/hi/data/l0/H45_diag_fee_20250208.bin +0 -0
  166. imap_processing/tests/hi/data/l0/H45_diag_fee_20250208_verify.csv +205 -0
  167. imap_processing/tests/hi/test_hi_l1b.py +22 -27
  168. imap_processing/tests/hi/test_hi_l1c.py +249 -18
  169. imap_processing/tests/hi/test_l1a.py +35 -7
  170. imap_processing/tests/hi/test_science_direct_event.py +3 -3
  171. imap_processing/tests/hi/test_utils.py +24 -2
  172. imap_processing/tests/hit/helpers/l1_validation.py +74 -73
  173. imap_processing/tests/hit/test_data/hskp_sample.ccsds +0 -0
  174. imap_processing/tests/hit/test_data/imap_hit_l0_raw_20100105_v001.pkts +0 -0
  175. imap_processing/tests/hit/test_decom_hit.py +5 -1
  176. imap_processing/tests/hit/test_hit_l1a.py +32 -36
  177. imap_processing/tests/hit/test_hit_l1b.py +300 -81
  178. imap_processing/tests/hit/test_hit_l2.py +716 -0
  179. imap_processing/tests/hit/test_hit_utils.py +184 -7
  180. imap_processing/tests/hit/validation_data/hit_l1b_standard_sample2_nsrl_v4_3decimals.csv +62 -62
  181. imap_processing/tests/hit/validation_data/hskp_sample_eu_3_6_2025.csv +89 -0
  182. imap_processing/tests/hit/validation_data/hskp_sample_raw.csv +89 -88
  183. imap_processing/tests/hit/validation_data/sci_sample_raw.csv +1 -1
  184. imap_processing/tests/ialirt/data/l0/461971383-404.bin +0 -0
  185. imap_processing/tests/ialirt/data/l0/461971384-405.bin +0 -0
  186. imap_processing/tests/ialirt/data/l0/461971385-406.bin +0 -0
  187. imap_processing/tests/ialirt/data/l0/461971386-407.bin +0 -0
  188. imap_processing/tests/ialirt/data/l0/461971387-408.bin +0 -0
  189. imap_processing/tests/ialirt/data/l0/461971388-409.bin +0 -0
  190. imap_processing/tests/ialirt/data/l0/461971389-410.bin +0 -0
  191. imap_processing/tests/ialirt/data/l0/461971390-411.bin +0 -0
  192. imap_processing/tests/ialirt/data/l0/461971391-412.bin +0 -0
  193. imap_processing/tests/ialirt/data/l0/sample_decoded_i-alirt_data.csv +383 -0
  194. imap_processing/tests/ialirt/unit/test_decom_ialirt.py +16 -81
  195. imap_processing/tests/ialirt/unit/test_grouping.py +81 -0
  196. imap_processing/tests/ialirt/unit/test_parse_mag.py +223 -0
  197. imap_processing/tests/ialirt/unit/test_process_codicehi.py +3 -3
  198. imap_processing/tests/ialirt/unit/test_process_codicelo.py +3 -10
  199. imap_processing/tests/ialirt/unit/test_process_ephemeris.py +4 -4
  200. imap_processing/tests/ialirt/unit/test_process_hit.py +3 -3
  201. imap_processing/tests/ialirt/unit/test_process_swapi.py +24 -16
  202. imap_processing/tests/ialirt/unit/test_process_swe.py +319 -6
  203. imap_processing/tests/ialirt/unit/test_time.py +16 -0
  204. imap_processing/tests/idex/conftest.py +127 -6
  205. imap_processing/tests/idex/test_data/imap_idex_l0_raw_20231218_v001.pkts +0 -0
  206. imap_processing/tests/idex/test_data/imap_idex_l0_raw_20241206_v001.pkts +0 -0
  207. imap_processing/tests/idex/test_data/imap_idex_l0_raw_20250108_v001.pkts +0 -0
  208. imap_processing/tests/idex/test_data/impact_14_tof_high_data.txt +4508 -4508
  209. imap_processing/tests/idex/test_idex_l0.py +33 -11
  210. imap_processing/tests/idex/test_idex_l1a.py +92 -21
  211. imap_processing/tests/idex/test_idex_l1b.py +106 -27
  212. imap_processing/tests/idex/test_idex_l2a.py +399 -0
  213. imap_processing/tests/idex/test_idex_l2b.py +93 -0
  214. imap_processing/tests/lo/test_cdfs/imap_lo_l1a_de_20241022_v002.cdf +0 -0
  215. imap_processing/tests/lo/test_cdfs/imap_lo_l1a_spin_20241022_v002.cdf +0 -0
  216. imap_processing/tests/lo/test_lo_l1a.py +3 -3
  217. imap_processing/tests/lo/test_lo_l1b.py +515 -6
  218. imap_processing/tests/lo/test_lo_l1c.py +1 -1
  219. imap_processing/tests/lo/test_lo_science.py +7 -7
  220. imap_processing/tests/lo/test_star_sensor.py +1 -1
  221. imap_processing/tests/mag/conftest.py +120 -2
  222. imap_processing/tests/mag/test_mag_decom.py +5 -4
  223. imap_processing/tests/mag/test_mag_l1a.py +51 -7
  224. imap_processing/tests/mag/test_mag_l1b.py +40 -59
  225. imap_processing/tests/mag/test_mag_l1c.py +354 -19
  226. imap_processing/tests/mag/test_mag_l2.py +130 -0
  227. imap_processing/tests/mag/test_mag_validation.py +247 -26
  228. imap_processing/tests/mag/validation/L1b/T009/MAGScience-normal-(2,2)-8s-20250204-16h39.csv +17 -0
  229. imap_processing/tests/mag/validation/L1b/T009/mag-l1a-l1b-t009-magi-out.csv +16 -16
  230. imap_processing/tests/mag/validation/L1b/T009/mag-l1a-l1b-t009-mago-out.csv +16 -16
  231. imap_processing/tests/mag/validation/L1b/T010/MAGScience-normal-(2,2)-8s-20250206-12h05.csv +17 -0
  232. imap_processing/tests/mag/validation/L1b/T011/MAGScience-normal-(2,2)-8s-20250204-16h08.csv +17 -0
  233. imap_processing/tests/mag/validation/L1b/T011/mag-l1a-l1b-t011-magi-out.csv +16 -16
  234. imap_processing/tests/mag/validation/L1b/T011/mag-l1a-l1b-t011-mago-out.csv +16 -16
  235. imap_processing/tests/mag/validation/L1b/T012/MAGScience-normal-(2,2)-8s-20250204-16h08.csv +17 -0
  236. imap_processing/tests/mag/validation/L1b/T012/data.bin +0 -0
  237. imap_processing/tests/mag/validation/L1b/T012/field_like_all_ranges.txt +19200 -0
  238. imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-cal.cdf +0 -0
  239. imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-in.csv +17 -0
  240. imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-magi-out.csv +17 -0
  241. imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-mago-out.csv +17 -0
  242. imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-magi-normal-in.csv +1217 -0
  243. imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-magi-normal-out.csv +1857 -0
  244. imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-mago-normal-in.csv +1217 -0
  245. imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-mago-normal-out.csv +1857 -0
  246. imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-magi-normal-in.csv +1217 -0
  247. imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-magi-normal-out.csv +1793 -0
  248. imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-mago-normal-in.csv +1217 -0
  249. imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-mago-normal-out.csv +1793 -0
  250. imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-magi-burst-in.csv +2561 -0
  251. imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-magi-normal-in.csv +961 -0
  252. imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-magi-normal-out.csv +1539 -0
  253. imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-mago-normal-in.csv +1921 -0
  254. imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-mago-normal-out.csv +2499 -0
  255. imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-magi-normal-in.csv +865 -0
  256. imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-magi-normal-out.csv +1196 -0
  257. imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-mago-normal-in.csv +1729 -0
  258. imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-mago-normal-out.csv +3053 -0
  259. imap_processing/tests/mag/validation/L2/imap_mag_l1b_norm-mago_20251017_v002.cdf +0 -0
  260. imap_processing/tests/mag/validation/calibration/imap_mag_l1b-calibration_20240229_v001.cdf +0 -0
  261. imap_processing/tests/mag/validation/calibration/imap_mag_l2-calibration-matrices_20251017_v004.cdf +0 -0
  262. imap_processing/tests/mag/validation/calibration/imap_mag_l2-offsets-norm_20251017_20251017_v001.cdf +0 -0
  263. imap_processing/tests/spacecraft/data/SSR_2024_190_20_08_12_0483851794_2_DA_apid0594_1packet.pkts +0 -0
  264. imap_processing/tests/spacecraft/test_quaternions.py +71 -0
  265. imap_processing/tests/spice/test_data/fake_repoint_data.csv +5 -0
  266. imap_processing/tests/spice/test_data/fake_spin_data.csv +11 -11
  267. imap_processing/tests/spice/test_geometry.py +9 -12
  268. imap_processing/tests/spice/test_kernels.py +1 -200
  269. imap_processing/tests/spice/test_pointing_frame.py +185 -0
  270. imap_processing/tests/spice/test_repoint.py +121 -0
  271. imap_processing/tests/spice/test_spin.py +50 -9
  272. imap_processing/tests/spice/test_time.py +14 -0
  273. imap_processing/tests/swapi/lut/imap_swapi_esa-unit-conversion_20250211_v000.csv +73 -0
  274. imap_processing/tests/swapi/lut/imap_swapi_lut-notes_20250211_v000.csv +1025 -0
  275. imap_processing/tests/swapi/test_swapi_l1.py +13 -11
  276. imap_processing/tests/swapi/test_swapi_l2.py +180 -8
  277. imap_processing/tests/swe/l0_data/2024051010_SWE_HK_packet.bin +0 -0
  278. imap_processing/tests/swe/l0_data/2024051011_SWE_CEM_RAW_packet.bin +0 -0
  279. imap_processing/tests/swe/l0_validation_data/idle_export_eu.SWE_APP_HK_20240510_092742.csv +49 -0
  280. imap_processing/tests/swe/l0_validation_data/idle_export_eu.SWE_CEM_RAW_20240510_092742.csv +593 -0
  281. imap_processing/tests/swe/lut/checker-board-indices.csv +24 -0
  282. imap_processing/tests/swe/lut/imap_swe_esa-lut_20250301_v000.csv +385 -0
  283. imap_processing/tests/swe/lut/imap_swe_l1b-in-flight-cal_20240510_20260716_v000.csv +3 -0
  284. imap_processing/tests/swe/test_swe_l1a.py +20 -2
  285. imap_processing/tests/swe/test_swe_l1a_cem_raw.py +52 -0
  286. imap_processing/tests/swe/test_swe_l1a_hk.py +68 -0
  287. imap_processing/tests/swe/test_swe_l1a_science.py +3 -3
  288. imap_processing/tests/swe/test_swe_l1b.py +162 -24
  289. imap_processing/tests/swe/test_swe_l2.py +153 -91
  290. imap_processing/tests/test_cli.py +171 -88
  291. imap_processing/tests/test_utils.py +140 -17
  292. imap_processing/tests/ultra/data/l0/FM45_UltraFM45_Functional_2024-01-22T0105_20240122T010548.CCSDS +0 -0
  293. imap_processing/tests/ultra/data/l0/ultra45_raw_sc_ultraimgrates_20220530_00.csv +164 -0
  294. imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_ultrarawimg_withFSWcalcs_FM45_40P_Phi28p5_BeamCal_LinearScan_phi2850_theta-000_20240207T102740.csv +3243 -3243
  295. imap_processing/tests/ultra/data/mock_data.py +369 -0
  296. imap_processing/tests/ultra/unit/conftest.py +115 -89
  297. imap_processing/tests/ultra/unit/test_badtimes.py +4 -4
  298. imap_processing/tests/ultra/unit/test_cullingmask.py +8 -6
  299. imap_processing/tests/ultra/unit/test_de.py +14 -13
  300. imap_processing/tests/ultra/unit/test_decom_apid_880.py +27 -76
  301. imap_processing/tests/ultra/unit/test_decom_apid_881.py +54 -11
  302. imap_processing/tests/ultra/unit/test_decom_apid_883.py +12 -10
  303. imap_processing/tests/ultra/unit/test_decom_apid_896.py +202 -55
  304. imap_processing/tests/ultra/unit/test_lookup_utils.py +23 -1
  305. imap_processing/tests/ultra/unit/test_spacecraft_pset.py +77 -0
  306. imap_processing/tests/ultra/unit/test_ultra_l1a.py +98 -305
  307. imap_processing/tests/ultra/unit/test_ultra_l1b.py +60 -14
  308. imap_processing/tests/ultra/unit/test_ultra_l1b_annotated.py +2 -2
  309. imap_processing/tests/ultra/unit/test_ultra_l1b_culling.py +26 -27
  310. imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +239 -70
  311. imap_processing/tests/ultra/unit/test_ultra_l1c.py +5 -5
  312. imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +114 -83
  313. imap_processing/tests/ultra/unit/test_ultra_l2.py +230 -0
  314. imap_processing/ultra/constants.py +1 -1
  315. imap_processing/ultra/l0/decom_tools.py +27 -39
  316. imap_processing/ultra/l0/decom_ultra.py +168 -204
  317. imap_processing/ultra/l0/ultra_utils.py +152 -136
  318. imap_processing/ultra/l1a/ultra_l1a.py +55 -271
  319. imap_processing/ultra/l1b/badtimes.py +1 -4
  320. imap_processing/ultra/l1b/cullingmask.py +2 -6
  321. imap_processing/ultra/l1b/de.py +116 -57
  322. imap_processing/ultra/l1b/extendedspin.py +20 -18
  323. imap_processing/ultra/l1b/lookup_utils.py +72 -9
  324. imap_processing/ultra/l1b/ultra_l1b.py +36 -16
  325. imap_processing/ultra/l1b/ultra_l1b_culling.py +66 -30
  326. imap_processing/ultra/l1b/ultra_l1b_extended.py +297 -94
  327. imap_processing/ultra/l1c/histogram.py +2 -6
  328. imap_processing/ultra/l1c/spacecraft_pset.py +84 -0
  329. imap_processing/ultra/l1c/ultra_l1c.py +8 -9
  330. imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +206 -108
  331. imap_processing/ultra/l2/ultra_l2.py +299 -0
  332. imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_LeftSlit.csv +526 -0
  333. imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_RightSlit.csv +526 -0
  334. imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_LeftSlit.csv +526 -0
  335. imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_RightSlit.csv +526 -0
  336. imap_processing/ultra/lookup_tables/FM45_Startup1_ULTRA_IMGPARAMS_20240719.csv +2 -2
  337. imap_processing/ultra/lookup_tables/FM90_Startup1_ULTRA_IMGPARAMS_20240719.csv +2 -0
  338. imap_processing/ultra/packet_definitions/README.md +38 -0
  339. imap_processing/ultra/packet_definitions/ULTRA_SCI_COMBINED.xml +15302 -482
  340. imap_processing/ultra/utils/ultra_l1_utils.py +31 -12
  341. imap_processing/utils.py +69 -29
  342. {imap_processing-0.11.0.dist-info → imap_processing-0.13.0.dist-info}/METADATA +10 -6
  343. imap_processing-0.13.0.dist-info/RECORD +578 -0
  344. imap_processing/cdf/config/imap_mag_l1_variable_attrs.yaml +0 -237
  345. imap_processing/hi/l1a/housekeeping.py +0 -27
  346. imap_processing/hi/l1b/hi_eng_unit_convert_table.csv +0 -154
  347. imap_processing/swe/l1b/swe_esa_lookup_table.csv +0 -1441
  348. imap_processing/swe/l1b/swe_l1b_science.py +0 -652
  349. imap_processing/tests/codice/data/imap_codice_l1a_hi-counters-aggregated_20240429_v001.cdf +0 -0
  350. imap_processing/tests/codice/data/imap_codice_l1a_hi-counters-singles_20240429_v001.cdf +0 -0
  351. imap_processing/tests/codice/data/imap_codice_l1a_hi-omni_20240429_v001.cdf +0 -0
  352. imap_processing/tests/codice/data/imap_codice_l1a_hi-sectored_20240429_v001.cdf +0 -0
  353. imap_processing/tests/codice/data/imap_codice_l1a_hskp_20100101_v001.cdf +0 -0
  354. imap_processing/tests/codice/data/imap_codice_l1a_lo-counters-aggregated_20240429_v001.cdf +0 -0
  355. imap_processing/tests/codice/data/imap_codice_l1a_lo-counters-singles_20240429_v001.cdf +0 -0
  356. imap_processing/tests/codice/data/imap_codice_l1a_lo-nsw-angular_20240429_v001.cdf +0 -0
  357. imap_processing/tests/codice/data/imap_codice_l1a_lo-nsw-priority_20240429_v001.cdf +0 -0
  358. imap_processing/tests/codice/data/imap_codice_l1a_lo-nsw-species_20240429_v001.cdf +0 -0
  359. imap_processing/tests/codice/data/imap_codice_l1a_lo-sw-angular_20240429_v001.cdf +0 -0
  360. imap_processing/tests/codice/data/imap_codice_l1a_lo-sw-priority_20240429_v001.cdf +0 -0
  361. imap_processing/tests/codice/data/imap_codice_l1a_lo-sw-species_20240429_v001.cdf +0 -0
  362. imap_processing/tests/codice/data/imap_codice_l1b_hi-counters-aggregated_20240429_v001.cdf +0 -0
  363. imap_processing/tests/codice/data/imap_codice_l1b_hi-counters-singles_20240429_v001.cdf +0 -0
  364. imap_processing/tests/codice/data/imap_codice_l1b_hi-omni_20240429_v001.cdf +0 -0
  365. imap_processing/tests/codice/data/imap_codice_l1b_hi-sectored_20240429_v001.cdf +0 -0
  366. imap_processing/tests/codice/data/imap_codice_l1b_hskp_20100101_v001.cdf +0 -0
  367. imap_processing/tests/codice/data/imap_codice_l1b_lo-counters-aggregated_20240429_v001.cdf +0 -0
  368. imap_processing/tests/codice/data/imap_codice_l1b_lo-counters-singles_20240429_v001.cdf +0 -0
  369. imap_processing/tests/codice/data/imap_codice_l1b_lo-nsw-angular_20240429_v001.cdf +0 -0
  370. imap_processing/tests/codice/data/imap_codice_l1b_lo-nsw-priority_20240429_v001.cdf +0 -0
  371. imap_processing/tests/codice/data/imap_codice_l1b_lo-nsw-species_20240429_v001.cdf +0 -0
  372. imap_processing/tests/codice/data/imap_codice_l1b_lo-sw-angular_20240429_v001.cdf +0 -0
  373. imap_processing/tests/codice/data/imap_codice_l1b_lo-sw-priority_20240429_v001.cdf +0 -0
  374. imap_processing/tests/codice/data/imap_codice_l1b_lo-sw-species_20240429_v001.cdf +0 -0
  375. imap_processing/tests/hi/data/l1/imap_hi_l1b_45sensor-de_20250415_v999.cdf +0 -0
  376. imap_processing/tests/hit/PREFLIGHT_raw_record_2023_256_15_59_04_apid1251.pkts +0 -0
  377. imap_processing/tests/hit/PREFLIGHT_raw_record_2023_256_15_59_04_apid1252.pkts +0 -0
  378. imap_processing/tests/hit/validation_data/hskp_sample_eu.csv +0 -89
  379. imap_processing/tests/hit/validation_data/sci_sample_raw1.csv +0 -29
  380. imap_processing/tests/idex/test_data/imap_idex_l0_raw_20231214_v001.pkts +0 -0
  381. imap_processing/tests/lo/test_cdfs/imap_lo_l1a_de_20100101_v001.cdf +0 -0
  382. imap_processing/tests/lo/test_cdfs/imap_lo_l1a_spin_20100101_v001.cdf +0 -0
  383. imap_processing/tests/swe/test_swe_l1b_science.py +0 -84
  384. imap_processing/tests/ultra/test_data/mock_data.py +0 -161
  385. imap_processing/ultra/l1c/pset.py +0 -40
  386. imap_processing/ultra/lookup_tables/dps_sensitivity45.cdf +0 -0
  387. imap_processing-0.11.0.dist-info/RECORD +0 -488
  388. /imap_processing/idex/packet_definitions/{idex_packet_definition.xml → idex_science_packet_definition.xml} +0 -0
  389. /imap_processing/tests/ialirt/{test_data → data}/l0/20240827095047_SWE_IALIRT_packet.bin +0 -0
  390. /imap_processing/tests/ialirt/{test_data → data}/l0/BinLog CCSDS_FRAG_TLM_20240826_152323Z_IALIRT_data_for_SDC.bin +0 -0
  391. /imap_processing/tests/ialirt/{test_data → data}/l0/IALiRT Raw Packet Telemetry.txt +0 -0
  392. /imap_processing/tests/ialirt/{test_data → data}/l0/apid01152.tlm +0 -0
  393. /imap_processing/tests/ialirt/{test_data → data}/l0/eu_SWP_IAL_20240826_152033.csv +0 -0
  394. /imap_processing/tests/ialirt/{test_data → data}/l0/hi_fsw_view_1_ccsds.bin +0 -0
  395. /imap_processing/tests/ialirt/{test_data → data}/l0/hit_ialirt_sample.ccsds +0 -0
  396. /imap_processing/tests/ialirt/{test_data → data}/l0/hit_ialirt_sample.csv +0 -0
  397. /imap_processing/tests/ialirt/{test_data → data}/l0/idle_export_eu.SWE_IALIRT_20240827_093852.csv +0 -0
  398. /imap_processing/tests/ialirt/{test_data → data}/l0/imap_codice_l1a_hi-ialirt_20240523200000_v0.0.0.cdf +0 -0
  399. /imap_processing/tests/ialirt/{test_data → data}/l0/imap_codice_l1a_lo-ialirt_20241110193700_v0.0.0.cdf +0 -0
  400. /imap_processing/{mag/l1b → tests/spacecraft}/__init__.py +0 -0
  401. /imap_processing/{swe/l1b/engineering_unit_convert_table.csv → tests/swe/lut/imap_swe_eu-conversion_20240510_v000.csv} +0 -0
  402. /imap_processing/tests/ultra/{test_data → data}/l0/FM45_40P_Phi28p5_BeamCal_LinearScan_phi28.50_theta-0.00_20240207T102740.CCSDS +0 -0
  403. /imap_processing/tests/ultra/{test_data → data}/l0/FM45_7P_Phi0.0_BeamCal_LinearScan_phi0.04_theta-0.01_20230821T121304.CCSDS +0 -0
  404. /imap_processing/tests/ultra/{test_data → data}/l0/FM45_TV_Cycle6_Hot_Ops_Front212_20240124T063837.CCSDS +0 -0
  405. /imap_processing/tests/ultra/{test_data → data}/l0/Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.CCSDS +0 -0
  406. /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_auxdata_Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.csv +0 -0
  407. /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_enaphxtofhangimg_FM45_TV_Cycle6_Hot_Ops_Front212_20240124T063837.csv +0 -0
  408. /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_ultraimgrates_Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.csv +0 -0
  409. /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_ultrarawimgevent_FM45_7P_Phi00_BeamCal_LinearScan_phi004_theta-001_20230821T121304.csv +0 -0
  410. /imap_processing/tests/ultra/{test_data → data}/l1/dps_exposure_helio_45_E1.cdf +0 -0
  411. /imap_processing/tests/ultra/{test_data → data}/l1/dps_exposure_helio_45_E12.cdf +0 -0
  412. /imap_processing/tests/ultra/{test_data → data}/l1/dps_exposure_helio_45_E24.cdf +0 -0
  413. {imap_processing-0.11.0.dist-info → imap_processing-0.13.0.dist-info}/LICENSE +0 -0
  414. {imap_processing-0.11.0.dist-info → imap_processing-0.13.0.dist-info}/WHEEL +0 -0
  415. {imap_processing-0.11.0.dist-info → imap_processing-0.13.0.dist-info}/entry_points.txt +0 -0
@@ -1,30 +1,32 @@
1
1
  "Tests pointing sets"
2
2
 
3
- import cdflib
3
+ import astropy_healpix.healpy as hp
4
4
  import numpy as np
5
+ import pandas as pd
5
6
  import pytest
6
- from cdflib import CDF
7
7
 
8
8
  from imap_processing import imap_module_directory
9
- from imap_processing.ena_maps.utils.spatial_utils import build_spatial_bins
10
9
  from imap_processing.ultra.l1c.ultra_l1c_pset_bins import (
11
10
  build_energy_bins,
11
+ get_background_rates,
12
12
  get_helio_exposure_times,
13
- get_histogram,
14
- get_pointing_frame_exposure_times,
15
- get_pointing_frame_sensitivity,
13
+ get_spacecraft_exposure_times,
14
+ get_spacecraft_histogram,
15
+ get_spacecraft_sensitivity,
16
+ grid_sensitivity,
16
17
  )
17
18
 
18
19
  BASE_PATH = imap_module_directory / "ultra" / "lookup_tables"
20
+ TEST_PATH = imap_module_directory / "tests" / "ultra" / "data" / "l1"
19
21
 
20
22
 
21
- @pytest.fixture()
23
+ @pytest.fixture
22
24
  def test_data():
23
25
  """Test data fixture."""
24
26
  vx_sc = np.array([-186.5575, 508.5697, 508.5697, 508.5697])
25
27
  vy_sc = np.array([-707.5707, -516.0282, -516.0282, -516.0282])
26
28
  vz_sc = np.array([618.0569, 892.6931, 892.6931, 892.6931])
27
- energy = np.array([3.384, 3.385, 200, 200])
29
+ energy = np.array([3.384, 3.385, 4.138, 4.138])
28
30
  v = np.column_stack((vx_sc, vy_sc, vz_sc))
29
31
 
30
32
  return v, energy
@@ -32,13 +34,13 @@ def test_data():
32
34
 
33
35
  def test_build_energy_bins():
34
36
  """Tests build_energy_bins function."""
35
- energy_bin_edges, energy_midpoints = build_energy_bins()
36
- energy_bin_start = [interval[0] for interval in energy_bin_edges]
37
- energy_bin_end = [interval[1] for interval in energy_bin_edges]
37
+ intervals, energy_midpoints, energy_bin_geometric_means = build_energy_bins()
38
+ energy_bin_start = [interval[0] for interval in intervals]
39
+ energy_bin_end = [interval[1] for interval in intervals]
38
40
 
39
41
  assert energy_bin_start[0] == 0
40
42
  assert energy_bin_start[1] == 3.385
41
- assert len(energy_bin_edges) == 24
43
+ assert len(intervals) == 24
42
44
  assert energy_midpoints[0] == (energy_bin_start[0] + energy_bin_end[0]) / 2
43
45
 
44
46
  # Comparison to expected values.
@@ -46,107 +48,136 @@ def test_build_energy_bins():
46
48
  np.testing.assert_allclose(energy_bin_start[-1], 279.810, atol=1e-4)
47
49
  np.testing.assert_allclose(energy_bin_end[-1], 341.989, atol=1e-4)
48
50
 
51
+ expected_geometric_means = np.sqrt(
52
+ np.array(energy_bin_start) * np.array(energy_bin_end)
53
+ )
54
+ np.testing.assert_allclose(
55
+ energy_bin_geometric_means, expected_geometric_means, atol=1e-4
56
+ )
57
+
49
58
 
50
- def test_get_histogram(test_data):
59
+ def test_get_spacecraft_histogram(test_data):
51
60
  """Tests get_histogram function."""
52
61
  v, energy = test_data
53
62
 
54
- az_bin_edges, el_bin_edges, az_bin_midpoints, el_bin_midpoints = (
55
- np.rad2deg(angle_radians) for angle_radians in (build_spatial_bins())
56
- )
57
- energy_bin_edges, _ = build_energy_bins()
58
-
59
- hist = get_histogram(v, energy, az_bin_edges, el_bin_edges, energy_bin_edges)
63
+ energy_bin_edges, _, _ = build_energy_bins()
64
+ subset_energy_bin_edges = energy_bin_edges[:3]
60
65
 
61
- assert hist.shape == (
62
- len(az_bin_edges) - 1,
63
- len(el_bin_edges) - 1,
64
- len(energy_bin_edges),
66
+ hist, latitude, longitude, n_pix = get_spacecraft_histogram(
67
+ v, energy, subset_energy_bin_edges, nside=1
65
68
  )
66
-
67
-
68
- def test_get_pointing_frame_exposure_times():
69
- """Tests get_pointing_frame_exposure_times function."""
70
-
71
- constant_exposure = BASE_PATH / "dps_grid45_compressed.cdf"
72
- spins_per_pointing = 5760
73
- exposure = get_pointing_frame_exposure_times(
74
- constant_exposure, spins_per_pointing, "45"
69
+ assert hist.shape == (len(subset_energy_bin_edges), hp.nside2npix(1))
70
+ assert n_pix == hp.nside2npix(1)
71
+ assert latitude.shape == (n_pix,)
72
+ assert longitude.shape == (n_pix,)
73
+
74
+ # Spot check that 2 counts are in the third energy bin
75
+ assert np.sum(hist[2, :]) == 2
76
+
77
+ # Test overlapping energy bins
78
+ overlapping_bins = [
79
+ (0.0, 3.385),
80
+ (2.5, 4.137),
81
+ (3.385, 5.057),
82
+ ]
83
+ hist, latitude, longitude, n_pix = get_spacecraft_histogram(
84
+ v, energy, overlapping_bins, nside=1
75
85
  )
76
-
77
- assert exposure.shape == (720, 360)
78
- # Assert that the exposure time at the highest azimuth is
79
- # 15s x spins per pointing.
80
- assert np.array_equal(
81
- exposure[:, 359], np.full_like(exposure[:, 359], spins_per_pointing * 15)
86
+ # Spot check that 3 counts are in the third energy bin
87
+ assert np.sum(hist[2, :]) == 3
88
+ assert n_pix == hp.nside2npix(1)
89
+ assert latitude.shape == (n_pix,)
90
+ assert longitude.shape == (n_pix,)
91
+
92
+
93
+ def test_get_background_rates():
94
+ """Tests get_background_rates function."""
95
+ background_rates = get_background_rates(nside=128)
96
+ assert background_rates.shape == hp.nside2npix(128)
97
+
98
+
99
+ @pytest.mark.external_test_data
100
+ def test_get_spacecraft_exposure_times():
101
+ """Test get_spacecraft_exposure_times function."""
102
+ constant_exposure = TEST_PATH / "ultra_90_dps_exposure.csv"
103
+ df_exposure = pd.read_csv(constant_exposure)
104
+ exposure_pointing = get_spacecraft_exposure_times(df_exposure)
105
+ assert exposure_pointing.shape == (196608,)
106
+
107
+ np.testing.assert_allclose(
108
+ exposure_pointing.values[22684:22686],
109
+ np.array([1.035, 1.035]) * 5760,
110
+ atol=1e-6,
82
111
  )
83
- # Assert that the exposure time at the lowest azimuth is 0 (no exposure).
84
- assert np.array_equal(exposure[:, 0], np.full_like(exposure[:, 359], 0.0))
85
112
 
86
113
 
87
- @pytest.mark.external_kernel()
114
+ @pytest.mark.external_kernel
88
115
  @pytest.mark.use_test_metakernel("imap_ena_sim_metakernel.template")
89
116
  def test_get_helio_exposure_times():
90
117
  """Tests get_helio_exposure_times function."""
91
118
 
92
- constant_exposure = BASE_PATH / "dps_grid45_compressed.cdf"
93
119
  start_time = 829485054.185627
94
120
  end_time = 829567884.185627
121
+
95
122
  mid_time = np.average([start_time, end_time])
96
123
 
97
- with cdflib.CDF(constant_exposure) as cdf_file:
98
- sc_exposure = cdf_file.varget("dps_grid45")
124
+ constant_exposure = TEST_PATH / "ultra_90_dps_exposure.csv"
125
+ df_exposure = pd.read_csv(constant_exposure)
99
126
 
100
- exposure_3d = get_helio_exposure_times(mid_time, sc_exposure)
127
+ helio_exposure = get_helio_exposure_times(mid_time, df_exposure)
101
128
 
102
- energy_bin_edges, energy_midpoints = build_energy_bins()
103
- az_bin_edges, el_bin_edges, az_bin_midpoints, el_bin_midpoints = (
104
- np.rad2deg(angle_radians) for angle_radians in (build_spatial_bins())
105
- )
129
+ _, energy_midpoints, _ = build_energy_bins()
106
130
 
107
- assert exposure_3d.shape == (
108
- len(el_bin_midpoints),
109
- len(az_bin_midpoints),
110
- len(energy_midpoints),
111
- )
131
+ nside = 128
132
+ npix = hp.nside2npix(nside)
133
+ assert helio_exposure.shape == (npix, len(energy_midpoints))
112
134
 
113
- cdf_files = [
114
- ("dps_exposure_helio_45_E1.cdf", "dps_exposure_helio_45_E1"),
115
- ("dps_exposure_helio_45_E12.cdf", "dps_exposure_helio_45_E12"),
116
- ("dps_exposure_helio_45_E24.cdf", "dps_exposure_helio_45_E24"),
117
- ]
135
+ total_input = np.sum(df_exposure["Exposure Time"].values)
136
+ total_output = np.sum(helio_exposure[:, 23])
118
137
 
119
- cdf_directory = imap_module_directory / "tests" / "ultra" / "test_data" / "l1"
138
+ assert np.allclose(total_input, total_output, atol=1e-6)
120
139
 
121
- exposures = []
122
140
 
123
- for file_name, var_name in cdf_files:
124
- file_path = cdf_directory / file_name
125
- with CDF(file_path) as cdf_file:
126
- exposure_data = cdf_file.varget(var_name)
127
- transposed_exposure = np.transpose(exposure_data, (2, 1, 0))
128
- exposures.append(transposed_exposure)
141
+ @pytest.mark.external_test_data
142
+ def test_get_spacecraft_sensitivity():
143
+ """Tests get_spacecraft_sensitivity function."""
144
+ # TODO: remove below here with lookup table aux api
145
+ efficiencies = TEST_PATH / "Ultra_90_DPS_efficiencies_all.csv"
146
+ geometric_function = TEST_PATH / "ultra_90_dps_gf.csv"
129
147
 
130
- assert np.array_equal(np.squeeze(exposures[0]), exposure_3d[:, :, 0])
131
- assert np.array_equal(np.squeeze(exposures[1]), exposure_3d[:, :, 11])
132
- assert np.array_equal(np.squeeze(exposures[2]), exposure_3d[:, :, 23])
148
+ df_efficiencies = pd.read_csv(efficiencies)
149
+ df_geometric_function = pd.read_csv(geometric_function)
133
150
 
151
+ sensitivity, energy_vals, right_ascension, declination = get_spacecraft_sensitivity(
152
+ df_efficiencies, df_geometric_function
153
+ )
154
+
155
+ assert sensitivity.shape == (df_efficiencies.shape[0], df_efficiencies.shape[1] - 2)
156
+ assert np.array_equal(energy_vals, np.arange(3.0, 80.5, 0.5))
134
157
 
135
- def test_get_pointing_frame_sensitivity():
136
- """Tests get_pointing_frame_sensitivity function."""
158
+ df_efficiencies_test = pd.DataFrame(
159
+ {"3.0keV": [1.0, 2.0], "3.5keV": [3.0, 4.0], "4.0keV": [5.0, 6.0]}
160
+ )
161
+
162
+ df_geometric_function_test = pd.DataFrame({"Response": [0.1, 0.2]})
163
+
164
+ df_sensitivity_test = df_efficiencies_test.mul(
165
+ df_geometric_function_test["Response"], axis=0
166
+ )
167
+
168
+ expected_sensitivity = pd.DataFrame(
169
+ {"3.0keV": [0.1, 0.4], "3.5keV": [0.3, 0.8], "4.0keV": [0.5, 1.2]}
170
+ )
137
171
 
138
- # TODO: energy bins need to be modified from N=90 to N=24.
139
- constant_sensitivity = BASE_PATH / "dps_sensitivity45.cdf"
140
- spins_per_pointing = 5760
141
- sensitivity = get_pointing_frame_sensitivity(
142
- constant_sensitivity,
143
- spins_per_pointing,
144
- "45",
172
+ assert np.allclose(
173
+ df_sensitivity_test.to_numpy(), expected_sensitivity.to_numpy(), atol=1e-6
145
174
  )
146
175
 
147
- assert sensitivity.shape == (90, 720, 360)
176
+ expected_result = sensitivity["3.0keV"].values
177
+ result = grid_sensitivity(df_efficiencies, df_geometric_function, 3.0)
148
178
 
149
- with cdflib.CDF(constant_sensitivity) as cdf_file:
150
- expected_sensitivity = cdf_file.varget("dps_sensitivity45") * spins_per_pointing
179
+ assert np.allclose(result, expected_result, atol=1e-5)
151
180
 
152
- assert np.array_equal(sensitivity, expected_sensitivity)
181
+ # Check that out-of-bounds energy returns all NaNs
182
+ result = grid_sensitivity(df_efficiencies, df_geometric_function, 2.5)
183
+ assert np.isnan(result).all()
@@ -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
@@ -130,14 +127,15 @@ def decompress_binary(
130
127
  current_position = 0
131
128
  decompressed_values: list = []
132
129
 
133
- while current_position < len(binary):
130
+ while current_position < len(binary) and len(decompressed_values) < array_length:
134
131
  # Read the width of the block
135
132
  width, current_position = read_and_advance(binary, width_bit, current_position)
136
- # If width is 0 or None, we don't have enough bits left
137
- if width is None or len(decompressed_values) >= array_length:
138
- break
133
+ # If width is 0, add 'block' number of zeroes and continue
134
+ if width == 0:
135
+ decompressed_values.extend([0] * block)
136
+ continue
139
137
 
140
- # For each block, read 16 values of the given width
138
+ # For each block, read 'block' values of the given width
141
139
  for _ in range(block):
142
140
  # Ensure there are enough bits left to read the width
143
141
  if len(binary) - current_position < width:
@@ -156,7 +154,7 @@ def decompress_image(
156
154
  binary_data: str,
157
155
  width_bit: int,
158
156
  mantissa_bit_length: int,
159
- ) -> npt.NDArray:
157
+ ) -> NDArray:
160
158
  """
161
159
  Will decompress a binary string representing an image into a matrix of pixel values.
162
160
 
@@ -177,14 +175,14 @@ def decompress_image(
177
175
 
178
176
  Returns
179
177
  -------
180
- p_decom : numpy.ndarray
178
+ p_decom : NDArray
181
179
  A 2D numpy array representing pixel values.
182
180
  Each pixel is stored as an unsigned 16-bit integer (uint16).
183
181
 
184
182
  Notes
185
183
  -----
186
184
  This process is described starting on page 168 in IMAP-Ultra Flight
187
- Software Specification document (7523-9009_Rev_-.pdf).
185
+ Software Specification document.
188
186
  """
189
187
  rows = 54
190
188
  cols = 180
@@ -239,44 +237,34 @@ def decompress_image(
239
237
 
240
238
 
241
239
  def read_image_raw_events_binary(
242
- packet: space_packet_parser.packets.CCSDSPacket, decom_data: dict
243
- ) -> dict:
240
+ event_data: bytes,
241
+ count: int,
242
+ ) -> NDArray:
244
243
  """
245
244
  Convert contents of binary string 'EVENTDATA' into values.
246
245
 
247
246
  Parameters
248
247
  ----------
249
- packet : space_packet_parser.packets.CCSDSPacket
250
- Packet.
251
- decom_data : dict
252
- Parsed data.
248
+ event_data : bytes
249
+ Event data.
250
+ count : int
251
+ Number of events.
253
252
 
254
253
  Returns
255
254
  -------
256
- decom_data : dict
257
- Each for loop appends to the existing dictionary.
255
+ event_data : NDArray
256
+ Event data.
258
257
  """
259
- binary = convert_to_binary_string(packet["EVENTDATA"])
260
- count = packet["COUNT"]
258
+ binary = convert_to_binary_string(event_data)
261
259
  # 166 bits per event
262
260
  event_length = 166 if count else 0
263
-
264
- # Uses fill value for all packets that do not contain event data.
265
- if count == 0:
266
- # if decom_data is empty, append fill values to all fields
267
- if not decom_data:
268
- for field in EVENT_FIELD_RANGES.keys():
269
- decom_data[field] = []
270
- append_fillval(decom_data, packet)
261
+ event_data_list = []
271
262
 
272
263
  # For all packets with event data, parses the binary string
273
- else:
274
- for i in range(count):
275
- start_index = i * event_length
276
- event_binary = binary[start_index : start_index + event_length]
277
- event_data = parse_event(event_binary)
278
-
279
- for key, value in event_data.items():
280
- 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)
281
269
 
282
- return decom_data
270
+ return np.array(event_data_list)