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
@@ -16,7 +16,7 @@ from imap_processing.ccsds.ccsds_data import CcsdsData
16
16
  from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
17
17
  from imap_processing.mag.constants import DataMode
18
18
  from imap_processing.mag.l0.mag_l0_data import MagL0, Mode
19
- from imap_processing.spice.time import met_to_j2000ns
19
+ from imap_processing.spice.time import met_to_ttj2000ns
20
20
 
21
21
  logger = logging.getLogger(__name__)
22
22
 
@@ -109,7 +109,7 @@ def generate_dataset(
109
109
  )
110
110
  vector_data[index, :vector_len] = datapoint.VECTORS
111
111
 
112
- shcoarse_data[index] = met_to_j2000ns(datapoint.SHCOARSE)
112
+ shcoarse_data[index] = met_to_ttj2000ns(datapoint.SHCOARSE)
113
113
  # Add remaining pieces to arrays
114
114
  for key, value in dataclasses.asdict(datapoint).items():
115
115
  if key not in ("ccsds_header", "VECTORS", "SHCOARSE"):
@@ -16,7 +16,10 @@ from imap_processing.mag.l1a.mag_l1a_data import (
16
16
  MagL1aPacketProperties,
17
17
  TimeTuple,
18
18
  )
19
- from imap_processing.spice.time import J2000_EPOCH, met_to_j2000ns
19
+ from imap_processing.spice.time import (
20
+ et_to_utc,
21
+ ttj2000ns_to_et,
22
+ )
20
23
 
21
24
  logger = logging.getLogger(__name__)
22
25
 
