imap-processing 0.7.0__py3-none-any.whl → 0.9.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 (172) hide show
  1. imap_processing/__init__.py +1 -1
  2. imap_processing/_version.py +2 -2
  3. imap_processing/ccsds/excel_to_xtce.py +36 -2
  4. imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +1 -1
  5. imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +145 -30
  6. imap_processing/cdf/config/imap_glows_l1b_variable_attrs.yaml +36 -36
  7. imap_processing/cdf/config/imap_hi_variable_attrs.yaml +136 -9
  8. imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +14 -0
  9. imap_processing/cdf/config/imap_hit_l1a_variable_attrs.yaml +63 -1
  10. imap_processing/cdf/config/imap_hit_l1b_variable_attrs.yaml +9 -0
  11. imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +14 -7
  12. imap_processing/cdf/config/imap_idex_l1a_variable_attrs.yaml +577 -235
  13. imap_processing/cdf/config/imap_idex_l1b_variable_attrs.yaml +326 -0
  14. imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +33 -23
  15. imap_processing/cdf/config/imap_mag_l1_variable_attrs.yaml +24 -28
  16. imap_processing/cdf/config/imap_ultra_l1a_variable_attrs.yaml +1 -0
  17. imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +137 -79
  18. imap_processing/cdf/config/imap_variable_schema.yaml +13 -0
  19. imap_processing/cdf/imap_cdf_manager.py +31 -27
  20. imap_processing/cdf/utils.py +3 -5
  21. imap_processing/cli.py +25 -14
  22. imap_processing/codice/codice_l1a.py +153 -63
  23. imap_processing/codice/constants.py +10 -10
  24. imap_processing/codice/decompress.py +10 -11
  25. imap_processing/codice/utils.py +1 -0
  26. imap_processing/glows/l1a/glows_l1a.py +1 -2
  27. imap_processing/glows/l1b/glows_l1b.py +3 -3
  28. imap_processing/glows/l1b/glows_l1b_data.py +59 -37
  29. imap_processing/glows/l2/glows_l2_data.py +123 -0
  30. imap_processing/hi/l1a/hi_l1a.py +4 -4
  31. imap_processing/hi/l1a/histogram.py +107 -109
  32. imap_processing/hi/l1a/science_direct_event.py +92 -225
  33. imap_processing/hi/l1b/hi_l1b.py +85 -11
  34. imap_processing/hi/l1c/hi_l1c.py +23 -1
  35. imap_processing/hi/packet_definitions/TLM_HI_COMBINED_SCI.xml +3994 -0
  36. imap_processing/hi/utils.py +1 -1
  37. imap_processing/hit/hit_utils.py +221 -0
  38. imap_processing/hit/l0/constants.py +118 -0
  39. imap_processing/hit/l0/decom_hit.py +100 -156
  40. imap_processing/hit/l1a/hit_l1a.py +170 -184
  41. imap_processing/hit/l1b/hit_l1b.py +33 -153
  42. imap_processing/ialirt/l0/process_codicelo.py +153 -0
  43. imap_processing/ialirt/l0/process_hit.py +5 -5
  44. imap_processing/ialirt/packet_definitions/ialirt_codicelo.xml +281 -0
  45. imap_processing/ialirt/process_ephemeris.py +212 -0
  46. imap_processing/idex/idex_l1a.py +65 -84
  47. imap_processing/idex/idex_l1b.py +192 -0
  48. imap_processing/idex/idex_variable_unpacking_and_eu_conversion.csv +33 -0
  49. imap_processing/idex/packet_definitions/idex_packet_definition.xml +97 -595
  50. imap_processing/lo/l0/decompression_tables/decompression_tables.py +17 -1
  51. imap_processing/lo/l0/lo_science.py +45 -13
  52. imap_processing/lo/l1a/lo_l1a.py +76 -8
  53. imap_processing/lo/packet_definitions/lo_xtce.xml +8344 -1849
  54. imap_processing/mag/l0/decom_mag.py +4 -3
  55. imap_processing/mag/l1a/mag_l1a.py +12 -13
  56. imap_processing/mag/l1a/mag_l1a_data.py +1 -2
  57. imap_processing/mag/l1b/mag_l1b.py +90 -7
  58. imap_processing/spice/geometry.py +156 -16
  59. imap_processing/spice/time.py +144 -2
  60. imap_processing/swapi/l1/swapi_l1.py +4 -4
  61. imap_processing/swapi/l2/swapi_l2.py +1 -1
  62. imap_processing/swapi/packet_definitions/swapi_packet_definition.xml +1535 -446
  63. imap_processing/swe/l1b/swe_l1b_science.py +8 -8
  64. imap_processing/swe/l2/swe_l2.py +134 -17
  65. imap_processing/tests/ccsds/test_data/expected_output.xml +2 -1
  66. imap_processing/tests/ccsds/test_excel_to_xtce.py +4 -4
  67. imap_processing/tests/cdf/test_imap_cdf_manager.py +0 -10
  68. imap_processing/tests/codice/conftest.py +1 -17
  69. imap_processing/tests/codice/data/imap_codice_l0_raw_20241110_v001.pkts +0 -0
  70. imap_processing/tests/codice/test_codice_l0.py +8 -2
  71. imap_processing/tests/codice/test_codice_l1a.py +127 -107
  72. imap_processing/tests/codice/test_codice_l1b.py +1 -0
  73. imap_processing/tests/codice/test_decompress.py +7 -7
  74. imap_processing/tests/conftest.py +100 -58
  75. imap_processing/tests/glows/conftest.py +6 -0
  76. imap_processing/tests/glows/test_glows_l1b.py +9 -9
  77. imap_processing/tests/glows/test_glows_l1b_data.py +9 -9
  78. imap_processing/tests/hi/test_data/l0/H90_NHK_20241104.bin +0 -0
  79. imap_processing/tests/hi/test_data/l0/H90_sci_cnt_20241104.bin +0 -0
  80. imap_processing/tests/hi/test_data/l0/H90_sci_de_20241104.bin +0 -0
  81. imap_processing/tests/hi/test_data/l1a/imap_hi_l1a_45sensor-de_20250415_v000.cdf +0 -0
  82. imap_processing/tests/hi/test_hi_l1b.py +73 -3
  83. imap_processing/tests/hi/test_hi_l1c.py +10 -2
  84. imap_processing/tests/hi/test_l1a.py +31 -58
  85. imap_processing/tests/hi/test_science_direct_event.py +58 -0
  86. imap_processing/tests/hi/test_utils.py +4 -3
  87. imap_processing/tests/hit/test_data/sci_sample1.ccsds +0 -0
  88. imap_processing/tests/hit/{test_hit_decom.py → test_decom_hit.py} +95 -36
  89. imap_processing/tests/hit/test_hit_l1a.py +299 -179
  90. imap_processing/tests/hit/test_hit_l1b.py +231 -24
  91. imap_processing/tests/hit/test_hit_utils.py +218 -0
  92. imap_processing/tests/hit/validation_data/hskp_sample_eu.csv +89 -0
  93. imap_processing/tests/hit/validation_data/sci_sample_raw1.csv +29 -0
  94. imap_processing/tests/ialirt/test_data/l0/apid01152.tlm +0 -0
  95. imap_processing/tests/ialirt/test_data/l0/imap_codice_l1a_lo-ialirt_20241110193700_v0.0.0.cdf +0 -0
  96. imap_processing/tests/ialirt/unit/test_process_codicelo.py +106 -0
  97. imap_processing/tests/ialirt/unit/test_process_ephemeris.py +109 -0
  98. imap_processing/tests/ialirt/unit/test_process_hit.py +9 -6
  99. imap_processing/tests/idex/conftest.py +2 -2
  100. imap_processing/tests/idex/imap_idex_l0_raw_20231214_v001.pkts +0 -0
  101. imap_processing/tests/idex/impact_14_tof_high_data.txt +4444 -4444
  102. imap_processing/tests/idex/test_idex_l0.py +4 -4
  103. imap_processing/tests/idex/test_idex_l1a.py +8 -2
  104. imap_processing/tests/idex/test_idex_l1b.py +126 -0
  105. imap_processing/tests/lo/test_lo_l1a.py +7 -16
  106. imap_processing/tests/lo/test_lo_science.py +69 -5
  107. imap_processing/tests/lo/test_pkts/imap_lo_l0_raw_20240803_v002.pkts +0 -0
  108. imap_processing/tests/lo/validation_data/Instrument_FM1_T104_R129_20240803_ILO_SCI_DE_dec_DN_with_fills.csv +1999 -0
  109. imap_processing/tests/mag/imap_mag_l1a_norm-magi_20251017_v001.cdf +0 -0
  110. imap_processing/tests/mag/test_mag_l1b.py +97 -7
  111. imap_processing/tests/spice/test_data/imap_ena_sim_metakernel.template +3 -1
  112. imap_processing/tests/spice/test_geometry.py +115 -9
  113. imap_processing/tests/spice/test_time.py +135 -6
  114. imap_processing/tests/swapi/test_swapi_decom.py +75 -69
  115. imap_processing/tests/swapi/test_swapi_l1.py +4 -4
  116. imap_processing/tests/swe/conftest.py +33 -0
  117. imap_processing/tests/swe/l1_validation/swe_l0_unpacked-data_20240510_v001_VALIDATION_L1B_v3.dat +4332 -0
  118. imap_processing/tests/swe/test_swe_l1b.py +29 -8
  119. imap_processing/tests/swe/test_swe_l2.py +64 -8
  120. imap_processing/tests/test_utils.py +2 -2
  121. imap_processing/tests/ultra/test_data/l0/ultra45_raw_sc_ultrarawimg_withFSWcalcs_FM45_40P_Phi28p5_BeamCal_LinearScan_phi2850_theta-000_20240207T102740.csv +3314 -3314
  122. imap_processing/tests/ultra/test_data/l1/dps_exposure_helio_45_E12.cdf +0 -0
  123. imap_processing/tests/ultra/test_data/l1/dps_exposure_helio_45_E24.cdf +0 -0
  124. imap_processing/tests/ultra/unit/test_de.py +113 -0
  125. imap_processing/tests/ultra/unit/test_spatial_utils.py +125 -0
  126. imap_processing/tests/ultra/unit/test_ultra_l1b.py +27 -3
  127. imap_processing/tests/ultra/unit/test_ultra_l1b_annotated.py +31 -10
  128. imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +55 -35
  129. imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +10 -68
  130. imap_processing/ultra/constants.py +12 -3
  131. imap_processing/ultra/l1b/de.py +168 -30
  132. imap_processing/ultra/l1b/ultra_l1b_annotated.py +24 -10
  133. imap_processing/ultra/l1b/ultra_l1b_extended.py +46 -80
  134. imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +60 -144
  135. imap_processing/ultra/utils/spatial_utils.py +221 -0
  136. {imap_processing-0.7.0.dist-info → imap_processing-0.9.0.dist-info}/METADATA +15 -14
  137. {imap_processing-0.7.0.dist-info → imap_processing-0.9.0.dist-info}/RECORD +142 -139
  138. imap_processing/cdf/cdf_attribute_manager.py +0 -322
  139. imap_processing/cdf/config/shared/default_global_cdf_attrs_schema.yaml +0 -246
  140. imap_processing/cdf/config/shared/default_variable_cdf_attrs_schema.yaml +0 -466
  141. imap_processing/hi/l0/decom_hi.py +0 -24
  142. imap_processing/hi/packet_definitions/hi_packet_definition.xml +0 -482
  143. imap_processing/hit/l0/data_classes/housekeeping.py +0 -240
  144. imap_processing/hit/l0/data_classes/science_packet.py +0 -259
  145. imap_processing/hit/l0/utils/hit_base.py +0 -57
  146. imap_processing/tests/cdf/shared/default_global_cdf_attrs_schema.yaml +0 -246
  147. imap_processing/tests/cdf/shared/default_variable_cdf_attrs_schema.yaml +0 -466
  148. imap_processing/tests/cdf/test_cdf_attribute_manager.py +0 -353
  149. imap_processing/tests/codice/data/imap_codice_l0_hi-counters-aggregated_20240429_v001.pkts +0 -0
  150. imap_processing/tests/codice/data/imap_codice_l0_hi-counters-singles_20240429_v001.pkts +0 -0
  151. imap_processing/tests/codice/data/imap_codice_l0_hi-omni_20240429_v001.pkts +0 -0
  152. imap_processing/tests/codice/data/imap_codice_l0_hi-pha_20240429_v001.pkts +0 -0
  153. imap_processing/tests/codice/data/imap_codice_l0_hi-sectored_20240429_v001.pkts +0 -0
  154. imap_processing/tests/codice/data/imap_codice_l0_hskp_20100101_v001.pkts +0 -0
  155. imap_processing/tests/codice/data/imap_codice_l0_lo-counters-aggregated_20240429_v001.pkts +0 -0
  156. imap_processing/tests/codice/data/imap_codice_l0_lo-counters-singles_20240429_v001.pkts +0 -0
  157. imap_processing/tests/codice/data/imap_codice_l0_lo-nsw-angular_20240429_v001.pkts +0 -0
  158. imap_processing/tests/codice/data/imap_codice_l0_lo-nsw-priority_20240429_v001.pkts +0 -0
  159. imap_processing/tests/codice/data/imap_codice_l0_lo-nsw-species_20240429_v001.pkts +0 -0
  160. imap_processing/tests/codice/data/imap_codice_l0_lo-pha_20240429_v001.pkts +0 -0
  161. imap_processing/tests/codice/data/imap_codice_l0_lo-sw-angular_20240429_v001.pkts +0 -0
  162. imap_processing/tests/codice/data/imap_codice_l0_lo-sw-priority_20240429_v001.pkts +0 -0
  163. imap_processing/tests/codice/data/imap_codice_l0_lo-sw-species_20240429_v001.pkts +0 -0
  164. imap_processing/tests/hi/test_decom.py +0 -55
  165. imap_processing/tests/hi/test_l1a_sci_de.py +0 -72
  166. imap_processing/tests/idex/imap_idex_l0_raw_20230725_v001.pkts +0 -0
  167. imap_processing/tests/mag/imap_mag_l1a_burst-magi_20231025_v001.cdf +0 -0
  168. /imap_processing/{hi/l0/__init__.py → tests/glows/test_glows_l2_data.py} +0 -0
  169. /imap_processing/tests/hit/test_data/{imap_hit_l0_hk_20100105_v001.pkts → imap_hit_l0_raw_20100105_v001.pkts} +0 -0
  170. {imap_processing-0.7.0.dist-info → imap_processing-0.9.0.dist-info}/LICENSE +0 -0
  171. {imap_processing-0.7.0.dist-info → imap_processing-0.9.0.dist-info}/WHEEL +0 -0
  172. {imap_processing-0.7.0.dist-info → imap_processing-0.9.0.dist-info}/entry_points.txt +0 -0
