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
@@ -0,0 +1,341 @@
1
+ """Mock expected data for use in some tests."""
2
+
3
+ import astropy_healpix.healpy as hp
4
+ import numpy as np
5
+ import spiceypy as spice
6
+ import xarray as xr
7
+
8
+ from imap_processing.ena_maps.utils.coordinates import CoordNames
9
+ from imap_processing.spice.kernels import ensure_spice
10
+ from imap_processing.spice.time import str_to_et
11
+ from imap_processing.ultra.l1c.ultra_l1c_pset_bins import build_energy_bins
12
+
13
+ DEFAULT_RECT_SPACING_DEG_L1C = 0.5
14
+ DEFAULT_HEALPIX_NSIDE_L1C = 128
15
+
16
+
17
+ def mock_l1c_pset_product_rectangular( # noqa: PLR0913
18
+ spacing_deg: float = DEFAULT_RECT_SPACING_DEG_L1C,
19
+ stripe_center_lat: int = 0,
20
+ width_scale: float = 10.0,
21
+ counts_scaling_params: tuple[int, float] = (100, 0.01),
22
+ peak_exposure: float = 1000.0,
23
+ timestr: str = "2025-01-01T00:00:00",
24
+ head: str = "45",
25
+ ) -> xr.Dataset:
26
+ """
27
+ Mock the L1C PSET product with recognizable but unrealistic counts.
28
+
29
+ This is not meant to perfectly mimic the real data, but to provide a
30
+ recognizable structure for L2 testing purposes.
31
+ Function will produce an xarray.Dataset with at least the variables and shapes:
32
+ counts: (1 epoch, num_energy_bins, num_lon_bins, num_lat_bins)
33
+ exposure_time: (num_lon_bins, num_lat_bins)
34
+ sensitivity: (1 epoch, num_energy_bins, num_lon_bins, num_lat_bins)
35
+
36
+ and the coordinate variables:
37
+ the epoch (assumed to be a single time for each product).
38
+ energy: (determined by build_energy_bins function)
39
+ longitude: (num_lon_bins)
40
+ latitude: (num_lat_bins)
41
+
42
+ While not a coordinate, PSETs can also be distinguished by the 'head' attribute.
43
+ head: Either '45' or '90'. Default is '45'.
44
+
45
+ The counts are generated along a stripe, centered at a given latitude.
46
+ This stripe can be thought of as a 'horizontal' line if the lon/az axis is plotted
47
+ as the x-axis and the lat/el axis is plotted as the y-axis. See the figure below.
48
+
49
+ ^ Elevation/Latitude
50
+ |
51
+ | 000000000000000000000000000000000000000000000000000 |
52
+ | 000000000000000000000000000000000000000000000000000 |
53
+ | 000000000000000000000000000000000000000000000000000 |
54
+ | 000000000000000000000000000000000000000000000000000 |
55
+ | 000000000000000000000000000000000000000000000000000 \
56
+ | 222222222222222222222222222222222222222222222222222 \
57
+ | 444444444444444444444444444444444444444444444444444 \
58
+ | 666666666666666666666666666666666666666666666666666 |
59
+ | 444444444444444444444444444444444444444444444444444 /
60
+ | 222222222222222222222222222222222222222222222222222 /
61
+ | 000000000000000000000000000000000000000000000000000 /
62
+ | 000000000000000000000000000000000000000000000000000 |
63
+ --------------------------------------------------------->
64
+ Azimuth/Longitude ->
65
+
66
+ Fig. 1: Example of the '90' sensor head stripe
67
+
68
+ Parameters
69
+ ----------
70
+ spacing_deg : float, optional
71
+ The bin spacing in degrees (default is 0.5 degrees).
72
+ stripe_center_lat : int, optional
73
+ The center latitude of the stripe in degrees (default is 0).
74
+ width_scale : float, optional
75
+ The width of the stripe in degrees (default is 20 degrees).
76
+ counts_scaling_params : tuple[int, float], optional
77
+ The parameters for the binomial distribution of counts (default is (100, 0.01)).
78
+ The 0th element is the number of trials to draw,
79
+ the 1st element scales the probability of success for each trial.
80
+ peak_exposure : float, optional
81
+ The peak exposure time (default is 1000.0).
82
+ timestr : str, optional
83
+ The time string for the epoch (default is "2025-01-01T00:00:00").
84
+ head : str, optional
85
+ The sensor head (either '45' or '90') (default is '45').
86
+ """
87
+ num_lat_bins = int(180 / spacing_deg)
88
+ num_lon_bins = int(360 / spacing_deg)
89
+ stripe_center_lat_bin = int((stripe_center_lat + 90) / spacing_deg)
90
+
91
+ _, energy_bin_midpoints, _ = build_energy_bins()
92
+ num_energy_bins = len(energy_bin_midpoints)
93
+
94
+ # 1 epoch x num_energy_bins x num_lon_bins x num_lat_bins
95
+ grid_shape = (1, num_energy_bins, num_lon_bins, num_lat_bins)
96
+
97
+ def get_binomial_counts(distance_scaling, lat_bin, central_lat_bin):
98
+ # Note, this is not quite correct, as it won't wrap around at 360 degrees
99
+ # but it's all meant to provide a recognizable pattern for testing
100
+ distance_lat_bin = np.abs(lat_bin - central_lat_bin)
101
+
102
+ rng = np.random.default_rng(seed=42)
103
+ return rng.binomial(
104
+ n=counts_scaling_params[0],
105
+ p=np.maximum(
106
+ 1 - (distance_lat_bin / distance_scaling), counts_scaling_params[1]
107
+ ),
108
+ )
109
+
110
+ counts = np.fromfunction(
111
+ lambda epoch, energy_bin, lon_bin, lat_bin: get_binomial_counts(
112
+ distance_scaling=width_scale,
113
+ lat_bin=lat_bin,
114
+ central_lat_bin=stripe_center_lat_bin,
115
+ ),
116
+ shape=grid_shape,
117
+ )
118
+
119
+ # exposure_time should be a gaussian distribution centered on the stripe
120
+ # with a width of 20 degrees
121
+ exposure_time = np.zeros(grid_shape[2:])
122
+ exposure_time = np.fromfunction(
123
+ lambda lon_bin, lat_bin: np.exp(
124
+ -((lat_bin - stripe_center_lat_bin) ** 2) / (2 * width_scale**2)
125
+ ),
126
+ shape=grid_shape[2:],
127
+ )
128
+ exposure_time /= exposure_time.max()
129
+ exposure_time *= peak_exposure
130
+ counts = counts.astype(int)
131
+ sensitivity = np.ones(grid_shape)
132
+
133
+ # Determine the epoch, which is TT time in nanoseconds since J2000 epoch
134
+ tdb_et = str_to_et(timestr)
135
+ tt_j2000ns = (
136
+ ensure_spice(spice.unitim, time_kernels_only=True)(tdb_et, "ET", "TT") * 1e9
137
+ )
138
+
139
+ pset_product = xr.Dataset(
140
+ {
141
+ "counts": (
142
+ [
143
+ CoordNames.TIME.value,
144
+ CoordNames.ENERGY.value,
145
+ CoordNames.AZIMUTH_L1C.value,
146
+ CoordNames.ELEVATION_L1C.value,
147
+ ],
148
+ counts,
149
+ ),
150
+ "exposure_time": (
151
+ [
152
+ CoordNames.TIME.value,
153
+ CoordNames.AZIMUTH_L1C.value,
154
+ CoordNames.ELEVATION_L1C.value,
155
+ ],
156
+ np.expand_dims(exposure_time, axis=0),
157
+ ),
158
+ "sensitivity": (
159
+ [
160
+ CoordNames.TIME.value,
161
+ CoordNames.ENERGY.value,
162
+ CoordNames.AZIMUTH_L1C.value,
163
+ CoordNames.ELEVATION_L1C.value,
164
+ ],
165
+ sensitivity,
166
+ ),
167
+ },
168
+ coords={
169
+ CoordNames.TIME.value: [
170
+ tt_j2000ns,
171
+ ],
172
+ CoordNames.ENERGY.value: energy_bin_midpoints,
173
+ CoordNames.AZIMUTH_L1C.value: np.arange(
174
+ 0 + spacing_deg / 2, 360, spacing_deg
175
+ ),
176
+ CoordNames.ELEVATION_L1C.value: np.arange(
177
+ -90 + spacing_deg / 2, 90, spacing_deg
178
+ ),
179
+ },
180
+ attrs={
181
+ "Logical_file_id": (
182
+ f"imap_ultra_l1c_{head}sensor-pset_{timestr[:4]}"
183
+ f"{timestr[5:7]}{timestr[8:10]}-repointNNNNN_vNNN"
184
+ )
185
+ },
186
+ )
187
+
188
+ return pset_product
189
+
190
+
191
+ def mock_l1c_pset_product_healpix( # noqa: PLR0913
192
+ nside: int = DEFAULT_HEALPIX_NSIDE_L1C,
193
+ stripe_center_lat: int = 0,
194
+ width_scale: float = 10.0,
195
+ counts_scaling_params: tuple[int, float] = (100, 0.01),
196
+ peak_exposure: float = 1000.0,
197
+ timestr: str = "2025-01-01T00:00:00",
198
+ head: str = "45",
199
+ ) -> xr.Dataset:
200
+ """
201
+ Mock the L1C PSET product with recognizable but unrealistic counts.
202
+
203
+ See the docstring for mock_l1c_pset_product_rectangular for more details about
204
+ the structure of the dataset.
205
+ The rectangular and Healpix mocked datasets are very similar in structure, though
206
+ the actual values at a given latitude and longitude may be different. This is only
207
+ meant to provide a recognizable structure for L2 testing purposes.
208
+
209
+ The counts are generated along a stripe, centered at a given latitude.
210
+ This stripe can be thought of as a 'vertical' line if the lon/az axis is plotted
211
+ as the x-axis and the lat/el axis is plotted as the y-axis. See the figure below.
212
+
213
+ ^ Elevation/Latitude
214
+ |
215
+ | 00000000000000000000 |
216
+ | 0000000000000000000000000000 |
217
+ | 0000000000000000000000000000000000000 |
218
+ | 0000000000000000000000000000000000000000000 |
219
+ | 00000000000000000000000000000000000000000000000 |
220
+ | 0000000000000000000000000000000000000000000000000 \
221
+ | 222222222222222222222222222222222222222222222222222 \
222
+ | 444444444444444444444444444444444444444444444444444 \
223
+ | 666666666666666666666666666666666666666666666666666 |
224
+ | 4444444444444444444444444444444444444444444444444 /
225
+ | 22222222222222222222222222222222222222222222222 /
226
+ | 0000000000000000000000000000000000000000000 /
227
+ | 0000000000000000000000000000000000000 |
228
+ | 0000000000000000000000000000 |
229
+ | 00000000000000000000 |
230
+ --------------------------------------------------------->
231
+ Azimuth/Longitude ->
232
+
233
+ Fig. 1: Example of the '90' sensor head stripe on a HEALPix grid
234
+
235
+ Parameters
236
+ ----------
237
+ nside : int, optional
238
+ The HEALPix nside parameter (default is 128).
239
+ stripe_center_lat : int, optional
240
+ The center latitude of the stripe in degrees (default is 0).
241
+ width_scale : float, optional
242
+ The width of the stripe in degrees (default is 10 degrees).
243
+ counts_scaling_params : tuple[int, float], optional
244
+ The parameters for the binomial distribution of counts (default is (100, 0.01)).
245
+ The 0th element is the number of trials to draw,
246
+ the 1st element scales the probability of success for each trial.
247
+ peak_exposure : float, optional
248
+ The peak exposure time (default is 1000.0).
249
+ timestr : str, optional
250
+ The time string for the epoch (default is "2025-01-01T00:00:00").
251
+ head : str, optional
252
+ The sensor head (either '45' or '90') (default is '45').
253
+ """
254
+ _, energy_bin_midpoints, _ = build_energy_bins()
255
+ num_energy_bins = len(energy_bin_midpoints)
256
+ npix = hp.nside2npix(nside)
257
+ counts = np.zeros(npix)
258
+ exposure_time = np.zeros(npix)
259
+
260
+ # Get latitude for each healpix pixel
261
+ pix_indices = np.arange(npix)
262
+ lon_pix, lat_pix = hp.pix2ang(nside, pix_indices, lonlat=True)
263
+
264
+ counts = np.zeros(shape=(num_energy_bins, npix))
265
+
266
+ # Calculate probability based on distance from target latitude
267
+ lat_diff = np.abs(lat_pix - stripe_center_lat)
268
+ prob_scaling_factor = counts_scaling_params[1] * np.exp(
269
+ -(lat_diff**2) / (2 * width_scale**2)
270
+ )
271
+ # Generate counts using binomial distribution
272
+ rng = np.random.default_rng(seed=42)
273
+ counts = np.array(
274
+ [
275
+ rng.binomial(n=counts_scaling_params[0], p=prob_scaling_factor)
276
+ for _ in range(num_energy_bins)
277
+ ]
278
+ )
279
+
280
+ # Generate exposure times using gaussian distribution
281
+ exposure_time = peak_exposure * (prob_scaling_factor / prob_scaling_factor.max())
282
+
283
+ # Ensure counts are integers
284
+ counts = counts.astype(int)
285
+ # add an epoch dimension
286
+ counts = np.expand_dims(counts, axis=0)
287
+ sensitivity = np.ones_like(counts)
288
+
289
+ # Determine the epoch, which is TT time in nanoseconds since J2000 epoch
290
+ tdb_et = str_to_et(timestr)
291
+ tt_j2000ns = (
292
+ ensure_spice(spice.unitim, time_kernels_only=True)(tdb_et, "ET", "TT") * 1e9
293
+ )
294
+
295
+ pset_product = xr.Dataset(
296
+ {
297
+ "counts": (
298
+ [
299
+ CoordNames.TIME.value,
300
+ CoordNames.ENERGY.value,
301
+ CoordNames.HEALPIX_INDEX.value,
302
+ ],
303
+ counts,
304
+ ),
305
+ "exposure_time": (
306
+ [CoordNames.HEALPIX_INDEX.value],
307
+ exposure_time,
308
+ ),
309
+ "sensitivity": (
310
+ [
311
+ CoordNames.TIME.value,
312
+ CoordNames.ENERGY.value,
313
+ CoordNames.HEALPIX_INDEX.value,
314
+ ],
315
+ sensitivity,
316
+ ),
317
+ CoordNames.AZIMUTH_L1C.value: (
318
+ [CoordNames.HEALPIX_INDEX.value],
319
+ lon_pix,
320
+ ),
321
+ CoordNames.ELEVATION_L1C.value: (
322
+ [CoordNames.HEALPIX_INDEX.value],
323
+ lat_pix,
324
+ ),
325
+ },
326
+ coords={
327
+ CoordNames.TIME.value: [
328
+ tt_j2000ns,
329
+ ],
330
+ CoordNames.ENERGY.value: energy_bin_midpoints,
331
+ CoordNames.HEALPIX_INDEX.value: pix_indices,
332
+ },
333
+ attrs={
334
+ "Logical_file_id": (
335
+ f"imap_ultra_l1c_{head}sensor-pset_{timestr[:4]}"
336
+ f"{timestr[5:7]}{timestr[8:10]}-repointNNNNN_vNNN"
337
+ )
338
+ },
339
+ )
340
+
341
+ return pset_product
@@ -4,6 +4,7 @@ from unittest import mock
4
4
 
