imap-processing 0.9.0__py3-none-any.whl → 0.11.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 (243) hide show
  1. imap_processing/_version.py +2 -2
  2. imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +749 -442
  3. imap_processing/cdf/config/imap_glows_global_cdf_attrs.yaml +7 -0
  4. imap_processing/cdf/config/imap_glows_l1a_variable_attrs.yaml +8 -2
  5. imap_processing/cdf/config/imap_glows_l1b_variable_attrs.yaml +0 -1
  6. imap_processing/cdf/config/imap_glows_l2_variable_attrs.yaml +358 -0
  7. imap_processing/cdf/config/imap_hi_variable_attrs.yaml +59 -25
  8. imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +22 -0
  9. imap_processing/cdf/config/imap_idex_l1a_variable_attrs.yaml +32 -8
  10. imap_processing/cdf/config/imap_idex_l1b_variable_attrs.yaml +94 -5
  11. imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +65 -37
  12. imap_processing/cdf/config/imap_swapi_variable_attrs.yaml +16 -1
  13. imap_processing/cdf/config/imap_swe_global_cdf_attrs.yaml +7 -0
  14. imap_processing/cdf/config/imap_swe_l1a_variable_attrs.yaml +14 -14
  15. imap_processing/cdf/config/imap_swe_l1b_variable_attrs.yaml +25 -24
  16. imap_processing/cdf/config/imap_swe_l2_variable_attrs.yaml +238 -0
  17. imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +100 -92
  18. imap_processing/cdf/utils.py +2 -2
  19. imap_processing/cli.py +45 -9
  20. imap_processing/codice/codice_l1a.py +104 -58
  21. imap_processing/codice/constants.py +111 -155
  22. imap_processing/codice/data/esa_sweep_values.csv +256 -256
  23. imap_processing/codice/data/lo_stepping_values.csv +128 -128
  24. imap_processing/ena_maps/ena_maps.py +519 -0
  25. imap_processing/ena_maps/utils/map_utils.py +145 -0
  26. imap_processing/ena_maps/utils/spatial_utils.py +226 -0
  27. imap_processing/glows/__init__.py +3 -0
  28. imap_processing/glows/ancillary/imap_glows_pipeline_settings_v001.json +52 -0
  29. imap_processing/glows/l1a/glows_l1a.py +72 -14
  30. imap_processing/glows/l1b/glows_l1b.py +2 -1
  31. imap_processing/glows/l1b/glows_l1b_data.py +25 -1
  32. imap_processing/glows/l2/glows_l2.py +324 -0
  33. imap_processing/glows/l2/glows_l2_data.py +156 -51
  34. imap_processing/hi/l1a/science_direct_event.py +57 -51
  35. imap_processing/hi/l1b/hi_l1b.py +43 -28
  36. imap_processing/hi/l1c/hi_l1c.py +225 -42
  37. imap_processing/hi/utils.py +20 -3
  38. imap_processing/hit/l0/constants.py +2 -2
  39. imap_processing/hit/l0/decom_hit.py +1 -1
  40. imap_processing/hit/l1a/hit_l1a.py +94 -13
  41. imap_processing/hit/l1b/hit_l1b.py +158 -9
  42. imap_processing/ialirt/l0/process_codicehi.py +156 -0
  43. imap_processing/ialirt/l0/process_codicelo.py +5 -2
  44. imap_processing/ialirt/packet_definitions/ialirt.xml +28 -20
  45. imap_processing/ialirt/packet_definitions/ialirt_codicehi.xml +241 -0
  46. imap_processing/ialirt/packet_definitions/ialirt_swapi.xml +170 -0
  47. imap_processing/ialirt/packet_definitions/ialirt_swe.xml +258 -0
  48. imap_processing/ialirt/process_ephemeris.py +72 -40
  49. imap_processing/idex/decode.py +241 -0
  50. imap_processing/idex/idex_l1a.py +143 -81
  51. imap_processing/idex/idex_l1b.py +244 -10
  52. imap_processing/lo/l0/lo_science.py +61 -0
  53. imap_processing/lo/l1a/lo_l1a.py +98 -10
  54. imap_processing/lo/l1b/lo_l1b.py +2 -2
  55. imap_processing/lo/l1c/lo_l1c.py +2 -2
  56. imap_processing/lo/packet_definitions/lo_xtce.xml +1082 -9178
  57. imap_processing/mag/l0/decom_mag.py +2 -2
  58. imap_processing/mag/l1a/mag_l1a.py +7 -7
  59. imap_processing/mag/l1a/mag_l1a_data.py +62 -30
  60. imap_processing/mag/l1b/mag_l1b.py +11 -6
  61. imap_processing/quality_flags.py +18 -3
  62. imap_processing/spice/geometry.py +149 -177
  63. imap_processing/spice/kernels.py +26 -26
  64. imap_processing/spice/spin.py +233 -0
  65. imap_processing/spice/time.py +96 -31
  66. imap_processing/swapi/l1/swapi_l1.py +60 -31
  67. imap_processing/swapi/packet_definitions/swapi_packet_definition.xml +363 -384
  68. imap_processing/swe/l1a/swe_l1a.py +8 -3
  69. imap_processing/swe/l1a/swe_science.py +24 -24
  70. imap_processing/swe/l1b/swe_l1b.py +2 -1
  71. imap_processing/swe/l1b/swe_l1b_science.py +181 -122
  72. imap_processing/swe/l2/swe_l2.py +337 -70
  73. imap_processing/swe/utils/swe_utils.py +28 -0
  74. imap_processing/tests/cdf/test_utils.py +2 -2
  75. imap_processing/tests/codice/conftest.py +20 -17
  76. imap_processing/tests/codice/data/validation/imap_codice_l1a_hskp_20241110193622_v0.0.0.cdf +0 -0
  77. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-counters-aggregated_20241110193700_v0.0.0.cdf +0 -0
  78. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-counters-singles_20241110193700_v0.0.0.cdf +0 -0
  79. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-angular_20241110193700_v0.0.0.cdf +0 -0
  80. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-priority_20241110193700_v0.0.0.cdf +0 -0
  81. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-species_20241110193700_v0.0.0.cdf +0 -0
  82. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-angular_20241110193700_v0.0.0.cdf +0 -0
  83. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-priority_20241110193700_v0.0.0.cdf +0 -0
  84. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-species_20241110193700_v0.0.0.cdf +0 -0
  85. imap_processing/tests/codice/test_codice_l0.py +55 -121
  86. imap_processing/tests/codice/test_codice_l1a.py +147 -59
  87. imap_processing/tests/conftest.py +81 -22
  88. imap_processing/tests/ena_maps/test_ena_maps.py +309 -0
  89. imap_processing/tests/ena_maps/test_map_utils.py +286 -0
  90. imap_processing/tests/ena_maps/test_spatial_utils.py +161 -0
  91. imap_processing/tests/glows/conftest.py +7 -1
  92. imap_processing/tests/glows/test_glows_l1a_cdf.py +3 -7
  93. imap_processing/tests/glows/test_glows_l1a_data.py +34 -6
  94. imap_processing/tests/glows/test_glows_l1b_data.py +29 -17
  95. imap_processing/tests/glows/test_glows_l2.py +101 -0
  96. imap_processing/tests/hi/conftest.py +3 -3
  97. imap_processing/tests/hi/data/l1/imap_hi_l1b_45sensor-de_20250415_v999.cdf +0 -0
  98. imap_processing/tests/hi/data/l1/imap_his_pset-calibration-prod-config_20240101_v001.csv +31 -0
  99. imap_processing/tests/hi/test_hi_l1b.py +14 -9
  100. imap_processing/tests/hi/test_hi_l1c.py +136 -36
  101. imap_processing/tests/hi/test_l1a.py +0 -2
  102. imap_processing/tests/hi/test_science_direct_event.py +18 -14
  103. imap_processing/tests/hi/test_utils.py +16 -11
  104. imap_processing/tests/hit/helpers/__init__.py +0 -0
  105. imap_processing/tests/hit/helpers/l1_validation.py +405 -0
  106. imap_processing/tests/hit/test_data/sci_sample.ccsds +0 -0
  107. imap_processing/tests/hit/test_decom_hit.py +8 -10
  108. imap_processing/tests/hit/test_hit_l1a.py +117 -180
  109. imap_processing/tests/hit/test_hit_l1b.py +149 -55
  110. imap_processing/tests/hit/validation_data/hit_l1b_standard_sample2_nsrl_v4_3decimals.csv +62 -0
  111. imap_processing/tests/hit/validation_data/sci_sample_raw.csv +62 -0
  112. imap_processing/tests/ialirt/test_data/l0/20240827095047_SWE_IALIRT_packet.bin +0 -0
  113. imap_processing/tests/ialirt/test_data/l0/BinLog CCSDS_FRAG_TLM_20240826_152323Z_IALIRT_data_for_SDC.bin +0 -0
  114. imap_processing/tests/ialirt/test_data/l0/eu_SWP_IAL_20240826_152033.csv +644 -0
  115. imap_processing/tests/ialirt/test_data/l0/hi_fsw_view_1_ccsds.bin +0 -0
  116. imap_processing/tests/ialirt/test_data/l0/idle_export_eu.SWE_IALIRT_20240827_093852.csv +914 -0
  117. imap_processing/tests/ialirt/test_data/l0/imap_codice_l1a_hi-ialirt_20240523200000_v0.0.0.cdf +0 -0
  118. imap_processing/tests/ialirt/unit/test_process_codicehi.py +106 -0
  119. imap_processing/tests/ialirt/unit/test_process_ephemeris.py +33 -5
  120. imap_processing/tests/ialirt/unit/test_process_swapi.py +85 -0
  121. imap_processing/tests/ialirt/unit/test_process_swe.py +106 -0
  122. imap_processing/tests/idex/conftest.py +29 -1
  123. imap_processing/tests/idex/test_data/compressed_2023_102_14_24_55.pkts +0 -0
  124. imap_processing/tests/idex/test_data/non_compressed_2023_102_14_22_26.pkts +0 -0
  125. imap_processing/tests/idex/test_idex_l0.py +6 -3
  126. imap_processing/tests/idex/test_idex_l1a.py +151 -1
  127. imap_processing/tests/idex/test_idex_l1b.py +124 -2
  128. imap_processing/tests/lo/test_lo_l1a.py +62 -2
  129. imap_processing/tests/lo/test_lo_science.py +85 -0
  130. imap_processing/tests/lo/validation_data/Instrument_FM1_T104_R129_20240803_ILO_SPIN_EU.csv +2 -0
  131. imap_processing/tests/mag/conftest.py +16 -0
  132. imap_processing/tests/mag/test_mag_decom.py +6 -4
  133. imap_processing/tests/mag/test_mag_l1a.py +36 -7
  134. imap_processing/tests/mag/test_mag_l1b.py +55 -4
  135. imap_processing/tests/mag/test_mag_validation.py +148 -0
  136. imap_processing/tests/mag/validation/L1a/T001/all_p_ones.txt +19200 -0
  137. imap_processing/tests/mag/validation/L1a/T001/mag-l0-l1a-t001-in.bin +0 -0
  138. imap_processing/tests/mag/validation/L1a/T001/mag-l0-l1a-t001-out.csv +17 -0
  139. imap_processing/tests/mag/validation/L1a/T002/all_n_ones.txt +19200 -0
  140. imap_processing/tests/mag/validation/L1a/T002/mag-l0-l1a-t002-in.bin +0 -0
  141. imap_processing/tests/mag/validation/L1a/T002/mag-l0-l1a-t002-out.csv +17 -0
  142. imap_processing/tests/mag/validation/L1a/T003/field_like.txt +19200 -0
  143. imap_processing/tests/mag/validation/L1a/T003/mag-l0-l1a-t003-in.bin +0 -0
  144. imap_processing/tests/mag/validation/L1a/T003/mag-l0-l1a-t003-out.csv +17 -0
  145. imap_processing/tests/mag/validation/L1a/T004/field_like.txt +19200 -0
  146. imap_processing/tests/mag/validation/L1a/T004/mag-l0-l1a-t004-in.bin +0 -0
  147. imap_processing/tests/mag/validation/L1a/T004/mag-l0-l1a-t004-out.csv +17 -0
  148. imap_processing/tests/mag/validation/L1a/T005/field_like_range_change.txt +19200 -0
  149. imap_processing/tests/mag/validation/L1a/T005/mag-l0-l1a-t005-in.bin +0 -0
  150. imap_processing/tests/mag/validation/L1a/T005/mag-l0-l1a-t005-out.csv +17 -0
  151. imap_processing/tests/mag/validation/L1a/T006/hdr_field.txt +19200 -0
  152. imap_processing/tests/mag/validation/L1a/T006/mag-l0-l1a-t006-in.bin +0 -0
  153. imap_processing/tests/mag/validation/L1a/T006/mag-l0-l1a-t006-out.csv +17 -0
  154. imap_processing/tests/mag/validation/L1a/T007/hdr_field_and_range_change.txt +19200 -0
  155. imap_processing/tests/mag/validation/L1a/T007/mag-l0-l1a-t007-in.bin +0 -0
  156. imap_processing/tests/mag/validation/L1a/T007/mag-l0-l1a-t007-out.csv +17 -0
  157. imap_processing/tests/mag/validation/L1a/T008/field_like_range_change.txt +19200 -0
  158. imap_processing/tests/mag/validation/L1a/T008/mag-l0-l1a-t008-in.bin +0 -0
  159. imap_processing/tests/mag/validation/L1a/T008/mag-l0-l1a-t008-out.csv +17 -0
  160. imap_processing/tests/mag/validation/L1b/T009/data.bin +0 -0
  161. imap_processing/tests/mag/validation/L1b/T009/field_like_all_ranges.txt +19200 -0
  162. imap_processing/tests/mag/validation/L1b/T009/mag-l1a-l1b-t009-in.csv +17 -0
  163. imap_processing/tests/mag/validation/L1b/T009/mag-l1a-l1b-t009-magi-out.csv +17 -0
  164. imap_processing/tests/mag/validation/L1b/T009/mag-l1a-l1b-t009-mago-out.csv +17 -0
  165. imap_processing/tests/mag/validation/L1b/T010/data.bin +0 -0
  166. imap_processing/tests/mag/validation/L1b/T010/field_like_all_ranges.txt +19200 -0
  167. imap_processing/tests/mag/validation/L1b/T010/mag-l1a-l1b-t010-in.csv +17 -0
  168. imap_processing/tests/mag/validation/L1b/T010/mag-l1a-l1b-t010-magi-out.csv +17 -0
  169. imap_processing/tests/mag/validation/L1b/T010/mag-l1a-l1b-t010-mago-out.csv +17 -0
  170. imap_processing/tests/mag/validation/L1b/T011/data.bin +0 -0
  171. imap_processing/tests/mag/validation/L1b/T011/field_like_all_ranges.txt +19200 -0
  172. imap_processing/tests/mag/validation/L1b/T011/mag-l1a-l1b-t011-in.csv +17 -0
  173. imap_processing/tests/mag/validation/L1b/T011/mag-l1a-l1b-t011-magi-out.csv +17 -0
  174. imap_processing/tests/mag/validation/L1b/T011/mag-l1a-l1b-t011-mago-out.csv +17 -0
  175. imap_processing/tests/spice/test_geometry.py +128 -133
  176. imap_processing/tests/spice/test_kernels.py +37 -37
  177. imap_processing/tests/spice/test_spin.py +184 -0
  178. imap_processing/tests/spice/test_time.py +43 -20
  179. imap_processing/tests/swapi/test_swapi_l1.py +11 -10
  180. imap_processing/tests/swapi/test_swapi_l2.py +13 -3
  181. imap_processing/tests/swe/test_swe_l1a.py +1 -1
  182. imap_processing/tests/swe/test_swe_l1b.py +20 -3
  183. imap_processing/tests/swe/test_swe_l1b_science.py +54 -35
  184. imap_processing/tests/swe/test_swe_l2.py +148 -5
  185. imap_processing/tests/test_cli.py +39 -7
  186. imap_processing/tests/test_quality_flags.py +19 -19
  187. imap_processing/tests/test_utils.py +3 -2
  188. imap_processing/tests/ultra/test_data/l0/ultra45_raw_sc_ultrarawimg_withFSWcalcs_FM45_40P_Phi28p5_BeamCal_LinearScan_phi2850_theta-000_20240207T102740.csv +3314 -3314
  189. imap_processing/tests/ultra/test_data/mock_data.py +161 -0
  190. imap_processing/tests/ultra/unit/conftest.py +73 -0
  191. imap_processing/tests/ultra/unit/test_badtimes.py +58 -0
  192. imap_processing/tests/ultra/unit/test_cullingmask.py +87 -0
  193. imap_processing/tests/ultra/unit/test_de.py +61 -60
  194. imap_processing/tests/ultra/unit/test_ultra_l1a.py +3 -3
  195. imap_processing/tests/ultra/unit/test_ultra_l1b.py +51 -77
  196. imap_processing/tests/ultra/unit/test_ultra_l1b_annotated.py +5 -5
  197. imap_processing/tests/ultra/unit/test_ultra_l1b_culling.py +114 -0
  198. imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +86 -26
  199. imap_processing/tests/ultra/unit/test_ultra_l1c.py +1 -1
  200. imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +3 -3
  201. imap_processing/ultra/constants.py +11 -1
  202. imap_processing/ultra/l1a/ultra_l1a.py +2 -2
  203. imap_processing/ultra/l1b/badtimes.py +22 -5
  204. imap_processing/ultra/l1b/cullingmask.py +31 -5
  205. imap_processing/ultra/l1b/de.py +32 -37
  206. imap_processing/ultra/l1b/extendedspin.py +44 -20
  207. imap_processing/ultra/l1b/ultra_l1b.py +21 -22
  208. imap_processing/ultra/l1b/ultra_l1b_culling.py +190 -0
  209. imap_processing/ultra/l1b/ultra_l1b_extended.py +81 -30
  210. imap_processing/ultra/l1c/histogram.py +6 -2
  211. imap_processing/ultra/l1c/pset.py +6 -2
  212. imap_processing/ultra/l1c/ultra_l1c.py +2 -3
  213. imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +4 -3
  214. imap_processing/ultra/utils/ultra_l1_utils.py +70 -14
  215. imap_processing/utils.py +2 -2
  216. {imap_processing-0.9.0.dist-info → imap_processing-0.11.0.dist-info}/METADATA +7 -2
  217. {imap_processing-0.9.0.dist-info → imap_processing-0.11.0.dist-info}/RECORD +235 -152
  218. imap_processing/tests/codice/data/eu_unit_lookup_table.csv +0 -101
  219. imap_processing/tests/codice/data/idle_export_eu.COD_NHK_20230822_122700 2.csv +0 -100
  220. imap_processing/tests/codice/data/idle_export_raw.COD_NHK_20230822_122700.csv +0 -100
  221. imap_processing/tests/codice/data/imap_codice_l0_raw_20241110_v001.pkts +0 -0
  222. imap_processing/tests/hi/test_data/l1a/imap_hi_l1a_45sensor-de_20250415_v000.cdf +0 -0
  223. imap_processing/tests/hit/test_data/sci_sample1.ccsds +0 -0
  224. imap_processing/tests/ultra/unit/test_spatial_utils.py +0 -125
  225. imap_processing/ultra/utils/spatial_utils.py +0 -221
  226. /imap_processing/tests/hi/{test_data → data}/l0/20231030_H45_APP_NHK.bin +0 -0
  227. /imap_processing/tests/hi/{test_data → data}/l0/20231030_H45_APP_NHK.csv +0 -0
  228. /imap_processing/tests/hi/{test_data → data}/l0/20231030_H45_SCI_CNT.bin +0 -0
  229. /imap_processing/tests/hi/{test_data → data}/l0/20231030_H45_SCI_DE.bin +0 -0
  230. /imap_processing/tests/hi/{test_data → data}/l0/H90_NHK_20241104.bin +0 -0
  231. /imap_processing/tests/hi/{test_data → data}/l0/H90_sci_cnt_20241104.bin +0 -0
  232. /imap_processing/tests/hi/{test_data → data}/l0/H90_sci_de_20241104.bin +0 -0
  233. /imap_processing/tests/hi/{test_data → data}/l0/README.txt +0 -0
  234. /imap_processing/tests/idex/{imap_idex_l0_raw_20231214_v001.pkts → test_data/imap_idex_l0_raw_20231214_v001.pkts} +0 -0
  235. /imap_processing/tests/idex/{impact_14_tof_high_data.txt → test_data/impact_14_tof_high_data.txt} +0 -0
  236. /imap_processing/tests/mag/{imap_mag_l1a_norm-magi_20251017_v001.cdf → validation/imap_mag_l1a_norm-magi_20251017_v001.cdf} +0 -0
  237. /imap_processing/tests/mag/{mag_l0_test_data.pkts → validation/mag_l0_test_data.pkts} +0 -0
  238. /imap_processing/tests/mag/{mag_l0_test_output.csv → validation/mag_l0_test_output.csv} +0 -0
  239. /imap_processing/tests/mag/{mag_l1_test_data.pkts → validation/mag_l1_test_data.pkts} +0 -0
  240. /imap_processing/tests/mag/{mag_l1a_test_output.csv → validation/mag_l1a_test_output.csv} +0 -0
  241. {imap_processing-0.9.0.dist-info → imap_processing-0.11.0.dist-info}/LICENSE +0 -0
  242. {imap_processing-0.9.0.dist-info → imap_processing-0.11.0.dist-info}/WHEEL +0 -0
  243. {imap_processing-0.9.0.dist-info → imap_processing-0.11.0.dist-info}/entry_points.txt +0 -0