@@ -3,6 +3,26 @@
3
3
  import numpy as np
4
4
  import xarray as xr
5
5
 
6
+ from imap_processing.cdf.utils import parse_filename_like
7
+ from imap_processing.spice.geometry import SpiceFrame
8
+ from imap_processing.ultra.l1b.ultra_l1b_annotated import (
9
+ get_annotated_particle_velocity,
10
+ )
11
+ from imap_processing.ultra.l1b.ultra_l1b_extended import (
12
+ StopType,
13
+ determine_species,
14
+ get_coincidence_positions,
15
+ get_ctof,
16
+ get_energy_pulse_height,
17
+ get_energy_ssd,
18
+ get_front_x_position,
19
+ get_front_y_position,
20
+ get_path_length,
21
+ get_ph_tof_and_back_positions,
22
+ get_ssd_back_position_and_tof_offset,
23
+ get_ssd_tof,
24
+ get_unit_vector,
25
+ )
6
26
  from imap_processing.ultra.utils.ultra_l1_utils import create_dataset
7
27
 
8
28
 
@@ -13,46 +33,164 @@ def calculate_de(de_dataset: xr.Dataset, name: str) -> xr.Dataset:
13
33
  Parameters
14
34
  ----------
15
35
  de_dataset : xarray.Dataset
