44import xarray as xr
55
66from imap_processing .cdf .imap_cdf_manager import ImapCdfAttributes
7+ from imap_processing .mag import imap_mag_sdc_configuration_v001 as configuration
78from imap_processing .mag .constants import DataMode
9+ from imap_processing .mag .l1b .mag_l1b import calibrate_vector
810from imap_processing .mag .l2 .mag_l2_data import MagL2
911
1012
1113def mag_l2 (
12- calibration_dataset : xr .Dataset ,
13- offset_dataset : xr .Dataset ,
14+ calibration_datasets : list [ xr .Dataset ] ,
15+ offsets_dataset : xr .Dataset ,
1416 input_data : xr .Dataset ,
1517) -> list [xr .Dataset ]:
1618 """
1719 Complete MAG L2 processing.
1820
21+ Processing uses 4 data input sources:
22+ 1. Calibration dataset
23+ These calibration files are ancillary files and may require multiple files to
24+ cover the entire timespan. They are not expected to change often. They are used
25+ to provide rotation matrices to correct the frame of the vectors. The same
26+ file(s) are used for both burst and norm calculations.
27+ 2. Offsets dataset
28+ This is one, hand-created file which must correspond exactly to an L1B
29+ (for burst) or L1C (for norm) data file. For each vector, this file includes
30+ offsets, timedelta, and quality flags. The offsets are added to the vectors,
31+ the timedelta is used to correct the epoch time, and the quality flags are
32+ directly passed into the output file.
33+ 3. Input data
34+ This is the L1B or L1C data file. It is used to provide the vectors and epoch
35+ time. It should always be MAGo in the nominal case, but it is possible that we
36+ will switch permanently to using MAGi (in the case of sensor failure, for
37+ example.) The offsets dataset and the input
38+ data are tightly related, so the input data filename is actually retrieved from
39+ the offset dataset to ensure they always match.
40+ 4. sdc-configuration
41+ This is a local configuration file for changes we never expect to make in
42+ flight. This is in the IMAP local repo because changes to these settings will
43+ require other code updates to validate the changes. In L2, the only setting used
44+ is "always_output_mago", which indicates whether we should always output MAGo.
45+ Note that if this ever is set to False, we will need to update the dependency
46+ system to set MAGi files as an upstream dependency.
47+
1948 Input data can be burst or normal mode, but MUST match the file in offset_dataset.
2049 TODO: retrieve the file from offset_dataset in cli.py.
50+ Calibration dataset is the same for all runs.
51+
52+ MAGi data is not used unless we indicate it.
2153
2254 Parameters
2355 ----------
24- calibration_dataset : xr.Dataset
25- Calibration ancillary file input .
26- offset_dataset : xr.Dataset
56+ calibration_datasets : list[ xr.Dataset]
57+ Calibration ancillary file inputs .
58+ offsets_dataset : xr.Dataset
2759 Offset ancillary file input.
2860 input_data : xr.Dataset
2961 Input data from MAG L1C or L1B.
@@ -36,39 +68,73 @@ def mag_l2(
3668 """
3769 # TODO we may need to combine multiple calibration datasets into one timeline.
3870
71+ always_output_mago = configuration .ALWAYS_OUTPUT_MAGO
72+
73+ # TODO Check that the input file matches the offsets file
74+ if not np .array_equal (input_data ["epoch" ].data , offsets_dataset ["epoch" ].data ):
75+ raise ValueError ("Input file and offsets file must have the same timestamps." )
76+
77+ calibration_matrix = retrieve_matrix_from_l2_calibration (
78+ calibration_datasets , always_output_mago
79+ )
80+
81+ vectors = np .apply_along_axis (
82+ func1d = calibrate_vector ,
83+ axis = 1 ,
84+ arr = input_data ["vectors" ].data ,
85+ calibration_matrix = calibration_matrix ,
86+ )
87+
3988 basic_test_data = MagL2 (
40- input_data [ " vectors" ]. data [:, :3 ], # level 2 vectors don't include range
89+ vectors [:, :3 ], # level 2 vectors don't include range
4190 input_data ["epoch" ].data ,
4291 input_data ["vectors" ].data [:, 3 ],
4392 {},
4493 np .zeros (len (input_data ["epoch" ].data )),
4594 np .zeros (len (input_data ["epoch" ].data )),
4695 DataMode .NORM ,
96+ offsets = offsets_dataset ["offsets" ].data ,
97+ timedelta = offsets_dataset ["timedeltas" ].data ,
4798 )
4899 attributes = ImapCdfAttributes ()
49100 attributes .add_instrument_global_attrs ("mag" )
50101 # temporarily point to l1c
51102 attributes .add_instrument_variable_attrs ("mag" , "l1c" )
52-
53103 return [basic_test_data .generate_dataset (attributes )]
54104
55105
56- def apply_calibration_matrix (
57- calibration_dataset : xr .Dataset , vectors : np . ndarray
58- ) -> np . ndarray :
106+ def retrieve_matrix_from_l2_calibration (
107+ calibration_datasets : list [ xr .Dataset ], use_mago : bool = True
108+ ) -> xr . DataArray :
59109 """
60- Apply the calibration file to the vectors to rotate them in space .
110+ Get the calibration matrix for the file .
61111
62112 Parameters
63113 ----------
64- calibration_dataset : xr.Dataset
65- Ancillary file input for calibration.
66- vectors : np.ndarray
67- (n, 4) array of vectors to rotate and timeshift .
114+ calibration_datasets : list[ xr.Dataset]
115+ Ancillary file inputs for calibration.
116+ use_mago : bool
117+ Use the MAGo calibration matrix. Default is True .
68118
69119 Returns
70120 -------
71121 np.ndarray
72- Rotated and timeshifted vectors.
122+ Calibration matrix in the shape (3, 3, 4) to rotate vectors.
73123 """
74- raise NotImplementedError
124+ # TODO: allow for multiple inputs
125+ if isinstance (calibration_datasets , list ):
126+ calibration_dataset = calibration_datasets [0 ]
127+ if len (calibration_datasets ) > 1 :
128+ raise NotImplementedError
129+ else :
130+ calibration_dataset = calibration_datasets
131+
132+ if use_mago :
133+ calibration_data = calibration_dataset ["URFTOORFO" ]
134+ else :
135+ calibration_data = calibration_dataset ["URFTOORFI" ]
136+
137+ # TODO will need to combine multiple files here
138+ # TODO: Check validity of the calibration file?
139+
140+ return calibration_data
0 commit comments