5
5
  import numpy as np
6
6
  import pytest
7
+ import xarray as xr
7
8
 
8
9
  from imap_processing import decom, imap_module_directory
9
10
  from imap_processing.ultra.l0.decom_ultra import process_ultra_apids
@@ -24,7 +25,7 @@ def ccsds_path():
24
25
  imap_module_directory
25
26
  / "tests"
26
27
  / "ultra"
27
- / "test_data"
28
+ / "data"
28
29
  / "l0"
29
30
  / "Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.CCSDS"
30
31
  )
@@ -37,7 +38,7 @@ def ccsds_path_events():
37
38
  imap_module_directory
38
39
  / "tests"
39
40
  / "ultra"
40
- / "test_data"
41
+ / "data"
41
42
  / "l0"
42
43
  / "FM45_7P_Phi0.0_BeamCal_LinearScan_phi0.04_theta-0.01_20230821T121304.CCSDS"
43
44
  )
@@ -50,7 +51,7 @@ def ccsds_path_theta_0():
50
51
  imap_module_directory
51
52
  / "tests"
52
53
  / "ultra"
53
- / "test_data"
54
+ / "data"
54
55
  / "l0"
55
56
  / "FM45_40P_Phi28p5_BeamCal_LinearScan_phi28.50_theta-0.00"
