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
@@ -0,0 +1,324 @@
1
+ """Module for GLOWS Level 2 processing."""
2
+
3
+ import dataclasses
4
+
5
+ import numpy as np
6
+ import xarray as xr
7
+ from numpy.typing import NDArray
8
+
9
+ from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
10
+ from imap_processing.glows import FLAG_LENGTH
11
+ from imap_processing.glows.l1b.glows_l1b_data import HistogramL1B
12
+ from imap_processing.glows.l2.glows_l2_data import DailyLightcurve, HistogramL2
13
+
14
+
15
+ def glows_l2(input_dataset: xr.Dataset, data_version: str) -> list[xr.Dataset]:
16
+ """
17
+ Will process GLoWS L2 data from L1 data.
18
+
19
+ Parameters
20
+ ----------
21
+ input_dataset : xarray.Dataset
22
+ Input L1B dataset.
23
+ data_version : str
24
+ Version for output.
25
+
26
+ Returns
27
+ -------
28
+ xarray.Dataset
29
+ Glows L2 Dataset.
30
+ """
31
+ cdf_attrs = ImapCdfAttributes()
32
+ cdf_attrs.add_instrument_global_attrs("glows")
33
+ cdf_attrs.add_instrument_variable_attrs("glows", "l2")
34
+ cdf_attrs.add_global_attribute("Data_version", data_version)
35
+
36
+ split_data = split_data_by_observational_day(input_dataset)
37
+ l2_output = []
38
+ for data in split_data:
39
+ l2 = generate_l2(data)
40
+ l2_output.append(create_l2_dataset(l2, cdf_attrs))
41
+
42
+ return l2_output
43
+
44
+
45
+ # TODO: filter good times out
46
+ def generate_l2(l1b_dataset: xr.Dataset) -> HistogramL2:
47
+ """
48
+ Generate L2 data from L1B data.
49
+
50
+ Returns L2 data in the form of a HistogramL2 dataclass.
51
+
52
+ Parameters
53
+ ----------
54
+ l1b_dataset : xarray.Dataset
55
+ Input L1B dataset.
56
+
57
+ Returns
58
+ -------
59
+ HistogramL2
60
+ L2 data in the form of a HistogramL2 dataclass.
61
+ """
62
+ # most of the values from L1B are averaged over a day
63
+ good_data = l1b_dataset.isel(
64
+ epoch=return_good_times(l1b_dataset["flags"], np.ones((FLAG_LENGTH,)))
65
+ )
66
+ # todo: bad angle filter
67
+ # TODO filter bad bins out. Needs to happen here while everything is still
68
+ # per-timestamp.
69
+
70
+ # one dataset collects multiple epoch values which need to be averaged down into
71
+ # one value.
72
+ all_variables = dataclasses.fields(HistogramL1B)
73
+
74
+ daily_lightcurve = DailyLightcurve(good_data)
75
+
76
+ var_outputs = {
77
+ "total_l1b_inputs": len(good_data["epoch"]),
78
+ "number_of_good_l1b_inputs": len(good_data["epoch"]),
79
+ # TODO replace post-filter
80
+ "identifier": 100, # TODO: retrieve from spin table
81
+ # TODO fill this in
82
+ "bad_time_flag_occurrences": np.zeros((1, FLAG_LENGTH)),
83
+ # Accumulate all the histograms from good times from the day into one
84
+ "daily_lightcurve": daily_lightcurve,
85
+ }
86
+
87
+ if len(good_data["epoch"]) != 0:
88
+ # Generate outputs that are passed in directly from L1B
89
+ var_outputs["start_time"] = good_data["epoch"].data[0]
90
+ var_outputs["end_time"] = good_data["epoch"].data[-1]
91
+
92
+ else:
93
+ # No good times in the file
94
+ var_outputs["start_time"] = l1b_dataset["imap_start_time"].data[0]
95
+ var_outputs["end_time"] = (
96
+ l1b_dataset["imap_start_time"].data[0]
97
+ + l1b_dataset["imap_time_offset"].data[0]
98
+ )
99
+
100
+ for field in all_variables:
101
+ var_name = field.name
102
+ if "average" in var_name:
103
+ # This results in a scalar value, so `keepdims=True` ensures we keep the
104
+ # epoch dimension.
105
+ var_outputs[var_name] = (
106
+ l1b_dataset[var_name].mean(dim="epoch", keepdims=True).data
107
+ )
108
+
109
+ var_outputs[var_name.replace("average", "std_dev")] = (
110
+ l1b_dataset[var_name].std(dim="epoch", keepdims=True).data
111
+ )
112
+
113
+ # l1b stuff is done
114
+ output = HistogramL2(**var_outputs)
115
+
116
+ return output
117
+
118
+
119
+ def filter_bad_bins(histograms: NDArray, bin_exclusions: NDArray) -> NDArray:
120
+ """
121
+ Filter out bad bins from the histogram.
122
+
123
+ Parameters
124
+ ----------
125
+ histograms : numpy.ndarray
126
+ Histogram data, with shape (n_timestamps, n_bins).
127
+ bin_exclusions : numpy.ndarray
128
+ Array of bin exclusions. This 2d array has a timestamp and bin filter array
129
+ pair. The bin filter array indicates "1" if a bin is to be excluded.
130
+
131
+ Returns
132
+ -------
133
+ numpy.ndarray
134
+ Histogram data with bad bins marked with -1.
135
+ """
136
+ # TODO: will need ancillary file imap_glows_exclusions_by_instr_team
137
+ # TODO: complete once unique_block_identifier is implemented
138
+ # file contains timestamp & bin filter array pairs. For the timestamp, the
139
+ # filter should be applied such that 1 excludes the bin.
140
+
141
+ # excluded bins can be marked with -1
142
+ return histograms
143
+
144
+
145
+ def split_data_by_observational_day(input_dataset: xr.Dataset) -> list[xr.Dataset]:
146
+ """
147
+ Return L1B data array for an observational day, given start and stop times.
148
+
149
+ Parameters
150
+ ----------
151
+ input_dataset : xarray.Dataset
152
+ Input L1B dataset.
153
+
154
+ Returns
155
+ -------
156
+ list : xarray.Dataset
157
+ List of L1B datasets, each representing a day of data.
158
+ """
159
+ # TODO: replace this with a query to the spin table to get the observational days
160
+
161
+ # Find the range of epoch values within the observational day.
162
+ # This should be replaced with a query to the spin table to get the observational
163
+ # days within the time range of the file and when those observational days
164
+ # start and stop
165
+
166
+ # Note: slice is inclusive on both sides.
167
+ # data_by_day = [
168
+ # input_dataset.sel(epoch=slice(day_ends[i], day_ends[i + 1]))
169
+ # for i in range(len(day_ends) - 1)
170
+ # ]
171
+ return [input_dataset]
172
+
173
+
174
+ def create_l2_dataset(
175
+ histogram_l2: HistogramL2, attrs: ImapCdfAttributes
176
+ ) -> xr.Dataset:
177
+ """
178
+ Create a xarray dataset from a HistogramL2 dataclass.
179
+
180
+ This dataset should include all the CDF attributes.
181
+
182
+ Parameters
183
+ ----------
184
+ histogram_l2 : HistogramL2
185
+ L2 data.
186
+ attrs : ImapCdfAttributes
187
+ CDF attributes for GLOWS L2.
188
+
189
+ Returns
190
+ -------
191
+ xarray.Dataset
192
+ L2 dataset for output to CDF file.
193
+ """
194
+ # Each L2 file only has one timestamp.
195
+ # TODO: If we want this to point to the start time, we need to set the attribute
196
+ # variable BIN_LOCATION to 0. Otherwise, we need this to be halfway between start
197
+ # time and end time.
198
+ time_data = np.array([histogram_l2.start_time], dtype=np.float64)
199
+ # TODO: Create CDF attributes
200
+ epoch_time = xr.DataArray(
201
+ time_data,
202
+ name="epoch",
203
+ dims=["epoch"],
204
+ attrs=attrs.get_variable_attributes("epoch", check_schema=False),
205
+ )
206
+
207
+ bins = xr.DataArray(
208
+ np.arange(histogram_l2.daily_lightcurve.number_of_bins),
209
+ name="bins",
210
+ dims=["bins"],
211
+ attrs=attrs.get_variable_attributes("bins_dim", check_schema=False),
212
+ )
213
+
214
+ bins_label = xr.DataArray(
215
+ -1,
216
+ name="bins_label",
217
+ attrs=attrs.get_variable_attributes("bins_label", check_schema=False),
218
+ )
219
+
220
+ flags = xr.DataArray(
221
+ np.ones(FLAG_LENGTH),
222
+ dims=["flags"],
223
+ attrs=attrs.get_variable_attributes("flags_dim", check_schema=False),
224
+ )
225
+
226
+ flags_label = xr.DataArray(
227
+ -1,
228
+ name="flags_label",
229
+ attrs=attrs.get_variable_attributes("flags_label", check_schema=False),
230
+ )
231
+
232
+ eclipic_data = xr.DataArray(
233
+ np.arange(3),
234
+ name="ecliptic",
235
+ dims=["ecliptic"],
236
+ attrs=attrs.get_variable_attributes("ecliptic_dim", check_schema=False),
237
+ )
238
+
239
+ output = xr.Dataset(
240
+ data_vars={"bins_label": bins_label, "flags_label": flags_label},
241
+ coords={
242
+ "epoch": epoch_time,
243
+ "bins": bins,
244
+ "flags": flags,
245
+ "ecliptic": eclipic_data,
246
+ },
247
+ attrs=attrs.get_global_attributes("imap_glows_l2_hist"),
248
+ )
249
+
250
+ ecliptic_variables = [
251
+ "spacecraft_location_average",
252
+ "spacecraft_location_std_dev",
253
+ "spacecraft_velocity_average",
254
+ "spacecraft_velocity_std_dev",
255
+ ]
256
+
257
+ for key, value in dataclasses.asdict(histogram_l2).items():
258
+ if key in ecliptic_variables:
259
+ output[key] = xr.DataArray(
260
+ value,
261
+ dims=["epoch", "ecliptic"],
262
+ attrs=attrs.get_variable_attributes(key),
263
+ )
264
+ elif key == "bad_time_flag_occurrences":
265
+ output[key] = xr.DataArray(
266
+ value,
267
+ dims=["epoch", "flags"],
268
+ attrs=attrs.get_variable_attributes(key),
269
+ )
270
+
271
+ elif key != "daily_lightcurve":
272
+ val = value
273
+ if type(value) != np.ndarray:
274
+ val = np.array([value])
275
+ output[key] = xr.DataArray(
276
+ val,
277
+ dims=["epoch"],
278
+ attrs=attrs.get_variable_attributes(key),
279
+ )
280
+
281
+ for key, value in dataclasses.asdict(histogram_l2.daily_lightcurve).items():
282
+ if key == "number_of_bins":
283
+ # number_of_bins does not have n_bins dimensions.
284
+ output[key] = xr.DataArray(
285
+ np.array([value]),
286
+ dims=["epoch"],
287
+ attrs=attrs.get_variable_attributes(key),
288
+ )
289
+ else:
290
+ output[key] = xr.DataArray(
291
+ np.array([value]),
292
+ dims=["epoch", "bins"],
293
+ attrs=attrs.get_variable_attributes(key),
294
+ )
295
+
296
+ return output
297
+
298
+
299
+ def return_good_times(flags: xr.DataArray, active_flags: NDArray) -> NDArray:
300
+ """
301
+ Return the good times based on the input flags.
302
+
303
+ Parameters
304
+ ----------
305
+ flags : xarray.DataArray
306
+ Flags dataset with shape (n_timestamps, n_flags). If a flag is active and set
307
+ to 1, the timestamp is considered good.
308
+
309
+ active_flags : numpy.ndarray
310
+ Array of active flags. If the flag is set to 1, it is considered active.
311
+
312
+ Returns
313
+ -------
314
+ numpy.ndarray
315
+ An array of indices for good times.
316
+ """
317
+ if len(active_flags) != flags.shape[1]:
318
+ print("Active flags don't matched expected length")
319
+
320
+ # A good time is where all the active flags are equal to one.
321
+ # Here, we mask the active indices using active_flags, and then return the times
322
+ # where all the active indices == 1.
323
+ good_times = np.where(np.all(flags[:, active_flags == 1] == 1, axis=1))[0]
324
+ return good_times
@@ -3,6 +3,139 @@
3
3
  from dataclasses import InitVar, dataclass, field
