imap-processing 0.7.0__py3-none-any.whl → 0.8.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 (124) hide show
  1. imap_processing/__init__.py +1 -1
  2. imap_processing/_version.py +2 -2
  3. imap_processing/ccsds/excel_to_xtce.py +34 -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 +36 -8
  8. imap_processing/cdf/config/imap_hit_l1b_variable_attrs.yaml +9 -0
  9. imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +7 -7
  10. imap_processing/cdf/config/imap_idex_l1a_variable_attrs.yaml +32 -33
  11. imap_processing/cdf/config/imap_mag_l1_variable_attrs.yaml +24 -28
  12. imap_processing/cdf/config/imap_ultra_l1a_variable_attrs.yaml +1 -0
  13. imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +133 -78
  14. imap_processing/cdf/config/imap_variable_schema.yaml +13 -0
  15. imap_processing/cdf/imap_cdf_manager.py +31 -27
  16. imap_processing/cli.py +12 -10
  17. imap_processing/codice/codice_l1a.py +151 -61
  18. imap_processing/codice/constants.py +1 -1
  19. imap_processing/codice/decompress.py +4 -9
  20. imap_processing/codice/utils.py +1 -0
  21. imap_processing/glows/l1b/glows_l1b.py +3 -3
  22. imap_processing/glows/l1b/glows_l1b_data.py +59 -37
  23. imap_processing/glows/l2/glows_l2_data.py +123 -0
  24. imap_processing/hi/l1a/histogram.py +1 -1
  25. imap_processing/hi/l1a/science_direct_event.py +1 -1
  26. imap_processing/hi/l1b/hi_l1b.py +85 -11
  27. imap_processing/hi/l1c/hi_l1c.py +23 -1
  28. imap_processing/hi/utils.py +1 -1
  29. imap_processing/hit/hit_utils.py +221 -0
  30. imap_processing/hit/l0/constants.py +118 -0
  31. imap_processing/hit/l0/decom_hit.py +186 -153
  32. imap_processing/hit/l1a/hit_l1a.py +20 -175
  33. imap_processing/hit/l1b/hit_l1b.py +33 -153
  34. imap_processing/idex/idex_l1a.py +10 -9
  35. imap_processing/lo/l0/decompression_tables/decompression_tables.py +1 -1
  36. imap_processing/lo/l0/lo_science.py +1 -1
  37. imap_processing/lo/packet_definitions/lo_xtce.xml +1 -3296
  38. imap_processing/mag/l0/decom_mag.py +4 -3
  39. imap_processing/mag/l1a/mag_l1a.py +11 -11
  40. imap_processing/mag/l1b/mag_l1b.py +89 -7
  41. imap_processing/spice/geometry.py +126 -4
  42. imap_processing/swapi/l1/swapi_l1.py +1 -1
  43. imap_processing/swapi/l2/swapi_l2.py +1 -1
  44. imap_processing/swe/l1b/swe_l1b_science.py +8 -8
  45. imap_processing/tests/ccsds/test_data/expected_output.xml +1 -0
  46. imap_processing/tests/ccsds/test_excel_to_xtce.py +4 -4
  47. imap_processing/tests/cdf/test_imap_cdf_manager.py +0 -10
  48. imap_processing/tests/codice/conftest.py +1 -17
  49. imap_processing/tests/codice/data/imap_codice_l0_raw_20241110_v001.pkts +0 -0
  50. imap_processing/tests/codice/test_codice_l0.py +8 -2
  51. imap_processing/tests/codice/test_codice_l1a.py +127 -107
  52. imap_processing/tests/codice/test_codice_l1b.py +1 -0
  53. imap_processing/tests/codice/test_decompress.py +7 -7
  54. imap_processing/tests/conftest.py +54 -15
  55. imap_processing/tests/glows/conftest.py +6 -0
  56. imap_processing/tests/glows/test_glows_l1b.py +9 -9
  57. imap_processing/tests/glows/test_glows_l1b_data.py +9 -9
  58. imap_processing/tests/glows/test_glows_l2_data.py +0 -0
  59. imap_processing/tests/hi/test_data/l1a/imap_hi_l1a_45sensor-de_20250415_v000.cdf +0 -0
  60. imap_processing/tests/hi/test_hi_l1b.py +71 -1
  61. imap_processing/tests/hi/test_hi_l1c.py +10 -2
  62. imap_processing/tests/hi/test_utils.py +4 -3
  63. imap_processing/tests/hit/{test_hit_decom.py → test_decom_hit.py} +84 -35
  64. imap_processing/tests/hit/test_hit_l1a.py +2 -197
  65. imap_processing/tests/hit/test_hit_l1b.py +156 -25
  66. imap_processing/tests/hit/test_hit_utils.py +218 -0
  67. imap_processing/tests/idex/conftest.py +1 -1
  68. imap_processing/tests/idex/imap_idex_l0_raw_20231214_v001.pkts +0 -0
  69. imap_processing/tests/idex/impact_14_tof_high_data.txt +4444 -4444
  70. imap_processing/tests/idex/test_idex_l0.py +3 -3
  71. imap_processing/tests/idex/test_idex_l1a.py +1 -1
  72. imap_processing/tests/lo/test_lo_science.py +2 -2
  73. imap_processing/tests/mag/imap_mag_l1a_norm-magi_20251017_v001.cdf +0 -0
  74. imap_processing/tests/mag/test_mag_l1b.py +59 -3
  75. imap_processing/tests/spice/test_data/imap_ena_sim_metakernel.template +3 -1
  76. imap_processing/tests/spice/test_geometry.py +84 -4
  77. imap_processing/tests/swe/conftest.py +33 -0
  78. imap_processing/tests/swe/l1_validation/swe_l0_unpacked-data_20240510_v001_VALIDATION_L1B_v3.dat +4332 -0
  79. imap_processing/tests/swe/test_swe_l1b.py +29 -8
  80. imap_processing/tests/test_utils.py +1 -1
  81. imap_processing/tests/ultra/test_data/l1/dps_exposure_helio_45_E12.cdf +0 -0
  82. imap_processing/tests/ultra/test_data/l1/dps_exposure_helio_45_E24.cdf +0 -0
  83. imap_processing/tests/ultra/unit/test_de.py +108 -0
  84. imap_processing/tests/ultra/unit/test_ultra_l1b.py +27 -3
  85. imap_processing/tests/ultra/unit/test_ultra_l1b_annotated.py +31 -10
  86. imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +21 -11
  87. imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +9 -44
  88. imap_processing/ultra/constants.py +8 -3
  89. imap_processing/ultra/l1b/de.py +174 -30
  90. imap_processing/ultra/l1b/ultra_l1b_annotated.py +24 -10
  91. imap_processing/ultra/l1b/ultra_l1b_extended.py +21 -14
  92. imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +70 -119
  93. {imap_processing-0.7.0.dist-info → imap_processing-0.8.0.dist-info}/METADATA +15 -14
  94. {imap_processing-0.7.0.dist-info → imap_processing-0.8.0.dist-info}/RECORD +98 -113
  95. imap_processing/cdf/cdf_attribute_manager.py +0 -322
  96. imap_processing/cdf/config/shared/default_global_cdf_attrs_schema.yaml +0 -246
  97. imap_processing/cdf/config/shared/default_variable_cdf_attrs_schema.yaml +0 -466
  98. imap_processing/hit/l0/data_classes/housekeeping.py +0 -240
  99. imap_processing/hit/l0/data_classes/science_packet.py +0 -259
  100. imap_processing/hit/l0/utils/hit_base.py +0 -57
  101. imap_processing/tests/cdf/shared/default_global_cdf_attrs_schema.yaml +0 -246
  102. imap_processing/tests/cdf/shared/default_variable_cdf_attrs_schema.yaml +0 -466
  103. imap_processing/tests/cdf/test_cdf_attribute_manager.py +0 -353
  104. imap_processing/tests/codice/data/imap_codice_l0_hi-counters-aggregated_20240429_v001.pkts +0 -0
  105. imap_processing/tests/codice/data/imap_codice_l0_hi-counters-singles_20240429_v001.pkts +0 -0
  106. imap_processing/tests/codice/data/imap_codice_l0_hi-omni_20240429_v001.pkts +0 -0
  107. imap_processing/tests/codice/data/imap_codice_l0_hi-pha_20240429_v001.pkts +0 -0
  108. imap_processing/tests/codice/data/imap_codice_l0_hi-sectored_20240429_v001.pkts +0 -0
  109. imap_processing/tests/codice/data/imap_codice_l0_hskp_20100101_v001.pkts +0 -0
  110. imap_processing/tests/codice/data/imap_codice_l0_lo-counters-aggregated_20240429_v001.pkts +0 -0
  111. imap_processing/tests/codice/data/imap_codice_l0_lo-counters-singles_20240429_v001.pkts +0 -0
  112. imap_processing/tests/codice/data/imap_codice_l0_lo-nsw-angular_20240429_v001.pkts +0 -0
  113. imap_processing/tests/codice/data/imap_codice_l0_lo-nsw-priority_20240429_v001.pkts +0 -0
  114. imap_processing/tests/codice/data/imap_codice_l0_lo-nsw-species_20240429_v001.pkts +0 -0
  115. imap_processing/tests/codice/data/imap_codice_l0_lo-pha_20240429_v001.pkts +0 -0
  116. imap_processing/tests/codice/data/imap_codice_l0_lo-sw-angular_20240429_v001.pkts +0 -0
  117. imap_processing/tests/codice/data/imap_codice_l0_lo-sw-priority_20240429_v001.pkts +0 -0
  118. imap_processing/tests/codice/data/imap_codice_l0_lo-sw-species_20240429_v001.pkts +0 -0
  119. imap_processing/tests/idex/imap_idex_l0_raw_20230725_v001.pkts +0 -0
  120. imap_processing/tests/mag/imap_mag_l1a_burst-magi_20231025_v001.cdf +0 -0
  121. /imap_processing/tests/hit/test_data/{imap_hit_l0_hk_20100105_v001.pkts → imap_hit_l0_raw_20100105_v001.pkts} +0 -0
  122. {imap_processing-0.7.0.dist-info → imap_processing-0.8.0.dist-info}/LICENSE +0 -0
  123. {imap_processing-0.7.0.dist-info → imap_processing-0.8.0.dist-info}/WHEEL +0 -0
  124. {imap_processing-0.7.0.dist-info → imap_processing-0.8.0.dist-info}/entry_points.txt +0 -0
