imap-processing 0.11.0__py3-none-any.whl → 0.12.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 (288) hide show
  1. imap_processing/__init__.py +10 -11
  2. imap_processing/_version.py +2 -2
  3. imap_processing/ccsds/excel_to_xtce.py +65 -16
  4. imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +6 -28
  5. imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +365 -42
  6. imap_processing/cdf/config/imap_glows_global_cdf_attrs.yaml +0 -5
  7. imap_processing/cdf/config/imap_hi_global_cdf_attrs.yaml +10 -11
  8. imap_processing/cdf/config/imap_hi_variable_attrs.yaml +17 -19
  9. imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +26 -13
  10. imap_processing/cdf/config/imap_hit_l1a_variable_attrs.yaml +106 -116
  11. imap_processing/cdf/config/imap_hit_l1b_variable_attrs.yaml +120 -145
  12. imap_processing/cdf/config/imap_hit_l2_variable_attrs.yaml +14 -0
  13. imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +6 -9
  14. imap_processing/cdf/config/imap_idex_l1a_variable_attrs.yaml +1 -1
  15. imap_processing/cdf/config/imap_lo_global_cdf_attrs.yaml +0 -12
  16. imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +1 -1
  17. imap_processing/cdf/config/imap_mag_global_cdf_attrs.yaml +9 -21
  18. imap_processing/cdf/config/imap_mag_l1a_variable_attrs.yaml +361 -0
  19. imap_processing/cdf/config/imap_mag_l1b_variable_attrs.yaml +160 -0
  20. imap_processing/cdf/config/imap_mag_l1c_variable_attrs.yaml +160 -0
  21. imap_processing/cdf/config/imap_spacecraft_global_cdf_attrs.yaml +18 -0
  22. imap_processing/cdf/config/imap_spacecraft_variable_attrs.yaml +40 -0
  23. imap_processing/cdf/config/imap_swapi_global_cdf_attrs.yaml +1 -5
  24. imap_processing/cdf/config/imap_swe_global_cdf_attrs.yaml +12 -4
  25. imap_processing/cdf/config/imap_swe_l1a_variable_attrs.yaml +16 -2
  26. imap_processing/cdf/config/imap_swe_l1b_variable_attrs.yaml +48 -52
  27. imap_processing/cdf/config/imap_swe_l2_variable_attrs.yaml +71 -47
  28. imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +2 -14
  29. imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +51 -2
  30. imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +29 -14
  31. imap_processing/cdf/utils.py +13 -7
  32. imap_processing/cli.py +23 -8
  33. imap_processing/codice/codice_l1a.py +207 -85
  34. imap_processing/codice/constants.py +1322 -568
  35. imap_processing/codice/decompress.py +2 -6
  36. imap_processing/ena_maps/ena_maps.py +480 -116
  37. imap_processing/ena_maps/utils/coordinates.py +19 -0
  38. imap_processing/ena_maps/utils/map_utils.py +14 -17
  39. imap_processing/ena_maps/utils/spatial_utils.py +45 -47
  40. imap_processing/hi/l1a/hi_l1a.py +24 -18
  41. imap_processing/hi/l1a/histogram.py +0 -1
  42. imap_processing/hi/l1a/science_direct_event.py +6 -8
  43. imap_processing/hi/l1b/hi_l1b.py +31 -39
  44. imap_processing/hi/l1c/hi_l1c.py +405 -17
  45. imap_processing/hi/utils.py +58 -12
  46. imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt0-factors_20250219_v002.csv +205 -0
  47. imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt1-factors_20250219_v002.csv +205 -0
  48. imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt2-factors_20250219_v002.csv +205 -0
  49. imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt3-factors_20250219_v002.csv +205 -0
  50. imap_processing/hit/ancillary/imap_hit_l1b-to-l2-summed-dt0-factors_20250219_v002.csv +68 -0
  51. imap_processing/hit/hit_utils.py +173 -1
  52. imap_processing/hit/l0/constants.py +20 -11
  53. imap_processing/hit/l0/decom_hit.py +18 -4
  54. imap_processing/hit/l1a/hit_l1a.py +45 -54
  55. imap_processing/hit/l1b/constants.py +317 -0
  56. imap_processing/hit/l1b/hit_l1b.py +367 -18
  57. imap_processing/hit/l2/constants.py +281 -0
  58. imap_processing/hit/l2/hit_l2.py +614 -0
  59. imap_processing/hit/packet_definitions/hit_packet_definitions.xml +1323 -71
  60. imap_processing/ialirt/l0/mag_l0_ialirt_data.py +155 -0
  61. imap_processing/ialirt/l0/parse_mag.py +246 -0
  62. imap_processing/ialirt/l0/process_swe.py +252 -0
  63. imap_processing/ialirt/packet_definitions/ialirt.xml +7 -3
  64. imap_processing/ialirt/packet_definitions/ialirt_mag.xml +115 -0
  65. imap_processing/ialirt/utils/grouping.py +114 -0
  66. imap_processing/ialirt/utils/time.py +29 -0
  67. imap_processing/idex/atomic_masses.csv +22 -0
  68. imap_processing/idex/decode.py +2 -2
  69. imap_processing/idex/idex_constants.py +25 -0
  70. imap_processing/idex/idex_l1a.py +6 -7
  71. imap_processing/idex/idex_l1b.py +4 -31
  72. imap_processing/idex/idex_l2a.py +789 -0
  73. imap_processing/idex/idex_variable_unpacking_and_eu_conversion.csv +39 -33
  74. imap_processing/lo/l0/lo_science.py +6 -0
  75. imap_processing/lo/l1a/lo_l1a.py +0 -1
  76. imap_processing/lo/l1b/lo_l1b.py +177 -25
  77. imap_processing/mag/constants.py +8 -0
  78. imap_processing/mag/imap_mag_sdc-configuration_v001.yaml +6 -0
  79. imap_processing/mag/l0/decom_mag.py +10 -3
  80. imap_processing/mag/l1a/mag_l1a.py +22 -11
  81. imap_processing/mag/l1a/mag_l1a_data.py +28 -3
  82. imap_processing/mag/l1b/mag_l1b.py +190 -48
  83. imap_processing/mag/l1c/interpolation_methods.py +211 -0
  84. imap_processing/mag/l1c/mag_l1c.py +447 -9
  85. imap_processing/quality_flags.py +1 -0
  86. imap_processing/spacecraft/packet_definitions/scid_x252.xml +538 -0
  87. imap_processing/spacecraft/quaternions.py +123 -0
  88. imap_processing/spice/geometry.py +16 -19
  89. imap_processing/spice/repoint.py +120 -0
  90. imap_processing/swapi/l1/swapi_l1.py +4 -0
  91. imap_processing/swapi/l2/swapi_l2.py +0 -1
  92. imap_processing/swe/l1a/swe_l1a.py +47 -8
  93. imap_processing/swe/l1a/swe_science.py +5 -2
  94. imap_processing/swe/l1b/swe_l1b_science.py +103 -56
  95. imap_processing/swe/l2/swe_l2.py +60 -65
  96. imap_processing/swe/packet_definitions/swe_packet_definition.xml +1121 -1
  97. imap_processing/swe/utils/swe_constants.py +63 -0
  98. imap_processing/swe/utils/swe_utils.py +85 -28
  99. imap_processing/tests/ccsds/test_data/expected_output.xml +40 -1
  100. imap_processing/tests/ccsds/test_excel_to_xtce.py +23 -20
  101. imap_processing/tests/cdf/test_data/imap_instrument2_global_cdf_attrs.yaml +0 -2
  102. imap_processing/tests/codice/conftest.py +1 -1
  103. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-counters-aggregated_20241110193700_v0.0.0.cdf +0 -0
  104. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-counters-singles_20241110193700_v0.0.0.cdf +0 -0
  105. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-ialirt_20241110193700_v0.0.0.cdf +0 -0
  106. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-omni_20241110193700_v0.0.0.cdf +0 -0
  107. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-pha_20241110193700_v0.0.0.cdf +0 -0
  108. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-priorities_20241110193700_v0.0.0.cdf +0 -0
  109. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-sectored_20241110193700_v0.0.0.cdf +0 -0
  110. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-counters-aggregated_20241110193700_v0.0.0.cdf +0 -0
  111. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-counters-singles_20241110193700_v0.0.0.cdf +0 -0
  112. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-ialirt_20241110193700_v0.0.0.cdf +0 -0
  113. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-angular_20241110193700_v0.0.0.cdf +0 -0
  114. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-priority_20241110193700_v0.0.0.cdf +0 -0
  115. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-species_20241110193700_v0.0.0.cdf +0 -0
  116. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-pha_20241110193700_v0.0.0.cdf +0 -0
  117. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-angular_20241110193700_v0.0.0.cdf +0 -0
  118. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-priority_20241110193700_v0.0.0.cdf +0 -0
  119. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-species_20241110193700_v0.0.0.cdf +0 -0
  120. imap_processing/tests/codice/test_codice_l1a.py +110 -46
  121. imap_processing/tests/codice/test_decompress.py +4 -4
  122. imap_processing/tests/conftest.py +166 -10
  123. imap_processing/tests/ena_maps/conftest.py +51 -0
  124. imap_processing/tests/ena_maps/test_ena_maps.py +638 -109
  125. imap_processing/tests/ena_maps/test_map_utils.py +66 -43
  126. imap_processing/tests/ena_maps/test_spatial_utils.py +16 -20
  127. imap_processing/tests/hi/data/l0/H45_diag_fee_20250208.bin +0 -0
  128. imap_processing/tests/hi/data/l0/H45_diag_fee_20250208_verify.csv +205 -0
  129. imap_processing/tests/hi/test_hi_l1b.py +12 -15
  130. imap_processing/tests/hi/test_hi_l1c.py +234 -6
  131. imap_processing/tests/hi/test_l1a.py +30 -0
  132. imap_processing/tests/hi/test_science_direct_event.py +1 -1
  133. imap_processing/tests/hi/test_utils.py +24 -2
  134. imap_processing/tests/hit/helpers/l1_validation.py +39 -39
  135. imap_processing/tests/hit/test_data/hskp_sample.ccsds +0 -0
  136. imap_processing/tests/hit/test_data/imap_hit_l0_raw_20100105_v001.pkts +0 -0
  137. imap_processing/tests/hit/test_decom_hit.py +4 -0
  138. imap_processing/tests/hit/test_hit_l1a.py +24 -28
  139. imap_processing/tests/hit/test_hit_l1b.py +304 -40
  140. imap_processing/tests/hit/test_hit_l2.py +454 -0
  141. imap_processing/tests/hit/test_hit_utils.py +112 -2
  142. imap_processing/tests/hit/validation_data/hskp_sample_eu_3_6_2025.csv +89 -0
  143. imap_processing/tests/hit/validation_data/hskp_sample_raw.csv +89 -88
  144. imap_processing/tests/ialirt/test_data/l0/461971383-404.bin +0 -0
  145. imap_processing/tests/ialirt/test_data/l0/461971384-405.bin +0 -0
  146. imap_processing/tests/ialirt/test_data/l0/461971385-406.bin +0 -0
  147. imap_processing/tests/ialirt/test_data/l0/461971386-407.bin +0 -0
  148. imap_processing/tests/ialirt/test_data/l0/461971387-408.bin +0 -0
  149. imap_processing/tests/ialirt/test_data/l0/461971388-409.bin +0 -0
  150. imap_processing/tests/ialirt/test_data/l0/461971389-410.bin +0 -0
  151. imap_processing/tests/ialirt/test_data/l0/461971390-411.bin +0 -0
  152. imap_processing/tests/ialirt/test_data/l0/461971391-412.bin +0 -0
  153. imap_processing/tests/ialirt/test_data/l0/sample_decoded_i-alirt_data.csv +383 -0
  154. imap_processing/tests/ialirt/unit/test_grouping.py +81 -0
  155. imap_processing/tests/ialirt/unit/test_parse_mag.py +168 -0
  156. imap_processing/tests/ialirt/unit/test_process_swe.py +208 -3
  157. imap_processing/tests/ialirt/unit/test_time.py +16 -0
  158. imap_processing/tests/idex/conftest.py +62 -6
  159. imap_processing/tests/idex/test_data/imap_idex_l0_raw_20231218_v001.pkts +0 -0
  160. imap_processing/tests/idex/test_data/impact_14_tof_high_data.txt +4508 -4508
  161. imap_processing/tests/idex/test_idex_l1a.py +48 -4
  162. imap_processing/tests/idex/test_idex_l1b.py +3 -3
  163. imap_processing/tests/idex/test_idex_l2a.py +383 -0
  164. imap_processing/tests/lo/test_cdfs/imap_lo_l1a_de_20241022_v002.cdf +0 -0
  165. imap_processing/tests/lo/test_cdfs/imap_lo_l1a_spin_20241022_v002.cdf +0 -0
  166. imap_processing/tests/lo/test_lo_l1b.py +148 -4
  167. imap_processing/tests/lo/test_lo_science.py +1 -0
  168. imap_processing/tests/mag/conftest.py +69 -0
  169. imap_processing/tests/mag/test_mag_decom.py +1 -1
  170. imap_processing/tests/mag/test_mag_l1a.py +38 -0
  171. imap_processing/tests/mag/test_mag_l1b.py +34 -53
  172. imap_processing/tests/mag/test_mag_l1c.py +251 -20
  173. imap_processing/tests/mag/test_mag_validation.py +109 -25
  174. imap_processing/tests/mag/validation/L1b/T009/MAGScience-normal-(2,2)-8s-20250204-16h39.csv +17 -0
  175. imap_processing/tests/mag/validation/L1b/T009/mag-l1a-l1b-t009-magi-out.csv +16 -16
  176. imap_processing/tests/mag/validation/L1b/T009/mag-l1a-l1b-t009-mago-out.csv +16 -16
  177. imap_processing/tests/mag/validation/L1b/T010/MAGScience-normal-(2,2)-8s-20250206-12h05.csv +17 -0
  178. imap_processing/tests/mag/validation/L1b/T011/MAGScience-normal-(2,2)-8s-20250204-16h08.csv +17 -0
  179. imap_processing/tests/mag/validation/L1b/T011/mag-l1a-l1b-t011-magi-out.csv +16 -16
  180. imap_processing/tests/mag/validation/L1b/T011/mag-l1a-l1b-t011-mago-out.csv +16 -16
  181. imap_processing/tests/mag/validation/L1b/T012/MAGScience-normal-(2,2)-8s-20250204-16h08.csv +17 -0
  182. imap_processing/tests/mag/validation/L1b/T012/data.bin +0 -0
  183. imap_processing/tests/mag/validation/L1b/T012/field_like_all_ranges.txt +19200 -0
  184. imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-cal.cdf +0 -0
  185. imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-in.csv +17 -0
  186. imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-magi-out.csv +17 -0
  187. imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-mago-out.csv +17 -0
  188. imap_processing/tests/mag/validation/imap_calibration_mag_20240229_v01.cdf +0 -0
  189. imap_processing/tests/spacecraft/__init__.py +0 -0
  190. imap_processing/tests/spacecraft/data/SSR_2024_190_20_08_12_0483851794_2_DA_apid0594_1packet.pkts +0 -0
  191. imap_processing/tests/spacecraft/test_quaternions.py +71 -0
  192. imap_processing/tests/spice/test_data/fake_repoint_data.csv +5 -0
  193. imap_processing/tests/spice/test_geometry.py +6 -9
  194. imap_processing/tests/spice/test_repoint.py +111 -0
  195. imap_processing/tests/swapi/test_swapi_l1.py +7 -3
  196. imap_processing/tests/swe/l0_data/2024051010_SWE_HK_packet.bin +0 -0
  197. imap_processing/tests/swe/l0_data/2024051011_SWE_CEM_RAW_packet.bin +0 -0
  198. imap_processing/tests/swe/l0_validation_data/idle_export_eu.SWE_APP_HK_20240510_092742.csv +49 -0
  199. imap_processing/tests/swe/l0_validation_data/idle_export_eu.SWE_CEM_RAW_20240510_092742.csv +593 -0
  200. imap_processing/tests/swe/test_swe_l1a.py +18 -0
  201. imap_processing/tests/swe/test_swe_l1a_cem_raw.py +52 -0
  202. imap_processing/tests/swe/test_swe_l1a_hk.py +68 -0
  203. imap_processing/tests/swe/test_swe_l1b_science.py +23 -4
  204. imap_processing/tests/swe/test_swe_l2.py +112 -30
  205. imap_processing/tests/test_cli.py +2 -2
  206. imap_processing/tests/test_utils.py +138 -16
  207. imap_processing/tests/ultra/data/l0/FM45_UltraFM45_Functional_2024-01-22T0105_20240122T010548.CCSDS +0 -0
  208. imap_processing/tests/ultra/data/l0/ultra45_raw_sc_ultraimgrates_20220530_00.csv +164 -0
  209. 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
  210. imap_processing/tests/ultra/data/mock_data.py +341 -0
  211. imap_processing/tests/ultra/unit/conftest.py +69 -26
  212. imap_processing/tests/ultra/unit/test_badtimes.py +2 -0
  213. imap_processing/tests/ultra/unit/test_cullingmask.py +4 -0
  214. imap_processing/tests/ultra/unit/test_de.py +12 -4
  215. imap_processing/tests/ultra/unit/test_decom_apid_881.py +44 -0
  216. imap_processing/tests/ultra/unit/test_spacecraft_pset.py +78 -0
  217. imap_processing/tests/ultra/unit/test_ultra_l1a.py +28 -12
  218. imap_processing/tests/ultra/unit/test_ultra_l1b.py +34 -6
  219. imap_processing/tests/ultra/unit/test_ultra_l1b_culling.py +22 -26
  220. imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +86 -51
  221. imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +94 -52
  222. imap_processing/ultra/l0/decom_tools.py +6 -5
  223. imap_processing/ultra/l1a/ultra_l1a.py +28 -56
  224. imap_processing/ultra/l1b/de.py +72 -28
  225. imap_processing/ultra/l1b/extendedspin.py +12 -14
  226. imap_processing/ultra/l1b/ultra_l1b.py +34 -9
  227. imap_processing/ultra/l1b/ultra_l1b_culling.py +65 -29
  228. imap_processing/ultra/l1b/ultra_l1b_extended.py +64 -19
  229. imap_processing/ultra/l1c/spacecraft_pset.py +86 -0
  230. imap_processing/ultra/l1c/ultra_l1c.py +7 -4
  231. imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +112 -61
  232. imap_processing/ultra/lookup_tables/ultra_90_dps_exposure_compressed.cdf +0 -0
  233. imap_processing/ultra/utils/ultra_l1_utils.py +20 -2
  234. imap_processing/utils.py +68 -28
  235. {imap_processing-0.11.0.dist-info → imap_processing-0.12.0.dist-info}/METADATA +8 -5
  236. {imap_processing-0.11.0.dist-info → imap_processing-0.12.0.dist-info}/RECORD +250 -199
  237. imap_processing/cdf/config/imap_mag_l1_variable_attrs.yaml +0 -237
  238. imap_processing/hi/l1a/housekeeping.py +0 -27
  239. imap_processing/tests/codice/data/imap_codice_l1a_hi-counters-aggregated_20240429_v001.cdf +0 -0
  240. imap_processing/tests/codice/data/imap_codice_l1a_hi-counters-singles_20240429_v001.cdf +0 -0
  241. imap_processing/tests/codice/data/imap_codice_l1a_hi-omni_20240429_v001.cdf +0 -0
  242. imap_processing/tests/codice/data/imap_codice_l1a_hi-sectored_20240429_v001.cdf +0 -0
  243. imap_processing/tests/codice/data/imap_codice_l1a_hskp_20100101_v001.cdf +0 -0
  244. imap_processing/tests/codice/data/imap_codice_l1a_lo-counters-aggregated_20240429_v001.cdf +0 -0
  245. imap_processing/tests/codice/data/imap_codice_l1a_lo-counters-singles_20240429_v001.cdf +0 -0
  246. imap_processing/tests/codice/data/imap_codice_l1a_lo-nsw-angular_20240429_v001.cdf +0 -0
  247. imap_processing/tests/codice/data/imap_codice_l1a_lo-nsw-priority_20240429_v001.cdf +0 -0
  248. imap_processing/tests/codice/data/imap_codice_l1a_lo-nsw-species_20240429_v001.cdf +0 -0
  249. imap_processing/tests/codice/data/imap_codice_l1a_lo-sw-angular_20240429_v001.cdf +0 -0
  250. imap_processing/tests/codice/data/imap_codice_l1a_lo-sw-priority_20240429_v001.cdf +0 -0
  251. imap_processing/tests/codice/data/imap_codice_l1a_lo-sw-species_20240429_v001.cdf +0 -0
  252. imap_processing/tests/codice/data/imap_codice_l1b_hi-counters-aggregated_20240429_v001.cdf +0 -0
  253. imap_processing/tests/codice/data/imap_codice_l1b_hi-counters-singles_20240429_v001.cdf +0 -0
  254. imap_processing/tests/codice/data/imap_codice_l1b_hi-omni_20240429_v001.cdf +0 -0
  255. imap_processing/tests/codice/data/imap_codice_l1b_hi-sectored_20240429_v001.cdf +0 -0
  256. imap_processing/tests/codice/data/imap_codice_l1b_hskp_20100101_v001.cdf +0 -0
  257. imap_processing/tests/codice/data/imap_codice_l1b_lo-counters-aggregated_20240429_v001.cdf +0 -0
  258. imap_processing/tests/codice/data/imap_codice_l1b_lo-counters-singles_20240429_v001.cdf +0 -0
  259. imap_processing/tests/codice/data/imap_codice_l1b_lo-nsw-angular_20240429_v001.cdf +0 -0
  260. imap_processing/tests/codice/data/imap_codice_l1b_lo-nsw-priority_20240429_v001.cdf +0 -0
  261. imap_processing/tests/codice/data/imap_codice_l1b_lo-nsw-species_20240429_v001.cdf +0 -0
  262. imap_processing/tests/codice/data/imap_codice_l1b_lo-sw-angular_20240429_v001.cdf +0 -0
  263. imap_processing/tests/codice/data/imap_codice_l1b_lo-sw-priority_20240429_v001.cdf +0 -0
  264. imap_processing/tests/codice/data/imap_codice_l1b_lo-sw-species_20240429_v001.cdf +0 -0
  265. imap_processing/tests/hi/data/l1/imap_hi_l1b_45sensor-de_20250415_v999.cdf +0 -0
  266. imap_processing/tests/hit/PREFLIGHT_raw_record_2023_256_15_59_04_apid1251.pkts +0 -0
  267. imap_processing/tests/hit/PREFLIGHT_raw_record_2023_256_15_59_04_apid1252.pkts +0 -0
  268. imap_processing/tests/hit/validation_data/hskp_sample_eu.csv +0 -89
  269. imap_processing/tests/hit/validation_data/sci_sample_raw1.csv +0 -29
  270. imap_processing/tests/idex/test_data/imap_idex_l0_raw_20231214_v001.pkts +0 -0
  271. imap_processing/tests/lo/test_cdfs/imap_lo_l1a_de_20100101_v001.cdf +0 -0
  272. imap_processing/tests/lo/test_cdfs/imap_lo_l1a_spin_20100101_v001.cdf +0 -0
  273. imap_processing/tests/ultra/test_data/mock_data.py +0 -161
  274. imap_processing/ultra/l1c/pset.py +0 -40
  275. /imap_processing/tests/ultra/{test_data → data}/l0/FM45_40P_Phi28p5_BeamCal_LinearScan_phi28.50_theta-0.00_20240207T102740.CCSDS +0 -0
  276. /imap_processing/tests/ultra/{test_data → data}/l0/FM45_7P_Phi0.0_BeamCal_LinearScan_phi0.04_theta-0.01_20230821T121304.CCSDS +0 -0
  277. /imap_processing/tests/ultra/{test_data → data}/l0/FM45_TV_Cycle6_Hot_Ops_Front212_20240124T063837.CCSDS +0 -0
  278. /imap_processing/tests/ultra/{test_data → data}/l0/Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.CCSDS +0 -0
  279. /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_auxdata_Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.csv +0 -0
  280. /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_enaphxtofhangimg_FM45_TV_Cycle6_Hot_Ops_Front212_20240124T063837.csv +0 -0
  281. /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_ultraimgrates_Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.csv +0 -0
  282. /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_ultrarawimgevent_FM45_7P_Phi00_BeamCal_LinearScan_phi004_theta-001_20230821T121304.csv +0 -0
  283. /imap_processing/tests/ultra/{test_data → data}/l1/dps_exposure_helio_45_E1.cdf +0 -0
  284. /imap_processing/tests/ultra/{test_data → data}/l1/dps_exposure_helio_45_E12.cdf +0 -0
  285. /imap_processing/tests/ultra/{test_data → data}/l1/dps_exposure_helio_45_E24.cdf +0 -0
  286. {imap_processing-0.11.0.dist-info → imap_processing-0.12.0.dist-info}/LICENSE +0 -0
  287. {imap_processing-0.11.0.dist-info → imap_processing-0.12.0.dist-info}/WHEEL +0 -0
  288. {imap_processing-0.11.0.dist-info → imap_processing-0.12.0.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,8 @@
1
1
  """Test coverage for imap_processing.hi.l1c.hi_l1c.py"""
2
2
 
3
+ from collections import namedtuple
3
4
  from unittest import mock
5
+ from unittest.mock import MagicMock
4
6
 
5
7
  import numpy as np
6
8
  import pandas as pd
@@ -9,9 +11,10 @@ import xarray as xr
9
11
 
10
12
  from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
11
13
  from imap_processing.cdf.utils import load_cdf, write_cdf
14
+ from imap_processing.hi.l1a.science_direct_event import DE_CLOCK_TICK_S
12
15
  from imap_processing.hi.l1c import hi_l1c
13
16
  from imap_processing.hi.l1c.hi_l1c import CalibrationProductConfig
14
- from imap_processing.hi.utils import HIAPID
17
+ from imap_processing.hi.utils import HIAPID, CoincidenceBitmap
15
18
 
16
19
 
17
20
  @pytest.fixture(scope="module")
@@ -37,10 +40,14 @@ def test_hi_l1c_not_implemented():
37
40
  hi_l1c.hi_l1c([None, None], "0")
38
41
 
39
42
 
43
+ @pytest.mark.external_test_data()
40
44
  @pytest.mark.external_kernel()
41
45
  @pytest.mark.use_test_metakernel("imap_ena_sim_metakernel.template")
42
- def test_generate_pset_dataset(hi_l1_test_data_path, hi_test_cal_prod_config_path):
46
+ def test_generate_pset_dataset(
47
+ hi_l1_test_data_path, hi_test_cal_prod_config_path, use_fake_spin_data_for_time
48
+ ):
43
49
  """Test coverage for generate_pset_dataset function"""
50
+ use_fake_spin_data_for_time(482372987.999)
44
51
  l1b_de_path = hi_l1_test_data_path / "imap_hi_l1b_45sensor-de_20250415_v999.cdf"
45
52
  l1b_dataset = load_cdf(l1b_de_path)
46
53
  l1c_dataset = hi_l1c.generate_pset_dataset(
@@ -54,9 +61,9 @@ def test_generate_pset_dataset(hi_l1_test_data_path, hi_test_cal_prod_config_pat
54
61
  np.testing.assert_array_equal(l1c_dataset.despun_z.data.shape, (1, 3))
55
62
  np.testing.assert_array_equal(l1c_dataset.hae_latitude.data.shape, (1, 3600))
56
63
  np.testing.assert_array_equal(l1c_dataset.hae_longitude.data.shape, (1, 3600))
64
+ np.testing.assert_array_equal(l1c_dataset.exposure_times.data.shape, (1, 9, 3600))
57
65
  for var in [
58
66
  "counts",
59
- "exposure_times",
60
67
  "background_rates",
61
68
  "background_rates_uncertainty",
62
69
  ]:
@@ -131,6 +138,218 @@ def test_pset_geometry(mock_frame_transform, mock_geom_frame_transform, sensor_s
131
138
  )
132
139
 
133
140
 
141
+ @pytest.mark.external_test_data()
142
+ def test_pset_counts(hi_l1_test_data_path, hi_test_cal_prod_config_path):
143
+ """Test coverage for pset_counts function."""
144
+ l1b_de_path = hi_l1_test_data_path / "imap_hi_l1b_45sensor-de_20250415_v999.cdf"
145
+ l1b_dataset = load_cdf(l1b_de_path)
146
+ cal_config_df = hi_l1c.CalibrationProductConfig.from_csv(
147
+ hi_test_cal_prod_config_path
148
+ )
149
+ empty_pset = hi_l1c.empty_pset_dataset(
150
+ l1b_dataset.esa_energy_step.data,
151
+ cal_config_df.cal_prod_config.number_of_products,
152
+ HIAPID.H90_SCI_DE.sensor,
153
+ )
154
+ counts_var = hi_l1c.pset_counts(empty_pset.coords, cal_config_df, l1b_dataset)
155
+ assert "counts" in counts_var
156
+
157
+
158
+ def test_get_tof_window_mask():
159
+ """Test coverage for get_tof_window_mask function."""
160
+ # Create a synthetic dataframe with required columns containing data
161
+ # intended to test all aspects of the function.
162
+ fill_vals = {
163
+ "tof_ab": -11,
164
+ "tof_ac1": -12,
165
+ "tof_bc1": -13,
166
+ "tof_c1c2": -14,
167
+ }
168
+ Row = namedtuple(
169
+ "Row",
170
+ [
171
+ "Index",
172
+ "tof_ab_low",
173
+ "tof_ab_high",
174
+ "tof_ac1_low",
175
+ "tof_ac1_high",
176
+ "tof_bc1_low",
177
+ "tof_bc1_high",
178
+ "tof_c1c2_low",
179
+ "tof_c1c2_high",
180
+ ],
181
+ )
182
+ prod_config_row = Row((1, 0), 0, 1, -1, 2, 1, 5, 4, 6)
183
+ synth_df = pd.DataFrame(
184
+ {
185
+ "tof_ab": np.array(
186
+ [0, 2, 1, 0, -1, -5, -11], dtype=np.int32
187
+ ), # T, F, T, T, F, F, FILL
188
+ "tof_ac1": np.array(
189
+ [-1, 2, -2, 0, 3, 0, -12], dtype=np.int32
190
+ ), # T, T, F, T, F, T, FILL
191
+ "tof_bc1": np.array(
192
+ [1, 5, 3, 0, 6, 2, -13], dtype=np.int32
193
+ ), # T, T, T, F, F, T, FILL
194
+ "tof_c1c2": np.array(
195
+ [4, 6, 5, 3, 7, -9, -14], dtype=np.int32
196
+ ), # T, T, T, F, F, F, FILL
197
+ },
198
+ )
199
+ expected_mask = np.array([True, False, False, False, False, False, True])
200
+ window_mask = hi_l1c.get_tof_window_mask(synth_df, prod_config_row, fill_vals)
201
+ np.testing.assert_array_equal(expected_mask, window_mask)
202
+
203
+
204
+ @mock.patch("imap_processing.hi.l1c.hi_l1c.get_spin_data", return_value=None)
205
+ @mock.patch("imap_processing.hi.l1c.hi_l1c.get_instrument_spin_phase")
206
+ @mock.patch("imap_processing.hi.l1c.hi_l1c.get_de_clock_ticks_for_esa_step")
207
+ @mock.patch("imap_processing.hi.l1c.hi_l1c.find_second_de_packet_data")
208
+ def test_pset_exposure(
209
+ mock_find_second_de_packet_data,
210
+ mock_de_clock_ticks,
211
+ mock_spin_phase,
212
+ mock_spin_data,
213
+ ):
214
+ """Test coverage for pset_exposure function"""
215
+ empty_pset = hi_l1c.empty_pset_dataset(
216
+ np.arange(2) + 1, 2, HIAPID.H90_SCI_DE.sensor
217
+ )
218
+ # Set the mock of find_second_de_packet_data to return a xr.Dataset
219
+ # with some dummy data. ESA 1 will get binned data once, ESA 2 will get
220
+ # binned data twice.
221
+ mock_find_second_de_packet_data.return_value = xr.Dataset(
222
+ coords={"epoch": xr.DataArray(np.arange(3), dims=["epoch"])},
223
+ data_vars={
224
+ "ccsds_met": xr.DataArray(np.arange(3), dims=["epoch"]),
225
+ "esa_energy_step": xr.DataArray(np.array([1, 2, 2]), dims=["epoch"]),
226
+ },
227
+ )
228
+ # Set mock of get_de_clock_ticks_for_esa_step and spin phase to generate
229
+ # deterministic histogram values.
230
+ # ESA step 1 should have repeating values of 3, 1.
231
+ # ESA step 2 should have repeating values of 6, 2
232
+ mock_spin_phase.return_value = np.concat(
233
+ [hi_l1c.SPIN_PHASE_BIN_CENTERS, hi_l1c.SPIN_PHASE_BIN_CENTERS[::2]]
234
+ )
235
+ mock_de_clock_ticks.return_value = (
236
+ np.zeros(hi_l1c.N_SPIN_BINS + hi_l1c.N_SPIN_BINS // 2),
237
+ np.concat([np.ones(hi_l1c.N_SPIN_BINS), np.ones(hi_l1c.N_SPIN_BINS // 2) * 2]),
238
+ )
239
+
240
+ # The above mocks mean no data needs to be in the l1b_dataset. It
241
+ # only needs to provide a logical source that contains "90sensor".
242
+ l1b_dataset = MagicMock()
243
+ l1b_dataset.attrs = {"Logical_source": "90sensor"}
244
+
245
+ # All the setup is done, call the pset_exposure function
246
+ exposure_dict = hi_l1c.pset_exposure(empty_pset.coords, l1b_dataset)
247
+
248
+ # Based on the spin phase and clock_tick mocks, the expected output is:
249
+ # - Repeated values of 3, 1 for the first half of the spin bins
250
+ # - Repeated values of 3, 2 for the second half of the spin bins
251
+ expected_values = np.stack(
252
+ [
253
+ np.tile([3, 1], hi_l1c.N_SPIN_BINS // 2),
254
+ np.tile([6, 2], hi_l1c.N_SPIN_BINS // 2),
255
+ ]
256
+ )[None, :, :]
257
+ np.testing.assert_array_equal(exposure_dict["exposure_times"].data, expected_values)
258
+
259
+
260
+ def test_find_second_de_packet_data():
261
+ """Test coverage for find_second_de_packet_data function"""
262
+ # Create a test l1b_dataset
263
+ # Expect to remove index 0 and 5 due to missing esa_step pair
264
+ # Expect to remove index 11 due to 0 being a calibration step
265
+ # Expect to return indices 2, 4, 7, 9, 13
266
+ esa_steps = np.array([1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 0, 0, 7, 7])
267
+ l1b_dataset = xr.Dataset(
268
+ coords={
269
+ "epoch": xr.DataArray(
270
+ np.arange(esa_steps.size),
271
+ dims=["epoch"],
272
+ ),
273
+ "event_met": xr.DataArray(
274
+ np.arange(10),
275
+ dims=["event_met"],
276
+ ),
277
+ },
278
+ data_vars={
279
+ "esa_step": xr.DataArray(
280
+ esa_steps,
281
+ dims=["epoch"],
282
+ ),
283
+ "coincidence_type": xr.DataArray(
284
+ np.ones(10),
285
+ dims=["event_met"],
286
+ ),
287
+ },
288
+ )
289
+ subset = hi_l1c.find_second_de_packet_data(l1b_dataset)
290
+ np.testing.assert_array_equal(subset.epoch.data, np.array([2, 4, 7, 9, 13]))
291
+
292
+
293
+ @pytest.fixture(scope="module")
294
+ def fake_spin_df():
295
+ """Generate a synthetic spin dataframe"""
296
+ # Generate some spin periods that vary by a random fraction of a second
297
+ spin_period = np.full(10, 15) + np.random.randn(10) / 10
298
+ d = {
299
+ "spin_start_time": np.add.accumulate(spin_period),
300
+ "spin_period_sec": spin_period,
301
+ }
302
+ spin_df = pd.DataFrame.from_dict(d)
303
+ return spin_df
304
+
305
+
306
+ def test_get_de_clock_ticks_for_esa_step(fake_spin_df):
307
+ """Test coverage for get_de_clock_ticks_for_esa_step function."""
308
+
309
+ # Test nominal cases where CCSDS met falls after 8th spin start and before
310
+ # the end spin in the table + 1/2 spin period
311
+ for _, spin_row in fake_spin_df.iloc[8:].iterrows():
312
+ for ccsds_met in np.linspace(
313
+ spin_row.spin_start_time,
314
+ spin_row.spin_start_time + np.floor(spin_row.spin_period_sec / 2),
315
+ 10,
316
+ ):
317
+ clock_tick_mets, clock_tick_weights = (
318
+ hi_l1c.get_de_clock_ticks_for_esa_step(ccsds_met, fake_spin_df)
319
+ )
320
+ np.testing.assert_array_equal(clock_tick_mets.shape, clock_tick_mets.shape)
321
+ # Verify last weight entry
322
+ exp_final_weight = (
323
+ np.absolute(
324
+ fake_spin_df.spin_start_time.to_numpy() - clock_tick_mets[-1]
325
+ ).min()
326
+ / DE_CLOCK_TICK_S
327
+ )
328
+ assert clock_tick_weights[-1] == exp_final_weight
329
+ assert np.all(clock_tick_weights[:-1] == 1)
330
+
331
+
332
+ def test_get_de_clock_ticks_for_esa_step_exceptions(fake_spin_df):
333
+ """Test the exception logic in the get_de_clock_ticks_for_esa_step function."""
334
+ # Test the ccsds_met being > 1/2 spin period past the spin start
335
+ bad_ccsds_met = (
336
+ fake_spin_df.iloc[8].spin_start_time
337
+ + fake_spin_df.iloc[8].spin_period_sec / 2
338
+ + 0.1
339
+ )
340
+ with pytest.raises(
341
+ ValueError, match="The difference between ccsds_met and spin_start_met"
342
+ ):
343
+ hi_l1c.get_de_clock_ticks_for_esa_step(bad_ccsds_met, fake_spin_df)
344
+
345
+ # Test the ccsds_met being too close to the start of the spin table
346
+ bad_ccsds_met = fake_spin_df.iloc[7].spin_start_time
347
+ with pytest.raises(
348
+ ValueError, match="Error determining start/end time for exposure time"
349
+ ):
350
+ hi_l1c.get_de_clock_ticks_for_esa_step(bad_ccsds_met, fake_spin_df)
351
+
352
+
134
353
  class TestCalibrationProductConfig:
135
354
  """
136
355
  All test coverage for the pd.DataFrame accessor extension "cal_prod_config".
@@ -138,7 +357,7 @@ class TestCalibrationProductConfig:
138
357
 
139
358
  def test_wrong_columns(self):
140
359
  """Test coverage for a dataframe with the wrong columns."""
141
- required_columns = CalibrationProductConfig.required_columns
360
+ required_columns = hi_l1c.CalibrationProductConfig.required_columns
142
361
  for exclude_column_name in required_columns:
143
362
  include_columns = set(required_columns) - {exclude_column_name}
144
363
  df = pd.DataFrame({col: [1, 2, 3] for col in include_columns})
@@ -147,10 +366,19 @@ class TestCalibrationProductConfig:
147
366
 
148
367
  def test_from_csv(self, hi_test_cal_prod_config_path):
149
368
  """Test coverage for read_csv function."""
369
+ df = hi_l1c.CalibrationProductConfig.from_csv(hi_test_cal_prod_config_path)
370
+ assert isinstance(df["coincidence_type_list"][0, 1], tuple)
371
+
372
+ def test_added_coincidence_type_values_column(self, hi_test_cal_prod_config_path):
150
373
  df = CalibrationProductConfig.from_csv(hi_test_cal_prod_config_path)
151
- assert isinstance(df["coincidence_type_list"][0, 1], list)
374
+ assert "coincidence_type_values" in df.columns
375
+ for _, row in df.iterrows():
376
+ for detect_string, val in zip(
377
+ row["coincidence_type_list"], row["coincidence_type_values"]
378
+ ):
379
+ assert val == CoincidenceBitmap.detector_hit_str_to_int(detect_string)
152
380
 
153
381
  def test_number_of_products(self, hi_test_cal_prod_config_path):
154
382
  """Test coverage for number of products accessor."""
155
- df = CalibrationProductConfig.from_csv(hi_test_cal_prod_config_path)
383
+ df = hi_l1c.CalibrationProductConfig.from_csv(hi_test_cal_prod_config_path)
156
384
  assert df.cal_prod_config.number_of_products == 2
@@ -1,4 +1,5 @@
1
1
  import numpy as np
2
+ import pandas as pd
2
3
 
3
4
  from imap_processing.cdf.utils import write_cdf
4
5
  from imap_processing.hi.l1a.hi_l1a import hi_l1a
@@ -23,6 +24,35 @@ def test_sci_de_decom(hi_l0_test_data_path):
23
24
  assert cdf_filepath.name == cdf_filename
24
25
 
25
26
 
27
+ def test_diag_fee_decom(hi_l0_test_data_path):
28
+ """Test diag_fee data"""
29
+ bin_data_path = hi_l0_test_data_path / "H45_diag_fee_20250208.bin"
30
+ processed_data = hi_l1a(packet_file_path=bin_data_path, data_version="001")
31
+ dataset = processed_data[0]
32
+ cdf_filepath = write_cdf(processed_data[0], istp=False)
33
+ assert cdf_filepath.name == "imap_hi_l1a_45sensor-diagfee_20250208_v001.cdf"
34
+
35
+ assert np.unique(processed_data[0]["pkt_apid"].values) == HIAPID.H45_DIAG_FEE.value
36
+
37
+ validation_df = pd.read_csv(
38
+ hi_l0_test_data_path / "H45_diag_fee_20250208_verify.csv"
39
+ )
40
+ val_to_test_map = {
41
+ "PHVERNO": "version",
42
+ "PHTYPE": "type",
43
+ "PHSHF": "sec_hdr_flg",
44
+ "PHAPID": "pkt_apid",
45
+ "PHGROUPF": "seq_flgs",
46
+ "PHSEQCNT": "src_seq_ctr",
47
+ "PHDLEN": "pkt_len",
48
+ }
49
+ for col_name, series in validation_df.items():
50
+ if col_name == "timestamp":
51
+ continue
52
+ ds_var_name = val_to_test_map.get(col_name, col_name.lower())
53
+ np.testing.assert_array_equal(series.values, dataset[ds_var_name].data)
54
+
55
+
26
56
  def test_app_nhk_decom(hi_l0_test_data_path):
27
57
  """Test housekeeping data"""
28
58
 
@@ -21,8 +21,8 @@ def test_parse_direct_events():
21
21
  # Encode the random events data into a bit-string
22
22
  bin_str = ""
23
23
  for i in range(n_events):
24
- bin_str += f"{exp_dict['trigger_id'][i]:02b}" # 2-bits for trigger_id
25
24
  bin_str += f"{exp_dict['de_tag'][i]:016b}" # 16-bits for de_tag
25
+ bin_str += f"{exp_dict['trigger_id'][i]:02b}" # 2-bits for trigger_id
26
26
  bin_str += f"{exp_dict['tof_1'][i]:010b}" # 10-bits for tof_1
27
27
  bin_str += f"{exp_dict['tof_2'][i]:010b}" # 10-bits for tof_2
28
28
  bin_str += f"{exp_dict['tof_3'][i]:010b}" # 10-bits for tof_3
@@ -7,6 +7,7 @@ import xarray as xr
7
7
  from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
8
8
  from imap_processing.hi.utils import (
9
9
  HIAPID,
10
+ CoincidenceBitmap,
10
11
  create_dataset_variables,
11
12
  full_dataarray,
12
13
  parse_sensor_number,
@@ -74,13 +75,13 @@ def test_full_dataarray(name, shape, fill_value, expected_shape):
74
75
  @pytest.mark.parametrize(
75
76
  "var_names, shape, fill_value, lookup_str",
76
77
  [
77
- (["delta_t_ab", "delta_t_ac1"], 5, None, "hi_de_{0}"),
78
+ (["tof_ab", "tof_ac1"], 5, None, "hi_de_{0}"),
78
79
  (["hae_latitude"], (3, 5), 0, "hi_pset_{0}"),
79
80
  ],
80
81
  )
81
82
  def test_create_dataset_variables(var_names, shape, fill_value, lookup_str):
82
83
  """Test coverage for `imap_processing.hi.utils.create_dataset_variables`"""
83
- var_names = ["delta_t_ab", "delta_t_ac1", "delta_t_bc1"]
84
+ var_names = ["tof_ab", "tof_ac1", "tof_bc1"]
84
85
  l1b_de_vars = create_dataset_variables(
85
86
  var_names, shape, fill_value=fill_value, att_manager_lookup_str="hi_de_{0}"
86
87
  )
@@ -100,3 +101,24 @@ def test_create_dataset_variables(var_names, shape, fill_value, lookup_str):
100
101
  assert data_array.shape == shape
101
102
  expected_fill_value = fill_value if fill_value is not None else attrs["FILLVAL"]
102
103
  np.testing.assert_array_equal(data_array, expected_fill_value)
104
+
105
+
106
+ @pytest.mark.parametrize(
107
+ "sensor_hit_str, expected_val",
108
+ [
109
+ ("ABC1C2", 15),
110
+ ("ABC1", 14),
111
+ ("AB", 12),
112
+ ("AC1C2", 11),
113
+ ("AC1", 10),
114
+ ("A", 8),
115
+ ("BC1C2", 7),
116
+ ("BC1", 6),
117
+ ("B", 4),
118
+ ("C1C2", 3),
119
+ ("C1", 2),
120
+ ],
121
+ )
122
+ def test_coincidence_type_string_to_int(sensor_hit_str, expected_val):
123
+ """Test coverage for coincidence_type_string_to_int function"""
124
+ assert CoincidenceBitmap.detector_hit_str_to_int(sensor_hit_str) == expected_val
@@ -48,16 +48,16 @@ RENAME_COLUMNS = {
48
48
  }
49
49
 
50
50
  MOD_VALUE_TO_SPECIES_ENERGY_MAP = {
51
- 0: {"species": "H", "energy_idx": 0},
52
- 1: {"species": "H", "energy_idx": 1},
53
- 2: {"species": "H", "energy_idx": 2},
54
- 3: {"species": "He4", "energy_idx": 0},
55
- 4: {"species": "He4", "energy_idx": 1},
56
- 5: {"species": "CNO", "energy_idx": 0},
57
- 6: {"species": "CNO", "energy_idx": 1},
58
- 7: {"species": "NeMgSi", "energy_idx": 0},
59
- 8: {"species": "NeMgSi", "energy_idx": 1},
60
- 9: {"species": "Fe", "energy_idx": 0},
51
+ 0: {"species": "h", "energy_bin": 0},
52
+ 1: {"species": "h", "energy_bin": 1},
53
+ 2: {"species": "h", "energy_bin": 2},
54
+ 3: {"species": "he4", "energy_bin": 0},
55
+ 4: {"species": "he4", "energy_bin": 1},
56
+ 5: {"species": "cno", "energy_bin": 0},
57
+ 6: {"species": "cno", "energy_bin": 1},
58
+ 7: {"species": "nemgsi", "energy_bin": 0},
59
+ 8: {"species": "nemgsi", "energy_bin": 1},
60
+ 9: {"species": "fe", "energy_bin": 0},
61
61
  }
62
62
 
63
63
 
@@ -170,7 +170,7 @@ def consolidate_rate_columns(
170
170
  def consolidate_sectorates(data: pd.DataFrame) -> pd.DataFrame:
171
171
  """Consolidate sector rate data into arrays.
172
172
 
173
- This function distinguishes between sector rate columns with three digits
173
+ This function distinguishes between sectored rate columns with three digits
174
174
  and those with four digits in their names.
175
175
 
176
176
  SECTORATES_000 SECTORATES_000_0 SECTORATES_000_1 SECTORATES_000_2...SECTORATES_120_9
@@ -185,9 +185,9 @@ def consolidate_sectorates(data: pd.DataFrame) -> pd.DataFrame:
185
185
 
186
186
  Columns with four digits (e.g., SECTORATES_000_0) include the sectorate
187
187
  values with a mod 10 value appended (e.g., 0). The mod 10 value determines
188
- the species and energy range the sector rates represent in the science frame.
188
+ the species and energy range the sectored rates represent in the science frame.
189
189
  There are 10 possible species and energy ranges, but only one has data per
190
- science frame. The validation data has 10 columns per 120 sector rates,
190
+ science frame. The validation data has 10 columns per 120 sectored rates,
191
191
  totaling 1200 columns per science frame. Each set of 10 columns will have
192
192
  only one value, resulting in an array that looks like this:
193
193
 
@@ -215,13 +215,13 @@ def consolidate_sectorates(data: pd.DataFrame) -> pd.DataFrame:
215
215
  ).columns
216
216
 
217
217
  data["sectorates"] = data[sectorates_three_digits].apply(
218
- lambda row: row.values.reshape(8, 15), axis=1
218
+ lambda row: row.values.reshape(15, 8), axis=1
219
219
  )
220
220
  data["sectorates_delta_plus"] = data[sectorates_delta_plus_three_digits].apply(
221
- lambda row: row.values.reshape(8, 15), axis=1
221
+ lambda row: row.values.reshape(15, 8), axis=1
222
222
  )
223
223
  data["sectorates_delta_minus"] = data[sectorates_delta_minus_three_digits].apply(
224
- lambda row: row.values.reshape(8, 15), axis=1
224
+ lambda row: row.values.reshape(15, 8), axis=1
225
225
  )
226
226
 
227
227
  sectorates_four_digits = data.filter(regex=r"^SECTORATES_\d{3}_\d{1}$").columns
@@ -279,7 +279,7 @@ def process_single_rates(data: pd.DataFrame) -> pd.DataFrame:
279
279
  def add_species_energy(data: pd.DataFrame) -> pd.DataFrame:
280
280
  """Add species and energy index to the validation data.
281
281
 
282
- The sector rate data is organized by species and energy index
282
+ The sectored rate data is organized by species and energy index
283
283
  in the processed data so this function adds this information
284
284
  to each row (i.e. science frame) in the validation data.
285
285
 
@@ -304,12 +304,12 @@ def add_species_energy(data: pd.DataFrame) -> pd.DataFrame:
304
304
  )
305
305
  )
306
306
  data["species"] = data["mod_10"].apply(
307
- lambda row: MOD_VALUE_TO_SPECIES_ENERGY_MAP[row]["species"].lower()
307
+ lambda row: MOD_VALUE_TO_SPECIES_ENERGY_MAP[row]["species"]
308
308
  if row is not None
309
309
  else None
310
310
  )
311
- data["energy_idx"] = data["mod_10"].apply(
312
- lambda row: MOD_VALUE_TO_SPECIES_ENERGY_MAP[row]["energy_idx"]
311
+ data["energy_bin"] = data["mod_10"].apply(
312
+ lambda row: MOD_VALUE_TO_SPECIES_ENERGY_MAP[row]["energy_bin"]
313
313
  if row is not None
314
314
  else None
315
315
  )
@@ -336,7 +336,7 @@ def compare_data(
336
336
  if field not in [
337
337
  "sc_tick_by_frame",
338
338
  "species",
339
- "energy_idx",
339
+ "energy_bin",
340
340
  ]:
341
341
  assert (
342
342
  field in actual_data.data_vars.keys()
@@ -344,47 +344,47 @@ def compare_data(
344
344
  if field not in skip:
345
345
  for frame in range(expected_data.shape[0]):
346
346
  if field == "species":
347
- # Compare sector rates data using species and energy index.
347
+ # Compare sectored rates data using species and energy index.
348
348
  # which are only present in the validation data. In the actual
349
- # data, sector rates are organized by species in 4D arrays.
350
- # i.e. h_counts_sectored has shape
351
- # (epoch, h_energy_index, declination, azimuth).
349
+ # data, sectored rates are organized by species in 4D arrays.
350
+ # i.e. h_sectored_counts has shape
351
+ # (epoch, h_energy_index, azimuth, declination).
352
352
  # species and energy index are used to find the correct
353
- # array of sector rate data from the actual data for comparison.
353
+ # array of sectored rate data from the actual data for comparison.
354
354
  species = expected_data[field][frame]
355
- energy_idx = expected_data["energy_idx"][frame]
355
+ energy_bin = expected_data["energy_bin"][frame]
356
356
  if "sectorates_delta_plus" in expected_data.columns:
357
357
  np.testing.assert_allclose(
358
- actual_data[f"{species}_counts_sectored_delta_plus"][frame][
359
- energy_idx
358
+ actual_data[f"{species}_sectored_counts_delta_plus"][frame][
359
+ energy_bin
360
360
  ].data,
361
361
  expected_data["sectorates_delta_plus"][frame],
362
362
  rtol=1e-7, # relative tolerance
363
363
  atol=1e-8, # absolute tolerance
364
- err_msg=f"Mismatch in {species}_counts_sectored_delta_"
365
- f"plus at frame {frame}, energy_idx {energy_idx}",
364
+ err_msg=f"Mismatch in {species}_sectored_counts_delta_"
365
+ f"plus at frame {frame}, energy_bin {energy_bin}",
366
366
  )
367
367
  if "sectorates_delta_minus" in expected_data.columns:
368
368
  np.testing.assert_allclose(
369
- actual_data[f"{species}_counts_sectored_delta_minus"][
369
+ actual_data[f"{species}_sectored_counts_delta_minus"][
370
370
  frame
371
- ][energy_idx].data,
371
+ ][energy_bin].data,
372
372
  expected_data["sectorates_delta_minus"][frame],
373
373
  rtol=1e-7,
374
374
  atol=1e-8,
375
- err_msg=f"Mismatch in {species}_counts_sectored_delta_"
376
- f"minus at frame {frame}, energy_idx {energy_idx}",
375
+ err_msg=f"Mismatch in {species}_sectored_counts_delta_"
376
+ f"minus at frame {frame}, energy_bin {energy_bin}",
377
377
  )
378
378
  else:
379
379
  np.testing.assert_allclose(
380
- actual_data[f"{species}_counts_sectored"][frame][
381
- energy_idx
380
+ actual_data[f"{species}_sectored_counts"][frame][
381
+ energy_bin
382
382
  ].data,
383
383
  expected_data["sectorates"][frame],
384
384
  rtol=1e-7,
385
385
  atol=1e-8,
386
- err_msg=f"Mismatch in {species}_counts_sectored at"
387
- f"frame {frame}, energy_idx {energy_idx}",
386
+ err_msg=f"Mismatch in {species}_sectored_counts at"
387
+ f"frame {frame}, energy_bin {energy_bin}",
388
388
  )
389
389
  elif field == "sc_tick_by_frame":
390
390
  # Get the sc_tick values for each frame in the actual data
@@ -7,6 +7,7 @@ from imap_processing import imap_module_directory
7
7
  from imap_processing.hit.hit_utils import (
8
8
  HitAPID,
9
9
  )
10
+ from imap_processing.hit.l0.constants import AZIMUTH_ANGLES, DECLINATION_ANGLES
10
11
  from imap_processing.hit.l0.decom_hit import (
11
12
  assemble_science_frames,
12
13
  decom_hit,
@@ -122,6 +123,9 @@ def test_parse_count_rates(sci_dataset):
122
123
  if count_rate_vars in list(sci_dataset.keys()):
123
124
  assert True
124
125
 
126
+ assert np.allclose(sci_dataset["declination"].values, DECLINATION_ANGLES)
127
+ assert np.allclose(sci_dataset["azimuth"].values, AZIMUTH_ANGLES)
128
+
125
129
 
126
130
  def test_is_sequential():
127
131
  """Test the is_sequential function."""