4
4
 
5
5
  import numpy as np
6
+ import xarray as xr
7
+ from numpy.typing import NDArray
8
+
9
+
10
+ @dataclass
11
+ class DailyLightcurve:
12
+ """
13
+ Dataclass describing the daily lightcurve.
14
+
15
+ Used inside the HistogramL2 class.
16
+
17
+ Attributes
18
+ ----------
19
+ spin_angle : numpy.ndarray
20
+ values of spin angle [deg] for bin centers (measured from the north)
21
+ photon_flux : numpy.ndarray
22
+ observational-day-averaged photon flux [Rayleigh]
23
+ raw_histograms : numpy.ndarray
24
+ sum of histograms across all timestamps
25
+ exposure_times : numpy.ndarray
26
+ exposure times for bins [s]
27
+ flux_uncertainties : numpy.ndarray
28
+ statistical uncertainties for photon flux [Rayleigh]
29
+ histogram_flag_array : numpy.ndarray
30
+ flags for bins
31
+ ecliptic_lon : numpy.ndarray
32
+ ecliptic longitude of bin centers [deg]
33
+ ecliptic_lat : numpy.ndarray
34
+ ecliptic latitude of bin centers [deg]
35
+ number_of_bins : int
36
+ number of bins in lightcurve
37
+ raw_uncertainties : numpy.ndarray
38
+ statistical uncertainties for raw histograms (sqrt of self.raw_histograms)
39
+ l1b_data : xarray.Dataset
40
+ L1B data filtered by good times, good angles, and good bins.
41
+ """
42
+
43
+ # All variables should have n_bin elements
44
+ spin_angle: np.ndarray = field(init=False)
45
+ photon_flux: np.ndarray = field(init=False)
46
+ raw_histograms: np.ndarray = field(init=False)
47
+ exposure_times: np.ndarray = field(init=False)
48
+ flux_uncertainties: np.ndarray = field(init=False)
49
+ # TODO: flag array
50
+ histogram_flag_array: np.ndarray = field(init=False)
51
+ # TODO: ecliptic coordinates
52
+ ecliptic_lon: np.ndarray = field(init=False)
53
+ ecliptic_lat: np.ndarray = field(init=False)
54
+ number_of_bins: int = field(init=False)
55
+ raw_uncertainties: np.ndarray = field(init=False)
56
+ l1b_data: InitVar[xr.Dataset]
57
+
58
+ def __post_init__(self, l1b_data: xr.Dataset) -> None:
59
+ """
60
+ Compute all the daily lightcurve variables from L1B data.
61
+
62
+ Parameters
63
+ ----------
64
+ l1b_data : xarray.Dataset
65
+ L1B data filtered by good times, good angles, and good bins for one
66
+ observation day.
67
+ """
68
+ self.raw_histograms = self.calculate_histogram_sums(l1b_data["histogram"].data)
69
+
70
+ exposure_times_per_timestamp = (
71
+ l1b_data["spin_period_average"]
72
+ * l1b_data["number_of_spins_per_block"]
73
+ / 3600
74
+ )
75
+
76
+ self.exposure_times = self.calculate_exposure_times(
77
+ l1b_data, exposure_times_per_timestamp
78
+ )
79
+ self.raw_uncertainties = np.sqrt(self.raw_histograms)
80
+ self.photon_flux = np.zeros(len(self.raw_histograms))
81
+ self.flux_uncertainties = np.zeros(len(self.raw_histograms))
82
+
83
+ # TODO: Only where exposure counts != 0
84
+ if len(self.exposure_times) != 0:
85
+ self.photon_flux = self.raw_histograms / self.exposure_times
86
+ self.flux_uncertainties = self.raw_uncertainties / self.exposure_times
87
+
88
+ # TODO: Average this, or should they all be the same?
89
+ self.spin_angle = np.average(l1b_data["imap_spin_angle_bin_cntr"].data, axis=0)
90
+
91
+ # TODO: is the first number here ok? Would it change mid-obs day?
92
+ self.number_of_bins = len(self.spin_angle)
93
+
94
+ self.histogram_flag_array = np.zeros(self.number_of_bins)
95
+ self.ecliptic_lon = np.zeros(self.number_of_bins)
96
+ self.ecliptic_lat = np.zeros(self.number_of_bins)
97
+
98
+ @staticmethod
99
+ def calculate_exposure_times(
100
+ good_times: xr.Dataset, exposure_count: xr.DataArray
101
+ ) -> NDArray:
102
+ """
103
+ Calculate exposure times for each bin across all the timestamps.
104
+
105
+ Parameters
106
+ ----------
107
+ good_times : xarray.Dataset
108
+ Dataset with only good times.
109
+ exposure_count : float
110
+ Exposure count for each valid timestamp and bin.
111
+
112
+ Returns
113
+ -------
114
+ numpy.ndarray
115
+ Array of summed exposure times for each bin.
116
+ """
117
+ weighted_sum = (good_times["histogram"].data != -1) * exposure_count.data[
118
+ :, np.newaxis
119
+ ]
120
+ return np.sum(weighted_sum, axis=0)
121
+
122
+ @staticmethod
123
+ def calculate_histogram_sums(histograms: NDArray) -> NDArray:
124
+ """
125
+ Calculate the sum of histograms across all timestamps.
126
+
127
+ Parameters
128
+ ----------
129
+ histograms : numpy.ndarray
130
+ Array of histograms across all timestamps.
131
+
132
+ Returns
133
+ -------
134
+ numpy.ndarray
135
+ Sum of valid histograms across all timestamps.
136
+ """
137
+ histograms[histograms == -1] = 0
138
+ return np.sum(histograms, axis=0)
6
139
 