@@ -1,5 +1,3 @@
1
- from unittest import mock
2
-
3
1
  import numpy as np
4
2
  import pytest
5
3
  import xarray as xr
@@ -32,45 +30,58 @@ def mock_data_l1a_rates_dict():
32
30
 
33
31
 
34
32
  @pytest.fixture()
35
- def mock_data_l1a_de_aux_dict():
36
- # Create sample data for the xarray Dataset
37
- epoch = np.arange(
38
- "2024-02-07T15:28:37", "2024-02-07T15:28:42", dtype="datetime64[s]"
39
- ).astype("datetime64[ns]")
40
-
41
- data_vars = {
42
- "var": ("epoch", np.zeros(5)),
43
- }
44
-
45
- attrs = {
46
- "Logical_source": "imap_ultra_l1a_45sensor-name",
47
- "Logical_source_description": "IMAP Mission ULTRA Instrument "
48
- "Level-1A Single-Sensor Data",
49
- }
33
+ def mock_data_l1b_de_dict():
34
+ epoch = np.array(
35
+ [760591786368000000, 760591787368000000, 760591788368000000],
36
+ dtype="datetime64[ns]",
37
+ )
38
+ data_dict = {"epoch": epoch, "x_front": np.zeros(3), "y_front": np.zeros(3)}
39
+ return data_dict
50
40
 