@@ -3,6 +3,27 @@
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_pulse_height,
14
+ determine_species_ssd,
15
+ get_coincidence_positions,
16
+ get_ctof,
17
+ get_energy_pulse_height,
18
+ get_energy_ssd,
19
+ get_front_x_position,
20
+ get_front_y_position,
21
+ get_path_length,
22
+ get_ph_tof_and_back_positions,
23
+ get_ssd_back_position_and_tof_offset,
24
+ get_ssd_tof,
25
+ get_unit_vector,
26
+ )
6
27
  from imap_processing.ultra.utils.ultra_l1_utils import create_dataset
7
28
 
8
29
 
@@ -13,46 +34,169 @@ def calculate_de(de_dataset: xr.Dataset, name: str) -> xr.Dataset:
13
34
  Parameters
14
35
  ----------
15
36
  de_dataset : xarray.Dataset
16
- Dataset containing direct event data.
37
+ L1a dataset containing direct event data.
17
38
  name : str
18
- Name of the dataset.
39
+ Name of the l1a dataset.
19
40
 
20
41
  Returns
21
42
  -------
22
43
  dataset : xarray.Dataset
23
- Dataset containing the data.
44
+ L1b de dataset.
24
45
  """
25
46
  de_dict = {}
47
+ sensor = parse_filename_like(name)["sensor"][0:2]
26
48
 
27
- # Placeholder for calculations
28
- epoch = de_dataset.coords["epoch"].values
49
+ # Instantiate arrays
50
+ yf = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
51
+ xb = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
52
+ yb = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
53
+ xc = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
54
+ d = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float64)
55
+ r = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
56
+ tof = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
57
+ etof = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
58
+ ctof = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
59
+ energy = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
60
+ # TODO: uint8 fills with zeros instead of nans.
61
+ # Confirm with Ultra team what fill values and dtype we want.
62
+ species_bin = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.uint8)
63
+ t2 = np.full(len(de_dataset["epoch"]), np.nan, dtype=np.float32)
29
64
 
65
+ # Drop events with invalid start type.
66
+ de_dataset = de_dataset.where(
67
+ de_dataset["START_TYPE"] != np.iinfo(np.int64).min, drop=True
68
+ )
69
+ # Define epoch.
30
70
  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)
71
+
72
+ xf = get_front_x_position(
73
+ de_dataset["START_TYPE"].data,
74
+ de_dataset["START_POS_TDC"].data,
75
+ )
76
+
77
+ # Pulse height
78
+ ph_indices = np.nonzero(
79
+ np.isin(de_dataset["STOP_TYPE"], [StopType.Top.value, StopType.Bottom.value])
80
+ )[0]
81
+ tof[ph_indices], t2[ph_indices], xb[ph_indices], yb[ph_indices] = (
82
+ get_ph_tof_and_back_positions(de_dataset, xf, f"ultra{sensor}")
83
+ )
84
+ d[ph_indices], yf[ph_indices] = get_front_y_position(
85
+ de_dataset["START_TYPE"].data[ph_indices], yb[ph_indices]
86
+ )
87
+ energy[ph_indices] = get_energy_pulse_height(
88
+ de_dataset["STOP_TYPE"].data[ph_indices],
89
+ de_dataset["ENERGY_PH"].data[ph_indices],
90
+ xb[ph_indices],
91
+ yb[ph_indices],
92
+ )
93
+ r[ph_indices] = get_path_length(
94
+ (xf[ph_indices], yf[ph_indices]),
95
+ (xb[ph_indices], yb[ph_indices]),
96
+ d[ph_indices],
97
+ )
98
+ species_bin[ph_indices] = determine_species_pulse_height(
99
+ energy[ph_indices], tof[ph_indices], r[ph_indices]
100
+ )
101
+ etof[ph_indices], xc[ph_indices] = get_coincidence_positions(
102
+ de_dataset.isel(epoch=ph_indices), t2[ph_indices], f"ultra{sensor}"
103
+ )
104
+ ctof[ph_indices] = get_ctof(tof[ph_indices], r[ph_indices], "PH")
105
+
106
+ # SSD
107
+ ssd_indices = np.nonzero(np.isin(de_dataset["STOP_TYPE"], StopType.SSD.value))[0]
108
+ tof[ssd_indices] = get_ssd_tof(de_dataset, xf)
109
+ yb[ssd_indices], _, ssd_number = get_ssd_back_position_and_tof_offset(de_dataset)
110
+ xc[ssd_indices] = np.zeros(len(ssd_indices))
111
+ xb[ssd_indices] = np.zeros(len(ssd_indices))
112
+ etof[ssd_indices] = np.zeros(len(ssd_indices))
113
+ d[ssd_indices], yf[ssd_indices] = get_front_y_position(
114
+ de_dataset["START_TYPE"].data[ssd_indices], yb[ssd_indices]
115
+ )
116
+ energy[ssd_indices] = get_energy_ssd(de_dataset, ssd_number)
117
+ r[ssd_indices] = get_path_length(
118
+ (xf[ssd_indices], yf[ssd_indices]),
119
+ (xb[ssd_indices], yb[ssd_indices]),
120
+ d[ssd_indices],
121
+ )
122
+ species_bin[ssd_indices] = determine_species_ssd(
123
+ energy[ssd_indices],
124
+ tof[ssd_indices],
125
+ r[ssd_indices],
126
+ )
127
+ ctof[ssd_indices] = get_ctof(tof[ssd_indices], r[ssd_indices], "SSD")
128
+
129
+ # Combine ph_yb and ssd_yb along with their indices
130
+ de_dict["x_front"] = xf.astype(np.float32)
131
+ de_dict["y_front"] = yf
132
+ de_dict["x_back"] = xb
133
+ de_dict["y_back"] = yb
134
+ de_dict["x_coin"] = xc
135
+ de_dict["tof_start_stop"] = tof
136
+ de_dict["tof_stop_coin"] = etof
137
+ de_dict["tof_corrected"] = ctof
138
+ de_dict["front_back_distance"] = d
139
+ de_dict["path_length"] = r
140
+
141
+ keys = [
142
+ "coincidence_type",
143
+ "start_type",
144
+ "event_type",
145
+ "de_event_met",
146
+ ]
147
+ dataset_keys = ["COIN_TYPE", "START_TYPE", "STOP_TYPE", "SHCOARSE"]
148
+
149
+ de_dict.update(
150
+ {key: de_dataset[dataset_key] for key, dataset_key in zip(keys, dataset_keys)}
151
+ )
152
+
153
+ vx_ultra, vy_ultra, vz_ultra = get_unit_vector(
154
+ (de_dict["x_front"], de_dict["y_front"]),
155
+ (de_dict["x_back"], de_dict["y_back"]),
156
+ de_dict["front_back_distance"],
157
+ de_dict["tof_start_stop"],
158
+ )
159
+
160
+ de_dict["vx_ultra"] = vx_ultra.astype(np.float32)
161
+ de_dict["vy_ultra"] = vy_ultra.astype(np.float32)
162
+ de_dict["vz_ultra"] = vz_ultra.astype(np.float32)
163
+ de_dict["energy"] = energy
164
+ de_dict["species"] = species_bin
165
+
166
+ # Annotated Events.
167
+ position = np.stack(
168
+ (de_dict["vx_ultra"], de_dict["vy_ultra"], de_dict["vz_ultra"]), axis=-1
169
+ )
170
+
171
+ ultra_frame = getattr(SpiceFrame, f"IMAP_ULTRA_{sensor}")
172
+ sc_velocity, sc_dps_velocity, helio_velocity = get_annotated_particle_velocity(
173
+ de_dataset.data_vars["EVENTTIMES"],
174
+ position,
175
+ ultra_frame,
176
+ SpiceFrame.IMAP_DPS,
177
+ SpiceFrame.IMAP_SPACECRAFT,
178
+ )
179
+
180
+ de_dict["vx_sc"], de_dict["vy_sc"], de_dict["vz_sc"] = (
181
+ sc_velocity[:, 0],
182
+ sc_velocity[:, 1],
183
+ sc_velocity[:, 2],
184
+ )
185
+ de_dict["vx_dps_sc"], de_dict["vy_dps_sc"], de_dict["vz_dps_sc"] = (
186
+ sc_dps_velocity[:, 0],
187
+ sc_dps_velocity[:, 1],
188
+ sc_dps_velocity[:, 2],
189
+ )
190
+ de_dict["vx_dps_helio"], de_dict["vy_dps_helio"], de_dict["vz_dps_helio"] = (
191
+ helio_velocity[:, 0],
192
+ helio_velocity[:, 1],
193
+ helio_velocity[:, 2],
194
+ )
195
+
196
+ # TODO: TBD.
197
+ de_dict["event_efficiency"] = np.full(
198
+ len(de_dataset["epoch"]), np.nan, dtype=np.float32
199
+ )
56
200
 
57
201
  dataset = create_dataset(de_dict, name, "l1b")
58
202
 
@@ -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,7 +648,7 @@ 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(tof: np.ndarray, path_length: np.ndarray, type: str) -> NDArray:
649
652
  """