7
140
 
8
141
  @dataclass
@@ -12,68 +145,68 @@ class HistogramL2:
12
145
 
13
146
  This class collects multiple HistogramL1B classes into one L2 per observational day.
14
147
 
15
- flight_software_version : str
148
+ Attributes
149
+ ----------
16
150
  number_of_good_l1b_inputs : int
17
- number of good-time Level-1B times used for generation of Level-2 data
151
+ number of good-time Level-1B times used for generation of Level-2 data.
18
152
  total_l1b_inputs : int
19
- number of all Level-1B times for observational day
153
+ number of all Level-1B times for observational day.
20
154
  identifier : int
21
155
  unique Level-2 histogram identifier
22
156
  start_time : numpy.double
23
- UTC start time of a given observational day
157
+ J2000 start time of a given observational day
24
158
  end_time : numpy.double
25
- UTC end time of a given observational day
159
+ J2000 end time of a given observational day
26
160
  daily_lightcurve : numpy.ndarray
27
161
  arrays for observational-day-accumulated lightcurve
28
162
  filter_temperature_average : numpy.ndarray
29
163
  observational-day-averaged filter temperature [Celsius deg]
30
- filter_temperature_variance : numpy.ndarray
164
+ filter_temperature_std_dev : numpy.ndarray
31
165
  standard deviation for filter temperature [Celsius deg]