56
57
  "_20240207T102740.CCSDS"
@@ -64,7 +65,7 @@ def ccsds_path_tof():
64
65
  imap_module_directory
65
66
  / "tests"
66
67
  / "ultra"
67
- / "test_data"
68
+ / "data"
68
69
  / "l0"
69
70
  / "FM45_TV_Cycle6_Hot_Ops_Front212_20240124T063837.CCSDS"
70
71
  )
@@ -88,7 +89,7 @@ def rates_test_path():
88
89
  "ultra45_raw_sc_ultraimgrates_Ultra45_EM_SwRI_Cal_Run7_ThetaScan_"
89
90
  "20220530T225054.csv"
90
91
  )
91
- return imap_module_directory / "tests" / "ultra" / "test_data" / "l0" / filename
92
+ return imap_module_directory / "tests" / "ultra" / "data" / "l0" / filename
92
93
 
93
94
 
94
95
  @pytest.fixture()
@@ -98,7 +99,7 @@ def aux_test_path():
98
99
  "ultra45_raw_sc_auxdata_Ultra45_EM_SwRI_Cal_Run7_ThetaScan_"
99
100
  "20220530T225054.csv"
100
101
  )
101
- return imap_module_directory / "tests" / "ultra" / "test_data" / "l0" / filename
102
+ return imap_module_directory / "tests" / "ultra" / "data" / "l0" / filename
102
103
 
