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
|
File without changes
|
imap_processing/mag/l2/mag_l2.py
CHANGED
|
@@ -6,8 +6,7 @@ import xarray as xr
|
|
|
6
6
|
from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
|
|
7
7
|
from imap_processing.mag import imap_mag_sdc_configuration_v001 as configuration
|
|
8
8
|
from imap_processing.mag.constants import DataMode
|
|
9
|
-
from imap_processing.mag.
|
|
10
|
-
from imap_processing.mag.l2.mag_l2_data import MagL2
|
|
9
|
+
from imap_processing.mag.l2.mag_l2_data import MagL2, ValidFrames
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
def mag_l2(
|
|
@@ -77,8 +76,8 @@ def mag_l2(
|
|
|
77
76
|
always_output_mago = configuration.ALWAYS_OUTPUT_MAGO
|
|
78
77
|
|
|
79
78
|
# TODO Check that the input file matches the offsets file
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
if not np.array_equal(input_data["epoch"].data, offsets_dataset["epoch"].data):
|
|
80
|
+
raise ValueError("Input file and offsets file must have the same timestamps.")
|
|
82
81
|
|
|
83
82
|
day: np.datetime64 = day_to_process.astype("datetime64[D]")
|
|
84
83
|
|
|
@@ -86,29 +85,35 @@ def mag_l2(
|
|
|
86
85
|
calibration_dataset, day, always_output_mago
|
|
87
86
|
)
|
|
88
87
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
axis=1,
|
|
92
|
-
arr=input_data["vectors"].data,
|
|
93
|
-
calibration_matrix=calibration_matrix,
|
|
88
|
+
cal_vectors = MagL2.apply_calibration(
|
|
89
|
+
vectors=input_data["vectors"].data, calibration_matrix=calibration_matrix
|
|
94
90
|
)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
91
|
+
# level 2 vectors don't include range
|
|
92
|
+
vectors = cal_vectors[:, :3]
|
|
93
|
+
|
|
94
|
+
l2_data = MagL2(
|
|
95
|
+
vectors=vectors,
|
|
96
|
+
epoch=input_data["epoch"].data,
|
|
97
|
+
range=input_data["vectors"].data[:, 3],
|
|
98
|
+
global_attributes={},
|
|
99
|
+
quality_flags=offsets_dataset["quality_flag"].data,
|
|
100
|
+
quality_bitmask=offsets_dataset["quality_bitmask"].data,
|
|
101
|
+
data_mode=mode,
|
|
104
102
|
offsets=offsets_dataset["offsets"].data,
|
|
105
103
|
timedelta=offsets_dataset["timedeltas"].data,
|
|
106
104
|
)
|
|
105
|
+
|
|
107
106
|
attributes = ImapCdfAttributes()
|
|
108
107
|
attributes.add_instrument_global_attrs("mag")
|
|
109
|
-
# temporarily point to l1c
|
|
110
108
|
attributes.add_instrument_variable_attrs("mag", "l2")
|
|
111
|
-
|
|
109
|
+
|
|
110
|
+
# Rotate from the MAG frame into the SRF frame
|
|
111
|
+
l2_data.rotate_frame(ValidFrames.SRF)
|
|
112
|
+
imap_srf = l2_data.generate_dataset(attributes, day)
|
|
113
|
+
l2_data.rotate_frame(ValidFrames.DSRF)
|
|
114
|
+
imap_dsrf = l2_data.generate_dataset(attributes, day)
|
|
115
|
+
|
|
116
|
+
return [imap_dsrf, imap_srf]
|
|
112
117
|
|
|
113
118
|
|
|
114
119
|
def retrieve_matrix_from_l2_calibration(
|
|
@@ -7,36 +7,43 @@ import numpy as np
|
|
|
7
7
|
import xarray as xr
|
|
8
8
|
|
|
9
9
|
from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
|
|
10
|
-
from imap_processing.mag.constants import DataMode
|
|
10
|
+
from imap_processing.mag.constants import FILLVAL, DataMode
|
|
11
|
+
from imap_processing.mag.l1b.mag_l1b import calibrate_vector
|
|
12
|
+
from imap_processing.spice.geometry import SpiceFrame, frame_transform
|
|
11
13
|
from imap_processing.spice.time import (
|
|
12
14
|
et_to_ttj2000ns,
|
|
13
15
|
str_to_et,
|
|
16
|
+
ttj2000ns_to_et,
|
|
14
17
|
)
|
|
15
18
|
|
|
16
19
|
|
|
17
20
|
class ValidFrames(Enum):
|
|
18
21
|
"""SPICE reference frames for output."""
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
MAG = SpiceFrame.IMAP_MAG
|
|
24
|
+
DSRF = SpiceFrame.IMAP_DPS
|
|
25
|
+
SRF = SpiceFrame.IMAP_SPACECRAFT
|
|
26
|
+
GSE = SpiceFrame.IMAP_GSE
|
|
27
|
+
RTN = SpiceFrame.IMAP_RTN
|
|
24
28
|
|
|
25
29
|
|
|
26
|
-
@dataclass
|
|
27
|
-
class
|
|
30
|
+
@dataclass(kw_only=True)
|
|
31
|
+
class MagL2L1dBase:
|
|
28
32
|
"""
|
|
29
|
-
|
|
33
|
+
Base class for MAG L2 and L1D data.
|
|
30
34
|
|
|
31
|
-
Since
|
|
35
|
+
Since these two data levels output identical files, and share some methods, this
|
|
36
|
+
superclass captures the tools in common, while allowing each subclass to define
|
|
37
|
+
individual attributes and algorithms.
|
|
32
38
|
|
|
33
|
-
|
|
39
|
+
May also be extended for I-ALiRT.
|
|
34
40
|
|
|
35
41
|
Attributes
|
|
36
42
|
----------
|
|
37
43
|
vectors: np.ndarray
|
|
38
44
|
Magnetic field vectors of size (n, 3) where n is the number of vectors.
|
|
39
|
-
Describes (x, y, z) components of the magnetic field.
|
|
45
|
+
Describes (x, y, z) components of the magnetic field. This field is the output
|
|
46
|
+
vectors, which are nominally from the MAGo sensor.
|
|
40
47
|
epoch: np.ndarray
|
|
41
48
|
Time of each vector in J2000 seconds. Should be of length n.
|
|
42
49
|
range: np.ndarray
|
|
@@ -48,10 +55,11 @@ class MagL2:
|
|
|
48
55
|
quality_bitmask: np.ndarray
|
|
49
56
|
Quality bitmask for each vector. Should be of length n. Copied from offset
|
|
50
57
|
file in L2, marked as good always in L1D.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
58
|
+
frame:
|
|
59
|
+
The reference frame of the input vectors. Starts as the MAG instrument frame.
|
|
60
|
+
epoch_et: np.ndarray
|
|
61
|
+
The epoch timestamps converted to ET format. Used for frame transformations.
|
|
62
|
+
Calculated on first use and then saved. Should not be passed in.
|
|
55
63
|
"""
|
|
56
64
|
|
|
57
65
|
vectors: np.ndarray
|
|
@@ -62,120 +70,13 @@ class MagL2:
|
|
|
62
70
|
quality_bitmask: np.ndarray
|
|
63
71
|
data_mode: DataMode
|
|
64
72
|
magnitude: np.ndarray = field(init=False)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
timedelta: InitVar[np.ndarray] = None
|
|
68
|
-
|
|
69
|
-
def __post_init__(self, offsets: np.ndarray, timedelta: np.ndarray) -> None:
|
|
70
|
-
"""
|
|
71
|
-
Calculate the magnitude of the vectors after initialization.
|
|
72
|
-
|
|
73
|
-
Parameters
|
|
74
|
-
----------
|
|
75
|
-
offsets : np.ndarray
|
|
76
|
-
Offsets to apply to the vectors. Should be of shape (n, 3) where n is the
|
|
77
|
-
number of vectors.
|
|
78
|
-
timedelta : np.ndarray
|
|
79
|
-
Time deltas to shift the timestamps by. Should be of length n.
|
|
80
|
-
Given in seconds.
|
|
81
|
-
"""
|
|
82
|
-
if offsets is not None:
|
|
83
|
-
self.vectors = self.apply_offsets(self.vectors, offsets)
|
|
84
|
-
if timedelta is not None:
|
|
85
|
-
self.epoch = self.shift_timestamps(self.epoch, timedelta)
|
|
86
|
-
|
|
87
|
-
self.magnitude = self.calculate_magnitude(self.vectors)
|
|
88
|
-
|
|
89
|
-
@staticmethod
|
|
90
|
-
def calculate_magnitude(
|
|
91
|
-
vectors: np.ndarray,
|
|
92
|
-
) -> np.ndarray:
|
|
93
|
-
"""
|
|
94
|
-
Given a list of vectors (x, y, z), calculate the magnitude of each vector.
|
|
95
|
-
|
|
96
|
-
For an input list of vectors of size (n, 3) returns a list of magnitudes of
|
|
97
|
-
size (n,).
|
|
98
|
-
|
|
99
|
-
Parameters
|
|
100
|
-
----------
|
|
101
|
-
vectors : np.ndarray
|
|
102
|
-
Array of vectors to calculate the magnitude of.
|
|
103
|
-
|
|
104
|
-
Returns
|
|
105
|
-
-------
|
|
106
|
-
np.ndarray
|
|
107
|
-
Array of magnitudes of the input vectors.
|
|
108
|
-
"""
|
|
109
|
-
return np.linalg.norm(vectors, axis=1) # type: ignore
|
|
110
|
-
|
|
111
|
-
@staticmethod
|
|
112
|
-
def apply_offsets(vectors: np.ndarray, offsets: np.ndarray) -> np.ndarray:
|
|
113
|
-
"""
|
|
114
|
-
Apply the offsets to the vectors by adding them together.
|
|
115
|
-
|
|
116
|
-
These offsets are used to shift the vectors in the x, y, and z directions.
|
|
117
|
-
They can either be provided through a custom offsets datafile, or calculated
|
|
118
|
-
using a gradiometry algorithm.
|
|
119
|
-
|
|
120
|
-
Parameters
|
|
121
|
-
----------
|
|
122
|
-
vectors : np.ndarray
|
|
123
|
-
Array of vectors to apply the offsets to. Should be of shape (n, 3) where n
|
|
124
|
-
is the number of vectors.
|
|
125
|
-
offsets : np.ndarray
|
|
126
|
-
Array of offsets to apply to the vectors. Should be of shape (n, 3) where n
|
|
127
|
-
is the number of vectors.
|
|
128
|
-
|
|
129
|
-
Returns
|
|
130
|
-
-------
|
|
131
|
-
np.ndarray
|
|
132
|
-
Array of vectors with offsets applied. Should be of shape (n, 3).
|
|
133
|
-
"""
|
|
134
|
-
if vectors.shape[0] != offsets.shape[0]:
|
|
135
|
-
raise ValueError("Vectors and offsets must have the same length.")
|
|
136
|
-
|
|
137
|
-
offset_vectors: np.ndarray = vectors[:, :3] + offsets
|
|
138
|
-
|
|
139
|
-
# TODO: CDF files don't have NaNs. Emailed MAG to ask what this will look like.
|
|
140
|
-
# Any values where offsets is nan must also be nan
|
|
141
|
-
offset_vectors[np.isnan(offsets).any(axis=1)] = np.nan
|
|
142
|
-
|
|
143
|
-
return offset_vectors
|
|
144
|
-
|
|
145
|
-
@staticmethod
|
|
146
|
-
def shift_timestamps(epoch: np.ndarray, timedelta: np.ndarray) -> np.ndarray:
|
|
147
|
-
"""
|
|
148
|
-
Shift the timestamps by the given timedelta.
|
|
149
|
-
|
|
150
|
-
If timedelta is positive, the epochs are shifted forward in time.
|
|
151
|
-
|
|
152
|
-
Parameters
|
|
153
|
-
----------
|
|
154
|
-
epoch : np.ndarray
|
|
155
|
-
Array of timestamps to shift. Should be of length n.
|
|
156
|
-
timedelta : np.ndarray
|
|
157
|
-
Array of time deltas to shift the timestamps by. Should be the same length
|
|
158
|
-
as epoch. Given in seconds.
|
|
159
|
-
|
|
160
|
-
Returns
|
|
161
|
-
-------
|
|
162
|
-
np.ndarray
|
|
163
|
-
Shifted timestamps.
|
|
164
|
-
"""
|
|
165
|
-
if epoch.shape[0] != timedelta.shape[0]:
|
|
166
|
-
raise ValueError(
|
|
167
|
-
"Input Epoch and offsets timedeltas must be the same length."
|
|
168
|
-
)
|
|
169
|
-
|
|
170
|
-
timedelta_ns = timedelta * 1e9
|
|
171
|
-
shifted_timestamps = epoch + timedelta_ns
|
|
172
|
-
return shifted_timestamps
|
|
73
|
+
frame: ValidFrames = ValidFrames.MAG
|
|
74
|
+
epoch_et: np.ndarray | None = field(init=False, default=None)
|
|
173
75
|
|
|
174
76
|
def generate_dataset(
|
|
175
77
|
self,
|
|
176
78
|
attribute_manager: ImapCdfAttributes,
|
|
177
79
|
day: np.datetime64,
|
|
178
|
-
frame: ValidFrames = ValidFrames.dsrf,
|
|
179
80
|
) -> xr.Dataset:
|
|
180
81
|
"""
|
|
181
82
|
Generate an xarray dataset from the dataclass.
|
|
@@ -189,8 +90,6 @@ class MagL2:
|
|
|
189
90
|
CDF attributes object for the correct level.
|
|
190
91
|
day : np.datetime64
|
|
191
92
|
The 24 hour day to process, as a numpy datetime format.
|
|
192
|
-
frame : ValidFrames
|
|
193
|
-
SPICE reference frame to rotate the data into.
|
|
194
93
|
|
|
195
94
|
Returns
|
|
196
95
|
-------
|
|
@@ -199,7 +98,9 @@ class MagL2:
|
|
|
199
98
|
"""
|
|
200
99
|
self.truncate_to_24h(day)
|
|
201
100
|
|
|
202
|
-
logical_source_id =
|
|
101
|
+
logical_source_id = (
|
|
102
|
+
f"imap_mag_l2_{self.data_mode.value.lower()}-{self.frame.name.lower()}"
|
|
103
|
+
)
|
|
203
104
|
direction = xr.DataArray(
|
|
204
105
|
np.arange(3),
|
|
205
106
|
name="direction",
|
|
@@ -242,8 +143,8 @@ class MagL2:
|
|
|
242
143
|
)
|
|
243
144
|
|
|
244
145
|
quality_bitmask = xr.DataArray(
|
|
245
|
-
self.
|
|
246
|
-
name="
|
|
146
|
+
self.quality_bitmask,
|
|
147
|
+
name="quality_bitmask",
|
|
247
148
|
dims=["epoch"],
|
|
248
149
|
attrs=attribute_manager.get_variable_attributes("qf"),
|
|
249
150
|
)
|
|
@@ -298,7 +199,6 @@ class MagL2:
|
|
|
298
199
|
"""
|
|
299
200
|
if self.epoch.shape[0] != self.vectors.shape[0]:
|
|
300
201
|
raise ValueError("Timestamps and vectors are not the same shape!")
|
|
301
|
-
|
|
302
202
|
start_timestamp_j2000 = et_to_ttj2000ns(str_to_et(str(timestamp)))
|
|
303
203
|
end_timestamp_j2000 = et_to_ttj2000ns(
|
|
304
204
|
str_to_et(str(timestamp + np.timedelta64(1, "D")))
|
|
@@ -313,3 +213,172 @@ class MagL2:
|
|
|
313
213
|
self.magnitude = self.magnitude[day_start_index:day_end_index]
|
|
314
214
|
self.quality_flags = self.quality_flags[day_start_index:day_end_index]
|
|
315
215
|
self.quality_bitmask = self.quality_bitmask[day_start_index:day_end_index]
|
|
216
|
+
|
|
217
|
+
@staticmethod
|
|
218
|
+
def calculate_magnitude(
|
|
219
|
+
vectors: np.ndarray,
|
|
220
|
+
) -> np.ndarray:
|
|
221
|
+
"""
|
|
222
|
+
Given a list of vectors (x, y, z), calculate the magnitude of each vector.
|
|
223
|
+
|
|
224
|
+
For an input list of vectors of size (n, 3) returns a list of magnitudes of
|
|
225
|
+
size (n,).
|
|
226
|
+
|
|
227
|
+
Parameters
|
|
228
|
+
----------
|
|
229
|
+
vectors : np.ndarray
|
|
230
|
+
Array of vectors to calculate the magnitude of.
|
|
231
|
+
|
|
232
|
+
Returns
|
|
233
|
+
-------
|
|
234
|
+
np.ndarray
|
|
235
|
+
Array of magnitudes of the input vectors.
|
|
236
|
+
"""
|
|
237
|
+
return np.linalg.norm(vectors, axis=1)
|
|
238
|
+
|
|
239
|
+
@staticmethod
|
|
240
|
+
def apply_calibration(
|
|
241
|
+
vectors: np.ndarray, calibration_matrix: np.ndarray
|
|
242
|
+
) -> np.ndarray:
|
|
243
|
+
"""
|
|
244
|
+
Apply the calibration matrix to the vectors.
|
|
245
|
+
|
|
246
|
+
This works by repeatedly calling the function calibrate_vector on the vectors
|
|
247
|
+
input.
|
|
248
|
+
|
|
249
|
+
Parameters
|
|
250
|
+
----------
|
|
251
|
+
vectors : np.ndarray
|
|
252
|
+
Array of vectors to apply the calibration to, including x,y,z and range.
|
|
253
|
+
Should be of shape (n, 4) where n is the number of vectors.
|
|
254
|
+
calibration_matrix : np.ndarray
|
|
255
|
+
Calibration matrix to apply to the vectors. Should be of shape (3, 3, 4).
|
|
256
|
+
|
|
257
|
+
Returns
|
|
258
|
+
-------
|
|
259
|
+
np.ndarray
|
|
260
|
+
Array of calibrated vectors. Should be of shape (n, 4).
|
|
261
|
+
"""
|
|
262
|
+
calibrated_vectors = np.apply_along_axis(
|
|
263
|
+
func1d=calibrate_vector,
|
|
264
|
+
axis=1,
|
|
265
|
+
arr=vectors,
|
|
266
|
+
calibration_matrix=calibration_matrix,
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
return calibrated_vectors
|
|
270
|
+
|
|
271
|
+
@staticmethod
|
|
272
|
+
def shift_timestamps(epoch: np.ndarray, timedelta: np.ndarray) -> np.ndarray:
|
|
273
|
+
"""
|
|
274
|
+
Shift the timestamps by the given timedelta.
|
|
275
|
+
|
|
276
|
+
If timedelta is positive, the epochs are shifted forward in time.
|
|
277
|
+
|
|
278
|
+
Parameters
|
|
279
|
+
----------
|
|
280
|
+
epoch : np.ndarray
|
|
281
|
+
Array of timestamps to shift. Should be of length n.
|
|
282
|
+
timedelta : np.ndarray
|
|
283
|
+
Array of time deltas to shift the timestamps by. Should be the same length
|
|
284
|
+
as epoch. Given in seconds.
|
|
285
|
+
|
|
286
|
+
Returns
|
|
287
|
+
-------
|
|
288
|
+
np.ndarray
|
|
289
|
+
Shifted timestamps.
|
|
290
|
+
"""
|
|
291
|
+
if epoch.shape[0] != timedelta.shape[0]:
|
|
292
|
+
raise ValueError(
|
|
293
|
+
"Input Epoch and offsets timedeltas must be the same length."
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
timedelta_ns = timedelta * 1e9
|
|
297
|
+
shifted_timestamps = epoch + timedelta_ns
|
|
298
|
+
return shifted_timestamps
|
|
299
|
+
|
|
300
|
+
def rotate_frame(self, end_frame: ValidFrames) -> None:
|
|
301
|
+
"""
|
|
302
|
+
Rotate the vector data in the class to the output frame.
|
|
303
|
+
|
|
304
|
+
Parameters
|
|
305
|
+
----------
|
|
306
|
+
end_frame : ValidFrames
|
|
307
|
+
The frame to rotate the data to. Must be one of the ValidFrames enum
|
|
308
|
+
values.
|
|
309
|
+
"""
|
|
310
|
+
if self.epoch_et is None:
|
|
311
|
+
self.epoch_et = ttj2000ns_to_et(self.epoch)
|
|
312
|
+
self.vectors = frame_transform(
|
|
313
|
+
self.epoch_et,
|
|
314
|
+
self.vectors,
|
|
315
|
+
from_frame=self.frame.value,
|
|
316
|
+
to_frame=end_frame.value,
|
|
317
|
+
)
|
|
318
|
+
self.frame = end_frame
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
@dataclass(kw_only=True)
|
|
322
|
+
class MagL2(MagL2L1dBase):
|
|
323
|
+
"""
|
|
324
|
+
Dataclass for MAG L2 data.
|
|
325
|
+
|
|
326
|
+
Since L2 and L1D should have the same structure, this can be used for either level.
|
|
327
|
+
|
|
328
|
+
Some of the methods are also static, so they can be used in i-ALiRT processing.
|
|
329
|
+
"""
|
|
330
|
+
|
|
331
|
+
offsets: InitVar[np.ndarray] = None
|
|
332
|
+
timedelta: InitVar[np.ndarray] = None
|
|
333
|
+
|
|
334
|
+
def __post_init__(self, offsets: np.ndarray, timedelta: np.ndarray) -> None:
|
|
335
|
+
"""
|
|
336
|
+
Calculate the magnitude of the vectors after initialization.
|
|
337
|
+
|
|
338
|
+
Parameters
|
|
339
|
+
----------
|
|
340
|
+
offsets : np.ndarray
|
|
341
|
+
Offsets to apply to the vectors. Should be of shape (n, 3) where n is the
|
|
342
|
+
number of vectors.
|
|
343
|
+
timedelta : np.ndarray
|
|
344
|
+
Time deltas to shift the timestamps by. Should be of length n.
|
|
345
|
+
Given in seconds.
|
|
346
|
+
"""
|
|
347
|
+
if offsets is not None:
|
|
348
|
+
self.vectors = self.apply_offsets(self.vectors, offsets)
|
|
349
|
+
if timedelta is not None:
|
|
350
|
+
self.epoch = self.shift_timestamps(self.epoch, timedelta)
|
|
351
|
+
|
|
352
|
+
self.magnitude = self.calculate_magnitude(self.vectors)
|
|
353
|
+
|
|
354
|
+
@staticmethod
|
|
355
|
+
def apply_offsets(vectors: np.ndarray, offsets: np.ndarray) -> np.ndarray:
|
|
356
|
+
"""
|
|
357
|
+
Apply the offsets to the vectors by adding them together.
|
|
358
|
+
|
|
359
|
+
These offsets are used to shift the vectors in the x, y, and z directions.
|
|
360
|
+
They can either be provided through a custom offsets datafile, or calculated
|
|
361
|
+
using a gradiometry algorithm.
|
|
362
|
+
|
|
363
|
+
Parameters
|
|
364
|
+
----------
|
|
365
|
+
vectors : np.ndarray
|
|
366
|
+
Array of vectors to apply the offsets to. Should be of shape (n, 3) where n
|
|
367
|
+
is the number of vectors.
|
|
368
|
+
offsets : np.ndarray
|
|
369
|
+
Array of offsets to apply to the vectors. Should be of shape (n, 3) where n
|
|
370
|
+
is the number of vectors.
|
|
371
|
+
|
|
372
|
+
Returns
|
|
373
|
+
-------
|
|
374
|
+
np.ndarray
|
|
375
|
+
Array of vectors with offsets applied. Should be of shape (n, 3).
|
|
376
|
+
"""
|
|
377
|
+
if vectors.shape[0] != offsets.shape[0]:
|
|
378
|
+
raise ValueError("Vectors and offsets must have the same length.")
|
|
379
|
+
|
|
380
|
+
offset_vectors: np.ndarray = vectors + offsets
|
|
381
|
+
|
|
382
|
+
# Any values where offsets is FILLVAL must also be FILLVAL
|
|
383
|
+
offset_vectors[(offsets == FILLVAL).any(axis=1), :] = FILLVAL
|
|
384
|
+
return offset_vectors
|
imap_processing/quality_flags.py
CHANGED
|
@@ -37,6 +37,14 @@ class ENAFlags(FlagNameMixin):
|
|
|
37
37
|
BADSPIN = 2**2 # bit 2, Bad spin
|
|
38
38
|
|
|
39
39
|
|
|
40
|
+
class ImapDEOutliersUltraFlags(FlagNameMixin):
|
|
41
|
+
"""IMAP Ultra flags."""
|
|
42
|
+
|
|
43
|
+
NONE = CommonFlags.NONE
|
|
44
|
+
FOV = 2**0 # bit 0
|
|
45
|
+
PHCORR = 2**1 # bit 1
|
|
46
|
+
|
|
47
|
+
|
|
40
48
|
class ImapHkUltraFlags(FlagNameMixin):
|
|
41
49
|
"""IMAP Ultra flags."""
|
|
42
50
|
|
|
@@ -53,14 +61,32 @@ class ImapAttitudeUltraFlags(FlagNameMixin):
|
|
|
53
61
|
NONE = CommonFlags.NONE
|
|
54
62
|
SPINRATE = 2**0 # bit 0
|
|
55
63
|
AUXMISMATCH = 2**1 # bit 1 # aux packet does not match Universal Spin Table
|
|
64
|
+
SPINPHASE = 2**2 # bit 2 # spin phase flagged by Universal Spin Table
|
|
65
|
+
SPINPERIOD = 2**3 # bit 3 # spin period flagged by Universal Spin Table
|
|
56
66
|
|
|
57
67
|
|
|
58
68
|
class ImapRatesUltraFlags(FlagNameMixin):
|
|
59
69
|
"""IMAP Ultra Rates flags."""
|
|
60
70
|
|
|
61
71
|
NONE = CommonFlags.NONE
|
|
62
|
-
|
|
63
|
-
|
|
72
|
+
HIGHRATES = 2**0 # bit 0
|
|
73
|
+
FIRSTSPIN = 2**1 # bit 1
|
|
74
|
+
LASTSPIN = 2**2 # bit 2
|
|
75
|
+
PARTIALSPIN = 2**2 # bit 2
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ImapDEScatteringUltraFlags(FlagNameMixin):
|
|
79
|
+
"""IMAP Ultra Scattering flags."""
|
|
80
|
+
|
|
81
|
+
NONE = CommonFlags.NONE
|
|
82
|
+
ABOVE_THRESHOLD = 2**0 # bit 0
|
|
83
|
+
NAN_PHI_OR_THETA = 2**1 # bit 1
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class ImapInstrumentUltraFlags(FlagNameMixin):
|
|
87
|
+
"""IMAP Ultra flags using other instruments."""
|
|
88
|
+
|
|
89
|
+
NONE = CommonFlags.NONE
|
|
64
90
|
|
|
65
91
|
|
|
66
92
|
class ImapLoFlags(FlagNameMixin):
|