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
@@ -0,0 +1,153 @@
1
+ """Functions to support I-ALiRT CoDICE Lo processing."""
2
+
3
+ import logging
4
+ from typing import Any
5
+
6
+ import numpy as np
7
+ import xarray as xr
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ def find_groups(data: xr.Dataset) -> xr.Dataset:
13
+ """
14
+ Find all occurrences of the sequential set of 233 values 0-232.
15
+
16
+ If a value is missing, or we are starting/ending
17
+ in the middle of a sequence we do not count that as a valid group.
18
+
19
+ Parameters
20
+ ----------
21
+ data : xr.Dataset
22
+ CoDICE Lo Dataset.
23
+
24
+ Returns
25
+ -------
26
+ grouped_data : xr.Dataset
27
+ Grouped data.
28
+ """
29
+ subcom_range = (0, 232)
30
+
31
+ data = data.sortby("cod_lo_acq", ascending=True)
32
+
33
+ # Use cod_lo_counter == 0 to define the beginning of the group.
34
+ # Find cod_lo_acq at this index and use it as the beginning time for the group.
35
+ start_sc_ticks = data["cod_lo_acq"][(data["cod_lo_counter"] == subcom_range[0])]
36
+ start_sc_tick = start_sc_ticks.min()
37
+ # Use cod_lo_counter == 232 to define the end of the group.
38
+ last_sc_ticks = data["cod_lo_acq"][
39
+ ([data["cod_lo_counter"] == subcom_range[-1]][-1])
40
+ ]
41
+ last_sc_tick = last_sc_ticks.max()
42
+
43
+ # Filter out data before the first cod_lo_counter=0 and
44
+ # after the last cod_lo_counter=232.
45
+ grouped_data = data.where(
46
+ (data["cod_lo_acq"] >= start_sc_tick) & (data["cod_lo_acq"] <= last_sc_tick),
47
+ drop=True,
48
+ )
49
+
50
+ # Assign labels based on the cod_lo_acq times.
51
+ group_labels = np.searchsorted(
52
+ start_sc_ticks, grouped_data["cod_lo_acq"], side="right"
53
+ )
54
+ # Example:
55
+ # grouped_data.coords
56
+ # Coordinates:
57
+ # * epoch (epoch) int64 7kB 315922822184000000 ... 315923721184000000
58
+ # * group (group) int64 7kB 1 1 1 1 1 1 1 1 1 ... 15 15 15 15 15 15 15 15 15
59
+ grouped_data["group"] = ("group", group_labels)
60
+
61
+ return grouped_data
62
+
63
+
64
+ def append_cod_lo_data(dataset: xr.Dataset) -> xr.Dataset:
65
+ """
66
+ Append the cod_lo_## data values and create an xarray.
67
+
68
+ Parameters
69
+ ----------
70
+ dataset : xr.Dataset
71
+ Original dataset of group.
72
+
73
+ Returns
74
+ -------
75
+ appended_dataset : xr.Dataset
76
+ Dataset with cod_lo_## stacked.
77
+ """
78
+ # Number of codice lo data rows
79
+ num_cod_lo_rows = 15
80
+ cod_lo_data = np.stack(
81
+ [dataset[f"cod_lo_data_{i:02}"].values for i in range(num_cod_lo_rows)], axis=1
82
+ )
83
+
84
+ repeated_data = {
85
+ var: np.repeat(dataset[var].values, num_cod_lo_rows)
86
+ for var in dataset.data_vars
87
+ if not var.startswith("cod_lo_data_")
88
+ }
89
+
90
+ repeated_data["cod_lo_appended"] = cod_lo_data.flatten()
91
+ repeated_epoch = np.repeat(dataset["epoch"].values, num_cod_lo_rows)
92
+
93
+ appended_dataset = xr.Dataset(
94
+ data_vars={name: ("epoch", values) for name, values in repeated_data.items()},
95
+ coords={"epoch": repeated_epoch},
96
+ )
97
+
98
+ return appended_dataset
99
+
100
+
101
+ def process_codicelo(xarray_data: xr.Dataset) -> list[dict]:
102
+ """
103
+ Create final data products.
104
+
105
+ Parameters
106
+ ----------
107
+ xarray_data : xr.Dataset
108
+ Parsed data.
109
+
110
+ Returns
111
+ -------
112
+ codicelo_data : list[dict]
113
+ Dictionary of final data product.
114
+
115
+ Notes
116
+ -----
117
+ This function is incomplete and will need to be updated to include the
118
+ necessary calculations and data products.
119
+ - Calculate species counts (pg 27 of Algorithm Document)
120
+ - Calculate rates (assume 4 minutes per group)
121
+ - Calculate L2 CoDICE pseudodensities (pg 37 of Algorithm Document)
122
+ - Calculate the public data products
123
+ """
124
+ grouped_data = find_groups(xarray_data)
125
+ unique_groups = np.unique(grouped_data["group"])
126
+ codicelo_data: list[dict[str, Any]] = [{}]
127
+
128
+ for group in unique_groups:
129
+ # cod_lo_counter values for the group should be 0-232 with no duplicates.
130
+ subcom_values = grouped_data["cod_lo_counter"][
131
+ (grouped_data["group"] == group).values
132
+ ]
133
+
134
+ # Ensure no duplicates and all values from 0 to 232 are present
135
+ if not np.array_equal(subcom_values, np.arange(233)):
136
+ logger.warning(
137
+ f"Group {group} does not contain all values from 0 to "
138
+ f"232 without duplicates."
139
+ )
140
+ continue
141
+
142
+ mask = grouped_data["group"] == group
143
+ filtered_indices = np.where(mask)[0]
144
+ group_data = grouped_data.isel(epoch=filtered_indices)
145
+
146
+ append_cod_lo_data(group_data)
147
+
148
+ # TODO: calculate species counts
149
+ # TODO: calculate rates
150
+ # TODO: calculate L2 CoDICE pseudodensities
151
+ # TODO: calculate the public data products
152
+
153
+ return codicelo_data
@@ -161,13 +161,12 @@ def process_hit(xarray_data: xr.Dataset) -> list[dict]:
161
161
 