103
104
 
104
105
  @pytest.fixture()
@@ -108,7 +109,7 @@ def events_test_path():
108
109
  "ultra45_raw_sc_ultrarawimgevent_FM45_7P_Phi00_BeamCal_"
109
110
  "LinearScan_phi004_theta-001_20230821T121304.csv"
110
111
  )
111
- return imap_module_directory / "tests" / "ultra" / "test_data" / "l0" / filename
112
+ return imap_module_directory / "tests" / "ultra" / "data" / "l0" / filename
112
113
 
113
114
 
114
115
  @pytest.fixture()
@@ -118,7 +119,7 @@ def tof_test_path():
118
119
  "ultra45_raw_sc_enaphxtofhangimg_FM45_TV_Cycle6_Hot_Ops_"
119
120
  "Front212_20240124T063837.csv"
120
121
  )
121
- return imap_module_directory / "tests" / "ultra" / "test_data" / "l0" / filename
122
+ return imap_module_directory / "tests" / "ultra" / "data" / "l0" / filename
122
123
 
123
124
 
124
125
  @pytest.fixture()
@@ -126,9 +127,7 @@ def decom_test_data(request, xtce_path):
126
127
  """Read test data from file"""
127
128
  apid = request.param["apid"]
128
129
  filename = request.param["filename"]