16
- Dataset containing direct event data.
36
+ L1a dataset containing direct event data.
17
37
  name : str
18
- Name of the dataset.
38
+ Name of the l1a dataset.
19
39
 
20
40
  Returns
21
41
  -------
22
42
  dataset : xarray.Dataset
23
- Dataset containing the data.
43
+ L1b de dataset.
24
44
  """
25
45
  de_dict = {}
46
+ sensor = parse_filename_like(name)["sensor"][0:2]
26
47
 
27
- # Placeholder for calculations
28
- epoch = de_dataset.coords["epoch"].values
48
+ # Instantiate arrays
49
+ yf = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
50
+ xb = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
51
+ yb = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
52
+ xc = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
53
+ d = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float64)
54
+ r = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
55
+ tof = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
56
+ etof = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
57
+ ctof = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
58
+ energy = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
59
+ # TODO: Confirm with Ultra team what fill values and dtype we want.
60
+ species_bin = np.full(len(de_dataset["epoch"]), "UNKNOWN", dtype="U10")
61
+ t2 = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
29
62
 
63
+ # Drop events with invalid start type.
64
+ de_dataset = de_dataset.where(
65
+ de_dataset["START_TYPE"] != np.iinfo(np.int64).min, drop=True
66
+ )
67
+ # Define epoch.
30
68
  de_dict["epoch"] = de_dataset["epoch"]
31
- de_dict["x_front"] = np.zeros(len(epoch), dtype=np.float64)
32
- de_dict["y_front"] = np.zeros(len(epoch), dtype=np.float64)
33
- de_dict["x_back"] = np.zeros(len(epoch), dtype=np.float64)
34
- de_dict["y_back"] = np.zeros(len(epoch), dtype=np.float64)
35
- de_dict["x_coin"] = np.zeros(len(epoch), dtype=np.float64)
36
- de_dict["tof_start_stop"] = np.zeros(len(epoch), dtype=np.float64)
37
- de_dict["tof_stop_coin"] = np.zeros(len(epoch), dtype=np.float64)
38
- de_dict["tof_corrected"] = np.zeros(len(epoch), dtype=np.float64)
39
- de_dict["eventtype"] = np.zeros(len(epoch), dtype=np.uint64)
40
- de_dict["vx_ultra"] = np.zeros(len(epoch), dtype=np.float64)
41
- de_dict["vy_ultra"] = np.zeros(len(epoch), dtype=np.float64)
42
- de_dict["vz_ultra"] = np.zeros(len(epoch), dtype=np.float64)
43
- de_dict["energy"] = np.zeros(len(epoch), dtype=np.float64)
44
- de_dict["species"] = np.zeros(len(epoch), dtype=np.uint64)
45
- de_dict["event_efficiency"] = np.zeros(len(epoch), dtype=np.float64)
46
- de_dict["vx_sc"] = np.zeros(len(epoch), dtype=np.float64)
47
- de_dict["vy_sc"] = np.zeros(len(epoch), dtype=np.float64)
48
- de_dict["vz_sc"] = np.zeros(len(epoch), dtype=np.float64)
49
- de_dict["vx_dps_sc"] = np.zeros(len(epoch), dtype=np.float64)
50
- de_dict["vy_dps_sc"] = np.zeros(len(epoch), dtype=np.float64)
51
- de_dict["vz_dps_sc"] = np.zeros(len(epoch), dtype=np.float64)
52
- de_dict["vx_dps_helio"] = np.zeros(len(epoch), dtype=np.float64)
53
- de_dict["vy_dps_helio"] = np.zeros(len(epoch), dtype=np.float64)
54
- de_dict["vz_dps_helio"] = np.zeros(len(epoch), dtype=np.float64)
55
- de_dict["eventtimes"] = np.zeros(len(epoch), dtype=np.float64)
69
+
70
+ xf = get_front_x_position(
71
+ de_dataset["START_TYPE"].data,
72
+ de_dataset["START_POS_TDC"].data,
73
+ )
74
+
75
+ # Pulse height
76
+ ph_indices = np.nonzero(
77
+ np.isin(de_dataset["STOP_TYPE"], [StopType.Top.value, StopType.Bottom.value])
78
+ )[0]
79
+ tof[ph_indices], t2[ph_indices], xb[ph_indices], yb[ph_indices] = (
80
+ get_ph_tof_and_back_positions(de_dataset, xf, f"ultra{sensor}")
81
+ )
82
+ d[ph_indices], yf[ph_indices] = get_front_y_position(
83
+ de_dataset["START_TYPE"].data[ph_indices], yb[ph_indices]
84
+ )
85
+ energy[ph_indices] = get_energy_pulse_height(
86
+ de_dataset["STOP_TYPE"].data[ph_indices],
87
+ de_dataset["ENERGY_PH"].data[ph_indices],
88
+ xb[ph_indices],
89
+ yb[ph_indices],
90
+ )
91
+ r[ph_indices] = get_path_length(
92
+ (xf[ph_indices], yf[ph_indices]),
93
+ (xb[ph_indices], yb[ph_indices]),
94
+ d[ph_indices],
95
+ )
96
+ species_bin[ph_indices] = determine_species(tof[ph_indices], r[ph_indices], "PH")
97
+ etof[ph_indices], xc[ph_indices] = get_coincidence_positions(
98
+ de_dataset.isel(epoch=ph_indices), t2[ph_indices], f"ultra{sensor}"
99
+ )
100
+ ctof[ph_indices], _ = get_ctof(tof[ph_indices], r[ph_indices], "PH")
101
+
102
+ # SSD
103
+ ssd_indices = np.nonzero(np.isin(de_dataset["STOP_TYPE"], StopType.SSD.value))[0]
104
+ tof[ssd_indices] = get_ssd_tof(de_dataset, xf)
105
+ yb[ssd_indices], _, ssd_number = get_ssd_back_position_and_tof_offset(de_dataset)
106
+ xc[ssd_indices] = np.zeros(len(ssd_indices))
107
+ xb[ssd_indices] = np.zeros(len(ssd_indices))
108
+ etof[ssd_indices] = np.zeros(len(ssd_indices))
109
+ d[ssd_indices], yf[ssd_indices] = get_front_y_position(
110
+ de_dataset["START_TYPE"].data[ssd_indices], yb[ssd_indices]
111
+ )
112
+ energy[ssd_indices] = get_energy_ssd(de_dataset, ssd_number)
113
+ r[ssd_indices] = get_path_length(
114
+ (xf[ssd_indices], yf[ssd_indices]),
115
+ (xb[ssd_indices], yb[ssd_indices]),
116
+ d[ssd_indices],
117
+ )
118
+ species_bin[ssd_indices] = determine_species(
119
+ tof[ssd_indices], r[ssd_indices], "SSD"
120
+ )
121
+ ctof[ssd_indices], _ = get_ctof(tof[ssd_indices], r[ssd_indices], "SSD")
122
+
123
+ # Combine ph_yb and ssd_yb along with their indices
124
+ de_dict["x_front"] = xf.astype(np.float32)
125
+ de_dict["y_front"] = yf
126
+ de_dict["x_back"] = xb
127
+ de_dict["y_back"] = yb
128
+ de_dict["x_coin"] = xc
129
+ de_dict["tof_start_stop"] = tof
130
+ de_dict["tof_stop_coin"] = etof
131
+ de_dict["tof_corrected"] = ctof
132
+ de_dict["front_back_distance"] = d
133
+ de_dict["path_length"] = r
134
+
135
+ keys = [
136
+ "coincidence_type",
137
+ "start_type",
138
+ "event_type",
139
+ "de_event_met",
140
+ ]
141
+ dataset_keys = ["COIN_TYPE", "START_TYPE", "STOP_TYPE", "SHCOARSE"]
142
+
143
+ de_dict.update(
144
+ {key: de_dataset[dataset_key] for key, dataset_key in zip(keys, dataset_keys)}
145
+ )
146
+
147
+ vx_ultra, vy_ultra, vz_ultra = get_unit_vector(
148
+ (de_dict["x_front"], de_dict["y_front"]),
149
+ (de_dict["x_back"], de_dict["y_back"]),
150
+ de_dict["front_back_distance"],
151
+ de_dict["tof_start_stop"],
152
+ )
153
+
154
+ de_dict["vx_ultra"] = vx_ultra.astype(np.float32)
155
+ de_dict["vy_ultra"] = vy_ultra.astype(np.float32)
156
+ de_dict["vz_ultra"] = vz_ultra.astype(np.float32)
157
+ de_dict["energy"] = energy
158
+ de_dict["species"] = species_bin
159
+
160
+ # Annotated Events.
161
+ position = np.stack(
162
+ (de_dict["vx_ultra"], de_dict["vy_ultra"], de_dict["vz_ultra"]), axis=-1
163
+ )
164
+
165
+ ultra_frame = getattr(SpiceFrame, f"IMAP_ULTRA_{sensor}")
166
+ sc_velocity, sc_dps_velocity, helio_velocity = get_annotated_particle_velocity(
167
+ de_dataset.data_vars["EVENTTIMES"],
168
+ position,
169
+ ultra_frame,
170
+ SpiceFrame.IMAP_DPS,
171
+ SpiceFrame.IMAP_SPACECRAFT,
172
+ )
173
+
174
+ de_dict["vx_sc"], de_dict["vy_sc"], de_dict["vz_sc"] = (
175
+ sc_velocity[:, 0],
176
+ sc_velocity[:, 1],
177
+ sc_velocity[:, 2],
178
+ )
179
+ de_dict["vx_dps_sc"], de_dict["vy_dps_sc"], de_dict["vz_dps_sc"] = (
180
+ sc_dps_velocity[:, 0],
181
+ sc_dps_velocity[:, 1],
182
+ sc_dps_velocity[:, 2],
183
+ )
184
+ de_dict["vx_dps_helio"], de_dict["vy_dps_helio"], de_dict["vz_dps_helio"] = (
185
+ helio_velocity[:, 0],
186
+ helio_velocity[:, 1],
187
+ helio_velocity[:, 2],
188
+ )
189
+
190
+ # TODO: TBD.
191
+ de_dict["event_efficiency"] = np.full(
192
+ len(de_dataset["epoch"]), np.nan, dtype=np.float32
193
+ )
56
194
 
57
195
  dataset = create_dataset(de_dict, name, "l1b")
58
196
 
@@ -1,7 +1,5 @@
1
1
  """Calculates Annotated Events for ULTRA L1b."""
2
2
 
3
- import typing
4
-
5
3
  import numpy as np
6
4
 
7
5
  from imap_processing.spice.geometry import (
@@ -9,17 +7,15 @@ from imap_processing.spice.geometry import (
9
7
  frame_transform,
10
8
  imap_state,
11
9
  )
12
- from imap_processing.spice.kernels import ensure_spice
13
10
 
14
11
 
15
- @ensure_spice
16
- @typing.no_type_check
17
- def get_particle_velocity(
12
+ def get_annotated_particle_velocity(
18
13
  time: np.ndarray,
19
14
  instrument_velocity: np.ndarray,
20
15
  instrument_frame: SpiceFrame,
21
16
  pointing_frame: SpiceFrame,
22
- ) -> tuple[np.ndarray, np.ndarray]:
17
+ spacecraft_frame: SpiceFrame,
18
+ ) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
23
19
  """