650
653
  Calculate the corrected TOF.
651
654
 
@@ -662,14 +665,18 @@ def get_ctof(tof: np.ndarray, path_length: np.ndarray) -> NDArray:
662
665
  Time of flight (tenths of a nanosecond).
663
666
  path_length : np.ndarray
664
667
  Path length (r) (hundredths of a millimeter).
668
+ type : str
669
+ Type of event, either "ph" or "ssd".
665
670
 
666
671
  Returns
667
672
  -------
668
673
  ctof : np.ndarray
669
674
  Corrected TOF (tenths of a ns).
670
675
  """
676
+ dmin_ctof = getattr(UltraConstants, f"DMIN_{type}_CTOF")
677
+
671
678
  # Multiply times 100 to convert to hundredths of a millimeter.
672
- ctof = tof * UltraConstants.DMIN * 100 / path_length
679
+ ctof = tof * dmin_ctof * 100 / path_length
673
680
 
674
681
  return ctof
675
682
 
@@ -706,7 +713,7 @@ def determine_species_pulse_height(
706
713
  Species bin.
707
714
  """
708
715
  # PH event TOF normalization to Z axis
709
- ctof = get_ctof(tof, path_length)
716
+ ctof = get_ctof(tof, path_length, "PH")
710
717
  # TODO: need lookup tables
711
718
  # placeholder
712
719
  bin = np.zeros(len(ctof))
@@ -749,7 +756,7 @@ def determine_species_ssd(
749
756
  Species bin.
750
757
  """
751
758
  # SSD event TOF normalization to Z axis
752
- ctof = get_ctof(tof, path_length)
759
+ ctof = get_ctof(tof, path_length, "SSD")
753
760
 
754
761
  bin = np.zeros(len(ctof)) # placeholder
755
762