32
166
  hv_voltage_average : numpy.ndarray
33
167
  observational-day-averaged channeltron voltage [volt]
34
- hv_voltage_variance : numpy.ndarray
168
+ hv_voltage_std_dev : numpy.ndarray
35
169
  standard deviation for channeltron voltage [volt]
36
170
  spin_period_average : numpy.ndarray
37
171
  observational-day-averaged spin period [s] (onboard value)
38
- spin_period_variance : numpy.ndarray
172
+ spin_period_std_dev : numpy.ndarray
39
173
  a standard deviation for spin period [s]
40
174
  pulse_length_average : numpy.ndarray
41
175
  observational-day-averaged pulse length [μs]
42
- pulse_length_variance : numpy.ndarray
176
+ pulse_length_std_dev : numpy.ndarray
43
177
  standard deviation for pulse length [μs]
44
178
  spin_period_ground_average : numpy.ndarray
45
179
  observational-day-averaged spin period [s] (ground value)
46
- spin_period_ground_variance : numpy.ndarray
180
+ spin_period_ground_std_dev : numpy.ndarray
47
181
  a standard deviation for spin period [s]
48
182
  position_angle_offset_average : numpy.ndarray
49
183
  observational-day-averaged GLOWS angular offset [deg]
50
- position_angle_offset_variance : numpy.ndarray
184
+ position_angle_offset_std_dev : numpy.ndarray
51
185
  standard deviation for GLOWS angular offset [seg]