24
20
  Get the particle velocity in the pointing (DPS) frame wrt the spacecraft.
25
21
 
@@ -33,11 +29,15 @@ def get_particle_velocity(
33
29
  Instrument frame.
34
30
  pointing_frame : SpiceFrame
35
31
  Pointing frame.
32
+ spacecraft_frame : SpiceFrame
33
+ Spacecraft frame.
36
34
 
37
35
  Returns
38
36
  -------
39
37
  particle_velocity_spacecraft : np.ndarray
40
38
  Particle velocity in the spacecraft frame.
39
+ particle_velocity_dps_spacecraft : np.ndarray
40
+ Particle velocity in DPS frame at rest WRT spacecraft .
41
41
  particle_velocity_heliosphere : np.ndarray
42
42
  Particle velocity in the heliosphere frame.
43
43
 
@@ -45,8 +45,16 @@ def get_particle_velocity(
45
45
  ----------
46
46
  https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy
47
47
  """
48
- # Particle velocity in the pointing (DPS) frame wrt spacecraft.
48
+ # Particle velocity in the spacecraft frame.
49
49
  particle_velocity_spacecraft = frame_transform(
50
+ et=time,
51
+ position=instrument_velocity,
52
+ from_frame=instrument_frame,
53
+ to_frame=spacecraft_frame,
54
+ )
55
+
56
+ # Particle velocity in the pointing (DPS) frame wrt spacecraft.
57
+ particle_velocity_dps_spacecraft = frame_transform(
50
58
  et=time,
51
59
  position=instrument_velocity,
52
60
  from_frame=instrument_frame,
@@ -61,6 +69,12 @@ def get_particle_velocity(
61
69
 
62
70
  # Apply Compton-Getting.
63
71
  # Particle velocity in the DPS frame wrt to the heliosphere
64
- particle_velocity_heliosphere = spacecraft_velocity + particle_velocity_spacecraft
72
+ particle_velocity_heliosphere = (
73
+ spacecraft_velocity + particle_velocity_dps_spacecraft
74
+ )
65
75
 
66
- return particle_velocity_spacecraft, particle_velocity_heliosphere
76
+ return (
77
+ particle_velocity_spacecraft,
78
+ particle_velocity_dps_spacecraft,
79
+ particle_velocity_heliosphere,
80
+ )
@@ -1,5 +1,6 @@
1
1
  """Calculates Extended Raw Events for ULTRA L1b."""
2
2
 
3
+ # TODO: Come back and add in FSW logic.
3
4
  import logging
4
5
  from enum import Enum
5
6
  from typing import ClassVar
@@ -252,7 +253,9 @@ def get_ph_tof_and_back_positions(
252
253
  return tof, t2, xb, yb
253
254
 
254
255
 
255
- def get_path_length(front_position: tuple, back_position: tuple, d: float) -> float:
256
+ def get_path_length(
257
+ front_position: tuple, back_position: tuple, d: np.ndarray
258
+ ) -> NDArray:
256
259
  """
257
260
  Calculate the path length.
258
261
 
@@ -262,15 +265,15 @@ def get_path_length(front_position: tuple, back_position: tuple, d: float) -> fl
262
265
  Front position (xf,yf) (hundredths of a millimeter).
263
266
  back_position : tuple of floats
264
267
  Back position (xb,yb) (hundredths of a millimeter).
265
- d : float
268
+ d : np.ndarray
266
269
  Distance from slit to foil (hundredths of a millimeter).
267
270
 
268
271
  Returns
269
272
  -------
270
- path_length : float
273
+ path_length : np.ndarray
271
274
  Path length (r) (hundredths of a millimeter).
272
275
  """
273
- path_length: float = np.sqrt(
276
+ path_length = np.sqrt(
274
277
  (front_position[0] - back_position[0]) ** 2
275
278
  + (front_position[1] - back_position[1]) ** 2
276
279
  + (d) ** 2
@@ -434,9 +437,9 @@ def get_coincidence_positions(
434
437
  return etof, xc_array * 100
435
438
 
436
439
 
437
- def get_particle_velocity(
438
- front_position: tuple[float, float],
439
- back_position: tuple[float, float],
440
+ def get_unit_vector(
441
+ front_position: tuple[NDArray, NDArray],
442
+ back_position: tuple[NDArray, NDArray],
440
443
  d: np.ndarray,
441
444
  tof: np.ndarray,
442
445
  ) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
@@ -486,9 +489,9 @@ def get_particle_velocity(
486
489
  vhat_y = -v_y / magnitude_v
487
490
  vhat_z = -v_z / magnitude_v
488
491
 
489
- vhat_x[tof < 0] = np.iinfo(np.int64).min # used as fillvals
490
- vhat_y[tof < 0] = np.iinfo(np.int64).min
491
- vhat_z[tof < 0] = np.iinfo(np.int64).min
492
+ vhat_x[tof < 0] = np.nan # used as fillvals
493
+ vhat_y[tof < 0] = np.nan
494
+ vhat_z[tof < 0] = np.nan
492
495
 
493
496
  return vhat_x, vhat_y, vhat_z
494
497
 
@@ -645,9 +648,11 @@ def get_energy_ssd(de_dataset: xarray.Dataset, ssd: np.ndarray) -> NDArray[np.fl
645
648
  return energy_norm
646
649
 
647
650
 
648
- def get_ctof(tof: np.ndarray, path_length: np.ndarray) -> NDArray:
651
+ def get_ctof(
652
+ tof: np.ndarray, path_length: np.ndarray, type: str
653
+ ) -> tuple[NDArray, NDArray]:
649
654
  """
650
- Calculate the corrected TOF.
655
+ Calculate the corrected TOF and the magnitude of the particle velocity.
651
656
 
652
657
  The corrected TOF (ctof) is the TOF normalized with respect
653
658
  to a fixed distance dmin between the front and back detectors.
@@ -662,30 +667,36 @@ def get_ctof(tof: np.ndarray, path_length: np.ndarray) -> NDArray:
662
667
  Time of flight (tenths of a nanosecond).
663
668
  path_length : np.ndarray
664
669
  Path length (r) (hundredths of a millimeter).
670
+ type : str
671
+ Type of event, either "PH" or "SSD".
665
672
 
666
673
  Returns
667
674
  -------
668
675
  ctof : np.ndarray
669
676
  Corrected TOF (tenths of a ns).
677
+ magnitude_v : np.ndarray
678
+ Magnitude of the particle velocity (km/s).
670
679
  """
680
+ dmin_ctof = getattr(UltraConstants, f"DMIN_{type}_CTOF")
681
+
671
682
  # Multiply times 100 to convert to hundredths of a millimeter.
672
- ctof = tof * UltraConstants.DMIN * 100 / path_length
683
+ ctof = tof * dmin_ctof * 100 / path_length
673
684
 
674
- return ctof
685
+ # Convert from mm/0.1ns to km/s.
686
+ magnitude_v = dmin_ctof / ctof * 1e4
675
687
 
688
+ return ctof, magnitude_v
676
689
 
677
- def determine_species_pulse_height(
678
- energy: np.ndarray, tof: np.ndarray, path_length: np.ndarray
679
- ) -> NDArray:
690
+
691
+ def determine_species(tof: np.ndarray, path_length: np.ndarray, type: str) -> NDArray:
680
692
  """
681
693
  Determine the species for pulse-height events.
682
694
 
683
- Species is determined from the particle energy and velocity.
695
+ Species is determined from the particle velocity.
684
696
  For velocity, the particle TOF is normalized with respect
685
697
  to a fixed distance dmin between the front and back detectors.
686
698
  The normalized TOF is termed the corrected TOF (ctof).
687
- Particle species are determined from
688
- the energy and ctof using a lookup table.
699
+ Particle species are determined from ctof using thresholds.
689
700
 
690
701
  Further description is available on pages 42-44 of
691
702
  IMAP-Ultra Flight Software Specification document
@@ -693,72 +704,27 @@ def determine_species_pulse_height(
693
704
 
694
705
  Parameters
695
706
  ----------
696
- energy : np.ndarray
697
- Energy from the SSD event (keV).
698
707
  tof : np.ndarray
699
708
  Time of flight of the SSD event (tenths of a nanosecond).
700
709
  path_length : np.ndarray
701
710
  Path length (r) (hundredths of a millimeter).
711
+ type : str
712
+ Type of data (PH or SSD).
702
713
 
703
714
  Returns
704
715
  -------
705
- bin : np.array
716
+ species_bin : np.array
706
717
  Species bin.
707
718
  """
708
- # PH event TOF normalization to Z axis
709
- ctof = get_ctof(tof, path_length)
710
- # TODO: need lookup tables
711
- # placeholder
712
- bin = np.zeros(len(ctof))
713
- # bin = PHxTOFSpecies[ctof, energy]
714
-
715
- return bin
716
-
717
-
718
- def determine_species_ssd(
719
- energy: np.ndarray, tof: np.ndarray, path_length: np.ndarray
720
- ) -> NDArray:
721
- """
722
- Determine the species for SSD events.
723
-
724
- Species is determined from the particle's energy and velocity.
725
- For velocity, the particle's TOF is normalized with respect
726
- to a fixed distance dmin between the front and back detectors.
727
- For SSD events, an adjustment is also made to the path length
728
- to account for the shorter distances that such events
729
- travel to reach the detector. The normalized TOF is termed
730
- the corrected tof (ctof). Particle species are determined from
731
- the energy and cTOF using a lookup table.
732
-
733
- Further description is available on pages 42-44 of
734
- IMAP-Ultra Flight Software Specification document
735
- (7523-9009_Rev_-.pdf).
736
-
737
- Parameters
738
- ----------
739
- energy : np.ndarray
740
- Energy from the SSD event (keV).
741
- tof : np.ndarray
742
- Time of flight of the SSD event (tenths of a nanosecond).
743
- path_length : np.ndarray
744
- Path length (r) (hundredths of a millimeter).
745
-
746
- Returns
747
- -------
748
- bin : np.ndarray
749
- Species bin.
750
- """
751
- # SSD event TOF normalization to Z axis
752
- ctof = get_ctof(tof, path_length)
753
-
754
- bin = np.zeros(len(ctof)) # placeholder
755
-
756
- # TODO: get these lookup tables
757
- # if r < get_image_params("PathSteepThresh"):
758
- # # bin = ExTOFSpeciesSteep[energy, ctof]
759
- # elif r < get_image_params("PathMediumThresh"):
760
- # # bin = ExTOFSpeciesMedium[energy, ctof]
761
- # else:
762
- # # bin = ExTOFSpeciesFlat[energy, ctof]
763
-
764
- return bin
719
+ # Event TOF normalization to Z axis
720
+ ctof, _ = get_ctof(tof, path_length, type)
721
+ # Initialize bin array
722
+ species_bin = np.full(len(ctof), "UNKNOWN", dtype="U10")
723
+
724
+ # Assign "H" to bins where cTOF is within the specified range
725
+ species_bin[
726
+ (ctof > UltraConstants.CTOF_SPECIES_MIN)
727
+ & (ctof < UltraConstants.CTOF_SPECIES_MAX)
728
+ ] = "H"
729
+
730
+ return species_bin