imap-processing 0.8.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.
- imap_processing/_version.py +2 -2
- imap_processing/ccsds/excel_to_xtce.py +2 -0
- imap_processing/cdf/config/imap_hi_variable_attrs.yaml +100 -1
- imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +14 -0
- imap_processing/cdf/config/imap_hit_l1a_variable_attrs.yaml +63 -1
- imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +7 -0
- imap_processing/cdf/config/imap_idex_l1a_variable_attrs.yaml +574 -231
- imap_processing/cdf/config/imap_idex_l1b_variable_attrs.yaml +326 -0
- imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +33 -23
- imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +7 -4
- imap_processing/cdf/utils.py +3 -5
- imap_processing/cli.py +13 -4
- imap_processing/codice/codice_l1a.py +5 -5
- imap_processing/codice/constants.py +9 -9
- imap_processing/codice/decompress.py +6 -2
- imap_processing/glows/l1a/glows_l1a.py +1 -2
- imap_processing/hi/l1a/hi_l1a.py +4 -4
- imap_processing/hi/l1a/histogram.py +106 -108
- imap_processing/hi/l1a/science_direct_event.py +91 -224
- imap_processing/hi/packet_definitions/TLM_HI_COMBINED_SCI.xml +3994 -0
- imap_processing/hit/l0/constants.py +2 -2
- imap_processing/hit/l0/decom_hit.py +12 -101
- imap_processing/hit/l1a/hit_l1a.py +164 -23
- imap_processing/ialirt/l0/process_codicelo.py +153 -0
- imap_processing/ialirt/l0/process_hit.py +5 -5
- imap_processing/ialirt/packet_definitions/ialirt_codicelo.xml +281 -0
- imap_processing/ialirt/process_ephemeris.py +212 -0
- imap_processing/idex/idex_l1a.py +55 -75
- imap_processing/idex/idex_l1b.py +192 -0
- imap_processing/idex/idex_variable_unpacking_and_eu_conversion.csv +33 -0
- imap_processing/idex/packet_definitions/idex_packet_definition.xml +97 -595
- imap_processing/lo/l0/decompression_tables/decompression_tables.py +16 -0
- imap_processing/lo/l0/lo_science.py +44 -12
- imap_processing/lo/l1a/lo_l1a.py +76 -8
- imap_processing/lo/packet_definitions/lo_xtce.xml +9877 -87
- imap_processing/mag/l1a/mag_l1a.py +1 -2
- imap_processing/mag/l1a/mag_l1a_data.py +1 -2
- imap_processing/mag/l1b/mag_l1b.py +2 -1
- imap_processing/spice/geometry.py +37 -19
- imap_processing/spice/time.py +144 -2
- imap_processing/swapi/l1/swapi_l1.py +3 -3
- imap_processing/swapi/packet_definitions/swapi_packet_definition.xml +1535 -446
- imap_processing/swe/l2/swe_l2.py +134 -17
- imap_processing/tests/ccsds/test_data/expected_output.xml +1 -1
- imap_processing/tests/codice/test_codice_l1a.py +8 -8
- imap_processing/tests/codice/test_decompress.py +4 -4
- imap_processing/tests/conftest.py +46 -43
- imap_processing/tests/hi/test_data/l0/H90_NHK_20241104.bin +0 -0
- imap_processing/tests/hi/test_data/l0/H90_sci_cnt_20241104.bin +0 -0
- imap_processing/tests/hi/test_data/l0/H90_sci_de_20241104.bin +0 -0
- imap_processing/tests/hi/test_hi_l1b.py +2 -2
- imap_processing/tests/hi/test_l1a.py +31 -58
- imap_processing/tests/hi/test_science_direct_event.py +58 -0
- imap_processing/tests/hit/test_data/sci_sample1.ccsds +0 -0
- imap_processing/tests/hit/test_decom_hit.py +60 -50
- imap_processing/tests/hit/test_hit_l1a.py +327 -12
- imap_processing/tests/hit/test_hit_l1b.py +76 -0
- imap_processing/tests/hit/validation_data/hskp_sample_eu.csv +89 -0
- imap_processing/tests/hit/validation_data/sci_sample_raw1.csv +29 -0
- imap_processing/tests/ialirt/test_data/l0/apid01152.tlm +0 -0
- imap_processing/tests/ialirt/test_data/l0/imap_codice_l1a_lo-ialirt_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/ialirt/unit/test_process_codicelo.py +106 -0
- imap_processing/tests/ialirt/unit/test_process_ephemeris.py +109 -0
- imap_processing/tests/ialirt/unit/test_process_hit.py +9 -6
- imap_processing/tests/idex/conftest.py +1 -1
- imap_processing/tests/idex/test_idex_l0.py +1 -1
- imap_processing/tests/idex/test_idex_l1a.py +7 -1
- imap_processing/tests/idex/test_idex_l1b.py +126 -0
- imap_processing/tests/lo/test_lo_l1a.py +7 -16
- imap_processing/tests/lo/test_lo_science.py +67 -3
- imap_processing/tests/lo/test_pkts/imap_lo_l0_raw_20240803_v002.pkts +0 -0
- imap_processing/tests/lo/validation_data/Instrument_FM1_T104_R129_20240803_ILO_SCI_DE_dec_DN_with_fills.csv +1999 -0
- imap_processing/tests/mag/test_mag_l1b.py +39 -5
- imap_processing/tests/spice/test_geometry.py +32 -6
- imap_processing/tests/spice/test_time.py +135 -6
- imap_processing/tests/swapi/test_swapi_decom.py +75 -69
- imap_processing/tests/swapi/test_swapi_l1.py +4 -4
- imap_processing/tests/swe/test_swe_l2.py +64 -8
- imap_processing/tests/test_utils.py +1 -1
- imap_processing/tests/ultra/test_data/l0/ultra45_raw_sc_ultrarawimg_withFSWcalcs_FM45_40P_Phi28p5_BeamCal_LinearScan_phi2850_theta-000_20240207T102740.csv +3314 -3314
- imap_processing/tests/ultra/unit/test_de.py +8 -3
- imap_processing/tests/ultra/unit/test_spatial_utils.py +125 -0
- imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +39 -29
- imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +2 -25
- imap_processing/ultra/constants.py +4 -0
- imap_processing/ultra/l1b/de.py +8 -14
- imap_processing/ultra/l1b/ultra_l1b_extended.py +29 -70
- imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +1 -36
- imap_processing/ultra/utils/spatial_utils.py +221 -0
- {imap_processing-0.8.0.dist-info → imap_processing-0.9.0.dist-info}/METADATA +1 -1
- {imap_processing-0.8.0.dist-info → imap_processing-0.9.0.dist-info}/RECORD +94 -76
- imap_processing/hi/l0/__init__.py +0 -0
- imap_processing/hi/l0/decom_hi.py +0 -24
- imap_processing/hi/packet_definitions/hi_packet_definition.xml +0 -482
- imap_processing/tests/hi/test_decom.py +0 -55
- imap_processing/tests/hi/test_l1a_sci_de.py +0 -72
- {imap_processing-0.8.0.dist-info → imap_processing-0.9.0.dist-info}/LICENSE +0 -0
- {imap_processing-0.8.0.dist-info → imap_processing-0.9.0.dist-info}/WHEEL +0 -0
- {imap_processing-0.8.0.dist-info → imap_processing-0.9.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"""IMAP Ultra utils for spatial binning and grid creation."""
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
from numpy.typing import NDArray
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def build_spatial_bins(
|
|
10
|
+
az_spacing: float = 0.5,
|
|
11
|
+
el_spacing: float = 0.5,
|
|
12
|
+
) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
|
|
13
|
+
"""
|
|
14
|
+
Build spatial bin boundaries for azimuth and elevation.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
az_spacing : float, optional
|
|
19
|
+
The azimuth bin spacing in degrees (default is 0.5 degrees).
|
|
20
|
+
el_spacing : float, optional
|
|
21
|
+
The elevation bin spacing in degrees (default is 0.5 degrees).
|
|
22
|
+
|
|
23
|
+
Returns
|
|
24
|
+
-------
|
|
25
|
+
az_bin_edges : np.ndarray
|
|
26
|
+
Array of azimuth bin boundary values.
|
|
27
|
+
el_bin_edges : np.ndarray
|
|
28
|
+
Array of elevation bin boundary values.
|
|
29
|
+
az_bin_midpoints : np.ndarray
|
|
30
|
+
Array of azimuth bin midpoint values.
|
|
31
|
+
el_bin_midpoints : np.ndarray
|
|
32
|
+
Array of elevation bin midpoint values.
|
|
33
|
+
"""
|
|
34
|
+
# Azimuth bins from 0 to 360 degrees.
|
|
35
|
+
az_bin_edges = np.arange(0, 360 + az_spacing, az_spacing)
|
|
36
|
+
az_bin_midpoints = az_bin_edges[:-1] + az_spacing / 2 # Midpoints between edges
|
|
37
|
+
|
|
38
|
+
# Elevation bins from -90 to 90 degrees.
|
|
39
|
+
el_bin_edges = np.arange(-90, 90 + el_spacing, el_spacing)
|
|
40
|
+
el_bin_midpoints = el_bin_edges[:-1] + el_spacing / 2 # Midpoints between edges
|
|
41
|
+
|
|
42
|
+
return az_bin_edges, el_bin_edges, az_bin_midpoints, el_bin_midpoints
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def build_solid_angle_map(
|
|
46
|
+
spacing: float, input_degrees: bool = True, output_degrees: bool = False
|
|
47
|
+
) -> NDArray:
|
|
48
|
+
"""
|
|
49
|
+
Build a solid angle map for a given spacing in degrees.
|
|
50
|
+
|
|
51
|
+
Parameters
|
|
52
|
+
----------
|
|
53
|
+
spacing : float
|
|
54
|
+
The bin spacing in the specified units.
|
|
55
|
+
input_degrees : bool, optional
|
|
56
|
+
If True, the input spacing is in degrees
|
|
57
|
+
(default is True for radians).
|
|
58
|
+
output_degrees : bool, optional
|
|
59
|
+
If True, the output solid angle map is in square degrees
|
|
60
|
+
(default is False for steradians).
|
|
61
|
+
|
|
62
|
+
Returns
|
|
63
|
+
-------
|
|
64
|
+
solid_angle_grid : np.ndarray
|
|
65
|
+
The solid angle map grid in steradians (default) or square degrees.
|
|
66
|
+
First index is latitude/el, second index is longitude/az.
|
|
67
|
+
"""
|
|
68
|
+
if input_degrees:
|
|
69
|
+
spacing = np.deg2rad(spacing)
|
|
70
|
+
|
|
71
|
+
if spacing <= 0:
|
|
72
|
+
raise ValueError("Spacing must be positive valued, non-zero.")
|
|
73
|
+
|
|
74
|
+
if not np.isclose((np.pi / spacing) % 1, 0):
|
|
75
|
+
raise ValueError("Spacing must divide evenly into pi radians.")
|
|
76
|
+
|
|
77
|
+
latitudes = np.arange(-np.pi / 2, np.pi / 2 + spacing, step=spacing)
|
|
78
|
+
sine_latitudes = np.sin(latitudes)
|
|
79
|
+
delta_sine_latitudes = np.diff(sine_latitudes)
|
|
80
|
+
solid_angle_by_latitude = np.abs(spacing * delta_sine_latitudes)
|
|
81
|
+
|
|
82
|
+
solid_angle_grid = np.repeat(
|
|
83
|
+
solid_angle_by_latitude[:, np.newaxis], (2 * np.pi) / spacing, axis=1
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
if output_degrees:
|
|
87
|
+
solid_angle_grid *= (180 / np.pi) ** 2
|
|
88
|
+
|
|
89
|
+
return solid_angle_grid
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def build_az_el_grid(
|
|
93
|
+
spacing: float,
|
|
94
|
+
input_degrees: bool = False,
|
|
95
|
+
output_degrees: bool = False,
|
|
96
|
+
centered_azimuth: bool = False,
|
|
97
|
+
centered_elevation: bool = True,
|
|
98
|
+
) -> tuple[NDArray, NDArray, NDArray, NDArray]:
|
|
99
|
+
"""
|
|
100
|
+
Build a 2D grid of azimuth and elevation angles.
|
|
101
|
+
|
|
102
|
+
Azimuth and Elevation values represent the center of each grid cell,
|
|
103
|
+
so the grid is offset by half the spacing.
|
|
104
|
+
|
|
105
|
+
Parameters
|
|
106
|
+
----------
|
|
107
|
+
spacing : float
|
|
108
|
+
Spacing of the grid in degrees if `input_degrees` is True, else radians.
|
|
109
|
+
input_degrees : bool, optional
|
|
110
|
+
Whether the spacing is specified in degrees and must be converted to radians,
|
|
111
|
+
by default False (indicating radians).
|
|
112
|
+
output_degrees : bool, optional
|
|
113
|
+
Whether the output azimuth and elevation angles should be in degrees,
|
|
114
|
+
by default False (indicating radians).
|
|
115
|
+
centered_azimuth : bool, optional
|
|
116
|
+
Whether the azimuth grid should be centered around 0 degrees/0 radians,
|
|
117
|
+
i.e. from -pi to pi radians, by default False, indicating 0 to 2pi radians.
|
|
118
|
+
If True, the azimuth grid will be from -pi to pi radians.
|
|
119
|
+
centered_elevation : bool, optional
|
|
120
|
+
Whether the elevation grid should be centered around 0 degrees/0 radians,
|
|
121
|
+
i.e. from -pi/2 to pi/2 radians, by default True.
|
|
122
|
+
If False, the elevation grid will be from 0 to pi radians.
|
|
123
|
+
|
|
124
|
+
Returns
|
|
125
|
+
-------
|
|
126
|
+
tuple[NDArray, NDArray, NDArray, NDArray]
|
|
127
|
+
- The evenly spaced, 1D range of azimuth angles
|
|
128
|
+
e.g.(0, 0.5, 1, ..., 359.5) deg.
|
|
129
|
+
- The evenly spaced, 1D range of elevation angles
|
|
130
|
+
e.g.(-90, -89.5, ..., 89.5) deg.
|
|
131
|
+
- The 2D grid of azimuth angles (azimuths for each elevation).
|
|
132
|
+
This grid will be constant along the elevation (0th) axis.
|
|
133
|
+
- The 2D grid of elevation angles (elevations for each azimuth).
|
|
134
|
+
This grid will be constant along the azimuth (1st) axis.
|
|
135
|
+
|
|
136
|
+
Raises
|
|
137
|
+
------
|
|
138
|
+
ValueError
|
|
139
|
+
If the spacing is not positive or does not divide evenly into pi radians.
|
|
140
|
+
"""
|
|
141
|
+
if input_degrees:
|
|
142
|
+
spacing = np.deg2rad(spacing)
|
|
143
|
+
|
|
144
|
+
if spacing <= 0:
|
|
145
|
+
raise ValueError("Spacing must be positive valued, non-zero.")
|
|
146
|
+
|
|
147
|
+
if not np.isclose((np.pi / spacing) % 1, 0):
|
|
148
|
+
raise ValueError("Spacing must divide evenly into pi radians.")
|
|
149
|
+
|
|
150
|
+
el_range = np.arange(spacing / 2, np.pi, spacing)
|
|
151
|
+
az_range = np.arange(spacing / 2, 2 * np.pi, spacing)
|
|
152
|
+
if centered_azimuth:
|
|
153
|
+
az_range = az_range - np.pi
|
|
154
|
+
if centered_elevation:
|
|
155
|
+
el_range = el_range - np.pi / 2
|
|
156
|
+
|
|
157
|
+
# Reverse the elevation range so that the grid is in the order
|
|
158
|
+
# defined by the Ultra prototype code (`build_dps_grid.m`).
|
|
159
|
+
el_range = el_range[::-1]
|
|
160
|
+
|
|
161
|
+
az_grid, el_grid = np.meshgrid(az_range, el_range)
|
|
162
|
+
|
|
163
|
+
if output_degrees:
|
|
164
|
+
az_range = np.rad2deg(az_range)
|
|
165
|
+
el_range = np.rad2deg(el_range)
|
|
166
|
+
az_grid = np.rad2deg(az_grid)
|
|
167
|
+
el_grid = np.rad2deg(el_grid)
|
|
168
|
+
|
|
169
|
+
return az_range, el_range, az_grid, el_grid
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
@typing.no_type_check
|
|
173
|
+
def rewrap_even_spaced_el_az_grid(
|
|
174
|
+
raveled_values: NDArray,
|
|
175
|
+
shape: typing.Optional[tuple[int]] = None,
|
|
176
|
+
extra_axis: bool = False,
|
|
177
|
+
) -> NDArray:
|
|
178
|
+
"""
|
|
179
|
+
Take an unwrapped (raveled) 1D array and reshapes it into a 2D el/az grid.
|
|
180
|
+
|
|
181
|
+
Assumes the following must be true of the original grid:
|
|
182
|
+
1. Grid was evenly spaced in angular space,
|
|
183
|
+
2. Grid had the same spacing in both azimuth and elevation.
|
|
184
|
+
3. Elevation is the 0th axis (and extends a total of 180 degrees),
|
|
185
|
+
4. Azimuth is the 1st axis (and extends a total of 360 degrees).
|
|
186
|
+
5. The grid was raveled in Fortran (F) order.
|
|
187
|
+
|
|
188
|
+
Parameters
|
|
189
|
+
----------
|
|
190
|
+
raveled_values : NDArray
|
|
191
|
+
1D array of values to be reshaped into a 2D grid.
|
|
192
|
+
shape : tuple[int], optional
|
|
193
|
+
The shape of the original grid, if known, by default None.
|
|
194
|
+
If None, the shape will be inferred from the size of the input array.
|
|
195
|
+
extra_axis : bool, optional
|
|
196
|
+
If True, input is a 2D array with latter axis being 'extra', non-spatial axis.
|
|
197
|
+
This axis (e.g. energy bins) will be preserved in the reshaped grid.
|
|
198
|
+
|
|
199
|
+
Returns
|
|
200
|
+
-------
|
|
201
|
+
NDArray
|
|
202
|
+
The reshaped 2D grid of values.
|
|
203
|
+
|
|
204
|
+
Raises
|
|
205
|
+
------
|
|
206
|
+
ValueError
|
|
207
|
+
If the input is not a 1D array or 2D array with an extra axis.
|
|
208
|
+
"""
|
|
209
|
+
if raveled_values.ndim not in (1, 2) or (
|
|
210
|
+
raveled_values.ndim == 2 and not extra_axis
|
|
211
|
+
):
|
|
212
|
+
raise ValueError("Input must be a 1D array or 2D array with extra axis.")
|
|
213
|
+
|
|
214
|
+
# We can infer the shape if its evenly spaced and 2D
|
|
215
|
+
if not shape:
|
|
216
|
+
spacing_deg = 1 / np.sqrt(raveled_values.shape[0] / (360 * 180))
|
|
217
|
+
shape = (int(180 // spacing_deg), int(360 // spacing_deg))
|
|
218
|
+
|
|
219
|
+
if extra_axis:
|
|
220
|
+
shape = (shape[0], shape[1], raveled_values.shape[1])
|
|
221
|
+
return raveled_values.reshape(shape, order="F")
|