diff --git a/imap_processing/ialirt/l0/process_swapi.py b/imap_processing/ialirt/l0/process_swapi.py index e987f80ac..f64bbe14d 100644 --- a/imap_processing/ialirt/l0/process_swapi.py +++ b/imap_processing/ialirt/l0/process_swapi.py @@ -14,7 +14,7 @@ from imap_processing.ialirt.utils.time import calculate_time from imap_processing.spice.time import met_to_ttj2000ns, met_to_utc from imap_processing.swapi.l1.swapi_l1 import process_sweep_data -from imap_processing.swapi.l2.swapi_l2 import SWAPI_LIVETIME +from imap_processing.swapi.l2.swapi_l2 import SWAPI_LIVETIME, select_first_63_passband_energies logger = logging.getLogger(__name__) @@ -76,6 +76,7 @@ def optimize_pseudo_parameters( Find the pseudo speed (u), density (n) and temperature (T) of solar wind particles. Fit a curve to calculated count rate values as a function of energy passband. + Takes count rates (with errors) and energy passbands for one single sweep as input. Parameters ---------- @@ -90,45 +91,50 @@ def optimize_pseudo_parameters( ------- solution_dict : dict Dictionary containing the optimized speed, density, and temperature values for - each sweep included in the input count_rates array. + the sweep included in the input count_rates array. """ - solution_dict = { # type: ignore - "pseudo_speed": [], - "pseudo_density": [], - "pseudo_temperature": [], - } - - for sweep in np.arange(count_rates.shape[0]): - current_sweep_count_rates = count_rates[sweep, :] - current_sweep_count_rate_errors = count_rate_error[sweep, :] - # Find the max count rate, and use the 5 points surrounding it - max_index = np.argmax(current_sweep_count_rates) - initial_speed_guess = np.sqrt(energy_passbands[max_index]) * Consts.speed_coeff - initial_param_guess = np.array( - [ - initial_speed_guess, - 5 * (400 / initial_speed_guess) ** 2, - 60000 * (initial_speed_guess / 400) ** 2, - ] - ) - sol = curve_fit( + assert len(count_rates.shape) == 1 + assert count_rates.shape == count_rate_error.shape == energy_passbands.shape, f'{count_rates.shape} {count_rate_error.shape} {energy_passbands.shape}' + + current_sweep_count_rates = count_rates + current_sweep_count_rate_errors = count_rate_error + # Find the max count rate, and use the 5 points surrounding it + max_index = np.argmax(current_sweep_count_rates) + initial_speed_guess = np.sqrt(energy_passbands[max_index]) * Consts.speed_coeff + initial_param_guess = np.array( + [ + initial_speed_guess, + 5 * (400 / initial_speed_guess) ** 2, + 60000 * (initial_speed_guess / 400) ** 2, + ] + ) + + fitting_point_range = range(max_index - 3, max_index + 3) + xdata = energy_passbands.take(fitting_point_range, mode="clip") + ydata = current_sweep_count_rates.take(fitting_point_range, mode="clip") + sigma = current_sweep_count_rate_errors.take(fitting_point_range, mode="clip") + + # exclude points where the count is zero (or rounded to zero) + # because the fitting algorithm cannot handle them + # (since zero count implies sigma=0) + is_valid_data = ydata > 0 + + if np.count_nonzero(is_valid_data) >= 3: + solution = curve_fit( f=count_rate, - xdata=energy_passbands.take( - range(max_index - 3, max_index + 3), mode="clip" - ), - ydata=current_sweep_count_rates.take( - range(max_index - 3, max_index + 3), mode="clip" - ), - sigma=current_sweep_count_rate_errors.take( - range(max_index - 3, max_index + 3), mode="clip" - ), + xdata=xdata[is_valid_data], + ydata=ydata[is_valid_data], + sigma=sigma[is_valid_data], p0=initial_param_guess, - ) - solution_dict["pseudo_speed"].append(sol[0][0]) - solution_dict["pseudo_density"].append(sol[0][1]) - solution_dict["pseudo_temperature"].append(sol[0][2]) + )[0] + else: + solution = [np.nan] * 3 - return solution_dict + return { + "pseudo_speed": solution[0], + "pseudo_density": solution[1], + "pseudo_temperature": solution[2] + } def process_swapi_ialirt( @@ -176,6 +182,7 @@ def process_swapi_ialirt( (grouped_dataset["group"] == group) ] + # TODO change this to the center of the interval met_values.append( int(grouped_dataset["met"][(grouped_dataset["group"] == group).values][0]) ) @@ -208,41 +215,40 @@ def process_swapi_ialirt( dtype="datetime64[ns]" ) - # Find the sweep's energy data for the latest time, where sweep_id == 2 - subset = calibration_lut_table[ - (calibration_lut_table["timestamp"] == calibration_lut_table["timestamp"].max()) - & (calibration_lut_table["Sweep #"] == 2) - ] - if subset.empty: - energy_passbands = np.full(NUM_IALIRT_ENERGY_STEPS, np.nan, dtype=np.float64) - else: - subset = subset.sort_values(["timestamp", "ESA Step #"]) - energy_passbands = ( - subset["Energy"][:NUM_IALIRT_ENERGY_STEPS].to_numpy().astype(float) - ) - - solution = optimize_pseudo_parameters( - raw_coin_rate, count_rate_error, energy_passbands - ) - swapi_data = [] - for entry in np.arange(0, len(solution["pseudo_speed"])): + if len(met_values) != len(raw_coin_rate): + raise RuntimeError('Inconsistent number of MET times and count sweep bins') + + for entry in np.arange(0, len(raw_coin_rate)): + entry_met = int(met_values[entry]) + entry_met_in_utc = met_to_utc(entry_met) + sweep_table_version = unpacked_data["swapi_version"].sel(epoch=entry_met, method="nearest").item() + energy_passbands = select_first_63_passband_energies( + calibration_table_df=calibration_lut_table, + time=entry_met_in_utc, + sweep_table_version=sweep_table_version + ) + solution = optimize_pseudo_parameters( + raw_coin_rate[entry], + count_rate_error[entry], + energy_passbands + ) swapi_data.append( { "apid": 478, - "met": int(met_values[entry]), - "met_in_utc": met_to_utc(met_values[entry]).split(".")[0], + "met": entry_met, + "met_in_utc": entry_met_in_utc.split(".")[0], "ttj2000ns": int(met_to_ttj2000ns(met_values[entry])), "instrument": "swapi", "swapi_pseudo_proton_speed": Decimal( - f"{solution['pseudo_speed'][entry]:.3f}" + f"{solution['pseudo_speed']:.3f}" ), "swapi_pseudo_proton_density": Decimal( - f"{solution['pseudo_density'][entry]:.3f}" + f"{solution['pseudo_density']:.3f}" ), "swapi_pseudo_proton_temperature": Decimal( - f"{solution['pseudo_temperature'][entry]:.3f}" + f"{solution['pseudo_temperature']:.3f}" ), } ) diff --git a/imap_processing/swapi/l2/swapi_l2.py b/imap_processing/swapi/l2/swapi_l2.py index a81f89d79..4c02c21ea 100644 --- a/imap_processing/swapi/l2/swapi_l2.py +++ b/imap_processing/swapi/l2/swapi_l2.py @@ -1,6 +1,7 @@ """SWAPI L2 processing module.""" import logging +import select import numpy as np import numpy.typing as npt @@ -68,34 +69,7 @@ def solve_full_sweep_energy( first_63_energies = [] for time, sweep_id in zip(data_time, sweep_table, strict=False): - # Find the sweep's ESA data for the given time and sweep_id - subset = esa_table_df[ - (esa_table_df["timestamp"] <= time) & (esa_table_df["Sweep #"] == sweep_id) - ] - if subset.empty: - # Get the earliest timestamp available - earliest_time = esa_table_df["timestamp"].min() - - # Find the sweep's ESA data for the earliest time and sweep_id - earliest_subset = esa_table_df[ - (esa_table_df["timestamp"] == earliest_time) - & (esa_table_df["Sweep #"] == sweep_id) - ] - if earliest_subset.empty: - raise ValueError( - f"No matching ESA table entry found for sweep ID {sweep_id} " - f"at time {time}, and no entries found for earliest time " - f"{earliest_time}." - ) - subset = earliest_subset - - # Subset data can contain multiple 72 energy values with last 9 fine energies - # with 'Solve' value. We need to sort by time and ESA step to maintain correct - # order. Then take the last group of 72 steps values and select first 63 - # values only. - subset = subset.sort_values(["timestamp", "ESA Step #"]) - grouped = subset["Energy"].values.reshape(-1, NUM_ENERGY_STEPS) - first_63 = grouped[-1, :63] + first_63 = select_first_63_passband_energies(esa_table_df, time, sweep_id) first_63_energies.append(first_63) # Find last 9 fine energy values of all sweeps data @@ -163,6 +137,33 @@ def solve_full_sweep_energy( return sweeps_energy_value +def select_first_63_passband_energies(calibration_table_df, time, sweep_table_version): + """ + Select passband energies from the calibration table based on the time and table version. + + :param calibration_table_df: + DataFrame containing entries for `timestamp`, `Sweep #`, `ESA Step #`, and `Energy`. + Each timestamp has 72 energy steps. + :param time: Time of measurement (calibration from after this time is excluded if earlier is available, otherwise earliest time is used) + :param sweep_table_version: Sweep table version (to select from the `Sweep #` column) + """ + + subset = calibration_table_df[calibration_table_df["Sweep #"] == sweep_table_version] + + table_timestamps = pd.to_datetime(subset["timestamp"], utc=True) + table_timestamps_before = table_timestamps[table_timestamps <= pd.to_datetime(time, utc=True)] + selected_timestamp = (table_timestamps_before.max() # latest one that is not after the input time + if not table_timestamps_before.empty + else table_timestamps.max()) + + subset = subset[table_timestamps == selected_timestamp] + + assert len(subset) == 72, f'{len(subset)} entries in calibration table for time {time}, sweep # {sweep_table_version}; 72 are required' + + subset = subset.sort_values(["timestamp", "ESA Step #"]) + return subset["Energy"][:63].to_numpy().astype(float) + + def swapi_l2( l1_dataset: xr.Dataset, esa_table_df: pd.DataFrame, diff --git a/imap_processing/tests/ialirt/data/l0/imap_swapi_esa-unit-conversion_20250626_v001.csv b/imap_processing/tests/ialirt/data/l0/imap_swapi_esa-unit-conversion_20250626_v001.csv new file mode 100644 index 000000000..ff96dc762 --- /dev/null +++ b/imap_processing/tests/ialirt/data/l0/imap_swapi_esa-unit-conversion_20250626_v001.csv @@ -0,0 +1,289 @@ +timestamp,ESA Step #,K factor,Voltage,Energy,Sweep #,ESA Index Number,LUT version number +2/11/2025 0:00,0,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,1,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,2,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,3,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,4,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,5,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,6,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,7,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,8,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,9,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,10,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,11,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,12,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,13,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,14,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,15,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,16,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,17,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,18,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,19,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,20,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,21,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,22,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,23,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,24,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,25,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,26,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,27,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,28,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,29,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,30,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,31,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,32,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,33,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,34,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,35,1.88,568,"1,068",0,560,4 +2/11/2025 0:00,36,1.88,522,981,0,576,4 +2/11/2025 0:00,37,1.88,479,901,0,592,4 +2/11/2025 0:00,38,1.88,440,828,0,608,4 +2/11/2025 0:00,39,1.88,404,760,0,624,4 +2/11/2025 0:00,40,1.88,371,698,0,640,4 +2/11/2025 0:00,41,1.88,341,641,0,656,4 +2/11/2025 0:00,42,1.88,313,589,0,672,4 +2/11/2025 0:00,43,1.88,289,544,0,688,4 +2/11/2025 0:00,44,1.88,264,497,0,704,4 +2/11/2025 0:00,45,1.88,244,459,0,720,4 +2/11/2025 0:00,46,1.88,224,421,0,736,4 +2/11/2025 0:00,47,1.88,207,389,0,752,4 +2/11/2025 0:00,48,1.88,189,355,0,768,4 +2/11/2025 0:00,49,1.88,174,326,0,784,4 +2/11/2025 0:00,50,1.88,159,298,0,800,4 +2/11/2025 0:00,51,1.88,146,275,0,816,4 +2/11/2025 0:00,52,1.88,134,252,0,832,4 +2/11/2025 0:00,53,1.88,124,234,0,848,4 +2/11/2025 0:00,54,1.88,114,214,0,864,4 +2/11/2025 0:00,55,1.88,104,195,0,880,4 +2/11/2025 0:00,56,1.88,96,181,0,896,4 +2/11/2025 0:00,57,1.88,89,167,0,912,4 +2/11/2025 0:00,58,1.88,82,153,0,928,4 +2/11/2025 0:00,59,1.88,74,139,0,944,4 +2/11/2025 0:00,60,1.88,69,129,0,960,4 +2/11/2025 0:00,61,1.88,64,120,0,976,4 +2/11/2025 0:00,62,1.88,57,107,0,992,4 +2/11/2025 0:00,63,1.88,Solve,Solve,0,-16,4 +2/11/2025 0:00,64,1.88,Solve,Solve,0,-12,4 +2/11/2025 0:00,65,1.88,Solve,Solve,0,-8,4 +2/11/2025 0:00,66,1.88,Solve,Solve,0,-4,4 +2/11/2025 0:00,67,1.88,Solve,Solve,0,0,4 +2/11/2025 0:00,68,1.88,Solve,Solve,0,4,4 +2/11/2025 0:00,69,1.88,Solve,Solve,0,8,4 +2/11/2025 0:00,70,1.88,Solve,Solve,0,12,4 +2/11/2025 0:00,71,1.88,Solve,Solve,0,16,4 +5/19/2025 0:00,0,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,1,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,2,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,3,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,4,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,5,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,6,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,7,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,8,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,9,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,10,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,11,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,12,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,13,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,14,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,15,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,16,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,17,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,18,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,19,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,20,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,21,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,22,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,23,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,24,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,25,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,26,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,27,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,28,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,29,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,30,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,31,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,32,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,33,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,34,1.93,586.51,1131.97314,0,544,6 +5/19/2025 0:00,35,1.93,537.83,1038.004216,0,560,6 +5/19/2025 0:00,36,1.93,493.18,951.8359709,0,576,6 +5/19/2025 0:00,37,1.93,452.24,872.8208434,0,592,6 +5/19/2025 0:00,38,1.93,414.7,800.3650292,0,608,6 +5/19/2025 0:00,39,1.93,380.27,733.9240176,0,624,6 +5/19/2025 0:00,40,1.93,348.7,672.9984993,0,640,6 +5/19/2025 0:00,41,1.93,319.76,617.1306146,0,656,6 +5/19/2025 0:00,42,1.93,293.21,565.9005123,0,672,6 +5/19/2025 0:00,43,1.93,268.87,518.9231943,0,688,6 +5/19/2025 0:00,44,1.93,246.55,475.8456225,0,704,6 +5/19/2025 0:00,45,1.93,226.09,436.3440658,0,720,6 +5/19/2025 0:00,46,1.93,207.32,400.1216672,0,736,6 +5/19/2025 0:00,47,1.93,190.11,366.9062125,0,752,6 +5/19/2025 0:00,48,1.93,174.33,336.4480852,0,768,6 +5/19/2025 0:00,49,1.93,159.85,308.5183902,0,784,6 +5/19/2025 0:00,50,1.93,146.58,282.9072338,0,800,6 +5/19/2025 0:00,51,1.93,134.42,259.4221462,0,816,6 +5/19/2025 0:00,52,1.93,123.26,237.8866352,0,832,6 +5/19/2025 0:00,53,1.93,113.03,218.13886,0,848,6 +5/19/2025 0:00,54,1.93,103.64,200.0304145,0,864,6 +5/19/2025 0:00,55,1.93,95.04,183.4252123,0,880,6 +5/19/2025 0:00,56,1.93,87.15,168.1984643,0,896,6 +5/19/2025 0:00,57,1.93,79.91,154.2357401,0,912,6 +5/19/2025 0:00,58,1.93,73.28,141.432109,0,928,6 +5/19/2025 0:00,59,1.93,67.2,129.6913506,0,944,6 +5/19/2025 0:00,60,1.93,61.62,118.9252324,0,960,6 +5/19/2025 0:00,61,1.93,56.5,109.0528461,0,976,6 +5/19/2025 0:00,62,1.93,51.81,100,0,992,6 +5/19/2025 0:00,63,1.93,Solve,Solve,0,-16,6 +5/19/2025 0:00,64,1.93,Solve,Solve,0,-12,6 +5/19/2025 0:00,65,1.93,Solve,Solve,0,-8,6 +5/19/2025 0:00,66,1.93,Solve,Solve,0,-4,6 +5/19/2025 0:00,67,1.93,Solve,Solve,0,0,6 +5/19/2025 0:00,68,1.93,Solve,Solve,0,4,6 +5/19/2025 0:00,69,1.93,Solve,Solve,0,8,6 +5/19/2025 0:00,70,1.93,Solve,Solve,0,12,6 +5/19/2025 0:00,71,1.93,Solve,Solve,0,16,6 +5/19/2025 0:00,0,1.93,2994.50,5779.393516,1,243,6 +5/19/2025 0:00,1,1.93,2994.50,5779.393516,1,243,6 +5/19/2025 0:00,2,1.93,2994.50,5779.393516,1,243,6 +5/19/2025 0:00,3,1.93,2994.50,5779.393516,1,243,6 +5/19/2025 0:00,4,1.93,2994.50,5779.393516,1,243,6 +5/19/2025 0:00,5,1.93,2994.50,5779.393516,1,243,6 +5/19/2025 0:00,6,1.93,2994.50,5779.393516,1,243,6 +5/19/2025 0:00,7,1.93,2994.50,5779.393516,1,243,6 +5/19/2025 0:00,8,1.93,2994.50,5779.393516,1,243,6 +5/19/2025 0:00,9,1.93,2994.50,5779.393516,1,243,6 +5/19/2025 0:00,10,1.93,2994.50,5779.393516,1,243,6 +5/19/2025 0:00,11,1.93,2994.50,5779.393516,1,243,6 +5/19/2025 0:00,12,1.93,2994.50,5779.393516,1,243,6 +5/19/2025 0:00,13,1.93,2994.50,5779.393516,1,243,6 +5/19/2025 0:00,14,1.93,2994.50,5779.393516,1,243,6 +5/19/2025 0:00,15,1.93,2994.50,5779.393516,1,243,6 +5/19/2025 0:00,16,1.93,2790.90,5386.444636,1,256,6 +5/19/2025 0:00,17,1.93,2559.22,4939.297624,1,272,6 +5/19/2025 0:00,18,1.93,2346.77,4529.269801,1,288,6 +5/19/2025 0:00,19,1.93,2151.96,4153.279775,1,304,6 +5/19/2025 0:00,20,1.93,1973.32,3808.501955,1,320,6 +5/19/2025 0:00,21,1.93,1809.51,3492.34531,1,336,6 +5/19/2025 0:00,22,1.93,1659.29,3202.433898,1,352,6 +5/19/2025 0:00,23,1.93,1521.55,2936.589015,1,368,6 +5/19/2025 0:00,24,1.93,1395.24,2692.812815,1,384,6 +5/19/2025 0:00,25,1.93,1279.42,2469.273304,1,400,6 +5/19/2025 0:00,26,1.93,1173.21,2264.290564,1,416,6 +5/19/2025 0:00,27,1.93,1075.82,2076.324136,1,432,6 +5/19/2025 0:00,28,1.93,986.51,1903.961438,1,448,6 +5/19/2025 0:00,29,1.93,904.62,1745.907151,1,464,6 +5/19/2025 0:00,30,1.93,829.52,1600.973485,1,480,6 +5/19/2025 0:00,31,1.93,760.66,1468.071254,1,496,6 +5/19/2025 0:00,32,1.93,697.51,1346.201688,1,512,6 +5/19/2025 0:00,33,1.93,639.61,1234.448926,1,528,6 +5/19/2025 0:00,34,1.93,586.51,1131.97314,1,544,6 +5/19/2025 0:00,35,1.93,537.83,1038.004216,1,560,6 +5/19/2025 0:00,36,1.93,493.18,951.8359709,1,576,6 +5/19/2025 0:00,37,1.93,452.24,872.8208434,1,592,6 +5/19/2025 0:00,38,1.93,414.70,800.3650292,1,608,6 +5/19/2025 0:00,39,1.93,380.27,733.9240176,1,624,6 +5/19/2025 0:00,40,1.93,348.70,672.9984993,1,640,6 +5/19/2025 0:00,41,1.93,319.76,617.1306146,1,656,6 +5/19/2025 0:00,42,1.93,293.21,565.9005123,1,672,6 +5/19/2025 0:00,43,1.93,268.87,518.9231943,1,688,6 +5/19/2025 0:00,44,1.93,246.55,475.8456225,1,704,6 +5/19/2025 0:00,45,1.93,226.09,436.3440658,1,720,6 +5/19/2025 0:00,46,1.93,207.32,400.1216672,1,736,6 +5/19/2025 0:00,47,1.93,190.11,366.9062125,1,752,6 +5/19/2025 0:00,48,1.93,174.33,336.4480852,1,768,6 +5/19/2025 0:00,49,1.93,159.85,308.5183902,1,784,6 +5/19/2025 0:00,50,1.93,146.58,282.9072338,1,800,6 +5/19/2025 0:00,51,1.93,134.42,259.4221462,1,816,6 +5/19/2025 0:00,52,1.93,123.26,237.8866352,1,832,6 +5/19/2025 0:00,53,1.93,113.03,218.13886,1,848,6 +5/19/2025 0:00,54,1.93,103.64,200.0304145,1,864,6 +5/19/2025 0:00,55,1.93,95.04,183.4252123,1,880,6 +5/19/2025 0:00,56,1.93,87.15,168.1984643,1,896,6 +5/19/2025 0:00,57,1.93,79.91,154.2357401,1,912,6 +5/19/2025 0:00,58,1.93,73.28,141.432109,1,928,6 +5/19/2025 0:00,59,1.93,67.20,129.6913506,1,944,6 +5/19/2025 0:00,60,1.93,61.62,118.9252324,1,960,6 +5/19/2025 0:00,61,1.93,56.50,109.0528461,1,976,6 +5/19/2025 0:00,62,1.93,51.81,100,1,992,6 +5/19/2025 0:00,63,1.93,Solve,Solve,1,-16,6 +5/19/2025 0:00,64,1.93,Solve,Solve,1,-12,6 +5/19/2025 0:00,65,1.93,Solve,Solve,1,-8,6 +5/19/2025 0:00,66,1.93,Solve,Solve,1,-4,6 +5/19/2025 0:00,67,1.93,Solve,Solve,1,0,6 +5/19/2025 0:00,68,1.93,Solve,Solve,1,4,6 +5/19/2025 0:00,69,1.93,Solve,Solve,1,8,6 +5/19/2025 0:00,70,1.93,Solve,Solve,1,12,6 +5/19/2025 0:00,71,1.93,Solve,Solve,1,16,6 +5/19/2025 0:00,0,1.93,10240,19763.2,2,0,6 +5/19/2025 0:00,1,1.93,10240,19763.2,2,16,6 +5/19/2025 0:00,2,1.93,9389.94292,18122.58984,2,32,6 +5/19/2025 0:00,3,1.93,8610.451958,16618.17228,2,48,6 +5/19/2025 0:00,4,1.93,7895.66918,15238.64152,2,64,6 +5/19/2025 0:00,5,1.93,7240.222941,13973.63028,2,80,6 +5/19/2025 0:00,6,1.93,6639.187515,12813.6319,2,96,6 +5/19/2025 0:00,7,1.93,6088.046074,11749.92892,2,112,6 +5/19/2025 0:00,8,1.93,5582.656751,10774.52753,2,128,6 +5/19/2025 0:00,9,1.93,5119.221508,9880.09751,2,144,6 +5/19/2025 0:00,10,1.93,4694.257593,9059.917155,2,160,6 +5/19/2025 0:00,11,1.93,4304.571373,8307.822749,2,176,6 +5/19/2025 0:00,12,1.93,3947.234325,7618.162247,2,192,6 +5/19/2025 0:00,13,1.93,3619.561035,6985.752798,2,208,6 +5/19/2025 0:00,14,1.93,3319.089016,6405.8418,2,224,6 +5/19/2025 0:00,15,1.93,3043.560196,5874.071178,2,240,6 +5/19/2025 0:00,16,1.93,2790.90,5386.444636,2,256,6 +5/19/2025 0:00,17,1.93,2559.22,4939.297624,2,272,6 +5/19/2025 0:00,18,1.93,2346.77,4529.269801,2,288,6 +5/19/2025 0:00,19,1.93,2151.96,4153.279775,2,304,6 +5/19/2025 0:00,20,1.93,1973.32,3808.501955,2,320,6 +5/19/2025 0:00,21,1.93,1809.51,3492.34531,2,336,6 +5/19/2025 0:00,22,1.93,1659.29,3202.433898,2,352,6 +5/19/2025 0:00,23,1.93,1521.55,2936.589015,2,368,6 +5/19/2025 0:00,24,1.93,1395.24,2692.812815,2,384,6 +5/19/2025 0:00,25,1.93,1279.42,2469.273304,2,400,6 +5/19/2025 0:00,26,1.93,1173.21,2264.290564,2,416,6 +5/19/2025 0:00,27,1.93,1075.82,2076.324136,2,432,6 +5/19/2025 0:00,28,1.93,986.51,1903.961438,2,448,6 +5/19/2025 0:00,29,1.93,904.62,1745.907151,2,464,6 +5/19/2025 0:00,30,1.93,829.52,1600.973485,2,480,6 +5/19/2025 0:00,31,1.93,760.66,1468.071254,2,496,6 +5/19/2025 0:00,32,1.93,697.51,1346.201688,2,512,6 +5/19/2025 0:00,33,1.93,639.61,1234.448926,2,528,6 +5/19/2025 0:00,34,1.93,586.51,1131.97314,2,544,6 +5/19/2025 0:00,35,1.93,537.83,1038.004216,2,560,6 +5/19/2025 0:00,36,1.93,493.18,951.8359709,2,576,6 +5/19/2025 0:00,37,1.93,452.24,872.8208434,2,592,6 +5/19/2025 0:00,38,1.93,414.70,800.3650292,2,608,6 +5/19/2025 0:00,39,1.93,380.27,733.9240176,2,624,6 +5/19/2025 0:00,40,1.93,348.70,672.9984993,2,640,6 +5/19/2025 0:00,41,1.93,319.76,617.1306146,2,656,6 +5/19/2025 0:00,42,1.93,293.21,565.9005123,2,672,6 +5/19/2025 0:00,43,1.93,268.87,518.9231943,2,688,6 +5/19/2025 0:00,44,1.93,246.55,475.8456225,2,704,6 +5/19/2025 0:00,45,1.93,226.09,436.3440658,2,720,6 +5/19/2025 0:00,46,1.93,207.32,400.1216672,2,736,6 +5/19/2025 0:00,47,1.93,190.11,366.9062125,2,752,6 +5/19/2025 0:00,48,1.93,174.33,336.4480852,2,768,6 +5/19/2025 0:00,49,1.93,159.85,308.5183902,2,784,6 +5/19/2025 0:00,50,1.93,146.58,282.9072338,2,800,6 +5/19/2025 0:00,51,1.93,134.42,259.4221462,2,816,6 +5/19/2025 0:00,52,1.93,123.26,237.8866352,2,832,6 +5/19/2025 0:00,53,1.93,113.03,218.13886,2,848,6 +5/19/2025 0:00,54,1.93,103.64,200.0304145,2,864,6 +5/19/2025 0:00,55,1.93,95.04,183.4252123,2,880,6 +5/19/2025 0:00,56,1.93,87.15,168.1984643,2,896,6 +5/19/2025 0:00,57,1.93,79.91,154.2357401,2,912,6 +5/19/2025 0:00,58,1.93,73.28,141.432109,2,928,6 +5/19/2025 0:00,59,1.93,67.20,129.6913506,2,944,6 +5/19/2025 0:00,60,1.93,61.62,118.9252324,2,960,6 +5/19/2025 0:00,61,1.93,56.50,109.0528461,2,976,6 +5/19/2025 0:00,62,1.93,51.81,100,2,992,6 +5/19/2025 0:00,63,1.93,Solve,Solve,2,-16,6 +5/19/2025 0:00,64,1.93,Solve,Solve,2,-12,6 +5/19/2025 0:00,65,1.93,Solve,Solve,2,-8,6 +5/19/2025 0:00,66,1.93,Solve,Solve,2,-4,6 +5/19/2025 0:00,67,1.93,Solve,Solve,2,0,6 +5/19/2025 0:00,68,1.93,Solve,Solve,2,4,6 +5/19/2025 0:00,69,1.93,Solve,Solve,2,8,6 +5/19/2025 0:00,70,1.93,Solve,Solve,2,12,6 +5/19/2025 0:00,71,1.93,Solve,Solve,2,16,6 diff --git a/imap_processing/tests/ialirt/data/l0/imap_swapi_esa-unit-conversion_20251201_v001.csv b/imap_processing/tests/ialirt/data/l0/imap_swapi_esa-unit-conversion_20251201_v001.csv new file mode 100644 index 000000000..24da17574 --- /dev/null +++ b/imap_processing/tests/ialirt/data/l0/imap_swapi_esa-unit-conversion_20251201_v001.csv @@ -0,0 +1,361 @@ +timestamp,ESA Step #,K factor,Voltage,Energy,Sweep #,ESA Index Number,LUT version number +2/11/2025 0:00,0,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,1,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,2,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,3,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,4,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,5,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,6,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,7,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,8,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,9,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,10,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,11,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,12,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,13,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,14,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,15,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,16,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,17,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,18,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,19,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,20,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,21,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,22,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,23,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,24,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,25,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,26,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,27,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,28,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,29,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,30,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,31,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,32,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,33,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,34,1.88,619,"1,163",0,544,4 +2/11/2025 0:00,35,1.88,568,"1,068",0,560,4 +2/11/2025 0:00,36,1.88,522,981,0,576,4 +2/11/2025 0:00,37,1.88,479,901,0,592,4 +2/11/2025 0:00,38,1.88,440,828,0,608,4 +2/11/2025 0:00,39,1.88,404,760,0,624,4 +2/11/2025 0:00,40,1.88,371,698,0,640,4 +2/11/2025 0:00,41,1.88,341,641,0,656,4 +2/11/2025 0:00,42,1.88,313,589,0,672,4 +2/11/2025 0:00,43,1.88,289,544,0,688,4 +2/11/2025 0:00,44,1.88,264,497,0,704,4 +2/11/2025 0:00,45,1.88,244,459,0,720,4 +2/11/2025 0:00,46,1.88,224,421,0,736,4 +2/11/2025 0:00,47,1.88,207,389,0,752,4 +2/11/2025 0:00,48,1.88,189,355,0,768,4 +2/11/2025 0:00,49,1.88,174,326,0,784,4 +2/11/2025 0:00,50,1.88,159,298,0,800,4 +2/11/2025 0:00,51,1.88,146,275,0,816,4 +2/11/2025 0:00,52,1.88,134,252,0,832,4 +2/11/2025 0:00,53,1.88,124,234,0,848,4 +2/11/2025 0:00,54,1.88,114,214,0,864,4 +2/11/2025 0:00,55,1.88,104,195,0,880,4 +2/11/2025 0:00,56,1.88,96,181,0,896,4 +2/11/2025 0:00,57,1.88,89,167,0,912,4 +2/11/2025 0:00,58,1.88,82,153,0,928,4 +2/11/2025 0:00,59,1.88,74,139,0,944,4 +2/11/2025 0:00,60,1.88,69,129,0,960,4 +2/11/2025 0:00,61,1.88,64,120,0,976,4 +2/11/2025 0:00,62,1.88,57,107,0,992,4 +2/11/2025 0:00,63,1.88,Solve,Solve,0,-16,4 +2/11/2025 0:00,64,1.88,Solve,Solve,0,-12,4 +2/11/2025 0:00,65,1.88,Solve,Solve,0,-8,4 +2/11/2025 0:00,66,1.88,Solve,Solve,0,-4,4 +2/11/2025 0:00,67,1.88,Solve,Solve,0,0,4 +2/11/2025 0:00,68,1.88,Solve,Solve,0,4,4 +2/11/2025 0:00,69,1.88,Solve,Solve,0,8,4 +2/11/2025 0:00,70,1.88,Solve,Solve,0,12,4 +2/11/2025 0:00,71,1.88,Solve,Solve,0,16,4 +5/19/2025 0:00,0,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,1,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,2,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,3,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,4,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,5,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,6,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,7,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,8,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,9,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,10,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,11,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,12,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,13,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,14,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,15,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,16,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,17,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,18,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,19,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,20,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,21,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,22,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,23,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,24,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,25,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,26,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,27,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,28,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,29,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,30,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,31,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,32,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,33,1.93,622.52,1201.466213,0,533,6 +5/19/2025 0:00,34,1.93,586.51,1131.97314,0,544,6 +5/19/2025 0:00,35,1.93,537.83,1038.004216,0,560,6 +5/19/2025 0:00,36,1.93,493.18,951.8359709,0,576,6 +5/19/2025 0:00,37,1.93,452.24,872.8208434,0,592,6 +5/19/2025 0:00,38,1.93,414.7,800.3650292,0,608,6 +5/19/2025 0:00,39,1.93,380.27,733.9240176,0,624,6 +5/19/2025 0:00,40,1.93,348.7,672.9984993,0,640,6 +5/19/2025 0:00,41,1.93,319.76,617.1306146,0,656,6 +5/19/2025 0:00,42,1.93,293.21,565.9005123,0,672,6 +5/19/2025 0:00,43,1.93,268.87,518.9231943,0,688,6 +5/19/2025 0:00,44,1.93,246.55,475.8456225,0,704,6 +5/19/2025 0:00,45,1.93,226.09,436.3440658,0,720,6 +5/19/2025 0:00,46,1.93,207.32,400.1216672,0,736,6 +5/19/2025 0:00,47,1.93,190.11,366.9062125,0,752,6 +5/19/2025 0:00,48,1.93,174.33,336.4480852,0,768,6 +5/19/2025 0:00,49,1.93,159.85,308.5183902,0,784,6 +5/19/2025 0:00,50,1.93,146.58,282.9072338,0,800,6 +5/19/2025 0:00,51,1.93,134.42,259.4221462,0,816,6 +5/19/2025 0:00,52,1.93,123.26,237.8866352,0,832,6 +5/19/2025 0:00,53,1.93,113.03,218.13886,0,848,6 +5/19/2025 0:00,54,1.93,103.64,200.0304145,0,864,6 +5/19/2025 0:00,55,1.93,95.04,183.4252123,0,880,6 +5/19/2025 0:00,56,1.93,87.15,168.1984643,0,896,6 +5/19/2025 0:00,57,1.93,79.91,154.2357401,0,912,6 +5/19/2025 0:00,58,1.93,73.28,141.432109,0,928,6 +5/19/2025 0:00,59,1.93,67.2,129.6913506,0,944,6 +5/19/2025 0:00,60,1.93,61.62,118.9252324,0,960,6 +5/19/2025 0:00,61,1.93,56.5,109.0528461,0,976,6 +5/19/2025 0:00,62,1.93,51.81,100,0,992,6 +5/19/2025 0:00,63,1.93,Solve,Solve,0,-16,6 +5/19/2025 0:00,64,1.93,Solve,Solve,0,-12,6 +5/19/2025 0:00,65,1.93,Solve,Solve,0,-8,6 +5/19/2025 0:00,66,1.93,Solve,Solve,0,-4,6 +5/19/2025 0:00,67,1.93,Solve,Solve,0,0,6 +5/19/2025 0:00,68,1.93,Solve,Solve,0,4,6 +5/19/2025 0:00,69,1.93,Solve,Solve,0,8,6 +5/19/2025 0:00,70,1.93,Solve,Solve,0,12,6 +5/19/2025 0:00,71,1.93,Solve,Solve,0,16,6 +5/19/2025 0:00,0,1.93,2994.5,5779.393516,1,243,6 +5/19/2025 0:00,1,1.93,2994.5,5779.393516,1,243,6 +5/19/2025 0:00,2,1.93,2994.5,5779.393516,1,243,6 +5/19/2025 0:00,3,1.93,2994.5,5779.393516,1,243,6 +5/19/2025 0:00,4,1.93,2994.5,5779.393516,1,243,6 +5/19/2025 0:00,5,1.93,2994.5,5779.393516,1,243,6 +5/19/2025 0:00,6,1.93,2994.5,5779.393516,1,243,6 +5/19/2025 0:00,7,1.93,2994.5,5779.393516,1,243,6 +5/19/2025 0:00,8,1.93,2994.5,5779.393516,1,243,6 +5/19/2025 0:00,9,1.93,2994.5,5779.393516,1,243,6 +5/19/2025 0:00,10,1.93,2994.5,5779.393516,1,243,6 +5/19/2025 0:00,11,1.93,2994.5,5779.393516,1,243,6 +5/19/2025 0:00,12,1.93,2994.5,5779.393516,1,243,6 +5/19/2025 0:00,13,1.93,2994.5,5779.393516,1,243,6 +5/19/2025 0:00,14,1.93,2994.5,5779.393516,1,243,6 +5/19/2025 0:00,15,1.93,2994.5,5779.393516,1,243,6 +5/19/2025 0:00,16,1.93,2790.9,5386.444636,1,256,6 +5/19/2025 0:00,17,1.93,2559.22,4939.297624,1,272,6 +5/19/2025 0:00,18,1.93,2346.77,4529.269801,1,288,6 +5/19/2025 0:00,19,1.93,2151.96,4153.279775,1,304,6 +5/19/2025 0:00,20,1.93,1973.32,3808.501955,1,320,6 +5/19/2025 0:00,21,1.93,1809.51,3492.34531,1,336,6 +5/19/2025 0:00,22,1.93,1659.29,3202.433898,1,352,6 +5/19/2025 0:00,23,1.93,1521.55,2936.589015,1,368,6 +5/19/2025 0:00,24,1.93,1395.24,2692.812815,1,384,6 +5/19/2025 0:00,25,1.93,1279.42,2469.273304,1,400,6 +5/19/2025 0:00,26,1.93,1173.21,2264.290564,1,416,6 +5/19/2025 0:00,27,1.93,1075.82,2076.324136,1,432,6 +5/19/2025 0:00,28,1.93,986.51,1903.961438,1,448,6 +5/19/2025 0:00,29,1.93,904.62,1745.907151,1,464,6 +5/19/2025 0:00,30,1.93,829.52,1600.973485,1,480,6 +5/19/2025 0:00,31,1.93,760.66,1468.071254,1,496,6 +5/19/2025 0:00,32,1.93,697.51,1346.201688,1,512,6 +5/19/2025 0:00,33,1.93,639.61,1234.448926,1,528,6 +5/19/2025 0:00,34,1.93,586.51,1131.97314,1,544,6 +5/19/2025 0:00,35,1.93,537.83,1038.004216,1,560,6 +5/19/2025 0:00,36,1.93,493.18,951.8359709,1,576,6 +5/19/2025 0:00,37,1.93,452.24,872.8208434,1,592,6 +5/19/2025 0:00,38,1.93,414.7,800.3650292,1,608,6 +5/19/2025 0:00,39,1.93,380.27,733.9240176,1,624,6 +5/19/2025 0:00,40,1.93,348.7,672.9984993,1,640,6 +5/19/2025 0:00,41,1.93,319.76,617.1306146,1,656,6 +5/19/2025 0:00,42,1.93,293.21,565.9005123,1,672,6 +5/19/2025 0:00,43,1.93,268.87,518.9231943,1,688,6 +5/19/2025 0:00,44,1.93,246.55,475.8456225,1,704,6 +5/19/2025 0:00,45,1.93,226.09,436.3440658,1,720,6 +5/19/2025 0:00,46,1.93,207.32,400.1216672,1,736,6 +5/19/2025 0:00,47,1.93,190.11,366.9062125,1,752,6 +5/19/2025 0:00,48,1.93,174.33,336.4480852,1,768,6 +5/19/2025 0:00,49,1.93,159.85,308.5183902,1,784,6 +5/19/2025 0:00,50,1.93,146.58,282.9072338,1,800,6 +5/19/2025 0:00,51,1.93,134.42,259.4221462,1,816,6 +5/19/2025 0:00,52,1.93,123.26,237.8866352,1,832,6 +5/19/2025 0:00,53,1.93,113.03,218.13886,1,848,6 +5/19/2025 0:00,54,1.93,103.64,200.0304145,1,864,6 +5/19/2025 0:00,55,1.93,95.04,183.4252123,1,880,6 +5/19/2025 0:00,56,1.93,87.15,168.1984643,1,896,6 +5/19/2025 0:00,57,1.93,79.91,154.2357401,1,912,6 +5/19/2025 0:00,58,1.93,73.28,141.432109,1,928,6 +5/19/2025 0:00,59,1.93,67.2,129.6913506,1,944,6 +5/19/2025 0:00,60,1.93,61.62,118.9252324,1,960,6 +5/19/2025 0:00,61,1.93,56.5,109.0528461,1,976,6 +5/19/2025 0:00,62,1.93,51.81,100,1,992,6 +5/19/2025 0:00,63,1.93,Solve,Solve,1,-16,6 +5/19/2025 0:00,64,1.93,Solve,Solve,1,-12,6 +5/19/2025 0:00,65,1.93,Solve,Solve,1,-8,6 +5/19/2025 0:00,66,1.93,Solve,Solve,1,-4,6 +5/19/2025 0:00,67,1.93,Solve,Solve,1,0,6 +5/19/2025 0:00,68,1.93,Solve,Solve,1,4,6 +5/19/2025 0:00,69,1.93,Solve,Solve,1,8,6 +5/19/2025 0:00,70,1.93,Solve,Solve,1,12,6 +5/19/2025 0:00,71,1.93,Solve,Solve,1,16,6 +5/19/2025 0:00,0,1.93,10240,19763.2,2,0,6 +5/19/2025 0:00,1,1.93,10240,19763.2,2,16,6 +5/19/2025 0:00,2,1.93,9389.94292,18122.58984,2,32,6 +5/19/2025 0:00,3,1.93,8610.451958,16618.17228,2,48,6 +5/19/2025 0:00,4,1.93,7895.66918,15238.64152,2,64,6 +5/19/2025 0:00,5,1.93,7240.222941,13973.63028,2,80,6 +5/19/2025 0:00,6,1.93,6639.187515,12813.6319,2,96,6 +5/19/2025 0:00,7,1.93,6088.046074,11749.92892,2,112,6 +5/19/2025 0:00,8,1.93,5582.656751,10774.52753,2,128,6 +5/19/2025 0:00,9,1.93,5119.221508,9880.09751,2,144,6 +5/19/2025 0:00,10,1.93,4694.257593,9059.917155,2,160,6 +5/19/2025 0:00,11,1.93,4304.571373,8307.822749,2,176,6 +5/19/2025 0:00,12,1.93,3947.234325,7618.162247,2,192,6 +5/19/2025 0:00,13,1.93,3619.561035,6985.752798,2,208,6 +5/19/2025 0:00,14,1.93,3319.089016,6405.8418,2,224,6 +5/19/2025 0:00,15,1.93,3043.560196,5874.071178,2,240,6 +5/19/2025 0:00,16,1.93,2790.9,5386.444636,2,256,6 +5/19/2025 0:00,17,1.93,2559.22,4939.297624,2,272,6 +5/19/2025 0:00,18,1.93,2346.77,4529.269801,2,288,6 +5/19/2025 0:00,19,1.93,2151.96,4153.279775,2,304,6 +5/19/2025 0:00,20,1.93,1973.32,3808.501955,2,320,6 +5/19/2025 0:00,21,1.93,1809.51,3492.34531,2,336,6 +5/19/2025 0:00,22,1.93,1659.29,3202.433898,2,352,6 +5/19/2025 0:00,23,1.93,1521.55,2936.589015,2,368,6 +5/19/2025 0:00,24,1.93,1395.24,2692.812815,2,384,6 +5/19/2025 0:00,25,1.93,1279.42,2469.273304,2,400,6 +5/19/2025 0:00,26,1.93,1173.21,2264.290564,2,416,6 +5/19/2025 0:00,27,1.93,1075.82,2076.324136,2,432,6 +5/19/2025 0:00,28,1.93,986.51,1903.961438,2,448,6 +5/19/2025 0:00,29,1.93,904.62,1745.907151,2,464,6 +5/19/2025 0:00,30,1.93,829.52,1600.973485,2,480,6 +5/19/2025 0:00,31,1.93,760.66,1468.071254,2,496,6 +5/19/2025 0:00,32,1.93,697.51,1346.201688,2,512,6 +5/19/2025 0:00,33,1.93,639.61,1234.448926,2,528,6 +5/19/2025 0:00,34,1.93,586.51,1131.97314,2,544,6 +5/19/2025 0:00,35,1.93,537.83,1038.004216,2,560,6 +5/19/2025 0:00,36,1.93,493.18,951.8359709,2,576,6 +5/19/2025 0:00,37,1.93,452.24,872.8208434,2,592,6 +5/19/2025 0:00,38,1.93,414.7,800.3650292,2,608,6 +5/19/2025 0:00,39,1.93,380.27,733.9240176,2,624,6 +5/19/2025 0:00,40,1.93,348.7,672.9984993,2,640,6 +5/19/2025 0:00,41,1.93,319.76,617.1306146,2,656,6 +5/19/2025 0:00,42,1.93,293.21,565.9005123,2,672,6 +5/19/2025 0:00,43,1.93,268.87,518.9231943,2,688,6 +5/19/2025 0:00,44,1.93,246.55,475.8456225,2,704,6 +5/19/2025 0:00,45,1.93,226.09,436.3440658,2,720,6 +5/19/2025 0:00,46,1.93,207.32,400.1216672,2,736,6 +5/19/2025 0:00,47,1.93,190.11,366.9062125,2,752,6 +5/19/2025 0:00,48,1.93,174.33,336.4480852,2,768,6 +5/19/2025 0:00,49,1.93,159.85,308.5183902,2,784,6 +5/19/2025 0:00,50,1.93,146.58,282.9072338,2,800,6 +5/19/2025 0:00,51,1.93,134.42,259.4221462,2,816,6 +5/19/2025 0:00,52,1.93,123.26,237.8866352,2,832,6 +5/19/2025 0:00,53,1.93,113.03,218.13886,2,848,6 +5/19/2025 0:00,54,1.93,103.64,200.0304145,2,864,6 +5/19/2025 0:00,55,1.93,95.04,183.4252123,2,880,6 +5/19/2025 0:00,56,1.93,87.15,168.1984643,2,896,6 +5/19/2025 0:00,57,1.93,79.91,154.2357401,2,912,6 +5/19/2025 0:00,58,1.93,73.28,141.432109,2,928,6 +5/19/2025 0:00,59,1.93,67.2,129.6913506,2,944,6 +5/19/2025 0:00,60,1.93,61.62,118.9252324,2,960,6 +5/19/2025 0:00,61,1.93,56.5,109.0528461,2,976,6 +5/19/2025 0:00,62,1.93,51.81,100,2,992,6 +5/19/2025 0:00,63,1.93,Solve,Solve,2,-16,6 +5/19/2025 0:00,64,1.93,Solve,Solve,2,-12,6 +5/19/2025 0:00,65,1.93,Solve,Solve,2,-8,6 +5/19/2025 0:00,66,1.93,Solve,Solve,2,-4,6 +5/19/2025 0:00,67,1.93,Solve,Solve,2,0,6 +5/19/2025 0:00,68,1.93,Solve,Solve,2,4,6 +5/19/2025 0:00,69,1.93,Solve,Solve,2,8,6 +5/19/2025 0:00,70,1.93,Solve,Solve,2,12,6 +5/19/2025 0:00,71,1.93,Solve,Solve,2,16,6 +12/1/2025 20:20,0,1.93,10240,19763.2,3,0,8 +12/1/2025 20:20,1,1.93,10240,19763.2,3,16,8 +12/1/2025 20:20,2,1.93,9389.94292,18122.58984,3,32,8 +12/1/2025 20:20,3,1.93,8610.451958,16618.17228,3,48,8 +12/1/2025 20:20,4,1.93,7895.66918,15238.64152,3,64,8 +12/1/2025 20:20,5,1.93,7240.222941,13973.63028,3,80,8 +12/1/2025 20:20,6,1.93,6639.187515,12813.6319,3,96,8 +12/1/2025 20:20,7,1.93,6088.046074,11749.92892,3,112,8 +12/1/2025 20:20,8,1.93,5582.656751,10774.52753,3,128,8 +12/1/2025 20:20,9,1.93,5119.221508,9880.09751,3,144,8 +12/1/2025 20:20,10,1.93,4694.257593,9059.917155,3,160,8 +12/1/2025 20:20,11,1.93,4304.571373,8307.822749,3,176,8 +12/1/2025 20:20,12,1.93,3947.234325,7618.162247,3,192,8 +12/1/2025 20:20,13,1.93,3619.561035,6985.752798,3,208,8 +12/1/2025 20:20,14,1.93,3319.089016,6405.8418,3,224,8 +12/1/2025 20:20,15,1.93,3043.560196,5874.071178,3,240,8 +12/1/2025 20:20,16,1.93,2790.9,5386.444636,3,256,8 +12/1/2025 20:20,17,1.93,2559.22,4939.297624,3,272,8 +12/1/2025 20:20,18,1.93,2346.77,4529.269801,3,288,8 +12/1/2025 20:20,19,1.93,2151.96,4153.279775,3,304,8 +12/1/2025 20:20,20,1.93,1973.32,3808.501955,3,320,8 +12/1/2025 20:20,21,1.93,1809.51,3492.34531,3,336,8 +12/1/2025 20:20,22,1.93,1659.29,3202.433898,3,352,8 +12/1/2025 20:20,23,1.93,1521.55,2936.589015,3,368,8 +12/1/2025 20:20,24,1.93,1395.24,2692.812815,3,384,8 +12/1/2025 20:20,25,1.93,1279.42,2469.273304,3,400,8 +12/1/2025 20:20,26,1.93,1173.21,2264.290564,3,416,8 +12/1/2025 20:20,27,1.93,1075.82,2076.324136,3,432,8 +12/1/2025 20:20,28,1.93,986.51,1903.961438,3,448,8 +12/1/2025 20:20,29,1.93,904.62,1745.907151,3,464,8 +12/1/2025 20:20,30,1.93,829.52,1600.973485,3,480,8 +12/1/2025 20:20,31,1.93,760.66,1468.071254,3,496,8 +12/1/2025 20:20,32,1.93,697.51,1346.201688,3,512,8 +12/1/2025 20:20,33,1.93,639.61,1234.448926,3,528,8 +12/1/2025 20:20,34,1.93,586.51,1131.97314,3,544,8 +12/1/2025 20:20,35,1.93,537.83,1038.004216,3,560,8 +12/1/2025 20:20,36,1.93,493.18,951.8359709,3,576,8 +12/1/2025 20:20,37,1.93,452.24,872.8208434,3,592,8 +12/1/2025 20:20,38,1.93,414.7,800.3650292,3,608,8 +12/1/2025 20:20,39,1.93,380.27,733.9240176,3,624,8 +12/1/2025 20:20,40,1.93,348.7,672.9984993,3,640,8 +12/1/2025 20:20,41,1.93,319.76,617.1306146,3,656,8 +12/1/2025 20:20,42,1.93,293.21,565.9005123,3,672,8 +12/1/2025 20:20,43,1.93,268.87,518.9231943,3,688,8 +12/1/2025 20:20,44,1.93,246.55,475.8456225,3,704,8 +12/1/2025 20:20,45,1.93,226.09,436.3440658,3,720,8 +12/1/2025 20:20,46,1.93,207.32,400.1216672,3,736,8 +12/1/2025 20:20,47,1.93,190.11,366.9062125,3,752,8 +12/1/2025 20:20,48,1.93,174.33,336.4480852,3,768,8 +12/1/2025 20:20,49,1.93,159.85,308.5183902,3,784,8 +12/1/2025 20:20,50,1.93,146.58,282.9072338,3,800,8 +12/1/2025 20:20,51,1.93,134.42,259.4221462,3,816,8 +12/1/2025 20:20,52,1.93,123.26,237.8866352,3,832,8 +12/1/2025 20:20,53,1.93,113.03,218.13886,3,848,8 +12/1/2025 20:20,54,1.93,103.64,200.0304145,3,864,8 +12/1/2025 20:20,55,1.93,95.04,183.4252123,3,880,8 +12/1/2025 20:20,56,1.93,87.15,168.1984643,3,896,8 +12/1/2025 20:20,57,1.93,79.91,154.2357401,3,912,8 +12/1/2025 20:20,58,1.93,73.28,141.432109,3,928,8 +12/1/2025 20:20,59,1.93,67.2,129.6913506,3,944,8 +12/1/2025 20:20,60,1.93,61.62,118.9252324,3,960,8 +12/1/2025 20:20,61,1.93,56.5,109.0528461,3,976,8 +12/1/2025 20:20,62,1.93,51.81,100,3,992,8 +12/1/2025 20:20,63,1.93,0,0,3,1023,8 +12/1/2025 20:20,64,1.93,0,0,3,1023,8 +12/1/2025 20:20,65,1.93,0,0,3,1023,8 +12/1/2025 20:20,66,1.93,Solve,Solve,3,-40,8 +12/1/2025 20:20,67,1.93,Solve,Solve,3,-24,8 +12/1/2025 20:20,68,1.93,Solve,Solve,3,-8,8 +12/1/2025 20:20,69,1.93,Solve,Solve,3,8,8 +12/1/2025 20:20,70,1.93,Solve,Solve,3,24,8 +12/1/2025 20:20,71,1.93,Solve,Solve,3,40,8 diff --git a/imap_processing/tests/ialirt/data/l0/iois_1_packets_2025_322_12_00_56 b/imap_processing/tests/ialirt/data/l0/iois_1_packets_2025_322_12_00_56 new file mode 100644 index 000000000..018b095ba Binary files /dev/null and b/imap_processing/tests/ialirt/data/l0/iois_1_packets_2025_322_12_00_56 differ diff --git a/imap_processing/tests/ialirt/data/l0/iois_1_packets_2025_343_00_00_17 b/imap_processing/tests/ialirt/data/l0/iois_1_packets_2025_343_00_00_17 new file mode 100644 index 000000000..74296cb98 Binary files /dev/null and b/imap_processing/tests/ialirt/data/l0/iois_1_packets_2025_343_00_00_17 differ diff --git a/imap_processing/tests/ialirt/unit/conftest.py b/imap_processing/tests/ialirt/unit/conftest.py index 8f75a1fb7..844402750 100644 --- a/imap_processing/tests/ialirt/unit/conftest.py +++ b/imap_processing/tests/ialirt/unit/conftest.py @@ -19,6 +19,42 @@ def sc_packet_path(): return packet_path, xtce_ialirt_path +@pytest.fixture +def swapi_presweep_sc_packet_path(): + """Returns the spacecraft packet directory.""" + packet_path = ( + imap_module_directory + / "tests" + / "ialirt" + / "data" + / "l0" + / "iois_1_packets_2025_322_12_00_56" + ) + xtce_ialirt_path = ( + imap_module_directory / "ialirt" / "packet_definitions" / "ialirt.xml" + ) + + return packet_path, xtce_ialirt_path + + +@pytest.fixture +def swapi_postsweep_sc_packet_path(): + """Returns the spacecraft packet directory.""" + packet_path = ( + imap_module_directory + / "tests" + / "ialirt" + / "data" + / "l0" + / "iois_1_packets_2025_343_00_00_17" + ) + xtce_ialirt_path = ( + imap_module_directory / "ialirt" / "packet_definitions" / "ialirt.xml" + ) + + return packet_path, xtce_ialirt_path + + @pytest.fixture def ialirt_mag_test_l1d_data(): """Returns the MAG I-ALiRT calibration dataset.""" diff --git a/imap_processing/tests/ialirt/unit/test_process_swapi.py b/imap_processing/tests/ialirt/unit/test_process_swapi.py index db1e5778a..a37574cd4 100644 --- a/imap_processing/tests/ialirt/unit/test_process_swapi.py +++ b/imap_processing/tests/ialirt/unit/test_process_swapi.py @@ -69,6 +69,28 @@ def sc_xarray_data(sc_packet_path): return sc_xarray_data +@pytest.fixture +def swapi_presweep_sc_xarray_data(swapi_presweep_sc_packet_path): + """Extract spacecraft packet for testing.""" + + packet_path, xtce_ialirt_path = swapi_presweep_sc_packet_path + sc_xarray_data = packet_file_to_datasets( + packet_path, xtce_ialirt_path, use_derived_value=False + )[478] + return sc_xarray_data + + +@pytest.fixture +def swapi_postsweep_sc_xarray_data(swapi_postsweep_sc_packet_path): + """Extract spacecraft packet for testing.""" + + packet_path, xtce_ialirt_path = swapi_postsweep_sc_packet_path + sc_xarray_data = packet_file_to_datasets( + packet_path, xtce_ialirt_path, use_derived_value=False + )[478] + return sc_xarray_data + + @pytest.fixture def ialirt_test_data(): """Extract test data for unit tests below.""" @@ -137,11 +159,14 @@ def test_process_swapi_ialirt( 0 : xarray_data["swapi_flag"].shape[0] ].data - energy_passbands = pd.read_csv( + calibration_lut_table = pd.read_csv( f"{imap_module_directory}/tests/ialirt/data/l0/swapi_ialirt_energy_steps.csv" ) + + # it is 0 in the template data, but there is no #0 in the table + xarray_data['swapi_version'] = (['epoch'], [calibration_lut_table['Sweep #'].max()] * len(xarray_data.epoch)) - swapi_result = process_swapi_ialirt(xarray_data, energy_passbands) + swapi_result = process_swapi_ialirt(xarray_data, calibration_lut_table) key_names = [ "apid", @@ -172,11 +197,10 @@ def test_count_rate(): ) -def test_optimize_parameters(): - """Test that the optimize_pseudo_parameters() function works correctly.""" - +@pytest.fixture +def test_data(): # The following files and values are all validation sets provided by the SWAPI team. - test_data = { + return { "test_set_1": { "file_name": "ialirt_test_data_u_sw_550_n_sw_5_T_sw_100000_v2.csv", "expected_values": { # expected output and acceptable tolerance @@ -203,70 +227,115 @@ def test_optimize_parameters(): }, } + +@pytest.fixture +def energy_passbands(): calibration_test_file = pd.read_csv( f"{imap_module_directory}/tests/ialirt/data/l0/swapi_ialirt_energy_steps.csv" ) energy_passbands = calibration_test_file["Energy"][0:63].to_numpy().astype(float) + return energy_passbands + + +def test_optimize_parameters(test_data, energy_passbands): + """Test that the optimize_pseudo_parameters() function works correctly.""" + + for test_set in test_data: + energy_data = pd.read_csv( + f"{imap_module_directory}/tests/ialirt/data/l0/" + f"{test_data[test_set]['file_name']}", + ) + count_rates = energy_data["Count Rates [Hz]"].to_numpy() + count_rates[0] = 0.0 # set the first count to zero + count_rates_errors = energy_data["Count Rates Error [Hz]"].to_numpy() + + # select the first 63 only + count_rates = count_rates[:63] + count_rates_errors = count_rates_errors[:63] + + result = optimize_pseudo_parameters( + count_rates, count_rates_errors, energy_passbands + ) + + for param in test_data[test_set]["expected_values"]: + np.testing.assert_allclose( + result[param], + test_data[test_set]["expected_values"][param][0], + rtol=test_data[test_set]["expected_values"][param][1], + err_msg=f"{param} did not match the expected result " + f"within the tolerance.", + ) + +def test_optimize_parameters_with_zero_count(test_data, energy_passbands): + """Test that the optimize_pseudo_parameters() function works correctly + with invalid count and count rate values.""" for test_set in test_data: energy_data = pd.read_csv( f"{imap_module_directory}/tests/ialirt/data/l0/" f"{test_data[test_set]['file_name']}", ) count_rates = energy_data["Count Rates [Hz]"].to_numpy() - count_rates[0] = 0.0 - count_rates = np.tile(count_rates, (2, 1)) + count_rates[0] = 0.0 # set the first count to zero + # so that there are N sweeps count_rates_errors = energy_data["Count Rates Error [Hz]"].to_numpy() - count_rates_errors = np.tile(count_rates_errors, (2, 1)) + + # select the first 63 only + count_rates = count_rates[:63] + count_rates_errors = count_rates_errors[:63] + + # set some points to have invalid values that should not affect the fit too much + i_peak = count_rates.argmax() # invalid values need to be close to the maximum to be detected + + # zero count/count rate + count_rates[i_peak + 2] = 0 + count_rates_errors[i_peak + 2] = 0 result = optimize_pseudo_parameters( count_rates, count_rates_errors, energy_passbands ) for param in test_data[test_set]["expected_values"]: - ( - np.testing.assert_allclose( - result[param][0], - test_data[test_set]["expected_values"][param][0], - rtol=test_data[test_set]["expected_values"][param][1], - ), - f"{param} did not match the expected result within the tolerance.", + # because invalid values inserted, higher error tolerance by 50% + np.testing.assert_allclose( + result[param], + test_data[test_set]["expected_values"][param][0], + rtol=0.5 + test_data[test_set]["expected_values"][param][1], + err_msg=f"{param} did not match the expected result " + f"within the tolerance.", ) @pytest.mark.external_test_data -def test_process_spacecraft_packet(sc_xarray_data): - """Tests spacecraft packet processing.""" +def test_process_presweep_spacecraft_packet(swapi_presweep_sc_xarray_data): + """Tests spacecraft packet processing presweep.""" calibration_file = pd.read_csv( - f"{imap_module_directory}/tests/ialirt/data/l0/swapi_ialirt_energy_steps.csv" + f"{imap_module_directory}/tests/ialirt/data/l0/" + f"imap_swapi_esa-unit-conversion_20250626_v001.csv" ) - # Case 1: Not fixing the sequence number attribute, which is all zeros. - swapi_product = process_swapi_ialirt(sc_xarray_data, calibration_file) - assert swapi_product == [] + swapi_product = process_swapi_ialirt( + swapi_presweep_sc_xarray_data, calibration_file + ) - # Case 2: Overwriting swapi_seq_number to be an acceptable array of numbers. - # Calculate how many times to tile the sequence to reach length of sc packet - target_length = sc_xarray_data["swapi_seq_number"].shape[0] - base_sequence = np.arange(12) - repeat_times = (target_length // len(base_sequence)) + 1 # Over-repeat + assert isinstance(swapi_product, list) + assert len(swapi_product) == 4 + + assert np.isclose(float(swapi_product[0]['swapi_pseudo_proton_speed']), 450, rtol=0.1) - # Tile the sequence and truncate to target_length - extended_data = np.tile(base_sequence, repeat_times)[:target_length] - sc_xarray_data["swapi_seq_number"].data = extended_data +@pytest.mark.external_test_data +def test_process_postsweep_spacecraft_packet(swapi_postsweep_sc_xarray_data): + """Tests spacecraft packet processing postsweep.""" + calibration_file = pd.read_csv( + f"{imap_module_directory}/tests/ialirt/data/l0/" + f"imap_swapi_esa-unit-conversion_20251201_v001.csv" + ) - swapi_product1 = process_swapi_ialirt(sc_xarray_data, calibration_file) - key_names = [ - "apid", - "met", - "met_in_utc", - "ttj2000ns", - "swapi_pseudo_proton_density", - "swapi_pseudo_proton_speed", - "swapi_pseudo_proton_temperature", - ] + swapi_product = process_swapi_ialirt( + swapi_postsweep_sc_xarray_data, calibration_file + ) - for key in key_names: - assert swapi_product1[0][key] is not None, ( - f"The expected attribute {key} was not filled in the result dict." - ) + assert isinstance(swapi_product, list) + assert len(swapi_product) == 4 + + assert np.isclose(float(swapi_product[0]['swapi_pseudo_proton_speed']), 400, rtol=0.1) \ No newline at end of file