52
- spin_axis_orientation_variance : numpy.ndarray
186
+ spin_axis_orientation_std_dev : numpy.ndarray
53
187
  standard deviation for spin-axis longitude and latitude [deg]
54
- spacecraft_location_variance : numpy.ndarray
55
- standard deviation for ecliptic coordinates [km] of IMAP
56
- spacecraft_velocity_variance : numpy.ndarray
57
- standard deviation for IMAP velocity components [km/s]
58
- spin_axis_orientation_average : numpy.ndarray
59
- observational-day-averaged spin-axis ecliptic longitude and latitude [deg]
60
188
  spacecraft_location_average : numpy.ndarray
61
189
  observational-day-averaged Cartesian ecliptic coordinates ⟨X⟩, ⟨Y ⟩, ⟨Z⟩ [km]
62
190
  of IMAP
191
+ spacecraft_location_std_dev : numpy.ndarray
192
+ standard deviation for ecliptic coordinates [km] of IMAP
63
193
  spacecraft_velocity_average : numpy.ndarray
64
194
  observational-day-averaged values ⟨VX ⟩, ⟨VY ⟩, ⟨VZ ⟩ of IMAP velocity
65
195
  components [km/s] (Cartesian ecliptic frame)
196
+ spacecraft_velocity_std_dev : numpy.ndarray
197
+ standard deviation for IMAP velocity components [km/s]
198
+ spin_axis_orientation_average : numpy.ndarray
199
+ observational-day-averaged spin-axis ecliptic longitude and latitude [deg]
66
200
  bad_time_flag_occurrences : numpy.ndarray
