imap-processing 0.17.0__py3-none-any.whl → 0.19.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/ancillary/ancillary_dataset_combiner.py +161 -1
- imap_processing/ccsds/excel_to_xtce.py +12 -0
- imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +6 -6
- imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +312 -274
- imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +39 -28
- imap_processing/cdf/config/imap_codice_l2_variable_attrs.yaml +1048 -183
- imap_processing/cdf/config/imap_constant_attrs.yaml +4 -2
- imap_processing/cdf/config/imap_glows_l1b_variable_attrs.yaml +12 -0
- imap_processing/cdf/config/imap_hi_global_cdf_attrs.yaml +5 -0
- imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +10 -4
- imap_processing/cdf/config/imap_hit_l1a_variable_attrs.yaml +163 -100
- imap_processing/cdf/config/imap_hit_l2_variable_attrs.yaml +4 -4
- imap_processing/cdf/config/imap_ialirt_l1_variable_attrs.yaml +97 -54
- imap_processing/cdf/config/imap_idex_l2a_variable_attrs.yaml +33 -4
- imap_processing/cdf/config/imap_idex_l2b_variable_attrs.yaml +44 -44
- imap_processing/cdf/config/imap_idex_l2c_variable_attrs.yaml +77 -61
- imap_processing/cdf/config/imap_lo_global_cdf_attrs.yaml +30 -0
- imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +4 -15
- imap_processing/cdf/config/imap_lo_l1c_variable_attrs.yaml +189 -98
- imap_processing/cdf/config/imap_mag_global_cdf_attrs.yaml +99 -2
- imap_processing/cdf/config/imap_mag_l1c_variable_attrs.yaml +24 -1
- imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +60 -0
- imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +99 -11
- imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +50 -7
- imap_processing/cli.py +121 -44
- imap_processing/codice/codice_l1a.py +165 -77
- imap_processing/codice/codice_l1b.py +1 -1
- imap_processing/codice/codice_l2.py +118 -19
- imap_processing/codice/constants.py +1217 -1089
- imap_processing/decom.py +1 -4
- imap_processing/ena_maps/ena_maps.py +32 -25
- imap_processing/ena_maps/utils/naming.py +8 -2
- imap_processing/glows/ancillary/imap_glows_exclusions-by-instr-team_20250923_v002.dat +10 -0
- imap_processing/glows/ancillary/imap_glows_map-of-excluded-regions_20250923_v002.dat +393 -0
- imap_processing/glows/ancillary/imap_glows_map-of-uv-sources_20250923_v002.dat +593 -0
- imap_processing/glows/ancillary/imap_glows_pipeline_settings_20250923_v002.json +54 -0
- imap_processing/glows/ancillary/imap_glows_suspected-transients_20250923_v002.dat +10 -0
- imap_processing/glows/l1b/glows_l1b.py +99 -9
- imap_processing/glows/l1b/glows_l1b_data.py +350 -38
- imap_processing/glows/l2/glows_l2.py +11 -0
- imap_processing/hi/hi_l1a.py +124 -3
- imap_processing/hi/hi_l1b.py +154 -71
- imap_processing/hi/hi_l2.py +84 -51
- imap_processing/hi/utils.py +153 -8
- imap_processing/hit/l0/constants.py +3 -0
- imap_processing/hit/l0/decom_hit.py +5 -8
- imap_processing/hit/l1a/hit_l1a.py +375 -45
- imap_processing/hit/l1b/constants.py +5 -0
- imap_processing/hit/l1b/hit_l1b.py +61 -131
- imap_processing/hit/l2/constants.py +1 -1
- imap_processing/hit/l2/hit_l2.py +10 -11
- imap_processing/ialirt/calculate_ingest.py +219 -0
- imap_processing/ialirt/constants.py +32 -1
- imap_processing/ialirt/generate_coverage.py +201 -0
- imap_processing/ialirt/l0/ialirt_spice.py +5 -2
- imap_processing/ialirt/l0/parse_mag.py +337 -29
- imap_processing/ialirt/l0/process_hit.py +5 -3
- imap_processing/ialirt/l0/process_swapi.py +41 -25
- imap_processing/ialirt/l0/process_swe.py +23 -7
- imap_processing/ialirt/process_ephemeris.py +70 -14
- imap_processing/ialirt/utils/constants.py +22 -16
- imap_processing/ialirt/utils/create_xarray.py +42 -19
- imap_processing/idex/idex_constants.py +1 -5
- imap_processing/idex/idex_l0.py +2 -2
- imap_processing/idex/idex_l1a.py +2 -3
- imap_processing/idex/idex_l1b.py +2 -3
- imap_processing/idex/idex_l2a.py +130 -4
- imap_processing/idex/idex_l2b.py +313 -119
- imap_processing/idex/idex_utils.py +1 -3
- imap_processing/lo/l0/lo_apid.py +1 -0
- imap_processing/lo/l0/lo_science.py +25 -24
- imap_processing/lo/l1a/lo_l1a.py +44 -0
- imap_processing/lo/l1b/lo_l1b.py +3 -3
- imap_processing/lo/l1c/lo_l1c.py +116 -50
- imap_processing/lo/l2/lo_l2.py +29 -29
- imap_processing/lo/lo_ancillary.py +55 -0
- imap_processing/lo/packet_definitions/lo_xtce.xml +5359 -106
- imap_processing/mag/constants.py +1 -0
- imap_processing/mag/l1a/mag_l1a.py +1 -0
- imap_processing/mag/l1a/mag_l1a_data.py +26 -0
- imap_processing/mag/l1b/mag_l1b.py +3 -2
- imap_processing/mag/l1c/interpolation_methods.py +14 -15
- imap_processing/mag/l1c/mag_l1c.py +23 -6
- imap_processing/mag/l1d/__init__.py +0 -0
- imap_processing/mag/l1d/mag_l1d.py +176 -0
- imap_processing/mag/l1d/mag_l1d_data.py +725 -0
- imap_processing/mag/l2/__init__.py +0 -0
- imap_processing/mag/l2/mag_l2.py +25 -20
- imap_processing/mag/l2/mag_l2_data.py +199 -130
- imap_processing/quality_flags.py +28 -2
- imap_processing/spice/geometry.py +101 -36
- imap_processing/spice/pointing_frame.py +1 -7
- imap_processing/spice/repoint.py +29 -2
- imap_processing/spice/spin.py +32 -8
- imap_processing/spice/time.py +60 -19
- imap_processing/swapi/l1/swapi_l1.py +10 -4
- imap_processing/swapi/l2/swapi_l2.py +66 -24
- imap_processing/swapi/swapi_utils.py +1 -1
- imap_processing/swe/l1b/swe_l1b.py +3 -6
- imap_processing/ultra/constants.py +28 -3
- imap_processing/ultra/l0/decom_tools.py +15 -8
- imap_processing/ultra/l0/decom_ultra.py +35 -11
- imap_processing/ultra/l0/ultra_utils.py +102 -12
- imap_processing/ultra/l1a/ultra_l1a.py +26 -6
- imap_processing/ultra/l1b/cullingmask.py +6 -3
- imap_processing/ultra/l1b/de.py +122 -26
- imap_processing/ultra/l1b/extendedspin.py +29 -2
- imap_processing/ultra/l1b/lookup_utils.py +424 -50
- imap_processing/ultra/l1b/quality_flag_filters.py +23 -0
- imap_processing/ultra/l1b/ultra_l1b_culling.py +356 -5
- imap_processing/ultra/l1b/ultra_l1b_extended.py +534 -90
- imap_processing/ultra/l1c/helio_pset.py +127 -7
- imap_processing/ultra/l1c/l1c_lookup_utils.py +256 -0
- imap_processing/ultra/l1c/spacecraft_pset.py +90 -15
- imap_processing/ultra/l1c/ultra_l1c.py +6 -0
- imap_processing/ultra/l1c/ultra_l1c_culling.py +85 -0
- imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +446 -341
- imap_processing/ultra/l2/ultra_l2.py +0 -1
- imap_processing/ultra/utils/ultra_l1_utils.py +40 -3
- imap_processing/utils.py +3 -4
- {imap_processing-0.17.0.dist-info → imap_processing-0.19.0.dist-info}/METADATA +3 -3
- {imap_processing-0.17.0.dist-info → imap_processing-0.19.0.dist-info}/RECORD +126 -126
- imap_processing/idex/idex_l2c.py +0 -250
- imap_processing/spice/kernels.py +0 -187
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_LeftSlit.csv +0 -526
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_RightSlit.csv +0 -526
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_LeftSlit.csv +0 -526
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_RightSlit.csv +0 -524
- imap_processing/ultra/lookup_tables/EgyNorm.mem.csv +0 -32769
- imap_processing/ultra/lookup_tables/FM45_Startup1_ULTRA_IMGPARAMS_20240719.csv +0 -2
- imap_processing/ultra/lookup_tables/FM90_Startup1_ULTRA_IMGPARAMS_20240719.csv +0 -2
- imap_processing/ultra/lookup_tables/dps_grid45_compressed.cdf +0 -0
- imap_processing/ultra/lookup_tables/ultra45_back-pos-luts.csv +0 -4097
- imap_processing/ultra/lookup_tables/ultra45_tdc_norm.csv +0 -2050
- imap_processing/ultra/lookup_tables/ultra90_back-pos-luts.csv +0 -4097
- imap_processing/ultra/lookup_tables/ultra90_tdc_norm.csv +0 -2050
- imap_processing/ultra/lookup_tables/yadjust.csv +0 -257
- {imap_processing-0.17.0.dist-info → imap_processing-0.19.0.dist-info}/LICENSE +0 -0
- {imap_processing-0.17.0.dist-info → imap_processing-0.19.0.dist-info}/WHEEL +0 -0
- {imap_processing-0.17.0.dist-info → imap_processing-0.19.0.dist-info}/entry_points.txt +0 -0
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
from decimal import Decimal
|
|
5
|
-
from typing import Optional
|
|
6
5
|
|
|
7
6
|
import numpy as np
|
|
8
7
|
import pandas as pd
|
|
@@ -10,13 +9,12 @@ import xarray as xr
|
|
|
10
9
|
from scipy.optimize import curve_fit
|
|
11
10
|
from scipy.special import erf
|
|
12
11
|
|
|
13
|
-
from imap_processing import imap_module_directory
|
|
14
12
|
from imap_processing.ialirt.constants import IalirtSwapiConstants as Consts
|
|
15
13
|
from imap_processing.ialirt.utils.grouping import find_groups
|
|
16
14
|
from imap_processing.ialirt.utils.time import calculate_time
|
|
17
15
|
from imap_processing.spice.time import met_to_ttj2000ns, met_to_utc
|
|
18
16
|
from imap_processing.swapi.l1.swapi_l1 import process_sweep_data
|
|
19
|
-
from imap_processing.swapi.l2.swapi_l2 import
|
|
17
|
+
from imap_processing.swapi.l2.swapi_l2 import SWAPI_LIVETIME
|
|
20
18
|
|
|
21
19
|
logger = logging.getLogger(__name__)
|
|
22
20
|
|
|
@@ -70,7 +68,7 @@ def count_rate(
|
|
|
70
68
|
def optimize_pseudo_parameters(
|
|
71
69
|
count_rates: np.ndarray,
|
|
72
70
|
count_rate_error: np.ndarray,
|
|
73
|
-
energy_passbands:
|
|
71
|
+
energy_passbands: np.ndarray,
|
|
74
72
|
) -> (dict)[str, list[float]]:
|
|
75
73
|
"""
|
|
76
74
|
Find the pseudo speed (u), density (n) and temperature (T) of solar wind particles.
|
|
@@ -84,7 +82,7 @@ def optimize_pseudo_parameters(
|
|
|
84
82
|
count_rate_error : np.ndarray
|
|
85
83
|
Standard deviation of the coincidence count rates parameter.
|
|
86
84
|
energy_passbands : np.ndarray, default None
|
|
87
|
-
Energy
|
|
85
|
+
Energy values, taken from the SWAPI lookup table.
|
|
88
86
|
|
|
89
87
|
Returns
|
|
90
88
|
-------
|
|
@@ -92,21 +90,6 @@ def optimize_pseudo_parameters(
|
|
|
92
90
|
Dictionary containing the optimized speed, density, and temperature values for
|
|
93
91
|
each sweep included in the input count_rates array.
|
|
94
92
|
"""
|
|
95
|
-
if not energy_passbands:
|
|
96
|
-
# Read in energy passbands
|
|
97
|
-
energy_data = pd.read_csv(
|
|
98
|
-
f"{imap_module_directory}/tests/swapi/lut/imap_swapi_esa-unit"
|
|
99
|
-
f"-conversion_20250211_v000.csv"
|
|
100
|
-
)
|
|
101
|
-
energy_passbands = (
|
|
102
|
-
energy_data["Energy"][0:63]
|
|
103
|
-
.replace(",", "", regex=True)
|
|
104
|
-
.to_numpy()
|
|
105
|
-
.astype(float)
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
# Initial guess pulled from page 52 of the IMAP SWAPI Instrument Algorithms Document
|
|
109
|
-
initial_param_guess = np.array([550, 5.27, 1e5])
|
|
110
93
|
solution_dict = { # type: ignore
|
|
111
94
|
"pseudo_speed": [],
|
|
112
95
|
"pseudo_density": [],
|
|
@@ -116,8 +99,16 @@ def optimize_pseudo_parameters(
|
|
|
116
99
|
for sweep in np.arange(count_rates.shape[0]):
|
|
117
100
|
current_sweep_count_rates = count_rates[sweep, :]
|
|
118
101
|
current_sweep_count_rate_errors = count_rate_error[sweep, :]
|
|
119
|
-
# Find the max count rate, and use the
|
|
102
|
+
# Find the max count rate, and use the 5 points surrounding it
|
|
120
103
|
max_index = np.argmax(current_sweep_count_rates)
|
|
104
|
+
initial_speed_guess = np.sqrt(energy_passbands[max_index]) * Consts.speed_coeff
|
|
105
|
+
initial_param_guess = np.array(
|
|
106
|
+
[
|
|
107
|
+
initial_speed_guess,
|
|
108
|
+
5 * (400 / initial_speed_guess) ** 2,
|
|
109
|
+
60000 * (initial_speed_guess / 400) ** 2,
|
|
110
|
+
]
|
|
111
|
+
)
|
|
121
112
|
sol = curve_fit(
|
|
122
113
|
f=count_rate,
|
|
123
114
|
xdata=energy_passbands.take(
|
|
@@ -138,7 +129,9 @@ def optimize_pseudo_parameters(
|
|
|
138
129
|
return solution_dict
|
|
139
130
|
|
|
140
131
|
|
|
141
|
-
def process_swapi_ialirt(
|
|
132
|
+
def process_swapi_ialirt(
|
|
133
|
+
unpacked_data: xr.Dataset, calibration_lut_table: pd.DataFrame
|
|
134
|
+
) -> list[dict]:
|
|
142
135
|
"""
|
|
143
136
|
Extract I-ALiRT variables and calculate coincidence count rate.
|
|
144
137
|
|
|
@@ -146,6 +139,8 @@ def process_swapi_ialirt(unpacked_data: xr.Dataset) -> list[dict]:
|
|
|
146
139
|
----------
|
|
147
140
|
unpacked_data : xr.Dataset
|
|
148
141
|
SWAPI I-ALiRT data that has been parsed from the spacecraft packet.
|
|
142
|
+
calibration_lut_table : pd.DataFrame
|
|
143
|
+
DataFrame containing the contents of the SWAPI esa-unit-conversion lookup table.
|
|
149
144
|
|
|
150
145
|
Returns
|
|
151
146
|
-------
|
|
@@ -191,10 +186,31 @@ def process_swapi_ialirt(unpacked_data: xr.Dataset) -> list[dict]:
|
|
|
191
186
|
continue
|
|
192
187
|
|
|
193
188
|
raw_coin_count = process_sweep_data(grouped_dataset, "swapi_coin_cnt")
|
|
194
|
-
raw_coin_rate = raw_coin_count /
|
|
195
|
-
count_rate_error = np.sqrt(raw_coin_count) /
|
|
189
|
+
raw_coin_rate = raw_coin_count / SWAPI_LIVETIME
|
|
190
|
+
count_rate_error = np.sqrt(raw_coin_count) / SWAPI_LIVETIME
|
|
196
191
|
|
|
197
|
-
|
|
192
|
+
# Extract energy values from the calibration lookup table file
|
|
193
|
+
calibration_lut_table["timestamp"] = pd.to_datetime(
|
|
194
|
+
calibration_lut_table["timestamp"], format="%m/%d/%Y %H:%M"
|
|
195
|
+
)
|
|
196
|
+
calibration_lut_table["timestamp"] = calibration_lut_table["timestamp"].to_numpy(
|
|
197
|
+
dtype="datetime64[ns]"
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
# Find the sweep's energy data for the latest time, where sweep_id == 2
|
|
201
|
+
subset = calibration_lut_table[
|
|
202
|
+
(calibration_lut_table["timestamp"] == calibration_lut_table["timestamp"].max())
|
|
203
|
+
& (calibration_lut_table["Sweep #"] == 2)
|
|
204
|
+
]
|
|
205
|
+
if subset.empty:
|
|
206
|
+
energy_passbands = np.full(63, np.nan, dtype=np.float64)
|
|
207
|
+
else:
|
|
208
|
+
subset = subset.sort_values(["timestamp", "ESA Step #"])
|
|
209
|
+
energy_passbands = subset["Energy"][0:63].to_numpy().astype(float)
|
|
210
|
+
|
|
211
|
+
solution = optimize_pseudo_parameters(
|
|
212
|
+
raw_coin_rate, count_rate_error, energy_passbands
|
|
213
|
+
)
|
|
198
214
|
|
|
199
215
|
swapi_data = []
|
|
200
216
|
|
|
@@ -542,16 +542,32 @@ def process_swe(accumulated_data: xr.Dataset, in_flight_cal_files: list) -> list
|
|
|
542
542
|
summed_first = normalized_first_half.sum(axis=(1, 2))
|
|
543
543
|
summed_second = normalized_second_half.sum(axis=(1, 2))
|
|
544
544
|
|
|
545
|
+
met_first_half = int(
|
|
546
|
+
grouped["met"].where(grouped["swe_seq"] == 0, drop=True).values[0]
|
|
547
|
+
)
|
|
548
|
+
met_second_half = int(
|
|
549
|
+
grouped["met"].where(grouped["swe_seq"] == 30, drop=True).values[0]
|
|
550
|
+
)
|
|
551
|
+
|
|
552
|
+
swe_data.append(
|
|
553
|
+
{
|
|
554
|
+
"apid": 478,
|
|
555
|
+
"met": met_first_half,
|
|
556
|
+
"met_in_utc": met_to_utc(met_first_half).split(".")[0],
|
|
557
|
+
"ttj2000ns": int(met_to_ttj2000ns(met_first_half)),
|
|
558
|
+
"swe_normalized_counts": [int(val) for val in summed_first],
|
|
559
|
+
"swe_counterstreaming_electrons": bde_first_half,
|
|
560
|
+
},
|
|
561
|
+
)
|
|
545
562
|
swe_data.append(
|
|
546
563
|
{
|
|
547
564
|
"apid": 478,
|
|
548
|
-
"met":
|
|
549
|
-
"met_in_utc": met_to_utc(
|
|
550
|
-
"ttj2000ns": int(met_to_ttj2000ns(
|
|
551
|
-
"
|
|
552
|
-
"
|
|
553
|
-
|
|
554
|
-
}
|
|
565
|
+
"met": met_second_half,
|
|
566
|
+
"met_in_utc": met_to_utc(met_second_half).split(".")[0],
|
|
567
|
+
"ttj2000ns": int(met_to_ttj2000ns(met_second_half)),
|
|
568
|
+
"swe_normalized_counts": [int(val) for val in summed_second],
|
|
569
|
+
"swe_counterstreaming_electrons": bde_second_half,
|
|
570
|
+
},
|
|
555
571
|
)
|
|
556
572
|
|
|
557
573
|
return swe_data
|
|
@@ -8,22 +8,20 @@ Reference: https://spiceypy.readthedocs.io/en/main/documentation.html.
|
|
|
8
8
|
|
|
9
9
|
import logging
|
|
10
10
|
import typing
|
|
11
|
-
from
|
|
11
|
+
from datetime import datetime, timedelta
|
|
12
12
|
|
|
13
13
|
import numpy as np
|
|
14
14
|
import spiceypy
|
|
15
15
|
from numpy import ndarray
|
|
16
16
|
|
|
17
|
+
from imap_processing.ialirt.constants import STATIONS
|
|
17
18
|
from imap_processing.spice.geometry import SpiceBody, SpiceFrame, imap_state
|
|
18
|
-
from imap_processing.spice.kernels import ensure_spice
|
|
19
19
|
from imap_processing.spice.time import et_to_utc, str_to_et
|
|
20
20
|
|
|
21
21
|
# Logger setup
|
|
22
22
|
logger = logging.getLogger(__name__)
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
@typing.no_type_check
|
|
26
|
-
@ensure_spice
|
|
27
25
|
def latitude_longitude_to_ecef(
|
|
28
26
|
longitude: float, latitude: float, altitude: float
|
|
29
27
|
) -> ndarray:
|
|
@@ -68,12 +66,11 @@ def latitude_longitude_to_ecef(
|
|
|
68
66
|
|
|
69
67
|
|
|
70
68
|
@typing.no_type_check
|
|
71
|
-
@ensure_spice
|
|
72
69
|
def calculate_azimuth_and_elevation(
|
|
73
70
|
longitude: float,
|
|
74
71
|
latitude: float,
|
|
75
72
|
altitude: float,
|
|
76
|
-
observation_time:
|
|
73
|
+
observation_time: float | np.ndarray,
|
|
77
74
|
target: str = SpiceBody.IMAP.name,
|
|
78
75
|
) -> tuple:
|
|
79
76
|
"""
|
|
@@ -137,8 +134,8 @@ def calculate_doppler(
|
|
|
137
134
|
longitude: float,
|
|
138
135
|
latitude: float,
|
|
139
136
|
altitude: float,
|
|
140
|
-
observation_time:
|
|
141
|
-
) ->
|
|
137
|
+
observation_time: float | np.ndarray,
|
|
138
|
+
) -> float | ndarray[float]:
|
|
142
139
|
"""
|
|
143
140
|
Calculate the doppler velocity.
|
|
144
141
|
|
|
@@ -194,7 +191,7 @@ def build_output(
|
|
|
194
191
|
latitude: float,
|
|
195
192
|
altitude: float,
|
|
196
193
|
time_endpoints: tuple[str, str],
|
|
197
|
-
time_step: float,
|
|
194
|
+
time_step: float = 60,
|
|
198
195
|
) -> dict[str, np.ndarray]:
|
|
199
196
|
"""
|
|
200
197
|
Build the output dictionary containing time, azimuth, elevation, and doppler.
|
|
@@ -210,7 +207,7 @@ def build_output(
|
|
|
210
207
|
time_endpoints : tuple[str, str]
|
|
211
208
|
Start and stop times in UTC.
|
|
212
209
|
time_step : float
|
|
213
|
-
Seconds between data points.
|
|
210
|
+
Seconds between data points. Default is 60.
|
|
214
211
|
|
|
215
212
|
Returns
|
|
216
213
|
-------
|
|
@@ -230,10 +227,10 @@ def build_output(
|
|
|
230
227
|
)
|
|
231
228
|
|
|
232
229
|
output_dict["time"] = et_to_utc(time_range, format_str="ISOC")
|
|
233
|
-
output_dict["azimuth"] = azimuth
|
|
234
|
-
output_dict["elevation"] = elevation
|
|
235
|
-
output_dict["doppler"] =
|
|
236
|
-
longitude, latitude, altitude, time_range
|
|
230
|
+
output_dict["azimuth"] = np.round(azimuth, 6)
|
|
231
|
+
output_dict["elevation"] = np.round(elevation, 6)
|
|
232
|
+
output_dict["doppler"] = np.round(
|
|
233
|
+
calculate_doppler(longitude, latitude, altitude, time_range), 6
|
|
237
234
|
)
|
|
238
235
|
|
|
239
236
|
logger.info(
|
|
@@ -242,3 +239,62 @@ def build_output(
|
|
|
242
239
|
)
|
|
243
240
|
|
|
244
241
|
return output_dict
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def generate_text_files(station: str, day: str) -> list[str]:
|
|
245
|
+
"""
|
|
246
|
+
Generate a pointing schedule text file and return it as a list of strings.
|
|
247
|
+
|
|
248
|
+
Parameters
|
|
249
|
+
----------
|
|
250
|
+
station : str
|
|
251
|
+
Station name.
|
|
252
|
+
day : str
|
|
253
|
+
The day for which to generate a pointing schedule, in ISO format.
|
|
254
|
+
Ex: "2025-08-11".
|
|
255
|
+
|
|
256
|
+
Returns
|
|
257
|
+
-------
|
|
258
|
+
lines : list[str]
|
|
259
|
+
A list of strings that makeup the lines of a pointing schedule file.
|
|
260
|
+
"""
|
|
261
|
+
station_properties = STATIONS[station]
|
|
262
|
+
|
|
263
|
+
day_as_datetime = datetime.fromisoformat(day)
|
|
264
|
+
time_endpoints = (
|
|
265
|
+
datetime.strftime(day_as_datetime, "%Y-%m-%d %H:%M:%S"),
|
|
266
|
+
datetime.strftime(day_as_datetime + timedelta(days=1), "%Y-%m-%d %H:%M:%S"),
|
|
267
|
+
)
|
|
268
|
+
output_dict = build_output(
|
|
269
|
+
station_properties[0],
|
|
270
|
+
station_properties[1],
|
|
271
|
+
station_properties[2],
|
|
272
|
+
time_endpoints,
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
lines = [
|
|
276
|
+
f"Station: {station}\n",
|
|
277
|
+
"Target: IMAP\n",
|
|
278
|
+
f"Creation date (UTC): {datetime.utcnow()}\n",
|
|
279
|
+
f"Start time: {time_endpoints[0]}\n",
|
|
280
|
+
f"End time: {time_endpoints[1]}\n",
|
|
281
|
+
"Cadence (sec): 60\n\n",
|
|
282
|
+
"Date/Time"
|
|
283
|
+
+ "Azimuth".rjust(29)
|
|
284
|
+
+ "Elevation".rjust(17)
|
|
285
|
+
+ "Doppler".rjust(15)
|
|
286
|
+
+ "\n",
|
|
287
|
+
"(UTC)" + "(deg.)".rjust(33) + "(deg.)".rjust(16) + "(km/s)".rjust(16) + "\n",
|
|
288
|
+
]
|
|
289
|
+
|
|
290
|
+
length = len(output_dict["time"])
|
|
291
|
+
for i in range(length):
|
|
292
|
+
lines.append(
|
|
293
|
+
f"{output_dict['time'][i]}"
|
|
294
|
+
+ f"{output_dict['azimuth'][i]}".rjust(16)
|
|
295
|
+
+ f"{output_dict['elevation'][i]}".rjust(16)
|
|
296
|
+
+ f"{output_dict['doppler'][i]}".rjust(15)
|
|
297
|
+
+ "\n"
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
return lines
|
|
@@ -2,19 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
IALIRT_KEYS = [
|
|
4
4
|
# H intensities in 15 energy ranges and binned into 4 azimuths and 4 spin angle bins
|
|
5
|
-
"
|
|
5
|
+
"codice_hi_h",
|
|
6
6
|
# C/O abundance ratio
|
|
7
|
-
"
|
|
7
|
+
"codice_lo_c_over_o_abundance",
|
|
8
8
|
# Mg/O abundance ratio
|
|
9
|
-
"
|
|
9
|
+
"codice_lo_mg_over_o_abundance",
|
|
10
10
|
# Fe/O abundance ratio
|
|
11
|
-
"
|
|
11
|
+
"codice_lo_fe_over_o_abundance",
|
|
12
12
|
# C+6/C+5 charge state ratio
|
|
13
|
-
"
|
|
13
|
+
"codice_lo_c_plus_6_over_c_plus_5_ratio",
|
|
14
14
|
# O+7/O+6 charge state ratio
|
|
15
|
-
"
|
|
15
|
+
"codice_lo_o_plus_7_over_o_plus_6_ratio",
|
|
16
16
|
# Fe low/Fe high charge state ratio
|
|
17
|
-
"
|
|
17
|
+
"codice_lo_fe_low_over_fe_high_ratio",
|
|
18
18
|
# Low energy (~300 keV) electrons (A-side)
|
|
19
19
|
"hit_e_a_side_low_en",
|
|
20
20
|
# Medium energy (~3 MeV) electrons (A-side)
|
|
@@ -37,26 +37,32 @@ IALIRT_KEYS = [
|
|
|
37
37
|
"hit_he_omni_low_en",
|
|
38
38
|
# High energy (15 to 70 MeV/nuc) He (Omnidirectional)
|
|
39
39
|
"hit_he_omni_high_en",
|
|
40
|
+
# MAG instrument epoch
|
|
41
|
+
"mag_epoch",
|
|
40
42
|
# Magnetic field vector in GSE coordinates
|
|
41
|
-
"
|
|
43
|
+
"mag_B_GSE",
|
|
42
44
|
# Magnetic field vector in GSM coordinates
|
|
43
|
-
"
|
|
45
|
+
"mag_B_GSM",
|
|
44
46
|
# Magnetic field vector in RTN coordinates
|
|
45
|
-
"
|
|
47
|
+
"mag_B_RTN",
|
|
48
|
+
# Magnitude of the magnetic field vector
|
|
49
|
+
"mag_B_magnitude",
|
|
46
50
|
# Azimuth angle (φ) of the magnetic field in GSM coordinates
|
|
47
|
-
"
|
|
51
|
+
"mag_phi_B_GSM",
|
|
48
52
|
# Elevation angle (θ) of the magnetic field in GSM coordinates
|
|
49
|
-
"
|
|
53
|
+
"mag_theta_B_GSM",
|
|
54
|
+
# Azimuth angle (φ) of the magnetic field in GSE coordinates
|
|
55
|
+
"mag_phi_B_GSE",
|
|
56
|
+
# Elevation angle (θ) of the magnetic field in GSE coordinates
|
|
57
|
+
"mag_theta_B_GSE",
|
|
50
58
|
# Pseudo density of solar wind protons
|
|
51
59
|
"swapi_pseudo_proton_density",
|
|
52
60
|
# Pseudo speed of solar wind protons in solar inertial frame
|
|
53
61
|
"swapi_pseudo_proton_speed",
|
|
54
62
|
# Pseudo temperature of solar wind protons in plasma frame
|
|
55
63
|
"swapi_pseudo_proton_temperature",
|
|
56
|
-
# SWE Normalized Counts
|
|
57
|
-
"
|
|
58
|
-
# SWE Normalized Counts - Half Cycle 2
|
|
59
|
-
"swe_normalized_counts_half_2",
|
|
64
|
+
# SWE Normalized Counts
|
|
65
|
+
"swe_normalized_counts",
|
|
60
66
|
# SWE Counterstreaming flag
|
|
61
67
|
"swe_counterstreaming_electrons",
|
|
62
68
|
]
|
|
@@ -48,6 +48,13 @@ def create_xarray_from_records(records: list[dict]) -> xr.Dataset: # noqa: PLR0
|
|
|
48
48
|
attrs=cdf_manager.get_variable_attributes("component", check_schema=False),
|
|
49
49
|
)
|
|
50
50
|
|
|
51
|
+
rtn_component = xr.DataArray(
|
|
52
|
+
["radial", "tangential", "normal"],
|
|
53
|
+
name="RTN_component",
|
|
54
|
+
dims=["RTN_component"],
|
|
55
|
+
attrs=cdf_manager.get_variable_attributes("RTN_componentt", check_schema=False),
|
|
56
|
+
)
|
|
57
|
+
|
|
51
58
|
esa_step = xr.DataArray(
|
|
52
59
|
data=np.arange(8, dtype=np.uint8),
|
|
53
60
|
name="esa_step",
|
|
@@ -57,32 +64,39 @@ def create_xarray_from_records(records: list[dict]) -> xr.Dataset: # noqa: PLR0
|
|
|
57
64
|
|
|
58
65
|
energy_ranges = xr.DataArray(
|
|
59
66
|
data=np.arange(15, dtype=np.uint8),
|
|
60
|
-
name="
|
|
61
|
-
dims=["
|
|
62
|
-
attrs=cdf_manager.get_variable_attributes(
|
|
67
|
+
name="codice_hi_h_energy_ranges",
|
|
68
|
+
dims=["codice_hi_h_energy_ranges"],
|
|
69
|
+
attrs=cdf_manager.get_variable_attributes(
|
|
70
|
+
"codice_hi_h_energy_ranges", check_schema=False
|
|
71
|
+
),
|
|
63
72
|
)
|
|
64
73
|
|
|
65
|
-
|
|
74
|
+
elevation = xr.DataArray(
|
|
66
75
|
data=np.arange(4, dtype=np.uint8),
|
|
67
|
-
name="
|
|
68
|
-
dims=["
|
|
69
|
-
attrs=cdf_manager.get_variable_attributes(
|
|
76
|
+
name="codice_hi_h_elevation",
|
|
77
|
+
dims=["codice_hi_h_elevation"],
|
|
78
|
+
attrs=cdf_manager.get_variable_attributes(
|
|
79
|
+
"codice_hi_h_elevation", check_schema=False
|
|
80
|
+
),
|
|
70
81
|
)
|
|
71
82
|
|
|
72
|
-
|
|
83
|
+
spin_angle = xr.DataArray(
|
|
73
84
|
data=np.arange(4, dtype=np.uint8),
|
|
74
|
-
name="
|
|
75
|
-
dims=["
|
|
76
|
-
attrs=cdf_manager.get_variable_attributes(
|
|
85
|
+
name="codice_hi_h_spin_angle",
|
|
86
|
+
dims=["codice_hi_h_spin_angle"],
|
|
87
|
+
attrs=cdf_manager.get_variable_attributes(
|
|
88
|
+
"codice_hi_h_spin_anglen", check_schema=False
|
|
89
|
+
),
|
|
77
90
|
)
|
|
78
91
|
|
|
79
92
|
coords = {
|
|
80
93
|
"epoch": epoch,
|
|
81
94
|
"component": component,
|
|
95
|
+
"RTN_component": rtn_component,
|
|
82
96
|
"esa_step": esa_step,
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"
|
|
97
|
+
"codice_hi_h_energy_ranges": energy_ranges,
|
|
98
|
+
"codice_hi_h_elevation": elevation,
|
|
99
|
+
"codice_hi_h_spin_angle": spin_angle,
|
|
86
100
|
}
|
|
87
101
|
dataset = xr.Dataset(
|
|
88
102
|
coords=coords,
|
|
@@ -93,13 +107,22 @@ def create_xarray_from_records(records: list[dict]) -> xr.Dataset: # noqa: PLR0
|
|
|
93
107
|
for key in instrument_keys:
|
|
94
108
|
attrs = cdf_manager.get_variable_attributes(key, check_schema=False)
|
|
95
109
|
fillval = attrs.get("FILLVAL")
|
|
96
|
-
if key
|
|
110
|
+
if key in ["mag_B_GSE", "mag_B_GSM"]:
|
|
97
111
|
data = np.full((n, 3), fillval, dtype=np.float32)
|
|
98
112
|
dims = ["epoch", "component"]
|
|
99
113
|
dataset[key] = xr.DataArray(data, dims=dims, attrs=attrs)
|
|
100
|
-
elif key
|
|
114
|
+
elif key == "mag_B_RTN":
|
|
115
|
+
data = np.full((n, 3), fillval, dtype=np.float32)
|
|
116
|
+
dims = ["epoch", "RTN_component"]
|
|
117
|
+
dataset[key] = xr.DataArray(data, dims=dims, attrs=attrs)
|
|
118
|
+
elif key.startswith("codice_hi"):
|
|
101
119
|
data = np.full((n, 15, 4, 4), fillval, dtype=np.float32)
|
|
102
|
-
dims = [
|
|
120
|
+
dims = [
|
|
121
|
+
"epoch",
|
|
122
|
+
"codice_hi_h_energy_ranges",
|
|
123
|
+
"codice_hi_h_elevation",
|
|
124
|
+
"codice_hi_h_spin_angle",
|
|
125
|
+
]
|
|
103
126
|
dataset[key] = xr.DataArray(data, dims=dims, attrs=attrs)
|
|
104
127
|
elif key == "swe_counterstreaming_electrons":
|
|
105
128
|
data = np.full(n, fillval, dtype=np.uint8)
|
|
@@ -123,11 +146,11 @@ def create_xarray_from_records(records: list[dict]) -> xr.Dataset: # noqa: PLR0
|
|
|
123
146
|
for key, val in record.items():
|
|
124
147
|
if key in ["apid", "met", "met_in_utc", "ttj2000ns"]:
|
|
125
148
|
continue
|
|
126
|
-
elif key
|
|
149
|
+
elif key in ["mag_B_GSE", "mag_B_GSM", "mag_B_RTN"]:
|
|
127
150
|
dataset[key].data[i, :] = val
|
|
128
151
|
elif key.startswith("swe_normalized_counts"):
|
|
129
152
|
dataset[key].data[i, :] = val
|
|
130
|
-
elif key.startswith("
|
|
153
|
+
elif key.startswith("codice_hi"):
|
|
131
154
|
dataset[key].data[i, :, :, :] = val
|
|
132
155
|
else:
|
|
133
156
|
dataset[key].data[i] = val
|
|
@@ -82,13 +82,9 @@ SPICE_ARRAYS = [
|
|
|
82
82
|
"spin_phase",
|
|
83
83
|
]
|
|
84
84
|
|
|
85
|
-
# Default IDEX Healpix parameters
|
|
86
|
-
# Used in IDEX l2c processing
|
|
87
|
-
IDEX_HEALPIX_NSIDE = 8
|
|
88
|
-
IDEX_HEALPIX_NESTED = False
|
|
89
85
|
# Default IDEX Rectangular parameters
|
|
90
86
|
# Used in IDEX l2c processing
|
|
91
|
-
IDEX_SPACING_DEG =
|
|
87
|
+
IDEX_SPACING_DEG = 6
|
|
92
88
|
|
|
93
89
|
# Define the pointing reference frame for IDEX
|
|
94
90
|
IDEX_EVENT_REFERENCE_FRAME = SpiceFrame.ECLIPJ2000
|
imap_processing/idex/idex_l0.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import Any
|
|
5
|
+
from typing import Any
|
|
6
6
|
|
|
7
7
|
from xarray import Dataset
|
|
8
8
|
|
|
@@ -13,7 +13,7 @@ logger = logging.getLogger(__name__)
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
def decom_packets(
|
|
16
|
-
packet_file:
|
|
16
|
+
packet_file: str | Path,
|
|
17
17
|
) -> tuple[list[Any], dict[int, Dataset], dict[int, Dataset]]:
|
|
18
18
|
"""
|
|
19
19
|
Decom IDEX data packets using IDEX packet definition.
|
imap_processing/idex/idex_l1a.py
CHANGED
|
@@ -17,7 +17,6 @@ Examples
|
|
|
17
17
|
import logging
|
|
18
18
|
from enum import IntEnum
|
|
19
19
|
from pathlib import Path
|
|
20
|
-
from typing import Union
|
|
21
20
|
|
|
22
21
|
import numpy as np
|
|
23
22
|
import numpy.typing as npt
|
|
@@ -61,7 +60,7 @@ class PacketParser:
|
|
|
61
60
|
The path and filename to the L0 file to read.
|
|
62
61
|
"""
|
|
63
62
|
|
|
64
|
-
def __init__(self, packet_file:
|
|
63
|
+
def __init__(self, packet_file: str | Path) -> None:
|
|
65
64
|
"""
|
|
66
65
|
Read a L0 pkts file and perform all of the decom work.
|
|
67
66
|
|
|
@@ -250,7 +249,7 @@ def _read_waveform_bits(waveform_raw: str, high_sample: bool = True) -> list[int
|
|
|
250
249
|
|
|
251
250
|
|
|
252
251
|
def calculate_idex_epoch_time(
|
|
253
|
-
shcoarse_time:
|
|
252
|
+
shcoarse_time: float | np.ndarray, shfine_time: float | np.ndarray
|
|
254
253
|
) -> npt.NDArray[np.int64]:
|
|
255
254
|
"""
|
|
256
255
|
Calculate the epoch time from the FPGA header time variables.
|
imap_processing/idex/idex_l1b.py
CHANGED
|
@@ -16,7 +16,6 @@ Examples
|
|
|
16
16
|
|
|
17
17
|
import logging
|
|
18
18
|
from enum import Enum
|
|
19
|
-
from typing import Union
|
|
20
19
|
|
|
21
20
|
import pandas as pd
|
|
22
21
|
import xarray as xr
|
|
@@ -226,7 +225,7 @@ def convert_waveforms(
|
|
|
226
225
|
|
|
227
226
|
def get_trigger_mode_and_level(
|
|
228
227
|
l1a_dataset: xr.Dataset,
|
|
229
|
-
) ->
|
|
228
|
+
) -> dict[str, xr.DataArray] | dict:
|
|
230
229
|
"""
|
|
231
230
|
Determine the trigger mode and threshold level for each event.
|
|
232
231
|
|
|
@@ -249,7 +248,7 @@ def get_trigger_mode_and_level(
|
|
|
249
248
|
|
|
250
249
|
def compute_trigger_values(
|
|
251
250
|
trigger_mode: int, trigger_controls: int, gain_channel: str
|
|
252
|
-
) ->
|
|
251
|
+
) -> tuple[str, int | float] | tuple[None, None]:
|
|
253
252
|
"""
|
|
254
253
|
Compute the trigger mode label and threshold level.
|
|
255
254
|
|