imap-processing 0.6.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.
Files changed (398) hide show
  1. imap_processing/__init__.py +34 -0
  2. imap_processing/_version.py +3 -0
  3. imap_processing/ccsds/__init__.py +0 -0
  4. imap_processing/ccsds/ccsds_data.py +55 -0
  5. imap_processing/ccsds/excel_to_xtce.py +477 -0
  6. imap_processing/cdf/__init__.py +0 -0
  7. imap_processing/cdf/cdf_attribute_manager.py +322 -0
  8. imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +212 -0
  9. imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +1358 -0
  10. imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +391 -0
  11. imap_processing/cdf/config/imap_constant_attrs.yaml +33 -0
  12. imap_processing/cdf/config/imap_default_global_cdf_attrs.yaml +17 -0
  13. imap_processing/cdf/config/imap_glows_global_cdf_attrs.yaml +41 -0
  14. imap_processing/cdf/config/imap_glows_l1a_variable_attrs.yaml +499 -0
  15. imap_processing/cdf/config/imap_glows_l1b_variable_attrs.yaml +334 -0
  16. imap_processing/cdf/config/imap_hi_global_cdf_attrs.yaml +51 -0
  17. imap_processing/cdf/config/imap_hi_variable_attrs.yaml +435 -0
  18. imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +27 -0
  19. imap_processing/cdf/config/imap_hit_l1a_variable_attrs.yaml +493 -0
  20. imap_processing/cdf/config/imap_hit_l1b_variable_attrs.yaml +564 -0
  21. imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +24 -0
  22. imap_processing/cdf/config/imap_idex_l1a_variable_attrs.yaml +426 -0
  23. imap_processing/cdf/config/imap_lo_global_cdf_attrs.yaml +90 -0
  24. imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +487 -0
  25. imap_processing/cdf/config/imap_lo_l1b_variable_attrs.yaml +121 -0
  26. imap_processing/cdf/config/imap_lo_l1c_variable_attrs.yaml +179 -0
  27. imap_processing/cdf/config/imap_mag_global_cdf_attrs.yaml +97 -0
  28. imap_processing/cdf/config/imap_mag_l1_variable_attrs.yaml +201 -0
  29. imap_processing/cdf/config/imap_swapi_global_cdf_attrs.yaml +33 -0
  30. imap_processing/cdf/config/imap_swapi_variable_attrs.yaml +137 -0
  31. imap_processing/cdf/config/imap_swe_global_cdf_attrs.yaml +24 -0
  32. imap_processing/cdf/config/imap_swe_l1a_variable_attrs.yaml +234 -0
  33. imap_processing/cdf/config/imap_swe_l1b_variable_attrs.yaml +273 -0
  34. imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +100 -0
  35. imap_processing/cdf/config/imap_ultra_l1a_variable_attrs.yaml +52 -0
  36. imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +297 -0
  37. imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +121 -0
  38. imap_processing/cdf/config/shared/default_global_cdf_attrs_schema.yaml +246 -0
  39. imap_processing/cdf/config/shared/default_variable_cdf_attrs_schema.yaml +466 -0
  40. imap_processing/cdf/imap_cdf_manager.py +64 -0
  41. imap_processing/cdf/utils.py +147 -0
  42. imap_processing/cli.py +863 -0
  43. imap_processing/codice/__init__.py +1 -0
  44. imap_processing/codice/codice_l0.py +54 -0
  45. imap_processing/codice/codice_l1a.py +558 -0
  46. imap_processing/codice/codice_l1b.py +194 -0
  47. imap_processing/codice/constants.py +986 -0
  48. imap_processing/codice/data/esa_sweep_values.csv +257 -0
  49. imap_processing/codice/data/lo_stepping_values.csv +129 -0
  50. imap_processing/codice/decompress.py +142 -0
  51. imap_processing/codice/packet_definitions/P_COD_NHK.xml +618 -0
  52. imap_processing/codice/packet_definitions/codice_packet_definition.xml +5073 -0
  53. imap_processing/codice/utils.py +95 -0
  54. imap_processing/decom.py +40 -0
  55. imap_processing/glows/__init__.py +1 -0
  56. imap_processing/glows/ancillary/l1b_conversion_table_v001.json +42 -0
  57. imap_processing/glows/l0/__init__.py +0 -0
  58. imap_processing/glows/l0/decom_glows.py +91 -0
  59. imap_processing/glows/l0/glows_l0_data.py +194 -0
  60. imap_processing/glows/l1a/glows_l1a.py +424 -0
  61. imap_processing/glows/l1a/glows_l1a_data.py +555 -0
  62. imap_processing/glows/l1b/glows_l1b.py +270 -0
  63. imap_processing/glows/l1b/glows_l1b_data.py +583 -0
  64. imap_processing/glows/packet_definitions/GLX_COMBINED.xml +254 -0
  65. imap_processing/glows/packet_definitions/P_GLX_TMSCDE.xml +97 -0
  66. imap_processing/glows/packet_definitions/P_GLX_TMSCHIST.xml +215 -0
  67. imap_processing/glows/utils/__init__.py +0 -0
  68. imap_processing/glows/utils/constants.py +105 -0
  69. imap_processing/hi/__init__.py +1 -0
  70. imap_processing/hi/l0/__init__.py +0 -0
  71. imap_processing/hi/l0/decom_hi.py +24 -0
  72. imap_processing/hi/l1a/__init__.py +0 -0
  73. imap_processing/hi/l1a/hi_l1a.py +73 -0
  74. imap_processing/hi/l1a/histogram.py +142 -0
  75. imap_processing/hi/l1a/housekeeping.py +27 -0
  76. imap_processing/hi/l1a/science_direct_event.py +341 -0
  77. imap_processing/hi/l1b/__init__.py +0 -0
  78. imap_processing/hi/l1b/hi_eng_unit_convert_table.csv +154 -0
  79. imap_processing/hi/l1b/hi_l1b.py +127 -0
  80. imap_processing/hi/l1c/__init__.py +0 -0
  81. imap_processing/hi/l1c/hi_l1c.py +228 -0
  82. imap_processing/hi/packet_definitions/__init__.py +0 -0
  83. imap_processing/hi/packet_definitions/hi_packet_definition.xml +482 -0
  84. imap_processing/hi/utils.py +27 -0
  85. imap_processing/hit/__init__.py +1 -0
  86. imap_processing/hit/l0/__init__.py +0 -0
  87. imap_processing/hit/l0/data_classes/housekeeping.py +240 -0
  88. imap_processing/hit/l0/data_classes/science_packet.py +259 -0
  89. imap_processing/hit/l0/decom_hit.py +467 -0
  90. imap_processing/hit/l0/utils/hit_base.py +57 -0
  91. imap_processing/hit/l1a/__init__.py +0 -0
  92. imap_processing/hit/l1a/hit_l1a.py +254 -0
  93. imap_processing/hit/l1b/hit_l1b.py +179 -0
  94. imap_processing/hit/packet_definitions/hit_packet_definitions.xml +1276 -0
  95. imap_processing/ialirt/__init__.py +0 -0
  96. imap_processing/ialirt/l0/__init__.py +0 -0
  97. imap_processing/ialirt/l0/process_hit.py +220 -0
  98. imap_processing/ialirt/packet_definitions/__init__.py +0 -0
  99. imap_processing/ialirt/packet_definitions/ialirt.xml +778 -0
  100. imap_processing/ialirt/packet_definitions/ialirt_hit.xml +186 -0
  101. imap_processing/idex/__init__.py +2 -0
  102. imap_processing/idex/idex_constants.py +27 -0
  103. imap_processing/idex/idex_l0.py +31 -0
  104. imap_processing/idex/idex_l1a.py +631 -0
  105. imap_processing/idex/packet_definitions/idex_packet_definition.xml +3162 -0
  106. imap_processing/lo/__init__.py +1 -0
  107. imap_processing/lo/l0/__init__.py +0 -0
  108. imap_processing/lo/l0/data_classes/science_direct_events.py +215 -0
  109. imap_processing/lo/l0/data_classes/star_sensor.py +98 -0
  110. imap_processing/lo/l0/decompression_tables/12_to_16_bit.csv +4097 -0
  111. imap_processing/lo/l0/decompression_tables/8_to_12_bit.csv +257 -0
  112. imap_processing/lo/l0/decompression_tables/8_to_16_bit.csv +257 -0
  113. imap_processing/lo/l0/decompression_tables/decompression_tables.py +75 -0
  114. imap_processing/lo/l0/lo_apid.py +15 -0
  115. imap_processing/lo/l0/lo_science.py +150 -0
  116. imap_processing/lo/l0/utils/binary_string.py +59 -0
  117. imap_processing/lo/l0/utils/bit_decompression.py +62 -0
  118. imap_processing/lo/l0/utils/lo_base.py +57 -0
  119. imap_processing/lo/l1a/__init__.py +0 -0
  120. imap_processing/lo/l1a/lo_l1a.py +157 -0
  121. imap_processing/lo/l1b/lo_l1b.py +160 -0
  122. imap_processing/lo/l1c/lo_l1c.py +180 -0
  123. imap_processing/lo/packet_definitions/lo_xtce.xml +3541 -0
  124. imap_processing/mag/__init__.py +2 -0
  125. imap_processing/mag/constants.py +108 -0
  126. imap_processing/mag/l0/decom_mag.py +170 -0
  127. imap_processing/mag/l0/mag_l0_data.py +118 -0
  128. imap_processing/mag/l1a/mag_l1a.py +317 -0
  129. imap_processing/mag/l1a/mag_l1a_data.py +1007 -0
  130. imap_processing/mag/l1b/__init__.py +0 -0
  131. imap_processing/mag/l1b/imap_calibration_mag_20240229_v01.cdf +0 -0
  132. imap_processing/mag/l1b/mag_l1b.py +125 -0
  133. imap_processing/mag/l1c/mag_l1c.py +57 -0
  134. imap_processing/mag/packet_definitions/MAG_SCI_COMBINED.xml +235 -0
  135. imap_processing/quality_flags.py +91 -0
  136. imap_processing/spice/__init__.py +1 -0
  137. imap_processing/spice/geometry.py +322 -0
  138. imap_processing/spice/kernels.py +459 -0
  139. imap_processing/spice/time.py +72 -0
  140. imap_processing/swapi/__init__.py +1 -0
  141. imap_processing/swapi/l1/__init__.py +0 -0
  142. imap_processing/swapi/l1/swapi_l1.py +685 -0
  143. imap_processing/swapi/l2/__init__.py +0 -0
  144. imap_processing/swapi/l2/swapi_l2.py +107 -0
  145. imap_processing/swapi/packet_definitions/__init__.py +0 -0
  146. imap_processing/swapi/packet_definitions/swapi_packet_definition.xml +708 -0
  147. imap_processing/swapi/swapi_utils.py +25 -0
  148. imap_processing/swe/__init__.py +1 -0
  149. imap_processing/swe/l1a/__init__.py +0 -0
  150. imap_processing/swe/l1a/swe_l1a.py +48 -0
  151. imap_processing/swe/l1a/swe_science.py +223 -0
  152. imap_processing/swe/l1b/engineering_unit_convert_table.csv +65 -0
  153. imap_processing/swe/l1b/swe_esa_lookup_table.csv +1441 -0
  154. imap_processing/swe/l1b/swe_l1b.py +49 -0
  155. imap_processing/swe/l1b/swe_l1b_science.py +557 -0
  156. imap_processing/swe/packet_definitions/__init__.py +0 -0
  157. imap_processing/swe/packet_definitions/swe_packet_definition.xml +303 -0
  158. imap_processing/swe/utils/__init__.py +0 -0
  159. imap_processing/swe/utils/swe_utils.py +9 -0
  160. imap_processing/tests/__init__.py +0 -0
  161. imap_processing/tests/ccsds/test_data/expected_output.xml +171 -0
  162. imap_processing/tests/ccsds/test_excel_to_xtce.py +285 -0
  163. imap_processing/tests/cdf/__init__.py +0 -0
  164. imap_processing/tests/cdf/imap_default_global_cdf_attrs.yaml +8 -0
  165. imap_processing/tests/cdf/shared/default_global_cdf_attrs_schema.yaml +246 -0
  166. imap_processing/tests/cdf/shared/default_variable_cdf_attrs_schema.yaml +466 -0
  167. imap_processing/tests/cdf/test_cdf_attribute_manager.py +353 -0
  168. imap_processing/tests/cdf/test_data/imap_default_global_test_cdf_attrs.yaml +7 -0
  169. imap_processing/tests/cdf/test_data/imap_instrument1_global_cdf_attrs.yaml +14 -0
  170. imap_processing/tests/cdf/test_data/imap_instrument1_level1_variable_attrs.yaml +23 -0
  171. imap_processing/tests/cdf/test_data/imap_instrument2_global_cdf_attrs.yaml +23 -0
  172. imap_processing/tests/cdf/test_data/imap_instrument2_level2_variable_attrs.yaml +30 -0
  173. imap_processing/tests/cdf/test_data/imap_test_global.yaml +26 -0
  174. imap_processing/tests/cdf/test_data/imap_test_variable.yaml +41 -0
  175. imap_processing/tests/cdf/test_imap_cdf_manager.py +62 -0
  176. imap_processing/tests/cdf/test_utils.py +109 -0
  177. imap_processing/tests/codice/__init__.py +0 -0
  178. imap_processing/tests/codice/conftest.py +56 -0
  179. imap_processing/tests/codice/data/eu_unit_lookup_table.csv +101 -0
  180. imap_processing/tests/codice/data/idle_export_eu.COD_NHK_20230822_122700 2.csv +100 -0
  181. imap_processing/tests/codice/data/idle_export_raw.COD_NHK_20230822_122700.csv +100 -0
  182. imap_processing/tests/codice/data/imap_codice_l0_hi-counters-aggregated_20240429_v001.pkts +0 -0
  183. imap_processing/tests/codice/data/imap_codice_l0_hi-counters-singles_20240429_v001.pkts +0 -0
  184. imap_processing/tests/codice/data/imap_codice_l0_hi-omni_20240429_v001.pkts +0 -0
  185. imap_processing/tests/codice/data/imap_codice_l0_hi-pha_20240429_v001.pkts +0 -0
  186. imap_processing/tests/codice/data/imap_codice_l0_hi-sectored_20240429_v001.pkts +0 -0
  187. imap_processing/tests/codice/data/imap_codice_l0_hskp_20100101_v001.pkts +0 -0
  188. imap_processing/tests/codice/data/imap_codice_l0_lo-counters-aggregated_20240429_v001.pkts +0 -0
  189. imap_processing/tests/codice/data/imap_codice_l0_lo-counters-singles_20240429_v001.pkts +0 -0
  190. imap_processing/tests/codice/data/imap_codice_l0_lo-nsw-angular_20240429_v001.pkts +0 -0
  191. imap_processing/tests/codice/data/imap_codice_l0_lo-nsw-priority_20240429_v001.pkts +0 -0
  192. imap_processing/tests/codice/data/imap_codice_l0_lo-nsw-species_20240429_v001.pkts +0 -0
  193. imap_processing/tests/codice/data/imap_codice_l0_lo-pha_20240429_v001.pkts +0 -0
  194. imap_processing/tests/codice/data/imap_codice_l0_lo-sw-angular_20240429_v001.pkts +0 -0
  195. imap_processing/tests/codice/data/imap_codice_l0_lo-sw-priority_20240429_v001.pkts +0 -0
  196. imap_processing/tests/codice/data/imap_codice_l0_lo-sw-species_20240429_v001.pkts +0 -0
  197. imap_processing/tests/codice/data/imap_codice_l1a_hi-counters-aggregated_20240429_v001.cdf +0 -0
  198. imap_processing/tests/codice/data/imap_codice_l1a_hi-counters-singles_20240429_v001.cdf +0 -0
  199. imap_processing/tests/codice/data/imap_codice_l1a_hi-omni_20240429_v001.cdf +0 -0
  200. imap_processing/tests/codice/data/imap_codice_l1a_hi-sectored_20240429_v001.cdf +0 -0
  201. imap_processing/tests/codice/data/imap_codice_l1a_hskp_20100101_v001.cdf +0 -0
  202. imap_processing/tests/codice/data/imap_codice_l1a_lo-counters-aggregated_20240429_v001.cdf +0 -0
  203. imap_processing/tests/codice/data/imap_codice_l1a_lo-counters-singles_20240429_v001.cdf +0 -0
  204. imap_processing/tests/codice/data/imap_codice_l1a_lo-nsw-angular_20240429_v001.cdf +0 -0
  205. imap_processing/tests/codice/data/imap_codice_l1a_lo-nsw-priority_20240429_v001.cdf +0 -0
  206. imap_processing/tests/codice/data/imap_codice_l1a_lo-nsw-species_20240429_v001.cdf +0 -0
  207. imap_processing/tests/codice/data/imap_codice_l1a_lo-sw-angular_20240429_v001.cdf +0 -0
  208. imap_processing/tests/codice/data/imap_codice_l1a_lo-sw-priority_20240429_v001.cdf +0 -0
  209. imap_processing/tests/codice/data/imap_codice_l1a_lo-sw-species_20240429_v001.cdf +0 -0
  210. imap_processing/tests/codice/data/imap_codice_l1b_hi-counters-aggregated_20240429_v001.cdf +0 -0
  211. imap_processing/tests/codice/data/imap_codice_l1b_hi-counters-singles_20240429_v001.cdf +0 -0
  212. imap_processing/tests/codice/data/imap_codice_l1b_hi-omni_20240429_v001.cdf +0 -0
  213. imap_processing/tests/codice/data/imap_codice_l1b_hi-sectored_20240429_v001.cdf +0 -0
  214. imap_processing/tests/codice/data/imap_codice_l1b_hskp_20100101_v001.cdf +0 -0
  215. imap_processing/tests/codice/data/imap_codice_l1b_lo-counters-aggregated_20240429_v001.cdf +0 -0
  216. imap_processing/tests/codice/data/imap_codice_l1b_lo-counters-singles_20240429_v001.cdf +0 -0
  217. imap_processing/tests/codice/data/imap_codice_l1b_lo-nsw-angular_20240429_v001.cdf +0 -0
  218. imap_processing/tests/codice/data/imap_codice_l1b_lo-nsw-priority_20240429_v001.cdf +0 -0
  219. imap_processing/tests/codice/data/imap_codice_l1b_lo-nsw-species_20240429_v001.cdf +0 -0
  220. imap_processing/tests/codice/data/imap_codice_l1b_lo-sw-angular_20240429_v001.cdf +0 -0
  221. imap_processing/tests/codice/data/imap_codice_l1b_lo-sw-priority_20240429_v001.cdf +0 -0
  222. imap_processing/tests/codice/data/imap_codice_l1b_lo-sw-species_20240429_v001.cdf +0 -0
  223. imap_processing/tests/codice/test_codice_l0.py +144 -0
  224. imap_processing/tests/codice/test_codice_l1a.py +187 -0
  225. imap_processing/tests/codice/test_codice_l1b.py +60 -0
  226. imap_processing/tests/codice/test_decompress.py +50 -0
  227. imap_processing/tests/conftest.py +372 -0
  228. imap_processing/tests/glows/direct_events_validation_data_l1a.csv +5704 -0
  229. imap_processing/tests/glows/glows_test_packet_20110921_v01.pkts +0 -0
  230. imap_processing/tests/glows/test_glows_decom.py +133 -0
  231. imap_processing/tests/glows/test_glows_l1a_cdf.py +85 -0
  232. imap_processing/tests/glows/test_glows_l1a_data.py +510 -0
  233. imap_processing/tests/glows/test_glows_l1b.py +348 -0
  234. imap_processing/tests/glows/test_glows_l1b_data.py +70 -0
  235. imap_processing/tests/hi/__init__.py +0 -0
  236. imap_processing/tests/hi/conftest.py +133 -0
  237. imap_processing/tests/hi/test_data/l0/20231030_H45_APP_NHK.bin +0 -0
  238. imap_processing/tests/hi/test_data/l0/20231030_H45_APP_NHK.csv +201 -0
  239. imap_processing/tests/hi/test_data/l0/20231030_H45_SCI_CNT.bin +0 -0
  240. imap_processing/tests/hi/test_data/l0/20231030_H45_SCI_DE.bin +0 -0
  241. imap_processing/tests/hi/test_data/l0/README.txt +54 -0
  242. imap_processing/tests/hi/test_decom.py +55 -0
  243. imap_processing/tests/hi/test_hi_l1b.py +31 -0
  244. imap_processing/tests/hi/test_hi_l1c.py +69 -0
  245. imap_processing/tests/hi/test_l1a.py +96 -0
  246. imap_processing/tests/hi/test_l1a_sci_de.py +72 -0
  247. imap_processing/tests/hi/test_utils.py +15 -0
  248. imap_processing/tests/hit/PREFLIGHT_raw_record_2023_256_15_59_04_apid1251.pkts +0 -0
  249. imap_processing/tests/hit/PREFLIGHT_raw_record_2023_256_15_59_04_apid1252.pkts +0 -0
  250. imap_processing/tests/hit/__init__.py +0 -0
  251. imap_processing/tests/hit/test_data/imap_hit_l0_hk_20100105_v001.pkts +0 -0
  252. imap_processing/tests/hit/test_data/sci_sample.ccsds +0 -0
  253. imap_processing/tests/hit/test_hit_decom.py +230 -0
  254. imap_processing/tests/hit/test_hit_l1a.py +224 -0
  255. imap_processing/tests/hit/test_hit_l1b.py +52 -0
  256. imap_processing/tests/hit/validation_data/hskp_sample_raw.csv +88 -0
  257. imap_processing/tests/ialirt/__init__.py +0 -0
  258. imap_processing/tests/ialirt/test_data/l0/IALiRT Raw Packet Telemetry.txt +33 -0
  259. imap_processing/tests/ialirt/test_data/l0/hit_ialirt_sample.ccsds +0 -0
  260. imap_processing/tests/ialirt/test_data/l0/hit_ialirt_sample.csv +1001 -0
  261. imap_processing/tests/ialirt/unit/__init__.py +0 -0
  262. imap_processing/tests/ialirt/unit/test_decom_ialirt.py +94 -0
  263. imap_processing/tests/ialirt/unit/test_process_hit.py +226 -0
  264. imap_processing/tests/idex/__init__.py +0 -0
  265. imap_processing/tests/idex/conftest.py +22 -0
  266. imap_processing/tests/idex/imap_idex_l0_raw_20230725_v001.pkts +0 -0
  267. imap_processing/tests/idex/impact_14_tof_high_data.txt +8189 -0
  268. imap_processing/tests/idex/test_idex_l0.py +45 -0
  269. imap_processing/tests/idex/test_idex_l1a.py +91 -0
  270. imap_processing/tests/lo/__init__.py +0 -0
  271. imap_processing/tests/lo/test_binary_string.py +21 -0
  272. imap_processing/tests/lo/test_bit_decompression.py +39 -0
  273. imap_processing/tests/lo/test_cdfs/imap_lo_l0_raw_20240627_v001.pkts +0 -0
  274. imap_processing/tests/lo/test_cdfs/imap_lo_l1a_de_20100101_v001.cdf +0 -0
  275. imap_processing/tests/lo/test_cdfs/imap_lo_l1a_spin_20100101_v001.cdf +0 -0
  276. imap_processing/tests/lo/test_cdfs/imap_lo_l1b_de_20100101_v001.cdf +0 -0
  277. imap_processing/tests/lo/test_lo_l1a.py +66 -0
  278. imap_processing/tests/lo/test_lo_l1b.py +74 -0
  279. imap_processing/tests/lo/test_lo_l1c.py +66 -0
  280. imap_processing/tests/lo/test_science_counts.py +41 -0
  281. imap_processing/tests/lo/test_science_direct_events.py +209 -0
  282. imap_processing/tests/lo/test_star_sensor.py +35 -0
  283. imap_processing/tests/mag/imap_mag_l1a_burst-magi_20231025_v001.cdf +0 -0
  284. imap_processing/tests/mag/mag_l0_test_data.pkts +0 -0
  285. imap_processing/tests/mag/mag_l0_test_output.csv +37 -0
  286. imap_processing/tests/mag/mag_l1_test_data.pkts +0 -0
  287. imap_processing/tests/mag/mag_l1a_test_output.csv +97 -0
  288. imap_processing/tests/mag/test_mag_decom.py +117 -0
  289. imap_processing/tests/mag/test_mag_l1a.py +856 -0
  290. imap_processing/tests/mag/test_mag_l1b.py +77 -0
  291. imap_processing/tests/mag/test_mag_l1c.py +40 -0
  292. imap_processing/tests/spice/__init__.py +0 -0
  293. imap_processing/tests/spice/test_data/imap_ena_sim_metakernel.template +4 -0
  294. imap_processing/tests/spice/test_data/imap_science_0001.tf +171 -0
  295. imap_processing/tests/spice/test_data/imap_sclk_0000.tsc +156 -0
  296. imap_processing/tests/spice/test_data/imap_sim_ck_2hr_2secsampling_with_nutation.bc +0 -0
  297. imap_processing/tests/spice/test_data/imap_simple_metakernel.template +3 -0
  298. imap_processing/tests/spice/test_data/imap_spk_demo.bsp +0 -0
  299. imap_processing/tests/spice/test_data/imap_wkcp.tf +1806 -0
  300. imap_processing/tests/spice/test_data/naif0012.tls +150 -0
  301. imap_processing/tests/spice/test_data/sim_1yr_imap_attitude.bc +0 -0
  302. imap_processing/tests/spice/test_data/sim_1yr_imap_pointing_frame.bc +0 -0
  303. imap_processing/tests/spice/test_geometry.py +214 -0
  304. imap_processing/tests/spice/test_kernels.py +272 -0
  305. imap_processing/tests/spice/test_time.py +35 -0
  306. imap_processing/tests/swapi/__init__.py +0 -0
  307. imap_processing/tests/swapi/conftest.py +16 -0
  308. imap_processing/tests/swapi/l0_data/__init__.py +0 -0
  309. imap_processing/tests/swapi/l0_data/imap_swapi_l0_raw_20231012_v001.pkts +0 -0
  310. imap_processing/tests/swapi/l0_validation_data/__init__.py +0 -0
  311. imap_processing/tests/swapi/l0_validation_data/idle_export_eu.SWP_AUT_20231012_125245.csv +124 -0
  312. imap_processing/tests/swapi/l0_validation_data/idle_export_eu.SWP_HK_20231012_125245.csv +98 -0
  313. imap_processing/tests/swapi/l0_validation_data/idle_export_eu.SWP_MG_20231012_125245.csv +9 -0
  314. imap_processing/tests/swapi/l0_validation_data/idle_export_eu.SWP_SCI_20231012_125245.csv +72 -0
  315. imap_processing/tests/swapi/l0_validation_data/idle_export_raw.SWP_AUT_20231012_125245.csv +124 -0
  316. imap_processing/tests/swapi/l0_validation_data/idle_export_raw.SWP_HK_20231012_125245.csv +98 -0
  317. imap_processing/tests/swapi/l0_validation_data/idle_export_raw.SWP_MG_20231012_125245.csv +9 -0
  318. imap_processing/tests/swapi/l0_validation_data/idle_export_raw.SWP_SCI_20231012_125245.csv +72 -0
  319. imap_processing/tests/swapi/test_swapi_decom.py +135 -0
  320. imap_processing/tests/swapi/test_swapi_l1.py +354 -0
  321. imap_processing/tests/swapi/test_swapi_l2.py +21 -0
  322. imap_processing/tests/swe/__init__.py +0 -0
  323. imap_processing/tests/swe/conftest.py +35 -0
  324. imap_processing/tests/swe/decompressed/20230927173238_4th_quarter_decompressed.csv +181 -0
  325. imap_processing/tests/swe/decompressed/20230927173253_1st_quarter_decompressed.csv +181 -0
  326. imap_processing/tests/swe/decompressed/20230927173308_2nd_quarter_decompressed.csv +181 -0
  327. imap_processing/tests/swe/decompressed/20230927173323_3rd_quarter_decompressed.csv +181 -0
  328. imap_processing/tests/swe/l0_data/2024051010_SWE_SCIENCE_packet.bin +0 -0
  329. imap_processing/tests/swe/l0_validation_data/idle_export_eu.SWE_SCIENCE_20240510_092742.csv +544 -0
  330. imap_processing/tests/swe/l0_validation_data/idle_export_raw.SWE_SCIENCE_20240510_092742.csv +363 -0
  331. imap_processing/tests/swe/test_swe_l1a.py +12 -0
  332. imap_processing/tests/swe/test_swe_l1a_science.py +129 -0
  333. imap_processing/tests/swe/test_swe_l1b.py +61 -0
  334. imap_processing/tests/swe/test_swe_l1b_science.py +65 -0
  335. imap_processing/tests/test_cli.py +229 -0
  336. imap_processing/tests/test_decom.py +66 -0
  337. imap_processing/tests/test_quality_flags.py +71 -0
  338. imap_processing/tests/test_utils.py +107 -0
  339. imap_processing/tests/ultra/__init__.py +0 -0
  340. imap_processing/tests/ultra/test_data/l0/FM45_40P_Phi28p5_BeamCal_LinearScan_phi28.50_theta-0.00_20240207T102740.CCSDS +0 -0
  341. imap_processing/tests/ultra/test_data/l0/FM45_7P_Phi0.0_BeamCal_LinearScan_phi0.04_theta-0.01_20230821T121304.CCSDS +0 -0
  342. imap_processing/tests/ultra/test_data/l0/FM45_TV_Cycle6_Hot_Ops_Front212_20240124T063837.CCSDS +0 -0
  343. imap_processing/tests/ultra/test_data/l0/Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.CCSDS +0 -0
  344. imap_processing/tests/ultra/test_data/l0/ultra45_raw_sc_auxdata_Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.csv +24 -0
  345. imap_processing/tests/ultra/test_data/l0/ultra45_raw_sc_enaphxtofhangimg_FM45_TV_Cycle6_Hot_Ops_Front212_20240124T063837.csv +105 -0
  346. imap_processing/tests/ultra/test_data/l0/ultra45_raw_sc_ultraimgrates_Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.csv +24 -0
  347. imap_processing/tests/ultra/test_data/l0/ultra45_raw_sc_ultrarawimg_withFSWcalcs_FM45_40P_Phi28p5_BeamCal_LinearScan_phi2850_theta-000_20240207T102740.csv +3314 -0
  348. imap_processing/tests/ultra/test_data/l0/ultra45_raw_sc_ultrarawimgevent_FM45_7P_Phi00_BeamCal_LinearScan_phi004_theta-001_20230821T121304.csv +702 -0
  349. imap_processing/tests/ultra/unit/__init__.py +0 -0
  350. imap_processing/tests/ultra/unit/conftest.py +210 -0
  351. imap_processing/tests/ultra/unit/test_decom_apid_880.py +98 -0
  352. imap_processing/tests/ultra/unit/test_decom_apid_881.py +50 -0
  353. imap_processing/tests/ultra/unit/test_decom_apid_883.py +44 -0
  354. imap_processing/tests/ultra/unit/test_decom_apid_896.py +104 -0
  355. imap_processing/tests/ultra/unit/test_lookup_utils.py +68 -0
  356. imap_processing/tests/ultra/unit/test_ultra_l1a.py +338 -0
  357. imap_processing/tests/ultra/unit/test_ultra_l1b.py +122 -0
  358. imap_processing/tests/ultra/unit/test_ultra_l1b_annotated.py +57 -0
  359. imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +342 -0
  360. imap_processing/tests/ultra/unit/test_ultra_l1c.py +104 -0
  361. imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +35 -0
  362. imap_processing/ultra/__init__.py +1 -0
  363. imap_processing/ultra/constants.py +60 -0
  364. imap_processing/ultra/l0/__init__.py +0 -0
  365. imap_processing/ultra/l0/decom_tools.py +281 -0
  366. imap_processing/ultra/l0/decom_ultra.py +278 -0
  367. imap_processing/ultra/l0/ultra_utils.py +326 -0
  368. imap_processing/ultra/l1a/__init__.py +0 -0
  369. imap_processing/ultra/l1a/ultra_l1a.py +319 -0
  370. imap_processing/ultra/l1b/badtimes.py +26 -0
  371. imap_processing/ultra/l1b/cullingmask.py +26 -0
  372. imap_processing/ultra/l1b/de.py +59 -0
  373. imap_processing/ultra/l1b/extendedspin.py +45 -0
  374. imap_processing/ultra/l1b/lookup_utils.py +165 -0
  375. imap_processing/ultra/l1b/ultra_l1b.py +65 -0
  376. imap_processing/ultra/l1b/ultra_l1b_annotated.py +54 -0
  377. imap_processing/ultra/l1b/ultra_l1b_extended.py +764 -0
  378. imap_processing/ultra/l1c/histogram.py +36 -0
  379. imap_processing/ultra/l1c/pset.py +36 -0
  380. imap_processing/ultra/l1c/ultra_l1c.py +52 -0
  381. imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +54 -0
  382. imap_processing/ultra/lookup_tables/EgyNorm.mem.csv +32769 -0
  383. imap_processing/ultra/lookup_tables/FM45_Startup1_ULTRA_IMGPARAMS_20240719.csv +2 -0
  384. imap_processing/ultra/lookup_tables/ultra45_back-pos-luts.csv +4097 -0
  385. imap_processing/ultra/lookup_tables/ultra45_tdc_norm.csv +2050 -0
  386. imap_processing/ultra/lookup_tables/ultra90_back-pos-luts.csv +4097 -0
  387. imap_processing/ultra/lookup_tables/ultra90_tdc_norm.csv +2050 -0
  388. imap_processing/ultra/lookup_tables/yadjust.csv +257 -0
  389. imap_processing/ultra/packet_definitions/ULTRA_SCI_COMBINED.xml +547 -0
  390. imap_processing/ultra/packet_definitions/__init__.py +0 -0
  391. imap_processing/ultra/utils/__init__.py +0 -0
  392. imap_processing/ultra/utils/ultra_l1_utils.py +50 -0
  393. imap_processing/utils.py +413 -0
  394. imap_processing-0.6.0.dist-info/LICENSE +21 -0
  395. imap_processing-0.6.0.dist-info/METADATA +107 -0
  396. imap_processing-0.6.0.dist-info/RECORD +398 -0
  397. imap_processing-0.6.0.dist-info/WHEEL +4 -0
  398. imap_processing-0.6.0.dist-info/entry_points.txt +4 -0