162
162
  Parameters
163
163
  ----------
164
- xarray_data : dict(xr.Dataset)
165
- Dictionary of xarray data including a single
166
- set for processing.
164
+ xarray_data : xr.Dataset
165
+ Parsed data.
167
166
 
168
167
  Returns
169
168
  -------
170
- hit_data : dict
169
+ hit_data : list[dict]
171
170
  Dictionary final data product.
172
171
  """
173
172
  hit_data = []
@@ -182,10 +181,11 @@ def process_hit(xarray_data: xr.Dataset) -> list[dict]:
182
181
 
183
182
  # Ensure no duplicates and all values from 0 to 59 are present
184
183
  if not np.array_equal(subcom_values, np.arange(60)):
185
- raise ValueError(
184
+ logger.warning(
186
185
  f"Group {group} does not contain all values from 0 to "
187
186
  f"59 without duplicates."
188
187
  )
188
+ continue
189
189
 
190
190
  fast_rate_1 = grouped_data["hit_fast_rate_1"][
191
191
  (grouped_data["group"] == group).values
@@ -0,0 +1,281 @@
1
+ <?xml version='1.0' encoding='UTF-8'?>
2
+ <xtce:SpaceSystem xmlns:xtce="http://www.omg.org/space/xtce" name="ialirt">
3
+ <xtce:Header date="2023-08-24T07:53:00MST" version="1.0" author="IMAP SDC" />
4
+ <xtce:TelemetryMetaData>
5
+ <xtce:ParameterTypeSet>
6
+ <!-- This file was manually created using content from:
7
+ I-ALiRT Packet Definitions: https://lasp.colorado.edu/galaxy/x/44nKCQ
8
+ -->
9
+ <xtce:IntegerParameterType name="uint0" signed="false">
10
+ <xtce:IntegerDataEncoding sizeInBits="0" encoding="unsigned" />
11
+ <xtce:UnitSet />
12
+ </xtce:IntegerParameterType>
13
+ <xtce:IntegerParameterType name="uint1" signed="false">
14
+ <xtce:IntegerDataEncoding sizeInBits="1" encoding="unsigned" />
15
+ <xtce:UnitSet />
16
+ </xtce:IntegerParameterType>
17
+ <xtce:IntegerParameterType name="uint2" signed="false">
18
+ <xtce:IntegerDataEncoding sizeInBits="2" encoding="unsigned" />
19
+ <xtce:UnitSet />
20
+ </xtce:IntegerParameterType>
21
+ <xtce:IntegerParameterType name="uint3" signed="false">
22
+ <xtce:IntegerDataEncoding sizeInBits="3" encoding="unsigned" />
23
+ <xtce:UnitSet />
24
+ </xtce:IntegerParameterType>
25
+ <xtce:IntegerParameterType name="uint4" signed="false">
26
+ <xtce:IntegerDataEncoding sizeInBits="4" encoding="unsigned" />
27
+ <xtce:UnitSet />
28
+ </xtce:IntegerParameterType>
29
+ <xtce:IntegerParameterType name="uint5" signed="false">
30
+ <xtce:IntegerDataEncoding sizeInBits="5" encoding="unsigned" />
31
+ <xtce:UnitSet />
32
+ </xtce:IntegerParameterType>
33
+ <xtce:IntegerParameterType name="uint6" signed="false">
34
+ <xtce:IntegerDataEncoding sizeInBits="6" encoding="unsigned" />
35
+ <xtce:UnitSet />
36
+ </xtce:IntegerParameterType>
37
+ <xtce:IntegerParameterType name="uint7" signed="false">
38
+ <xtce:IntegerDataEncoding sizeInBits="7" encoding="unsigned" />
39
+ <xtce:UnitSet />
40
+ </xtce:IntegerParameterType>
41
+ <xtce:IntegerParameterType name="uint8" signed="false">
42
+ <xtce:IntegerDataEncoding sizeInBits="8" encoding="unsigned" />
43
+ <xtce:UnitSet />
44
+ </xtce:IntegerParameterType>
45
+ <xtce:IntegerParameterType name="uint9" signed="false">
46
+ <xtce:IntegerDataEncoding sizeInBits="9" encoding="unsigned" />
47
+ <xtce:UnitSet />
48
+ </xtce:IntegerParameterType>
49
+ <xtce:IntegerParameterType name="uint10" signed="false">
50
+ <xtce:IntegerDataEncoding sizeInBits="10" encoding="unsigned" />
51
+ <xtce:UnitSet />
52
+ </xtce:IntegerParameterType>
53
+ <xtce:IntegerParameterType name="uint11" signed="false">
54
+ <xtce:IntegerDataEncoding sizeInBits="11" encoding="unsigned" />
55
+ <xtce:UnitSet />
56
+ </xtce:IntegerParameterType>
57
+ <xtce:IntegerParameterType name="uint12" signed="false">
58
+ <xtce:IntegerDataEncoding sizeInBits="12" encoding="unsigned" />
59
+ <xtce:UnitSet />
60
+ </xtce:IntegerParameterType>
61
+ <xtce:IntegerParameterType name="uint13" signed="false">
62
+ <xtce:IntegerDataEncoding sizeInBits="13" encoding="unsigned" />
63
+ <xtce:UnitSet />
64
+ </xtce:IntegerParameterType>
65
+ <xtce:IntegerParameterType name="uint14" signed="false">
66
+ <xtce:IntegerDataEncoding sizeInBits="14" encoding="unsigned" />
67
+ <xtce:UnitSet />
68
+ </xtce:IntegerParameterType>
69
+ <xtce:IntegerParameterType name="uint15" signed="false">
70
+ <xtce:IntegerDataEncoding sizeInBits="15" encoding="unsigned" />
71
+ <xtce:UnitSet />
72
+ </xtce:IntegerParameterType>
73
+ <xtce:IntegerParameterType name="uint16" signed="false">
74
+ <xtce:IntegerDataEncoding sizeInBits="16" encoding="unsigned" />
75
+ <xtce:UnitSet />
76
+ </xtce:IntegerParameterType>
77
+ <xtce:IntegerParameterType name="uint17" signed="false">
78
+ <xtce:IntegerDataEncoding sizeInBits="17" encoding="unsigned" />
79
+ <xtce:UnitSet />
80
+ </xtce:IntegerParameterType>
81
+ <xtce:IntegerParameterType name="uint18" signed="false">
82
+ <xtce:IntegerDataEncoding sizeInBits="18" encoding="unsigned" />
83
+ <xtce:UnitSet />
84
+ </xtce:IntegerParameterType>
85
+ <xtce:IntegerParameterType name="uint19" signed="false">
86
+ <xtce:IntegerDataEncoding sizeInBits="19" encoding="unsigned" />
87
+ <xtce:UnitSet />
88
+ </xtce:IntegerParameterType>
89
+ <xtce:IntegerParameterType name="uint20" signed="false">
90
+ <xtce:IntegerDataEncoding sizeInBits="20" encoding="unsigned" />
91
+ <xtce:UnitSet />
92
+ </xtce:IntegerParameterType>
93
+ <xtce:IntegerParameterType name="uint21" signed="false">
94
+ <xtce:IntegerDataEncoding sizeInBits="21" encoding="unsigned" />
95
+ <xtce:UnitSet />
96
+ </xtce:IntegerParameterType>
97
+ <xtce:IntegerParameterType name="uint22" signed="false">
98
+ <xtce:IntegerDataEncoding sizeInBits="22" encoding="unsigned" />
99
+ <xtce:UnitSet />
100
+ </xtce:IntegerParameterType>
101
+ <xtce:IntegerParameterType name="uint23" signed="false">
102
+ <xtce:IntegerDataEncoding sizeInBits="23" encoding="unsigned" />
103
+ <xtce:UnitSet />
104
+ </xtce:IntegerParameterType>
105
+ <xtce:IntegerParameterType name="uint24" signed="false">
106
+ <xtce:IntegerDataEncoding sizeInBits="24" encoding="unsigned" />
107
+ <xtce:UnitSet />
108
+ </xtce:IntegerParameterType>
109
+ <xtce:IntegerParameterType name="uint25" signed="false">
110
+ <xtce:IntegerDataEncoding sizeInBits="25" encoding="unsigned" />
111
+ <xtce:UnitSet />
112
+ </xtce:IntegerParameterType>
113
+ <xtce:IntegerParameterType name="uint26" signed="false">
114
+ <xtce:IntegerDataEncoding sizeInBits="26" encoding="unsigned" />
115
+ <xtce:UnitSet />
116
+ </xtce:IntegerParameterType>
117
+ <xtce:IntegerParameterType name="uint27" signed="false">
118
+ <xtce:IntegerDataEncoding sizeInBits="27" encoding="unsigned" />
119
+ <xtce:UnitSet />
120
+ </xtce:IntegerParameterType>
121
+ <xtce:IntegerParameterType name="uint28" signed="false">
122
+ <xtce:IntegerDataEncoding sizeInBits="28" encoding="unsigned" />
123
+ <xtce:UnitSet />
124
+ </xtce:IntegerParameterType>
125
+ <xtce:IntegerParameterType name="uint29" signed="false">
126
+ <xtce:IntegerDataEncoding sizeInBits="29" encoding="unsigned" />
127
+ <xtce:UnitSet />
128
+ </xtce:IntegerParameterType>
129
+ <xtce:IntegerParameterType name="uint30" signed="false">
130
+ <xtce:IntegerDataEncoding sizeInBits="30" encoding="unsigned" />
131
+ <xtce:UnitSet />
132
+ </xtce:IntegerParameterType>
133
+ <xtce:IntegerParameterType name="uint31" signed="false">
134
+ <xtce:IntegerDataEncoding sizeInBits="31" encoding="unsigned" />
135
+ <xtce:UnitSet />
136
+ </xtce:IntegerParameterType>
137
+ <xtce:IntegerParameterType name="uint32" signed="false">
138
+ <xtce:IntegerDataEncoding sizeInBits="32" encoding="unsigned" />
139
+ <xtce:UnitSet />
140
+ </xtce:IntegerParameterType>
141
+ <xtce:IntegerParameterType name="uint48" signed="false">
142
+ <xtce:IntegerDataEncoding sizeInBits="48" encoding="unsigned" />
143
+ <xtce:UnitSet />
144
+ </xtce:IntegerParameterType>
145
+ </xtce:ParameterTypeSet>
146
+ <xtce:ParameterSet>
147
+ <!-- Within the ParameterSet, utilize the data types defined in the ParameterTypeSet to create variables
148
+ with their respective data types. -->
149
+
150
+ <!--CCSDS Header Elements-->
151
+ <xtce:Parameter name="VERSION" parameterTypeRef="uint3">
152
+ <xtce:LongDescription>CCSDS Packet Version Number (always 0)</xtce:LongDescription>
153
+ </xtce:Parameter>
154
+ <xtce:Parameter name="TYPE" parameterTypeRef="uint1">
155
+ <xtce:LongDescription>CCSDS Packet Type Indicator (0=telemetry)</xtce:LongDescription>
156
+ </xtce:Parameter>
157
+ <xtce:Parameter name="SEC_HDR_FLG" parameterTypeRef="uint1">
158
+ <xtce:LongDescription>CCSDS Packet Secondary Header Flag (always 1)</xtce:LongDescription>
159
+ </xtce:Parameter>
160
+ <xtce:Parameter name="PKT_APID" parameterTypeRef="uint11">
161
+ <xtce:LongDescription>CCSDS Packet Application Process ID</xtce:LongDescription>
162
+ </xtce:Parameter>
163
+ <xtce:Parameter name="SEQ_FLGS" parameterTypeRef="uint2">
164
+ <xtce:LongDescription>CCSDS Packet Grouping Flags (3=not part of group)</xtce:LongDescription>
165
+ </xtce:Parameter>
166
+ <xtce:Parameter name="SRC_SEQ_CTR" parameterTypeRef="uint14">
167
+ <xtce:LongDescription>CCSDS Packet Sequence Count (increments with each new packet)</xtce:LongDescription>
168
+ </xtce:Parameter>
169
+ <xtce:Parameter name="PKT_LEN" parameterTypeRef="uint16">
170
+ <xtce:LongDescription>CCSDS Packet Length (number of bytes after Packet length minus 1)</xtce:LongDescription>
171
+ </xtce:Parameter>
172
+ <!-- CoDICE LO -->
173
+ <xtce:Parameter name="COD_LO_SHCOARSE" parameterTypeRef="uint32">
174
+ <xtce:LongDescription>CODICE LO MET at End of Data Acquisition</xtce:LongDescription>
175
+ </xtce:Parameter>
176
+ <xtce:Parameter name="COD_LO_ACQ" parameterTypeRef="uint32">
177
+ <xtce:LongDescription>CODICE LO MET at End of Data Acquisition</xtce:LongDescription>
178
+ </xtce:Parameter>
179
+ <xtce:Parameter name="COD_LO_STATUS" parameterTypeRef="uint8">
180
+ <xtce:LongDescription>CODICE LO Status</xtce:LongDescription>
181
+ </xtce:Parameter>
182
+ <xtce:Parameter name="COD_LO_COUNTER" parameterTypeRef="uint8">
183
+ <xtce:LongDescription>CODICE LO Counter</xtce:LongDescription>
184
+ </xtce:Parameter>
185
+ <xtce:Parameter name="COD_LO_DATA_00" parameterTypeRef="uint8">
186
+ <xtce:LongDescription>CODICE LO Data_00</xtce:LongDescription>
187
+ </xtce:Parameter>
188
+ <xtce:Parameter name="COD_LO_DATA_01" parameterTypeRef="uint8">
189
+ <xtce:LongDescription>CODICE LO Data_01</xtce:LongDescription>
190
+ </xtce:Parameter>
191
+ <xtce:Parameter name="COD_LO_DATA_02" parameterTypeRef="uint8">
192
+ <xtce:LongDescription>CODICE LO Data_02</xtce:LongDescription>
193
+ </xtce:Parameter>
194
+ <xtce:Parameter name="COD_LO_DATA_03" parameterTypeRef="uint8">
195
+ <xtce:LongDescription>CODICE LO Data_03</xtce:LongDescription>
196
+ </xtce:Parameter>
197
+ <xtce:Parameter name="COD_LO_DATA_04" parameterTypeRef="uint8">
198
+ <xtce:LongDescription>CODICE LO Data_04</xtce:LongDescription>
199
+ </xtce:Parameter>
200
+ <xtce:Parameter name="COD_LO_DATA_05" parameterTypeRef="uint8">
201
+ <xtce:LongDescription>CODICE LO Data_05</xtce:LongDescription>
202
+ </xtce:Parameter>
203
+ <xtce:Parameter name="COD_LO_DATA_06" parameterTypeRef="uint8">
204
+ <xtce:LongDescription>CODICE LO Data_06</xtce:LongDescription>
205
+ </xtce:Parameter>
206
+ <xtce:Parameter name="COD_LO_DATA_07" parameterTypeRef="uint8">
207
+ <xtce:LongDescription>CODICE LO Data_07</xtce:LongDescription>
208
+ </xtce:Parameter>
209
+ <xtce:Parameter name="COD_LO_DATA_08" parameterTypeRef="uint8">
210
+ <xtce:LongDescription>CODICE LO Data_08</xtce:LongDescription>
211
+ </xtce:Parameter>
212
+ <xtce:Parameter name="COD_LO_DATA_09" parameterTypeRef="uint8">
213
+ <xtce:LongDescription>CODICE LO Data_09</xtce:LongDescription>
214
+ </xtce:Parameter>
215
+ <xtce:Parameter name="COD_LO_DATA_10" parameterTypeRef="uint8">
216
+ <xtce:LongDescription>CODICE LO Data_10</xtce:LongDescription>
217
+ </xtce:Parameter>
218
+ <xtce:Parameter name="COD_LO_DATA_11" parameterTypeRef="uint8">
219
+ <xtce:LongDescription>CODICE LO Data_11</xtce:LongDescription>
220
+ </xtce:Parameter>
221
+ <xtce:Parameter name="COD_LO_DATA_12" parameterTypeRef="uint8">
222
+ <xtce:LongDescription>CODICE LO Data_12</xtce:LongDescription>
223
+ </xtce:Parameter>
224
+ <xtce:Parameter name="COD_LO_DATA_13" parameterTypeRef="uint8">
225
+ <xtce:LongDescription>CODICE LO Data_13</xtce:LongDescription>
226
+ </xtce:Parameter>
227
+ <xtce:Parameter name="COD_LO_DATA_14" parameterTypeRef="uint8">
228
+ <xtce:LongDescription>CODICE LO Data_14</xtce:LongDescription>
229
+ </xtce:Parameter>
230
+ <xtce:Parameter name="COD_LO_SPARE_0" parameterTypeRef="uint8">
231
+ <xtce:LongDescription>CODICE LO Spare 0</xtce:LongDescription>
232
+ </xtce:Parameter>
233
+ <!-- CoDICE LO -->
234
+ </xtce:ParameterSet>
235
+ <!-- End metadata -->
236
+ <xtce:ContainerSet>
237
+ <xtce:SequenceContainer name="CCSDSPacket" >
238
+ <xtce:EntryList>
239
+ <xtce:ParameterRefEntry parameterRef="VERSION" />
240
+ <xtce:ParameterRefEntry parameterRef="TYPE" />
241
+ <xtce:ParameterRefEntry parameterRef="SEC_HDR_FLG" />
242
+ <xtce:ParameterRefEntry parameterRef="PKT_APID" />
243
+ <xtce:ParameterRefEntry parameterRef="SEQ_FLGS" />
244
+ <xtce:ParameterRefEntry parameterRef="SRC_SEQ_CTR" />
245
+ <xtce:ParameterRefEntry parameterRef="PKT_LEN" />
246
+ </xtce:EntryList>
247
+ </xtce:SequenceContainer>
248
+ <xtce:SequenceContainer name="IALiRTPacket">
249
+ <xtce:BaseContainer containerRef="CCSDSPacket">
250
+ <xtce:RestrictionCriteria>
251
+ <xtce:Comparison parameterRef="PKT_APID" value="1152" useCalibratedValue="false" />
252
+ </xtce:RestrictionCriteria>
253
+ </xtce:BaseContainer>
254
+ <xtce:EntryList>
255
+ <!-- CoDICE LO -->
256
+ <xtce:ParameterRefEntry parameterRef="COD_LO_SHCOARSE"/>
257
+ <xtce:ParameterRefEntry parameterRef="COD_LO_ACQ"/>
258
+ <xtce:ParameterRefEntry parameterRef="COD_LO_STATUS"/>
259
+ <xtce:ParameterRefEntry parameterRef="COD_LO_COUNTER"/>
260
+ <xtce:ParameterRefEntry parameterRef="COD_LO_DATA_00"/>
261
+ <xtce:ParameterRefEntry parameterRef="COD_LO_DATA_01"/>
262
+ <xtce:ParameterRefEntry parameterRef="COD_LO_DATA_02"/>
263
+ <xtce:ParameterRefEntry parameterRef="COD_LO_DATA_03"/>
264
+ <xtce:ParameterRefEntry parameterRef="COD_LO_DATA_04"/>
265
+ <xtce:ParameterRefEntry parameterRef="COD_LO_DATA_05"/>
266
+ <xtce:ParameterRefEntry parameterRef="COD_LO_DATA_06"/>
267
+ <xtce:ParameterRefEntry parameterRef="COD_LO_DATA_07"/>
268
+ <xtce:ParameterRefEntry parameterRef="COD_LO_DATA_08"/>
269
+ <xtce:ParameterRefEntry parameterRef="COD_LO_DATA_09"/>
270
+ <xtce:ParameterRefEntry parameterRef="COD_LO_DATA_10"/>
271
+ <xtce:ParameterRefEntry parameterRef="COD_LO_DATA_11"/>
272
+ <xtce:ParameterRefEntry parameterRef="COD_LO_DATA_12"/>
273
+ <xtce:ParameterRefEntry parameterRef="COD_LO_DATA_13"/>
274
+ <xtce:ParameterRefEntry parameterRef="COD_LO_DATA_14"/>
275
+ <xtce:ParameterRefEntry parameterRef="COD_LO_SPARE_0"/>
276
+ <!-- CoDICE LO -->
277
+ </xtce:EntryList>
278
+ </xtce:SequenceContainer>
279
+ </xtce:ContainerSet>
280
+ </xtce:TelemetryMetaData>
281
+ </xtce:SpaceSystem>
@@ -0,0 +1,212 @@
1
+ """
2
+ Find azimuth (degrees), elevation (degrees), and doppler shift (Hz).
3
+
4
+ Based on ephemeris data and ground station location (longitude, latitude, altitude).
5
+
6
+ Reference: https://spiceypy.readthedocs.io/en/main/documentation.html.
7
+ """
8
+
9
+ import logging
10
+ import typing
11
+ from typing import Union
12
+
13
+ import numpy as np
14
+ import spiceypy as spice
15
+ from numpy import ndarray
16
+
17
+ from imap_processing.spice.geometry import SpiceBody
18
+ from imap_processing.spice.kernels import ensure_spice
19
+ from imap_processing.spice.time import et_to_utc, str_to_et
20
+
21
+ # Logger setup
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ @typing.no_type_check
26
+ def calculate_doppler(
27
+ observation_time: Union[float, np.ndarray],
28
+ ) -> Union[int, ndarray[float]]:
29
+ """
30
+ Calculate the doppler shift. Placeholder for now.
31
+
32
+ Parameters
33
+ ----------
34
+ observation_time : float or np.ndarray
35
+ Time at which the state of the target relative to the observer
36
+ is to be computed. Expressed as ephemeris time, seconds past J2000 TDB.
37
+
38
+ Returns
39
+ -------
40
+ doppler : float or np.ndarray[float]
41
+ Doppler shift. Currently a throwaway value.
42
+ """
43
+ if isinstance(observation_time, np.ndarray):
44
+ return np.ones(len(observation_time), dtype=float)
45
+ else:
46
+ return 1
47
+
48
+
49
+ @typing.no_type_check
50
+ @ensure_spice
51
+ def latitude_longitude_to_ecef(
52
+ longitude: float, latitude: float, altitude: float
53
+ ) -> ndarray:
54
+ """
55
+ Convert geodetic coordinates to rectangular coordinates.
56
+
57
+ Earth-Centered, Earth-Fixed (ECEF) coordinates are a Cartesian coordinate system
58
+ with an origin at the center of the Earth.
59
+
60
+ Parameters
61
+ ----------
62
+ longitude : float
63
+ Longitude in decimal degrees. Positive east of prime meridian, negative to west.
64
+ latitude : float
65
+ Latitude in decimal degrees. Positive north of equator, negative to south.
66
+ altitude : float
67
+ Altitude in kilometers.
68
+
69
+ Returns
70
+ -------
71
+ rect_coords : ndarray
72
+ Rectangular coordinates in kilometers.
73
+ """
74
+ latitude_radians = np.deg2rad(latitude)
75
+ longitude_radians = np.deg2rad(longitude)
76
+
77
+ # Retrieve Earth's radii from SPICE
78
+ # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.bod
79
+ # (url cont.) vrd
80
+ radii = spice.bodvrd("EARTH", "RADII", 3)[1]
81
+ equatorial_radius = radii[0] # Equatorial radius in km
82
+ polar_radius = radii[2] # Polar radius in km
83
+ flattening = (equatorial_radius - polar_radius) / equatorial_radius
84
+
85
+ # Convert geodetic coordinates to rectangular coordinates
86
+ # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.geo
87
+ # (url cont.) rec
88
+ rect_coords = spice.georec(
89
+ longitude_radians, latitude_radians, altitude, equatorial_radius, flattening
90
+ )
91
+
92
+ return rect_coords
93
+
94
+
95
+ @typing.no_type_check
96
+ @ensure_spice
97
+ def calculate_azimuth_and_elevation(
98
+ longitude: float,
99
+ latitude: float,
100
+ altitude: float,
101
+ observation_time: Union[float, np.ndarray],
102
+ target: SpiceBody = SpiceBody.IMAP.name,
103
+ ) -> tuple:
104
+ """
105
+ Calculate azimuth and elevation.
106
+
107
+ Parameters
108
+ ----------
109
+ longitude : float
110
+ Longitude in decimal degrees. Positive east of prime meridian,
111
+ negative to west.
112
+ latitude : float
113
+ Latitude in decimal degrees. Positive north of equator, negative
114
+ to south.
115
+ altitude : float
116
+ Altitude in kilometers.
117
+ observation_time : float or np.ndarray
118
+ Time at which the state of the target relative to the observer
119
+ is to be computed. Expressed as ephemeris time, seconds past J2000 TDB.
120
+ target : str (Optional)
121
+ The target body. Default is "IMAP".
122
+
123
+ Returns
124
+ -------
125
+ azimuth : np.ndarray
126
+ Azimuth in degrees.
127
+ elevation : np.ndarray
128
+ Elevation in degrees.
129
+ """
130
+ observer_position_ecef = latitude_longitude_to_ecef(longitude, latitude, altitude)
131
+
132
+ if not isinstance(observation_time, np.ndarray):
133
+ observation_time = [observation_time]
134
+
135
+ azimuth = []
136
+ elevation = []
137
+
138
+ # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.azlcpo
139
+ for timestamp in observation_time:
140
+ azel_results = spice.azlcpo(
141
+ method="Ellipsoid", # Only method supported
142
+ target=target, # target ephemeris object
143
+ et=timestamp, # time of observation
144
+ abcorr="LT+S", # Aberration correction
145
+ azccw=False, # Azimuth measured clockwise from the positive y-axis
146
+ elplsz=True, # Elevation increases from the XY plane toward +Z
147
+ obspos=observer_position_ecef, # observer pos. to center of motion
148
+ obsctr="EARTH", # Name of the center of motion
149
+ obsref="IAU_EARTH", # Body-fixed, body-centered reference frame wrt
150
+ # observer's center
151
+ )
152
+ azimuth.append(np.rad2deg(azel_results[0][1]))
153
+ elevation.append(np.rad2deg(azel_results[0][2]))
154
+
155
+ # TODO: potentially use the velocity components returned from azlcpo to
156
+ # TODO: calculate doppler
157
+
158
+ return np.asarray(azimuth), np.asarray(elevation)
159
+
160
+
161
+ def build_output(
162
+ longitude: float,
163
+ latitude: float,
164
+ altitude: float,
165
+ time_endpoints: tuple[str, str],
166
+ time_step: float,
167
+ ) -> dict[str, np.ndarray]:
168
+ """
169
+ Build the output dictionary containing time, azimuth, elevation, and doppler.
170
+
171
+ Parameters
172
+ ----------
173
+ longitude : float
174
+ Longitude in decimal degrees. Positive east of prime meridian, negative to west.
175
+ latitude : float
176
+ Latitude in decimal degrees. Positive north of equator, negative to south.
177
+ altitude : float
178
+ Altitude in kilometers.
179
+ time_endpoints : tuple[str, str]
180
+ Start and stop times in UTC.
181
+ time_step : float
182
+ Seconds between data points.
183
+
184
+ Returns
185
+ -------
186
+ output_dict: dict[str, np.ndarray]
187
+ Keys are time, azimuth, elevation and doppler. Values are calculated for every
188
+ timestamp between start_utc_input and stop_utc_input, spaced by time_step.
189
+ """
190
+ output_dict: dict[str, np.ndarray] = {}
191
+
192
+ start_et_input = str_to_et(time_endpoints[0])
193
+ stop_et_input = str_to_et(time_endpoints[1])
194
+ time_range = np.arange(start_et_input, stop_et_input, time_step)
195
+
196
+ # For now, assume that kernel management will be handled by ensure spice
197
+ # for obs_time in np.arange(start_et_input, stop_et_input, time_step):
198
+ azimuth, elevation = calculate_azimuth_and_elevation(
199
+ longitude, latitude, altitude, time_range
200
+ )
201
+
202
+ output_dict["time"] = et_to_utc(time_range, format_str="ISOC")
203
+ output_dict["azimuth"] = azimuth
204
+ output_dict["elevation"] = elevation
205
+ output_dict["doppler"] = calculate_doppler(time_range)
206
+
207
+ logger.info(
208
+ f"Calculated azimuth, elevation and doppler for time range from "
209
+ f"{start_et_input} to {stop_et_input}."
210
+ )
211
+
212
+ return output_dict