51
- dataset = xr.Dataset(data_vars, coords={"epoch": epoch}, attrs=attrs)
52
41
 
42
+ @pytest.fixture()
43
+ def mock_data_l1b_extendedspin_dict():
44
+ spin = np.array(
45
+ [0, 1, 2],
46
+ dtype="uint32",
47
+ )
48
+ energy = np.array(
49
+ [0, 1],
50
+ dtype="int32",
51
+ )
52
+ quality = np.zeros((2, 3), dtype="uint16")
53
53
  data_dict = {
54
- "imap_ultra_l1a_45sensor-de": dataset,
55
- "imap_ultra_l1a_45sensor-aux": dataset,
54
+ "spin_number": spin,
55
+ "energy_bin_geometric_mean": energy,
56
+ "quality_ena_rates": quality,
56
57
  }
57
-
58
58
  return data_dict
59
59
 
60
60
 
61
- @pytest.fixture()
62
- def mock_data_l1b_dict():
63
- epoch = np.array(
64
- [760591786368000000, 760591787368000000, 760591788368000000],
65
- dtype="datetime64[ns]",
61
+ def test_create_extendedspin_dataset(mock_data_l1b_extendedspin_dict):
62
+ """Tests that dataset is created as expected."""
63
+ dataset = create_dataset(
64
+ mock_data_l1b_extendedspin_dict,
65
+ "imap_ultra_l1b_45sensor-extendedspin",
66
+ "l1b",
67
+ "001",
68
+ )
69
+
70
+ assert "spin_number" in dataset.coords
71
+ assert "energy_bin_geometric_mean" in dataset.coords
72
+ assert dataset.coords["spin_number"].dtype == "uint32"
73
+ assert dataset.attrs["Logical_source"] == "imap_ultra_l1b_45sensor-extendedspin"
74
+ assert dataset["quality_ena_rates"].attrs["UNITS"] == " "
75
+ np.testing.assert_array_equal(
76
+ dataset["quality_ena_rates"], np.zeros((2, 3), dtype="uint16")
66
77
  )
67
- data_dict = {"epoch": epoch, "x_front": np.zeros(3), "y_front": np.zeros(3)}
68
- return data_dict
69
78
 
70
79
 
71
- def test_create_dataset(mock_data_l1b_dict):
80
+ def test_create_de_dataset(mock_data_l1b_de_dict):
72
81
  """Tests that dataset is created as expected."""
73
- dataset = create_dataset(mock_data_l1b_dict, "imap_ultra_l1b_45sensor-de", "l1b")
82
+ dataset = create_dataset(
83
+ mock_data_l1b_de_dict, "imap_ultra_l1b_45sensor-de", "l1b", "001"
84
+ )
74
85
 
75
86
  assert "epoch" in dataset.coords
76
87
  assert dataset.coords["epoch"].dtype == "datetime64[ns]"
@@ -79,58 +90,21 @@ def test_create_dataset(mock_data_l1b_dict):
79
90
  np.testing.assert_array_equal(dataset["x_front"], np.zeros(3))
80
91
 
81
92
 
82
- def test_ultra_l1b_rates(mock_data_l1a_rates_dict):
93
+ def test_ultra_l1b(l1b_datasets):
83
94
  """Tests that L1b data is created."""
84
- output_datasets = ultra_l1b(mock_data_l1a_rates_dict, data_version="001")
85
95
 
86
- assert len(output_datasets) == 3
87
- assert (
88
- output_datasets[0].attrs["Logical_source"]
89
- == "imap_ultra_l1b_45sensor-extendedspin"
90
- )
91
- assert (
92
- output_datasets[1].attrs["Logical_source"]
93
- == "imap_ultra_l1b_45sensor-cullingmask"
94
- )
95
- assert (
96
- output_datasets[2].attrs["Logical_source"] == "imap_ultra_l1b_45sensor-badtimes"
97
- )
98
- assert (
99
- output_datasets[0].attrs["Logical_source_description"]
100
- == "IMAP-Ultra Instrument Level-1B Extended Spin Data."
101
- )
96
+ assert len(l1b_datasets) == 4
102
97
 
98
+ # Define the suffixes and prefix
99
+ prefix = "imap_ultra_l1b_45sensor"
100
+ suffixes = ["de", "extendedspin", "cullingmask", "badtimes"]
101
+
102
+ for i in range(len(suffixes)):
103
+ expected_logical_source = f"{prefix}-{suffixes[i]}"
104
+ assert l1b_datasets[i].attrs["Logical_source"] == expected_logical_source
103
105
 
104
- @pytest.mark.external_kernel()
105
- @pytest.mark.use_test_metakernel("imap_ena_sim_metakernel.template")
106
- @mock.patch("imap_processing.ultra.l1b.de.get_annotated_particle_velocity")
107
- def test_ultra_l1b_de(mock_get_annotated_particle_velocity, de_dataset):
108
- """Tests that L1b data is created."""
109
- data_dict = {}
110
- data_dict[de_dataset.attrs["Logical_source"]] = de_dataset
111
- data_dict["imap_ultra_l1a_45sensor-aux"] = de_dataset
112
-
113
- # Mock get_annotated_particle_velocity to avoid needing kernels
114
- def side_effect_func(event_times, position, ultra_frame, dps_frame, sc_frame):
115
- """
116
- Mock behavior of get_annotated_particle_velocity.
117
-
118
- Returns NaN-filled arrays matching the expected output shape.
119
- """
120
- num_events = event_times.size
121
- return (
122
- np.full((num_events, 3), np.nan), # sc_velocity
123
- np.full((num_events, 3), np.nan), # sc_dps_velocity
124
- np.full((num_events, 3), np.nan), # helio_velocity
125
- )
126
-
127
- mock_get_annotated_particle_velocity.side_effect = side_effect_func
128
- output_datasets = ultra_l1b(data_dict, data_version="001")
129
-
130
- assert len(output_datasets) == 1
131
- assert output_datasets[0].attrs["Logical_source"] == "imap_ultra_l1b_45sensor-de"
132
106
  assert (
133
- output_datasets[0].attrs["Logical_source_description"]
107
+ l1b_datasets[0].attrs["Logical_source_description"]
134
108
  == "IMAP-Ultra Instrument Level-1B Direct Event Data."
135
109
  )
136
110
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  import numpy as np
4
4
  import pytest
5
- import spiceypy as spice
5
+ import spiceypy
6
6
 
7
7
  from imap_processing.spice.geometry import SpiceFrame
8
8
  from imap_processing.ultra.l1b.ultra_l1b_annotated import (
@@ -31,9 +31,9 @@ def kernels(spice_test_data_path):
31
31
  @pytest.mark.external_kernel()
32
32
  def test_get_particle_velocity(spice_test_data_path, kernels):
33
33
  """Tests get_particle_velocity function."""
34
- spice.furnsh(kernels)
34
+ spiceypy.furnsh(kernels)
35
35
 
36
- pointing_cover = spice.ckcov(
36
+ pointing_cover = spiceypy.ckcov(
37
37
  str(spice_test_data_path / "sim_1yr_imap_pointing_frame.bc"),
38
38
  SpiceFrame.IMAP_DPS.value,
39
39
  True,
@@ -42,7 +42,7 @@ def test_get_particle_velocity(spice_test_data_path, kernels):
42
42
  "TDB",
43
43
  )
44
44
  # Get start and end time of first interval
45
- start, _ = spice.wnfetd(pointing_cover, 0)
45
+ start, _ = spiceypy.wnfetd(pointing_cover, 0)
46
46
 
47
47
  times = np.array([start])
48
48
  instrument_velocity = np.array([[41.18609, -471.24467, -832.8784]])
@@ -71,7 +71,7 @@ def test_get_particle_velocity(spice_test_data_path, kernels):
71
71
  magnitude_sc_90 = np.linalg.norm(sc_velocity_90)
72
72
  magnitude_dps_45 = np.linalg.norm(sc_dps_velocity_45)
73
73
  magnitude_dps_90 = np.linalg.norm(sc_dps_velocity_90)
74
- state, lt = spice.spkezr("IMAP", times, "IMAP_DPS", "NONE", "SUN")
74
+ state, lt = spiceypy.spkezr("IMAP", times, "IMAP_DPS", "NONE", "SUN")
75
75
 
76
76
  assert np.allclose(magnitude_sc_45, magnitude_sc_90, atol=1e-6)
77
77
  assert np.allclose(magnitude_dps_45, magnitude_dps_90, atol=1e-6)
@@ -0,0 +1,114 @@
1
+ """Tests Culling for ULTRA L1b."""
2
+
3
+ import numpy as np
4
+ import pytest
5
+
6
+ from imap_processing.quality_flags import ImapAttitudeUltraFlags, ImapRatesUltraFlags
7
+ from imap_processing.ultra.constants import UltraConstants
8
+ from imap_processing.ultra.l1b.ultra_l1b_culling import (
9
+ flag_attitude,
10
+ flag_spin,
11
+ get_energy_histogram,
12
+ get_n_sigma,
13
+ get_spin,
14
+ )
15
+
16
+
17
+ @pytest.fixture()
18
+ def test_data(use_fake_spin_data_for_time):
19
+ """Fixture to compute and return test data."""
20
+
21
+ time = np.arange(0, 32, 2)
22
+ spin_number = np.array([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2])
23
+ energy = np.array([5, 5, 5, 5, 5, 5, 5, 5, 5, 15, 15, 25, -2, -2, -2, 2])
24
+
25
+ use_fake_spin_data_for_time(time[0], time[-1])
26
+
27
+ energy_edges = UltraConstants.CULLING_ENERGY_BIN_EDGES
28
+ unique_spins = np.unique(spin_number)
29
+ expected_counts = np.zeros((len(energy_edges) - 1, len(unique_spins)))
30
+
31
+ for spin_idx, spin in enumerate(unique_spins):
32
+ for energy_idx in range(len(energy_edges) - 1):
33
+ count = np.sum(
34
+ (spin_number == spin)
35
+ & (energy >= energy_edges[energy_idx])
36
+ & (energy < energy_edges[energy_idx + 1])
37
+ )
38
+ expected_counts[energy_idx, spin_idx] = count
39
+
40
+ return time, spin_number, energy, expected_counts
41
+
42
+
43
+ def test_get_spin(use_fake_spin_data_for_time):
44
+ """Tests get_spin function."""
45
+
46
+ nspins = 5
47
+ spin_period = 15
48
+ start = 0
49
+ stop = start + (nspins + 1) * spin_period
50
+ use_fake_spin_data_for_time(start, stop)
51
+ spin_number = get_spin(np.linspace(start, stop, num=20))
52
+
53
+ assert len(spin_number) == len(np.linspace(start, stop, num=20))
54
+ expected_num_spins = np.ceil((stop - start) / 15) + 1
55
+ assert np.array_equal(len(np.unique(spin_number)), expected_num_spins)
56
+
57
+
58
+ def test_get_energy_histogram(test_data):
59
+ """Tests get_energy_histogram function."""
60
+
61
+ _, spin_number, energy, expected_counts = test_data
62
+
63
+ hist, _, counts, duration = get_energy_histogram(spin_number, energy)
64
+
65
+ assert np.all(counts == expected_counts)
66
+ assert np.all(hist == expected_counts / 15)
67
+ assert duration == 15
68
+
69
+
70
+ def test_flag_attitude(use_fake_spin_data_for_time):
71
+ """Tests flag_attitude function."""
72
+
73
+ start = 4.45015658e08
74
+ stop = 4.45015873e08
75
+ use_fake_spin_data_for_time(start, stop)
76
+ quality_flags, spin_rates, spin_period, spin_start_time = flag_attitude(
77
+ np.linspace(start, stop, num=20)
78
+ )
79
+
80
+ flag = ImapAttitudeUltraFlags(quality_flags[0])
81
+ assert flag.name == "NONE"
82
+ assert np.all(quality_flags == ImapAttitudeUltraFlags.NONE.value)
83
+ assert np.all(spin_rates == 60 / spin_period)
84
+ assert np.all(np.diff(spin_start_time) == 15)
85
+
86
+
87
+ def test_get_n_sigma():
88
+ """Tests get_six_sigma function."""
89
+
90
+ counts = np.array([[16, 4, 1], [0, 0, 0], [1, 1, 1], [2, 0, 5]])
91
+ threshold = get_n_sigma(counts / 15, 15, 6)
92
+
93
+ assert np.all(threshold >= 3 / 15)
94
+ mean = np.mean(counts[0] / 15)
95
+ squared_differences = (counts[0] / 15 - mean) ** 2
96
+ variance = np.mean(squared_differences)
97
+ std_dev = np.sqrt(variance)
98
+
99
+ np.testing.assert_allclose(mean + std_dev * 6, threshold[0], atol=1e-2, rtol=0)
100
+
101
+
102
+ def test_flag_spin(test_data):
103
+ """Tests flag_spin function."""
104
+
105
+ time, _, energy, expected_counts = test_data
106
+ quality_flags, spin, energy, _ = flag_spin(time, energy, 1)
107
+ threshold = get_n_sigma(expected_counts / 15, 15, 1)
108
+
109
+ # At the first energy level were the rates > threshold and the counts > threshold?
110
+ assert np.all(
111
+ quality_flags[expected_counts == 0] == ImapRatesUltraFlags.ZEROCOUNTS.value
112
+ )
113
+ high_rates_flag = quality_flags[expected_counts / 15 > threshold[:, np.newaxis]]
114
+ assert np.all(high_rates_flag == ImapRatesUltraFlags.HIGHRATES.value)
@@ -13,6 +13,9 @@ from imap_processing.ultra.l1b.ultra_l1b_extended import (
13
13
  determine_species,
14
14
  get_coincidence_positions,
15
15
  get_ctof,
16
+ get_de_az_el,
17
+ get_de_energy_kev,
18
+ get_de_velocity,
16
19
  get_energy_pulse_height,
17
20
  get_energy_ssd,
18
21
  get_front_x_position,
@@ -21,7 +24,6 @@ from imap_processing.ultra.l1b.ultra_l1b_extended import (
21
24
  get_ph_tof_and_back_positions,
22
25
  get_ssd_back_position_and_tof_offset,
23
26
  get_ssd_tof,
24
- get_unit_vector,
25
27
  )
26
28
 
27
29
 
@@ -213,41 +215,42 @@ def test_calculate_etof_xc(de_dataset, yf_fixture):
213
215
  )
214
216
 
215
217
 
216
- def test_get_unit_vector(de_dataset, yf_fixture):
217
- """Tests get_unit_vector function."""
218
+ def test_get_de_velocity(de_dataset, yf_fixture):
219
+ """Tests get_de_velocity function."""
218
220
  df_filt, _, _ = yf_fixture
221
+ df_ph = df_filt[np.isin(df_filt["StopType"], [StopType.PH.value])]
219
222
 
220
- ph_indices = np.nonzero(
221
- np.isin(de_dataset["STOP_TYPE"], [StopType.Top.value, StopType.Bottom.value])
222
- )[0]
223
-
224
- ph_rows = df_filt.iloc[ph_indices]
225
- test_xf = ph_rows["Xf"].astype("float").values
226
- test_yf = ph_rows["Yf"].astype("float").values
227
- test_xb = ph_rows["Xb"].astype("float").values
228
- test_yb = ph_rows["Yb"].astype("float").values
229
- test_d = ph_rows["d"].astype("float").values
230
- test_tof = ph_rows["TOF"].astype("float").values
223
+ test_xf, test_yf, test_xb, test_yb, test_d, test_tof = (
224
+ df_ph[col].astype("float").values
225
+ for col in ["Xf", "Yf", "Xb", "Yb", "d", "TOF"]
226
+ )
231
227
 
232
- vhat_x, vhat_y, vhat_z = get_unit_vector(
228
+ v = get_de_velocity(
233
229
  (test_xf, test_yf),
234
230
  (test_xb, test_yb),
235
231
  test_d,
236
232
  test_tof,
237
233
  )
238
- # FSW test data should be negative and not have an analysis
239
- # for negative tof values.
240
- assert vhat_x[test_tof > 0] == pytest.approx(
241
- -df_filt["vhatX"].iloc[ph_indices].astype("float").values[test_tof > 0],
242
- rel=1e-2,
234
+
235
+ v_x, v_y, v_z = v[:, 0], v[:, 1], v[:, 2]
236
+
237
+ np.testing.assert_allclose(
238
+ v_x[test_tof > 0],
239
+ df_ph["vx"].astype("float").values[test_tof > 0],
240
+ atol=1e-01,
241
+ rtol=0,
243
242
  )
244
- assert vhat_y[test_tof > 0] == pytest.approx(
245
- -df_filt["vhatY"].iloc[ph_indices].astype("float").values[test_tof > 0],
246
- rel=1e-2,
243
+ np.testing.assert_allclose(
244
+ v_y[test_tof > 0],
245
+ df_ph["vy"].astype("float").values[test_tof > 0],
246
+ atol=1e-01,
247
+ rtol=0,
247
248
  )
248
- assert vhat_z[test_tof > 0] == pytest.approx(
249
- -df_filt["vhatZ"].iloc[ph_indices].astype("float").values[test_tof > 0],
250
- rel=1e-2,
249
+ np.testing.assert_allclose(
250
+ v_z[test_tof > 0],
251
+ df_ph["vz"].astype("float").values[test_tof > 0],
252
+ atol=1e-01,
253
+ rtol=0,
251
254
  )
252
255
 
253
256
 
@@ -264,6 +267,37 @@ def test_get_ssd_tof(de_dataset, yf_fixture):
264
267
  )
265
268
 
266
269
 
270
+ def test_get_de_energy_kev(de_dataset, yf_fixture):
271
+ """Tests get_de_energy_kev function."""
272
+ df_filt, _, _ = yf_fixture
273
+ df_ph = df_filt[np.isin(df_filt["StopType"], [StopType.PH.value])]
274
+ df_ph = df_ph[df_ph["energy_revised"].astype("str") != "FILL"]
275
+
276
+ species_bin_ph = determine_species(
277
+ df_ph["TOF"].astype("float").to_numpy(),
278
+ df_ph["r"].astype("float").to_numpy(),
279
+ "PH",
280
+ )
281
+ test_xf, test_yf, test_xb, test_yb, test_d, test_tof = (
282
+ df_ph[col].astype("float").values
283
+ for col in ["Xf", "Yf", "Xb", "Yb", "d", "TOF"]
284
+ )
285
+
286
+ v = get_de_velocity(
287
+ (test_xf, test_yf),
288
+ (test_xb, test_yb),
289
+ test_d,
290
+ test_tof,
291
+ )
292
+
293
+ energy = get_de_energy_kev(v, species_bin_ph)
294
+ index_hydrogen = np.where(species_bin_ph == "H")
295
+ actual_energy = energy[index_hydrogen[0]]
296
+ expected_energy = df_ph["energy_revised"].astype("float")
297
+
298
+ np.testing.assert_allclose(actual_energy, expected_energy, atol=1e-01, rtol=0)
299
+
300
+
267
301
  def test_get_energy_ssd(de_dataset, yf_fixture):
268
302
  """Tests get_energy_ssd function."""
269
303
  df_filt, _, _ = yf_fixture
@@ -360,3 +394,29 @@ def test_determine_species(yf_fixture):
360
394
 
361
395
  np.testing.assert_array_equal(h_indices_ph, ctof_indices_ph)
362
396
  np.testing.assert_array_equal(h_indices_ssd, ctof_indices_ssd)
397
+
398
+
399
+ def test_get_de_az_el(de_dataset, yf_fixture):
400
+ """Tests get_de_az_el function."""
401
+ df_filt, _, _ = yf_fixture
402
+ df_filt = df_filt[
403
+ (df_filt["event_theta"].astype("str") != "FILL")
404
+ & (df_filt["TOF"].astype("float") >= 0)
405
+ ]
406
+ df_ph = df_filt[np.isin(df_filt["StopType"], [StopType.PH.value])]
407
+
408
+ test_xf, test_yf, test_xb, test_yb, test_d, test_tof = (
409
+ df_ph[col].astype("float").values
410
+ for col in ["Xf", "Yf", "Xb", "Yb", "d", "TOF"]
411
+ )
412
+
413
+ v = get_de_velocity(
414
+ (test_xf, test_yf),
415
+ (test_xb, test_yb),
416
+ test_d,
417
+ test_tof,
418
+ )
419
+ az, _ = get_de_az_el(v)
420
+ expected_phi = df_ph["event_phi"].astype("float")
421
+
422
+ np.testing.assert_allclose(az, expected_phi % (2 * np.pi), atol=1e-03, rtol=0)
@@ -68,7 +68,7 @@ def mock_data_l1c_dict():
68
68
  def test_create_dataset(mock_data_l1c_dict):
69
69
  """Tests that dataset is created as expected."""
70
70
  dataset = create_dataset(
71
- mock_data_l1c_dict, "imap_ultra_l1c_45sensor-histogram", "l1c"
71
+ mock_data_l1c_dict, "imap_ultra_l1c_45sensor-histogram", "l1c", "001"
72
72
  )
73
73
 
74
74
  assert "epoch" in dataset.coords
@@ -6,6 +6,7 @@ import pytest
6
6
  from cdflib import CDF
7
7
 
8
8
  from imap_processing import imap_module_directory
9
+ from imap_processing.ena_maps.utils.spatial_utils import build_spatial_bins
9
10
  from imap_processing.ultra.l1c.ultra_l1c_pset_bins import (
10
11
  build_energy_bins,
11
12
  get_helio_exposure_times,
@@ -13,7 +14,6 @@ from imap_processing.ultra.l1c.ultra_l1c_pset_bins import (
13
14
  get_pointing_frame_exposure_times,
14
15
  get_pointing_frame_sensitivity,
15
16
  )
16
- from imap_processing.ultra.utils.spatial_utils import build_spatial_bins
17
17
 
18
18
  BASE_PATH = imap_module_directory / "ultra" / "lookup_tables"
19
19
 
@@ -52,7 +52,7 @@ def test_get_histogram(test_data):
52
52
  v, energy = test_data
53
53
 
54
54
  az_bin_edges, el_bin_edges, az_bin_midpoints, el_bin_midpoints = (
55
- build_spatial_bins()
55
+ np.rad2deg(angle_radians) for angle_radians in (build_spatial_bins())
56
56
  )
57
57
  energy_bin_edges, _ = build_energy_bins()
58
58
 
@@ -101,7 +101,7 @@ def test_get_helio_exposure_times():
101
101
 
102
102
  energy_bin_edges, energy_midpoints = build_energy_bins()
103
103
  az_bin_edges, el_bin_edges, az_bin_midpoints, el_bin_midpoints = (
104
- build_spatial_bins()
104
+ np.rad2deg(angle_radians) for angle_radians in (build_spatial_bins())
105
105
  )
106
106
 
107
107
  assert exposure_3d.shape == (
@@ -1,6 +1,7 @@
1
1
  """Module for constants and useful shared classes used in Ultra."""
2
2
 
3
3
  from dataclasses import dataclass
4
+ from typing import ClassVar
4
5
 
5
6
 
6
7
  @dataclass(frozen=True)
@@ -60,7 +61,8 @@ class UltraConstants:
60
61
  ) # SSD-specific correction to DMIN [mm]
61
62
 
62
63
  # Conversion factors
63
- KEV_J = 1.602180000000000e-16 # 1.6021766339999e-16 # keV to joules
64
+ KEV_J = 1.602177e-16 # keV to joules
65
+ J_KEV = 1 / KEV_J # joules to keV
64
66
  MASS_H = 1.6735575e-27 # Mass of a hydrogen atom in kilograms.
65
67
 
66
68
  # Energy bin constants
@@ -71,3 +73,11 @@ class UltraConstants:
71
73
  # Constants for species determination based on ctof range.
72
74
  CTOF_SPECIES_MIN = 50
73
75
  CTOF_SPECIES_MAX = 200
76
+
77
+ # RPMs for the Ultra instrument.
78
+ # TODO: this is a placeholder.
79
+ CULLING_RPM_MIN = 2.0
80
+ CULLING_RPM_MAX = 6.0
81
+
82
+ # Thresholds for culling based on counts.
83
+ CULLING_ENERGY_BIN_EDGES: ClassVar[list] = [-1e5, 0, 10, 20, 1e5]
@@ -11,7 +11,7 @@ import xarray as xr
11
11
 
12
12
  from imap_processing import decom, imap_module_directory
13
13
  from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
14
- from imap_processing.spice.time import met_to_j2000ns
14
+ from imap_processing.spice.time import met_to_ttj2000ns
15
15
  from imap_processing.ultra.l0.decom_ultra import process_ultra_apids
16
16
  from imap_processing.ultra.l0.ultra_utils import (
17
17
  ULTRA_AUX,
@@ -69,7 +69,7 @@ def initiate_data_arrays(decom_ultra: dict, apid: int) -> xr.Dataset:
69
69
  cdf_manager.add_instrument_variable_attrs("ultra", "l1a")
70
70
 
71
71
  epoch_time = xr.DataArray(
72
- met_to_j2000ns(raw_time),
72
+ met_to_ttj2000ns(raw_time),
73
73
  name="epoch",
74
74
  dims=["epoch"],
75
75
  attrs=cdf_manager.get_variable_attributes("epoch"),
@@ -1,26 +1,43 @@
1
1
  """Calculate Badtimes."""
2
2
 
3
+ import numpy as np
3
4
  import xarray as xr
5
+ from numpy.typing import NDArray
4
6
 
5
7
  from imap_processing.ultra.utils.ultra_l1_utils import create_dataset
6
8
 
7
9
 
8
- def calculate_badtimes(extended_spin_dict: dict, name: str) -> xr.Dataset:
10
+ def calculate_badtimes(
11
+ extendedspin_dataset: xr.Dataset,
12
+ cullingmask_spins: NDArray,
13
+ name: str,
14
+ data_version: str,
15
+ ) -> xr.Dataset:
9
16
  """
10
17
  Create dataset with defined datatypes for Badtimes Data.
11
18
 
12
19
  Parameters
13
20
  ----------
14
- extended_spin_dict : dict
15
- L1b data dictionary.
21
+ extendedspin_dataset : xarray.Dataset
22
+ Dataset containing the data.
23
+ cullingmask_spins : np.ndarray
24
+ Dataset containing the culled data.
16
25
  name : str
17
26
  Name of the dataset.
27
+ data_version : str
28
+ Version of the data.
18
29
 
19
30
  Returns
20
31
  -------
21
32
  badtimes_dataset : xarray.Dataset
22
- Dataset containing the data.
33
+ Dataset containing the extendedspin data that has been culled.
23
34
  """
24
- badtimes_dataset = create_dataset(extended_spin_dict, name, "l1b")
35
+ culled_spins = np.setdiff1d(
36
+ extendedspin_dataset["spin_number"].values, cullingmask_spins
37
+ )
38
+
39
+ filtered_dataset = extendedspin_dataset.sel(spin_number=culled_spins)
40
+
41
+ badtimes_dataset = create_dataset(filtered_dataset, name, "l1b", data_version)
25
42
 
26
43
  return badtimes_dataset
@@ -2,25 +2,51 @@
2
2
 
3
3
  import xarray as xr
4
4
 
5
+ from imap_processing.quality_flags import ImapAttitudeUltraFlags, ImapRatesUltraFlags
5
6
  from imap_processing.ultra.utils.ultra_l1_utils import create_dataset
6
7
 
7
8
 
8
- def calculate_cullingmask(extended_spin_dict: dict, name: str) -> xr.Dataset:
9
+ def calculate_cullingmask(
10
+ extendedspin_dataset: xr.Dataset, name: str, data_version: str
11
+ ) -> xr.Dataset:
9
12
  """
10
13
  Create dataset with defined datatype for Culling Mask Data.
11
14
 
12
15
  Parameters
13
16
  ----------
14
- extended_spin_dict : dict
15
- L1b data dictionary.
17
+ extendedspin_dataset : xarray.Dataset
18
+ Dataset containing the data.
16
19
  name : str
17
20
  Name of the dataset.
21
+ data_version : str
22
+ Version of the data.
18
23
 
19
24
  Returns
20
25
  -------
21
26
  cullingmask_dataset : xarray.Dataset
22
- Dataset containing the data.
27
+ Dataset containing the extendedspin data that remains after culling.
23
28
  """
24
- cullingmask_dataset = create_dataset(extended_spin_dict, name, "l1b")
29
+ # If the spin rate was too high or low then the spin should be thrown out.
30
+ # If the rates at any energy level are too high then throw out the entire spin.
31
+ good_mask = (
32
+ (
33
+ extendedspin_dataset["quality_attitude"]
34
+ & ImapAttitudeUltraFlags.SPINRATE.value
35
+ )
36
+ == 0
37
+ ) & (
38
+ (
39
+ (
40
+ extendedspin_dataset["quality_ena_rates"]
41
+ & ImapRatesUltraFlags.HIGHRATES.value
42
+ )
43
+ == 0
44
+ ).all(dim="energy_bin_geometric_mean")
45
+ )
46
+ filtered_dataset = extendedspin_dataset.sel(
47
+ spin_number=extendedspin_dataset["spin_number"][good_mask]
48
+ )
49
+
50
+ cullingmask_dataset = create_dataset(filtered_dataset, name, "l1b", data_version)
25
51
 
26
52
  return cullingmask_dataset