@@ -148,15 +151,12 @@ def process_packets(
148
151
  mago_is_primary = mag_l0.PRI_SENS == PrimarySensor.MAGO.value
149
152
 
150
153
  primary_day = (
151
- J2000_EPOCH
152
- + met_to_j2000ns(primary_start_time.to_seconds()).astype("timedelta64[ns]")
154
+ et_to_utc(ttj2000ns_to_et([primary_start_time.to_j2000ns()]))[0]
153
155
  ).astype("datetime64[D]")
154
156
  secondary_day = (
155
- J2000_EPOCH
156
- + met_to_j2000ns(secondary_start_time.to_seconds()).astype(
157
- "timedelta64[ns]"
158
- )
157
+ et_to_utc(ttj2000ns_to_et([secondary_start_time.to_j2000ns()]))[0]
159
158
  ).astype("datetime64[D]")
159
+
160
160
  primary_packet_properties = MagL1aPacketProperties(
161
161
  mag_l0.SHCOARSE,
162
162
  primary_start_time,
@@ -16,7 +16,7 @@ from imap_processing.mag.constants import (
16
16
  MAX_FINE_TIME,
17
17
  RANGE_BIT_WIDTH,
18
18
  )
19
- from imap_processing.spice.time import J2000_EPOCH, met_to_j2000ns
19
+ from imap_processing.spice.time import met_to_ttj2000ns
20
20
 
21
21
 
22
22
  @dataclass
@@ -24,19 +24,20 @@ class TimeTuple:
24
24
  """
25
25
  Class for storing fine time/coarse time for MAG data.
26
26
 
27
- Course time is mission SCLK in seconds. Fine time is 16bit unsigned sub-second
27
+ Coarse time is MET in seconds. Fine time is 16bit unsigned sub-second
28
28
  counter.
29
29
 
30
30
  Attributes
31
31
  ----------
32
32
  coarse_time : int
33
- Coarse time in seconds.
33
+ Coarse time in seconds (MET).
34
34
  fine_time : int
35
- Subsecond.
35
+ Subsecond counter, equal to fine_time/max_int16 seconds.
36
36
 
37
37
  Methods
38
38
  -------
39
39
  to_seconds()
40
+ to_j2000ns()
40
41
  """
41
42
 
42
43
  coarse_time: int
@@ -79,6 +80,19 @@ class TimeTuple:
79
80
  """
80
81
  return float(self.coarse_time + self.fine_time / MAX_FINE_TIME)
81
82
 
83
+ def to_j2000ns(self) -> np.int64:
84
+ """
85
+ Convert time tuple into J2000ns.
86
+
87
+ Returns
88
+ -------
89
+ j2000ns : numpy.int64
90
+ Time in nanoseconds since J2000 epoch.
91
+ """
92
+ coarse_j2000ns = np.int64(met_to_ttj2000ns(self.coarse_time))
93
+ fine_ns = np.int64(self.fine_time / MAX_FINE_TIME * 1e9)
94
+ return coarse_j2000ns + fine_ns
95
+
82
96
 
83
97
  @dataclass
84
98
  class MagL1aPacketProperties:
@@ -204,7 +218,7 @@ class MagL1a:
204
218
  Sequence number of the most recent packet added to the object
205
219
  missing_sequences : list[int]
206
220
  List of missing sequence numbers in the day
207
- start_time : numpy.datetime64
221
+ start_time : numpy.int64
208
222
  Start time of the day, in ns since J2000 epoch
209
223
  compression_flags : np.ndarray
210
224
  Array of flags to indication compression and width for all timestamps in the
@@ -233,10 +247,10 @@ class MagL1a:
233
247
  shcoarse: int
234
248
  vectors: np.ndarray
235
249
  starting_packet: InitVar[MagL1aPacketProperties]
236
- packet_definitions: dict[np.datetime64, MagL1aPacketProperties] = field(init=False)
250
+ packet_definitions: dict[np.int64, MagL1aPacketProperties] = field(init=False)
237
251
  most_recent_sequence: int = field(init=False)
238
252
  missing_sequences: list[int] = field(default_factory=list)
239
- start_time: np.datetime64 = field(init=False)
253
+ start_time: np.int64 = field(init=False)
240
254
  compression_flags: np.ndarray | None = field(init=False, default=None)
241
255
 
242
256
  def __post_init__(self, starting_packet: MagL1aPacketProperties) -> None:
@@ -248,10 +262,7 @@ class MagL1a:
248
262
  starting_packet : MagL1aPacketProperties
249
263
  The packet properties for the first packet in the day, including start time.
250
264
  """
251
- # TODO should this be from starting_packet
252
- self.start_time = (J2000_EPOCH + met_to_j2000ns(self.shcoarse)).astype(
253
- "datetime64[D]"
254
- )
265
+ self.start_time = np.int64(met_to_ttj2000ns(starting_packet.shcoarse))
255
266
  self.packet_definitions = {self.start_time: starting_packet}
256
267
  # most_recent_sequence is the sequence number of the packet used to initialize
257
268
  # the object
@@ -338,8 +349,8 @@ class MagL1a:
338
349
  cdf.utils.met_to_j2000ns.
339
350
  """
340
351
  timedelta = np.timedelta64(int(1 / vectors_per_sec * 1e9), "ns")
341
- # TODO: validate that start_time from SHCOARSE is precise enough
342
- start_time_ns = met_to_j2000ns(start_time.to_seconds())
352
+ # TODO: From finetime and coarsetime, depends per packet
353
+ start_time_ns = start_time.to_j2000ns()
343
354
 
344
355
  # Calculate time skips for each vector in ns
345
356
  times = np.reshape(
@@ -616,7 +627,7 @@ class MagL1a:
616
627
  (expected_range_data_length - end_padding) * has_range_data_section
617
628
  )
618
629
 
619
- # Cut off the first vector width and the end range data section if it exists.
630
+ # Cut off the first vector width and the range data section.
620
631
  vector_bits = bit_array[first_vector_width - 1 : end_vector]
621
632
 
622
633
  # Shift the bit array over one to the left, then sum them up. This is used to
@@ -731,7 +742,7 @@ class MagL1a:
731
742
  primary_count,
732
743
  uncompressed_vector_size,
733
744
  compression_width,
734
- )
745
+ )[0]
735
746
 
736
747
  # Secondary vector processing
737
748
  first_secondary_vector = MagL1a.unpack_one_vector(
@@ -747,8 +758,7 @@ class MagL1a:
747
758
  secondary_split_bits = np.split(
748
759
  vector_bits[: secondary_boundaries[-1]], secondary_boundaries[:-1]
749
760
  )[1:]
750
-
751
- secondary_vectors = MagL1a._process_vector_section(
761
+ secondary_process_vectors = MagL1a._process_vector_section(
752
762
  vector_bits,
753
763
  secondary_split_bits,
754
764
  secondary_boundaries[-1],
@@ -757,21 +767,28 @@ class MagL1a:
757
767
  uncompressed_vector_size,
758
768
  compression_width,
759
769
  )
770
+ secondary_vectors = secondary_process_vectors[0]
771
+
772
+ # The range data length has 2 bits per vector, minus 2 for the uncompressed
773
+ # first vectors in the primary and secondary sensors.
774
+ # Then, the range data length is padded to the nearest 8 bits.
775
+ end_vector = ((secondary_process_vectors[1] + first_vector_width) + 7) // 8 * 8
760
776
 
761
777
  # If there is a range data section, it describes all the data, compressed or
762
778
  # uncompressed.
763
779
  if has_range_data_section:
780
+ range_bits = bit_array[end_vector:]
764
781
  primary_vectors = MagL1a.process_range_data_section(
765
- bit_array[end_vector : end_vector + (primary_count - 1) * 2],
782
+ range_bits[: (primary_count - 1) * 2],
766
783
  primary_vectors,
767
784
  )
768
785
  secondary_vectors = MagL1a.process_range_data_section(
769
- bit_array[
770
- end_vector + (primary_count - 1) * 2 : end_vector
771
- + (primary_count + secondary_count - 2) * 2
786
+ range_bits[
787
+ (primary_count - 1) * 2 : (primary_count + secondary_count - 2) * 2
772
788
  ],
773
789
  secondary_vectors,
774
790
  )
791
+
775
792
  return primary_vectors, secondary_vectors
776
793
 
777
794
  @staticmethod
@@ -783,7 +800,7 @@ class MagL1a:
783
800
  vector_count: int,
784
801
  uncompressed_vector_size: int,
785
802
  compression_width: int,
786
- ) -> np.ndarray:
803
+ ) -> tuple[np.ndarray, int]:
787
804
  """
788
805
  Generate a section of vector data, primary or secondary.
789
806
 
@@ -809,20 +826,33 @@ class MagL1a:
809
826
 
810
827
  Returns
811
828
  -------
812
- numpy.ndarray
813
- An array of processed vectors.
829
+ (numpy.ndarray, int)
830
+ A tuple consisting of: an array of processed vectors, and the index of the
831
+ end of the last vector. In a fully compressed case, this will be the same
832
+ as last_index.
814
833
  """
834
+ compressed_count = math.ceil(len(split_bits) / AXIS_COUNT) + 1
835
+ uncompressed_count = vector_count - compressed_count
836
+
837
+ # will have either 0 or 8 extra bits
838
+ # If we have more splits than vectors, we have included part of the range
839
+ # data section. Drop those values and update end_vector.
840
+ if len(split_bits) > (vector_count - 1) * AXIS_COUNT:
841
+ split_bits = split_bits[: (vector_count - 1) * AXIS_COUNT]
842
+ last_index -= 8
843
+
844
+ end = last_index + uncompressed_vector_size * uncompressed_count
845
+
815
846
  vector_diffs = list(map(MagL1a.decode_fib_zig_zag, split_bits))
847
+ # if we got more than the expected length, we may have gotten some of the range
848
+ # data section.
849
+
816
850
  vectors = MagL1a.convert_diffs_to_vectors(
817
851
  first_vector, vector_diffs, vector_count
818
852
  )
819
853
  # If we are missing any vectors from primary_split_bits, we know we have
820
854
  # uncompressed vectors to process.
821
- compressed_count = math.ceil(len(split_bits) / AXIS_COUNT) + 1
822
- uncompressed_count = vector_count - compressed_count
823
-
824
855
  if uncompressed_count:
825
- end = last_index + uncompressed_vector_size * uncompressed_count
826
856
  uncompressed_vectors = vector_bits[last_index : end + 1]
827
857
 
828
858
  for i in range(uncompressed_count):
@@ -836,8 +866,11 @@ class MagL1a:
836
866
  )
837
867
  vectors[i + compressed_count] = decoded_vector
838
868
  vectors[i + compressed_count][3] = vectors[0][3]
869
+ # If there is an extra byte, this is from the range data section.
870
+ if len(vector_bits[last_index:]) - end == 8:
871
+ end -= 8
839
872
 
840
- return vectors
873
+ return (vectors, end)
841
874
 
842
875
  @staticmethod
843
876
  def process_range_data_section(
@@ -870,7 +903,6 @@ class MagL1a:
870
903
  "Incorrect length for range_data, there should be two bits per vector, "
871
904
  "excluding the first."
872
905
  )
873
-
874
906
  updated_vectors: np.ndarray = np.copy(vectors)
875
907
  range_str = "".join([str(i) for i in range_data])
876
908
  for i in range(len(vectors) - 1):
@@ -4,12 +4,13 @@ from pathlib import Path
4
4
 
5
5
  import numpy as np
6
6
  import xarray as xr
7
+ from xarray import Dataset
7
8
 
8
9
  from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
9
10
  from imap_processing.cdf.utils import load_cdf
10
11
 
11
12
 
12
- def mag_l1b(input_dataset: xr.Dataset, version: str) -> xr.Dataset:
13
+ def mag_l1b(input_dataset: xr.Dataset, version: str) -> Dataset:
13
14
  """
14
15
  Will process MAG L1B data from L1A data.
15
16
 
@@ -28,6 +29,9 @@ def mag_l1b(input_dataset: xr.Dataset, version: str) -> xr.Dataset:
28
29
  # TODO:
29
30
  # Read in calibration file
30
31
  # multiply all vectors by calibration file
32
+ if "raw" in input_dataset.attrs["Logical_source"]:
33
+ # Raw files should not be processed in L1B.
34
+ raise ValueError("Raw L1A file passed into L1B. Unable to process.")
31
35
 
32
36
  output_dataset = mag_l1b_processing(input_dataset)
33
37
  attribute_manager = ImapCdfAttributes()
@@ -64,6 +68,7 @@ def mag_l1b_processing(input_dataset: xr.Dataset) -> xr.Dataset:
64
68
  """
65
69
  # TODO: There is a time alignment step that will add a lot of complexity.
66
70
  # This needs to be done once we have some SPICE time data.
71
+
67
72
  mag_attributes = ImapCdfAttributes()
68
73
  mag_attributes.add_instrument_variable_attrs("mag", "l1")
69
74
 
@@ -73,6 +78,7 @@ def mag_l1b_processing(input_dataset: xr.Dataset) -> xr.Dataset:
73
78
  calibration_dataset = load_cdf(
74
79
  Path(__file__).parent / "imap_calibration_mag_20240229_v01.cdf"
75
80
  )
81
+ # TODO: add time shift
76
82
  # TODO: Check validity of time range for calibration
77
83
  if "mago" in input_dataset.attrs["Logical_source"][0]:
78
84
  calibration_matrix = calibration_dataset["MFOTOURFO"]
@@ -137,8 +143,9 @@ def update_vector(
137
143
  tuple[numpy.ndarray, numpy.ndarray]
138
144
  Updated vector and the same compression flags.
139
145
  """
140
- vector = calibrate_vector(input_vector, calibration_matrix)
141
- return rescale_vector(vector, input_compression), input_compression
146
+ vector = rescale_vector(input_vector, input_compression)
147
+ cal_vector = calibrate_vector(vector, calibration_matrix)
148
+ return cal_vector, input_compression
142
149
 
143
150
 
144
151
  def rescale_vector(
@@ -200,9 +207,7 @@ def calibrate_vector(
200
207
  Calibrated vector.
201
208
  """
202
209
  updated_vector = input_vector.copy()
203
-
204
- updated_vector[:3] = np.matmul(
210
+ updated_vector[:3] = np.dot(
205
211
  calibration_matrix.values[:, :, int(input_vector[3])], input_vector[:3]
206
212
  )
207
-
208
213
  return updated_vector
@@ -37,14 +37,29 @@ class ENAFlags(FlagNameMixin):
37
37
  BADSPIN = 2**2 # bit 2, Bad spin
38
38
 
39
39
 
40
- class ImapUltraFlags(FlagNameMixin):
40
+ class ImapHkUltraFlags(FlagNameMixin):
41
41
  """IMAP Ultra flags."""
42
42
 
43
43
  NONE = CommonFlags.NONE
44
44
  INF = CommonFlags.INF # bit 0
45
45
  NEG = CommonFlags.NEG # bit 1
46
46
  BADSPIN = ENAFlags.BADSPIN # bit 2
47
- FLAG1 = 2**3 # bit 2
47
+ FLAG1 = 2**3 # bit 3
48
+
49
+
50
+ class ImapAttitudeUltraFlags(FlagNameMixin):
51
+ """IMAP Ultra flags."""
52
+
53
+ NONE = CommonFlags.NONE
54
+ SPINRATE = 2**0 # bit 0
55
+
56
+
57
+ class ImapRatesUltraFlags(FlagNameMixin):
58
+ """IMAP Ultra Rates flags."""
59
+
60
+ NONE = CommonFlags.NONE
61
+ ZEROCOUNTS = 2**0 # bit 0
62
+ HIGHRATES = 2**1 # bit 1
48
63
 
49
64
 
50
65
  class ImapLoFlags(FlagNameMixin):
@@ -54,7 +69,7 @@ class ImapLoFlags(FlagNameMixin):
54
69
  INF = CommonFlags.INF # bit 0
55
70
  NEG = CommonFlags.NEG # bit 1
56
71
  BADSPIN = ENAFlags.BADSPIN # bit 2
57
- FLAG2 = 2**3 # bit 2
72
+ FLAG2 = 2**3 # bit 3
58
73
 
59
74
 
60
75
  class HitFlags(