67
201
  numbers of occurrences of blocks for each bad-time flag during observational day
68
202
  """
69
203
 
70
- flight_software_version: str
71
204
  number_of_good_l1b_inputs: int
72
205
  total_l1b_inputs: int
73
- # identifier: int # comes from unique_block_identifier
206
+ identifier: int # TODO: Should be the official pointing number
74
207
  start_time: np.double
75
208
  end_time: np.double
76
- daily_lightcurve: np.ndarray = field(init=False)
209
+ daily_lightcurve: DailyLightcurve
77
210
  filter_temperature_average: np.ndarray[np.double]
78
211
  filter_temperature_std_dev: np.ndarray[np.double]
79
212
  hv_voltage_average: np.ndarray[np.double]
@@ -87,37 +220,9 @@ class HistogramL2:
87
220
  position_angle_offset_average: np.ndarray[np.double]
88
221
  position_angle_offset_std_dev: np.ndarray[np.double]
89
222
  spin_axis_orientation_std_dev: np.ndarray[np.double]
223
+ spacecraft_location_average: np.ndarray[np.double]
90
224
  spacecraft_location_std_dev: np.ndarray[np.double]
225
+ spacecraft_velocity_average: np.ndarray[np.double]
91
226
  spacecraft_velocity_std_dev: np.ndarray[np.double]
92
227
  spin_axis_orientation_average: np.ndarray[np.double]
93
- spacecraft_location_average: np.ndarray[np.double]
94
- spacecraft_velocity_average: np.ndarray[np.double]
95
228
  bad_time_flag_occurrences: np.ndarray
96
- histogram: InitVar[np.ndarray]
97
-
98
- def __post_init__(self, histogram: np.ndarray) -> None:
99
- """
100
- Post-initialization method to generate the daily light curve from one histogram.
101
-
102
- Parameters
103
- ----------
104
- histogram : numpy.ndarray
105
- Histogram data from L1B, of shape (bins,) where bins is nominally 3600.
106
- """
107
- self.daily_lightcurve = self.generate_lightcurve(histogram)
108
-
109
- def generate_lightcurve(self, histogram: np.ndarray) -> np.ndarray:
110
- """
111
- Given an array of (n, bins) histograms, generate one lightcurve of size (bins).
112
-
113
- Parameters
114
- ----------
115
- histogram : numpy.ndarray
116
- Histogram data from L1B, of shape (bins,) where bins is nominally 3600.
117
-
118
- Returns
119
- -------
120
- numpy.ndarray
121
- Lightcurve of size (bins).
122
- """
123
- return np.zeros(3600) # type: ignore[no-any-return]