@@ -0,0 +1,459 @@
1
+ """Functions for furnishing and tracking SPICE kernels."""
2
+
3
+ import functools
4
+ import logging
5
+ import os
6
+ from collections.abc import Generator
7
+ from contextlib import contextmanager
8
+ from pathlib import Path
9
+ from typing import Any, Callable, Optional, Union, overload
10
+
11
+ import numpy as np
12
+ import spiceypy as spice
13
+ from numpy.typing import NDArray
14
+ from spiceypy.utils.exceptions import SpiceyError
15
+
16
+ from imap_processing import imap_module_directory
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ # Declarations to help with typing. Taken from mypy documentation on
22
+ # decorator-factories:
23
+ # https://mypy.readthedocs.io/en/stable/generics.html#decorator-factories
24
+ # Bare decorator usage
25
+ @overload
26
+ def ensure_spice(
27
+ __func: Callable[..., Any],
28
+ ) -> Callable[..., Any]: ... # numpydoc ignore=GL08
29
+ # Decorator with arguments
30
+ @overload
31
+ def ensure_spice(
32
+ *, time_kernels_only: bool = False
33
+ ) -> Callable[[Callable[..., Any]], Callable[..., Any]]: ... # numpydoc ignore=GL08
34
+ # Implementation
35
+ def ensure_spice(
36
+ __func: Optional[Callable[..., Any]] = None, *, time_kernels_only: bool = False
37
+ ) -> Union[Callable[..., Any], Callable[[Callable[..., Any]], Callable[..., Any]]]:
38
+ """
39
+ Decorator/wrapper that automatically furnishes SPICE kernels.
40
+
41
+ Parameters
42
+ ----------
43
+ __func : Callable
44
+ The function requiring SPICE that we are going to wrap if being used
45
+ explicitly, otherwise None, in which case ensure_spice is being used,
46
+ not as a function wrapper (see l2a_processing.py) but as a true
47
+ decorator without an explicit function argument.
48
+ time_kernels_only : bool
49
+ Specify that we only need to furnish time kernels (if SPICE_METAKERNEL
50
+ is set, we still just furnish that metakernel and assume the time
51
+ kernels are included.
52
+
53
+ Returns
54
+ -------
55
+ Callable
56
+ Decorated function, with spice error handling.
57
+
58
+ Notes
59
+ -----
60
+ Before trying to understand this piece of code, read this:
61
+ https://stackoverflow.com/questions/5929107/decorators-with-parameters/60832711#60832711
62
+
63
+ **Control flow overview:**
64
+ 1. Try simply calling the wrapped function naively.
65
+ * SUCCESS? Great! We're done.
66
+ * SpiceyError? Go to step 2.
67
+
68
+ 2. Furnish metakernel at SPICE_METAKERNEL
69
+ * SUCCESS? Great, return the original function again (so it can be
70
+ re-run).
71
+ * KeyError? Seems like SPICE_METAKERNEL isn't set, no problem. Go to
72
+ step 3.
73
+
74
+ 3. Did we get the parameter time_kernels_only=True?
75
+ --> YES? We only need LSK and SCLK kernels to run this function. Go fetch
76
+ those and furnish and return the original function (so it can be re-run).
77
+ --> NO? Dang. This is sort of the end of the line. Re-raise the error
78
+ generated from the failed spiceypy function call but add a better
79
+ message to it.
80
+
81
+ Examples
82
+ --------
83
+ There are three ways to use this object
84
+
85
+ 1. A decorator with no arguments
86
+ >>> @ensure_spice
87
+ ... def my_spicey_func(a, b):
88
+ ... pass
89
+
90
+ 2. A decorator with parameters. This is useful
91
+ if we only need the latest SCLK and LSK kernels for the function involved.
92
+ >>> @ensure_spice(time_kernels_only=True)
93
+ ... def my_spicey_time_func(a, b):
94
+ ... pass
95
+
96
+ 3. An explicit wrapper function, providing a dynamically set value for
97
+ parameters, e.g. time_kernels_only
98
+ >>> wrapped = ensure_spice(spicey_func, time_kernels_only=True)
99
+ ... result = wrapped(*args, **kwargs)
100
+ """
101
+
102
+ def _decorator(func: Callable[..., Callable]) -> Callable:
103
+ """
104
+ Decorate or wrap input function depending on how ensure_spice is used.
105
+
106
+ Parameters
107
+ ----------
108
+ func : Callable
109
+ The function to be decorated/wrapped.
110
+
111
+ Returns
112
+ -------
113
+ Callable
114
+ If used as a function wrapper, the decorated function is returned.
115
+ """
116
+
117
+ @functools.wraps(func)
118
+ def wrapper_ensure_spice(*args: Any, **kwargs: Any) -> Any:
119
+ """
120
+ Wrap the function that ensure_spice is used on.
121
+
122
+ Parameters
123
+ ----------
124
+ *args : list
125
+ The positional arguments passed to the decorated function.
126
+ **kwargs
127
+ The keyword arguments passed to the decorated function.
128
+
129
+ Returns
130
+ -------
131
+ Object
132
+ Output from wrapped function.
133
+ """
134
+ try:
135
+ # Step 1.
136
+ return func(
137
+ *args, **kwargs
138
+ ) # Naive first try. Maybe SPICE is already furnished.
139
+ except SpiceyError as spicey_err:
140
+ try:
141
+ # Step 2.
142
+ if os.getenv("SPICE_METAKERNEL"):
143
+ metakernel_path = os.getenv("SPICE_METAKERNEL")
144
+ spice.furnsh(metakernel_path)
145
+ else:
146
+ furnish_time_kernel()
147
+ except KeyError:
148
+ # TODO: An additional step that was used on EMUS was to get
149
+ # a custom metakernel from the SDC API based on an input
150
+ # time range.
151
+ if time_kernels_only:
152
+ # Step 3.
153
+ # TODO: Decide if this is useful for IMAP. Possible
154
+ # implementation could include downloading
155
+ # the most recent leapsecond kernel from NAIF (see:
156
+ # https://lasp.colorado.edu/nucleus/projects/LIBSDC/repos/libera_utils/browse/libera_utils/spice_utils.py
157
+ # for LIBERA implementation of downloading and caching
158
+ # kernels) and finding the most recent IMAP clock
159
+ # kernel in EFS.
160
+ raise NotImplementedError from spicey_err
161
+ else:
162
+ raise SpiceyError(
163
+ "When calling a function requiring SPICE, we failed "
164
+ "to load a metakernel. SPICE_METAKERNEL is not set,"
165
+ "and time_kernels_only is not set to True"
166
+ ) from spicey_err
167
+ return func(*args, **kwargs)
168
+
169
+ return wrapper_ensure_spice
170
+
171
+ # Note: This return was originally implemented as a ternary operator, but
172
+ # this caused mypy to fail due to this bug:
173
+ # https://github.com/python/mypy/issues/4134
174
+ if callable(__func):
175
+ return _decorator(__func)
176
+ else:
177
+ return _decorator
178
+
179
+
180
+ @contextmanager
181
+ def open_spice_ck_file(pointing_frame_path: Path) -> Generator[int, None, None]:
182
+ """
183
+ Context manager for handling SPICE CK files.
184
+
185
+ Parameters
186
+ ----------
187
+ pointing_frame_path : str
188
+ Path to the CK file.
189
+
190
+ Yields
191
+ ------
192
+ handle : int
193
+ Handle to the opened CK file.
194
+ """
195
+ # TODO: We will need to figure out if ck kernel changes
196
+ # and how that will affect appending to the pointing
197
+ # frame kernel.
198
+ if pointing_frame_path.exists():
199
+ handle = spice.dafopw(str(pointing_frame_path))
200
+ else:
201
+ handle = spice.ckopn(str(pointing_frame_path), "CK", 0)
202
+ try:
203
+ yield handle
204
+ finally:
205
+ spice.ckcls(handle)
206
+
207
+
208
+ @ensure_spice
209
+ def create_pointing_frame(pointing_frame_path: Path, ck_path: Path) -> None:
210
+ """
211
+ Create the pointing frame.
212
+
213
+ Parameters
214
+ ----------
215
+ pointing_frame_path : Path
216
+ Location of pointing frame kernel.
217
+ ck_path : Path
218
+ Location of the CK kernel.
219
+
220
+ Notes
221
+ -----
222
+ Kernels required to be furnished:
223
+ "imap_science_0001.tf",
224
+ "imap_sclk_0000.tsc",
225
+ "imap_sim_ck_2hr_2secsampling_with_nutation.bc" or
226
+ "sim_1yr_imap_attitude.bc",
227
+ "imap_wkcp.tf",
228
+ "naif0012.tls"
229
+
230
+ Assumptions:
231
+ - The MOC has removed timeframe in which nutation/procession are present.
232
+ TODO: We may come back and have a check for this.
233
+ - We will continue to append to the pointing frame kernel.
234
+ TODO: Figure out how we want to handle the file size becoming too large.
235
+ - For now we can only furnish a single ck kernel.
236
+ TODO: This will not be the case once we add the ability to query the .csv.
237
+
238
+ References
239
+ ----------
240
+ https://numpydoc.readthedocs.io/en/latest/format.html#references
241
+ """
242
+ # Get IDs.
243
+ # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.gipool
244
+ id_imap_dps = spice.gipool("FRAME_IMAP_DPS", 0, 1)
245
+ id_imap_sclk = spice.gipool("CK_-43000_SCLK", 0, 1)
246
+
247
+ # Verify that only ck_path kernel is loaded.
248
+ count = spice.ktotal("ck")
249
+ loaded_ck_kernel, _, _, _ = spice.kdata(count - 1, "ck")
250
+
251
+ if count != 1 or str(ck_path) != loaded_ck_kernel:
252
+ raise ValueError(f"Error: Expected CK kernel {ck_path}")
253
+
254
+ # If the pointing frame kernel already exists, find the last time.
255
+ if pointing_frame_path.exists():
256
+ # Get the last time in the pointing frame kernel.
257
+ pointing_cover = spice.ckcov(
258
+ str(pointing_frame_path), int(id_imap_dps), True, "SEGMENT", 0, "TDB"
259
+ )
260
+ num_segments = spice.wncard(pointing_cover)
261
+ _, et_end_pointing_frame = spice.wnfetd(pointing_cover, num_segments - 1)
262
+ else:
263
+ et_end_pointing_frame = None
264
+
265
+ # TODO: Query for .csv file to get the pointing start and end times.
266
+ # TODO: Remove next four lines once query is added.
267
+ id_imap_spacecraft = spice.gipool("FRAME_IMAP_SPACECRAFT", 0, 1)
268
+ ck_cover = spice.ckcov(
269
+ str(ck_path), int(id_imap_spacecraft), True, "INTERVAL", 0, "TDB"
270
+ )
271
+ num_intervals = spice.wncard(ck_cover)
272
+
273
+ with open_spice_ck_file(pointing_frame_path) as handle:
274
+ # TODO: this will change to the number of pointings.
275
+ for i in range(num_intervals):
276
+ # Get the coverage window
277
+ # TODO: this will change to pointing start and end time.
278
+ et_start, et_end = spice.wnfetd(ck_cover, i)
279
+ et_times = _get_et_times(et_start, et_end)
280
+
281
+ # TODO: remove after query is added.
282
+ if (
283
+ et_end_pointing_frame is not None
284
+ and et_times[0] < et_end_pointing_frame
285
+ ):
286
+ break
287
+
288
+ # Create a rotation matrix
289
+ rotation_matrix = _create_rotation_matrix(et_times)
290
+
291
+ # Convert the rotation matrix to a quaternion.
292
+ # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.m2q
293
+ q_avg = spice.m2q(rotation_matrix)
294
+
295
+ # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.sce2c
296
+ # Convert start and end times to SCLK.
297
+ sclk_begtim = spice.sce2c(int(id_imap_sclk), et_times[0])
298
+ sclk_endtim = spice.sce2c(int(id_imap_sclk), et_times[-1])
299
+
300
+ # Create the pointing frame kernel.
301
+ # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.ckw02
302
+ spice.ckw02(
303
+ # Handle of an open CK file.
304
+ handle,
305
+ # Start time of the segment.
306
+ sclk_begtim,
307
+ # End time of the segment.
308
+ sclk_endtim,
309
+ # Pointing frame ID.
310
+ int(id_imap_dps),
311
+ # Reference frame.
312
+ "ECLIPJ2000", # Reference frame
313
+ # Identifier.
314
+ "IMAP_DPS",
315
+ # Number of pointing intervals.
316
+ 1,
317
+ # Start times of individual pointing records within segment.
318
+ # Since there is only a single record this is equal to sclk_begtim.
319
+ np.array([sclk_begtim]),
320
+ # End times of individual pointing records within segment.
321
+ # Since there is only a single record this is equal to sclk_endtim.
322
+ np.array([sclk_endtim]), # Single stop time
323
+ # Average quaternion.
324
+ q_avg,
325
+ # 0.0 Angular rotation terms.
326
+ np.array([0.0, 0.0, 0.0]),
327
+ # Rates (seconds per tick) at which the quaternion and
328
+ # angular velocity change.
329
+ np.array([1.0]),
330
+ )
331
+
332
+
333
+ def _get_et_times(et_start: float, et_end: float) -> NDArray[np.float64]:
334
+ """
335
+ Get times for pointing start and stop.
336
+
337
+ Parameters
338
+ ----------
339
+ et_start : float
340
+ Pointing start time.
341
+ et_end : float
342
+ Pointing end time.
343
+
344
+ Returns
345
+ -------
346
+ et_times : numpy.ndarray
347
+ Array of times between et_start and et_end.
348
+ """
349
+ # TODO: Queried pointing start and stop times here.
350
+ # TODO removing the @ensure_spice decorator when using the repointing table.
351
+
352
+ # 1 spin/15 seconds; 10 quaternions / spin.
353
+ num_samples = (et_end - et_start) / 15 * 10
354
+ # There were rounding errors when using spice.pxform so np.ceil and np.floor
355
+ # were used to ensure the start and end times were included in the array.
356
+ et_times = np.linspace(
357
+ np.ceil(et_start * 1e6) / 1e6, np.floor(et_end * 1e6) / 1e6, int(num_samples)
358
+ )
359
+
360
+ return et_times
361
+
362
+
363
+ @ensure_spice
364
+ def _average_quaternions(et_times: np.ndarray) -> NDArray:
365
+ """
366
+ Average the quaternions.
367
+
368
+ Parameters
369
+ ----------
370
+ et_times : numpy.ndarray
371
+ Array of times between et_start and et_end.
372
+
373
+ Returns
374
+ -------
375
+ q_avg : np.ndarray
376
+ Average quaternion.
377
+ """
378
+ aggregate = np.zeros((4, 4))
379
+ for tdb in et_times:
380
+ # we use a quick and dirty method here for grabbing the quaternions
381
+ # from the attitude kernel. Depending on how well the kernel input
382
+ # data is built and sampled, there may or may not be aliasing with this
383
+ # approach. If it turns out that we need to pull the quaternions
384
+ # directly from the CK there are several routines that exist to do this
385
+ # but it's not straight forward. We'll revisit this if needed.
386
+
387
+ # Rotation matrix from IMAP spacecraft frame to ECLIPJ2000.
388
+ # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.pxform
389
+ body_rots = spice.pxform("IMAP_SPACECRAFT", "ECLIPJ2000", tdb)
390
+ # Convert rotation matrix to quaternion.
391
+ # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.m2q
392
+ body_quat = spice.m2q(body_rots)
393
+
394
+ # Standardize the quaternion so that they may be compared.
395
+ body_quat = body_quat * np.sign(body_quat[0])
396
+ # Aggregate quaternions into a single matrix.
397
+ aggregate += np.outer(body_quat, body_quat)
398
+
399
+ # Reference: "On Averaging Rotations".
400
+ # Link: https://link.springer.com/content/pdf/10.1023/A:1011129215388.pdf
401
+ aggregate /= len(et_times)
402
+
403
+ # Compute eigen values and vectors of the matrix A
404
+ # Eigenvalues tell you how much "influence" each
405
+ # direction (eigenvector) has.
406
+ # The largest eigenvalue corresponds to the direction
407
+ # that has the most influence.
408
+ # The eigenvector corresponding to the largest
409
+ # eigenvalue points in the direction that has the most
410
+ # combined rotation influence.
411
+ eigvals, eigvecs = np.linalg.eig(aggregate)
412
+ # q0: The scalar part of the quaternion.
413
+ # q1, q2, q3: The vector part of the quaternion.
414
+ q_avg = eigvecs[:, np.argmax(eigvals)]
415
+
416
+ return q_avg
417
+
418
+
419
+ def _create_rotation_matrix(et_times: np.ndarray) -> NDArray:
420
+ """
421
+ Create a rotation matrix.
422
+
423
+ Parameters
424
+ ----------
425
+ et_times : numpy.ndarray
426
+ Array of times between et_start and et_end.
427
+
428
+ Returns
429
+ -------
430
+ rotation_matrix : np.ndarray
431
+ Rotation matrix.
432
+ """
433
+ # Averaged quaternions.
434
+ q_avg = _average_quaternions(et_times)
435
+
436
+ # Converts the averaged quaternion (q_avg) into a rotation matrix
437
+ # and get inertial z axis.
438
+ # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.q2m
439
+ z_avg = spice.q2m(list(q_avg))[:, 2]
440
+ # y_avg is perpendicular to both z_avg and the standard Z-axis.
441
+ y_avg = np.cross(z_avg, [0, 0, 1])
442
+ # x_avg is perpendicular to y_avg and z_avg.
443
+ x_avg = np.cross(y_avg, z_avg)
444
+
445
+ # Construct the rotation matrix from x_avg, y_avg, z_avg
446
+ rotation_matrix = np.asarray([x_avg, y_avg, z_avg])
447
+
448
+ return rotation_matrix
449
+
450
+
451
+ def furnish_time_kernel() -> None:
452
+ """Furnish the time kernels."""
453
+ spice_test_data_path = imap_module_directory / "tests/spice/test_data"
454
+
455
+ # TODO: we need to load these kernels from EFS volumen that is
456
+ # mounted to batch volume and extend this to generate metakernell
457
+ # which is TBD.
458
+ spice.furnsh(str(spice_test_data_path / "imap_sclk_0000.tsc"))
459
+ spice.furnsh(str(spice_test_data_path / "naif0012.tls"))
@@ -0,0 +1,72 @@
1
+ """Time conversion functions that rely on SPICE."""
2
+
3
+ import typing
4
+ from collections.abc import Collection
5
+ from typing import Union
6
+
7
+ import numpy as np
8
+ import numpy.typing as npt
9
+ import spiceypy as spice
10
+
11
+ from imap_processing.spice import IMAP_SC_ID
12
+ from imap_processing.spice.kernels import ensure_spice
13
+
14
+ TICK_DURATION = 2e-5 # 20 microseconds as defined in imap_sclk_0000.tsc
15
+
16
+
17
+ def met_to_j2000ns(
18
+ met: npt.ArrayLike,
19
+ ) -> npt.NDArray[np.int64]:
20
+ """
21
+ Convert mission elapsed time (MET) to nanoseconds since J2000.
22
+
23
+ Parameters
24
+ ----------
25
+ met : array_like
26
+ Number of seconds since epoch according to the spacecraft clock.
27
+
28
+ Returns
29
+ -------
30
+ array_like or scalar, int64
31
+ The mission elapsed time converted to nanoseconds since the J2000 epoch.
32
+
33
+ Notes
34
+ -----
35
+ There are two options when using SPICE to convert from SCLK time (MET) to
36
+ J2000. The conversion can be done on SCLK strings as input or using double
37
+ precision continuous spacecraft clock "ticks". The latter is more accurate
38
+ as it will correctly convert fractional clock ticks to nanoseconds. Since
39
+ some IMAP instruments contain clocks with higher precision than 1 SCLK
40
+ "tick" which is defined to be 20 microseconds, according to the sclk kernel,
41
+ it is preferable to use the higher accuracy method.
42
+ """
43
+ sclk_ticks = np.asarray(met, dtype=float) / TICK_DURATION
44
+ return np.asarray(_sct2e_wrapper(sclk_ticks) * 1e9, dtype=np.int64)
45
+
46
+
47
+ @typing.no_type_check
48
+ @ensure_spice
49
+ def _sct2e_wrapper(
50
+ sclk_ticks: Union[float, Collection[float]],
51
+ ) -> Union[float, np.ndarray]:
52
+ """
53
+ Convert encoded spacecraft clock "ticks" to ephemeris time.
54
+
55
+ Decorated wrapper for spiceypy.sct2e that vectorizes the function in addition
56
+ to wrapping with the @ensure_spice automatic kernel furnishing functionality.
57
+ https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/sct2e_c.html
58
+
59
+ Parameters
60
+ ----------
61
+ sclk_ticks : Union[float, Collection[float]]
62
+ Input sclk ticks value(s) to be converted to ephemeris time.
63
+
64
+ Returns
65
+ -------
66
+ ephemeris_time: np.ndarray
67
+ Ephemeris time, seconds past J2000.
68
+ """
69
+ if isinstance(sclk_ticks, Collection):
70
+ return np.array([spice.sct2e(IMAP_SC_ID, s) for s in sclk_ticks])
71
+ else:
72
+ return spice.sct2e(IMAP_SC_ID, sclk_ticks)
@@ -0,0 +1 @@
1
+ __version__ = "001"
File without changes