129
- ccsds_path = (
130
- imap_module_directory / "tests" / "ultra" / "test_data" / "l0" / filename
131
- )
130
+ ccsds_path = imap_module_directory / "tests" / "ultra" / "data" / "l0" / filename
132
131
 
133
132
  packets = decom.decom_packets(ccsds_path, xtce_path)
134
133
  grouped_data = group_by_apid(packets)
@@ -144,7 +143,7 @@ def events_fsw_comparison_theta_0():
144
143
  "ultra45_raw_sc_ultrarawimg_withFSWcalcs_FM45_40P_Phi28p5_"
145
144
  "BeamCal_LinearScan_phi2850_theta-000_20240207T102740.csv"
146
145
  )
147
- return imap_module_directory / "tests" / "ultra" / "test_data" / "l0" / filename
146
+ return imap_module_directory / "tests" / "ultra" / "data" / "l0" / filename
148
147
 
149
148
 
150
149
  @pytest.fixture()
@@ -158,17 +157,14 @@ def de_dataset(ccsds_path_theta_0, xtce_path):
158
157
  decom_ultra_aux = process_ultra_apids(
159
158
  grouped_data[ULTRA_AUX.apid[0]], ULTRA_AUX.apid[0]
160
159
  )
161
- dataset = ultra_l1a.create_dataset(
160
+ de_dataset = ultra_l1a.create_dataset(
162
161
  {
163
162
  ULTRA_EVENTS.apid[0]: decom_ultra_events,
164
163
  ULTRA_AUX.apid[0]: decom_ultra_aux,
165
164
  }
166
165
  )
167
- # Remove start_type with fill values
168
- l1a_de_dataset = dataset.where(
169
- dataset["START_TYPE"] != np.iinfo(np.int64).min, drop=True
170
- )
171
- return l1a_de_dataset
166
+
167
+ return de_dataset
172
168
 
173
169
 
174
170
  @pytest.fixture()
@@ -203,21 +199,49 @@ def aux_dataset(ccsds_path_theta_0, xtce_path):
203
199
  return l1a_aux_dataset
204
200
 
205
201
 
202
+ @pytest.fixture()
203
+ def faux_aux_dataset():
204
+ """Fixture to compute and return aux test data."""
205
+
206
+ num_spins = 15
207
+ spin_duration = 15 # in seconds
208
+
209
+ epoch = np.arange(0, num_spins, 1)
210
+ spin_number = np.arange(127, 142)
211
+ spin_start_time = np.arange(1905, 2115 + spin_duration, spin_duration)
212
+ spin_period_sec = np.full(num_spins, 15)
213
+ spin_period_sec[-1] = 14
214
+ spin_start_sec = np.arange(1905, 2130, 15)
215
+ spin_start_subsec = np.zeros(num_spins)
216
+
217
+ test_aux_dataset = xr.Dataset(
218
+ data_vars={
219
+ "TIMESPINSTART": ("epoch", spin_start_sec),
220
+ "TIMESPINSTARTSUB": ("epoch", spin_start_subsec),
221
+ "DURATION": ("epoch", spin_period_sec),
222
+ "SPINNUMBER": ("epoch", spin_number),
223
+ "TIMESPINDATA": ("epoch", spin_start_time),
224
+ "SPINPERIOD": ("epoch", spin_period_sec),
225
+ },
226
+ coords={"epoch": ("epoch", epoch)},
227
+ )
228
+
229
+ return test_aux_dataset
230
+
231
+
206
232
  @pytest.fixture()
207
233
  @mock.patch("imap_processing.ultra.l1b.de.get_annotated_particle_velocity")
208
- def l1b_datasets(
209
- mock_get_annotated_particle_velocity, de_dataset, use_fake_spin_data_for_time
234
+ def l1b_de_dataset(
235
+ mock_get_annotated_particle_velocity,
236
+ de_dataset,
237
+ use_fake_spin_data_for_time,
210
238
  ):
211
239
  """L1B test data"""
212
240
 
213
241
  data_dict = {}
214
242
  data_dict[de_dataset.attrs["Logical_source"]] = de_dataset
215
- # TODO: this is a placeholder for the hk dataset.
216
- data_dict["imap_ultra_l1a_45sensor-hk"] = aux_dataset
217
- data_dict["imap_ultra_l1a_45sensor-rates"] = rates_dataset
218
- use_fake_spin_data_for_time(
219
- de_dataset["EVENTTIMES"][0], de_dataset["EVENTTIMES"][-1]
220
- )
243
+ # Create a spin table that cover spin 0-141
244
+ use_fake_spin_data_for_time(0, 141 * 15)
221
245
 
222
246
  # Mock get_annotated_particle_velocity to avoid needing kernels
223
247
  def side_effect_func(event_times, position, ultra_frame, dps_frame, sc_frame):
@@ -238,3 +262,22 @@ def l1b_datasets(
238
262
  output_datasets = ultra_l1b(data_dict, data_version="001")
239
263
 
240
264
  return output_datasets
265
+
266
+
267
+ @pytest.fixture()
268
+ def l1b_extendedspin_dataset(
269
+ l1b_de_dataset,
270
+ rates_dataset,
271
+ faux_aux_dataset,
272
+ ):
273
+ """L1B de test data"""
274
+ data_dict = {}
275
+ data_dict["imap_ultra_l1b_45sensor-de"] = l1b_de_dataset[0]
276
+ data_dict["imap_ultra_l1a_45sensor-aux"] = faux_aux_dataset
277
+ # TODO: this is a placeholder for the hk dataset.
278
+ data_dict["imap_ultra_l1a_45sensor-hk"] = faux_aux_dataset
279
+ data_dict["imap_ultra_l1a_45sensor-rates"] = rates_dataset
280
+
281
+ output_datasets = ultra_l1b(data_dict, data_version="001")
282
+
283
+ return output_datasets
@@ -11,6 +11,7 @@ def test_calculate_badtimes():
11
11
 
12
12
  spin_numbers = np.array([0, 1, 2, 3])
13
13
  energy_bins = np.array([10, 20, 30, 40])
14
+ spin_start_time = np.array([0, 1, 2, 3])
14
15
 
15
16
  quality_attitude = np.full(
16
17
  spin_numbers.shape, ImapAttitudeUltraFlags.NONE.value, dtype=np.uint16
@@ -36,6 +37,7 @@ def test_calculate_badtimes():
36
37
  ("energy_bin_geometric_mean", "spin_number"),
37
38
  quality_ena_rates,
38
39
  ),
40
+ "spin_start_time": (("spin_number",), spin_start_time),
39
41
  },
40
42
  coords={
41
43
  "spin_number": spin_numbers,
@@ -10,6 +10,7 @@ def test_calculate_cullingmask_attitude():
10
10
 
11
11
  spin_numbers = np.array([0, 1])
12
12
  energy_bins = np.array([10, 20, 30, 40])
13
+ spin_start_time = np.array([0, 1])
13
14
 
14
15
  quality_attitude = np.full(
15
16
  spin_numbers.shape, ImapAttitudeUltraFlags.NONE.value, dtype=np.uint16
@@ -29,6 +30,7 @@ def test_calculate_cullingmask_attitude():
29
30
  ("energy_bin_geometric_mean", "spin_number"),
30
31
  quality_ena_rates,
31
32
  ),
33
+ "spin_start_time": (("spin_number",), spin_start_time),
32
34
  },
33
35
  coords={
34
36
  "spin_number": spin_numbers,
@@ -47,6 +49,7 @@ def test_calculate_cullingmask_rates():
47
49
  """Test calculate_cullingmask for rates culling."""
48
50
  spin_numbers = np.array([0, 1, 2, 3])
49
51
  energy_bins = np.array([10, 20, 30, 40])
52
+ spin_start_time = np.array([0, 1, 2, 3])
50
53
 
51
54
  quality_attitude = np.full(
52
55
  spin_numbers.shape, ImapAttitudeUltraFlags.NONE.value, dtype=np.uint16
@@ -72,6 +75,7 @@ def test_calculate_cullingmask_rates():
72
75
  ("energy_bin_geometric_mean", "spin_number"),
73
76
  quality_ena_rates,
74
77
  ),
78
+ "spin_start_time": (("spin_number",), spin_start_time),
75
79
  },
76
80
  coords={
77
81
  "spin_number": spin_numbers,
@@ -17,10 +17,13 @@ def df_filt(de_dataset, events_fsw_comparison_theta_0):
17
17
  return df_filt
18
18
 
19
19
 
20
- def test_calculate_de(l1b_datasets, df_filt):
20
+ def test_calculate_de(l1b_de_dataset, df_filt):
21
21
  """Tests calculate_de function."""
22
22
 
23
- l1b_de_dataset = l1b_datasets[0]
23
+ l1b_de_dataset = l1b_de_dataset[0]
24
+ l1b_de_dataset = l1b_de_dataset.where(
25
+ l1b_de_dataset["start_type"] != np.iinfo(np.int64).min, drop=True
26
+ )
24
27
  # Front and back positions
25
28
  assert np.allclose(l1b_de_dataset["x_front"].data, df_filt["Xf"].astype("float"))
26
29
  assert np.allclose(l1b_de_dataset["y_front"], df_filt["Yf"].astype("float"))
@@ -94,8 +97,13 @@ def test_calculate_de(l1b_datasets, df_filt):
94
97
  rtol=1e-2,
95
98
  )
96
99
  assert np.allclose(
97
- l1b_de_dataset["azimuth"].values[condition],
98
- df_filt["event_phi"].astype("float").values[condition] % (2 * np.pi),
100
+ l1b_de_dataset["phi"].values,
101
+ df_filt["phi"].astype("float").values,
102
+ rtol=1e-2,
103
+ )
104
+ assert np.allclose(
105
+ l1b_de_dataset["theta"].values,
106
+ df_filt["theta"].astype("float").values,
99
107
  rtol=1e-2,
100
108
  )
101
109
 
@@ -4,6 +4,7 @@ import numpy as np
4
4
  import pandas as pd
5
5
  import pytest
6
6
 
7
+ from imap_processing import imap_module_directory
7
8
  from imap_processing.ultra.l0.ultra_utils import RATES_KEYS, ULTRA_RATES
8
9
 
9
10
 
@@ -48,3 +49,46 @@ def test_image_rate_decom(decom_test_data, rates_test_path):
48
49
  for name in RATES_KEYS:
49
50
  arr.append(decom_ultra[name][total_packets - 1])
50
51
  assert expected_arrn == arr
52
+
53
+
54
+ @pytest.mark.parametrize(
55
+ "decom_test_data",
56
+ [
57
+ pytest.param(
58
+ {
59
+ "apid": ULTRA_RATES.apid[0],
60
+ "filename": "FM45_UltraFM45_Functional_2024-01-22T0105_"
61
+ "20240122T010548.CCSDS",
62
+ }
63
+ )
64
+ ],
65
+ indirect=True,
66
+ )
67
+ def test_image_rate_decom_zero_width(decom_test_data):
68
+ """This function tests for cases in which the width is zero within the packet."""
69
+ test_path = (
70
+ imap_module_directory
71
+ / "tests"
72
+ / "ultra"
73
+ / "data"
74
+ / "l0"
75
+ / "ultra45_raw_sc_ultraimgrates_20220530_00.csv"
76
+ )
77
+
78
+ decom_ultra, _ = decom_test_data
79
+
80
+ df = pd.read_csv(test_path, index_col="MET")
81
+ total_packets = 163
82
+
83
+ np.testing.assert_array_equal(df.SID, decom_ultra["SID"])
84
+ np.testing.assert_array_equal(df.Spin, decom_ultra["SPIN"])
85
+ np.testing.assert_array_equal(df.AbortFlag, decom_ultra["ABORTFLAG"])
86
+ np.testing.assert_array_equal(df.StartDelay, decom_ultra["STARTDELAY"])
87
+
88
+ for i in range(total_packets):
89
+ t = int(df["SequenceCount"].iloc[i]) # Ensure we get an integer value
90
+ expected_arr = json.loads(df.loc[df["SequenceCount"] == t, "Counts"].values[0])
91
+ arr = []
92
+ for name in RATES_KEYS:
93
+ arr.append(decom_ultra[name][i])
94
+ assert expected_arr == arr