From 977842e76ddc52a1d83809efdaf081c3e535388a Mon Sep 17 00:00:00 2001 From: Laura Sandoval Date: Wed, 14 Aug 2024 11:16:43 -0600 Subject: [PATCH 1/4] update --- .../ultra/unit/test_ultra_l1b_extended.py | 23 ++++ imap_processing/ultra/l1b/lookup_utils.py | 4 +- .../ultra/l1b/ultra_l1b_extended.py | 112 ++++++++++++++++++ 3 files changed, 138 insertions(+), 1 deletion(-) diff --git a/imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py b/imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py index b48c62afe..aaf4be4ae 100644 --- a/imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +++ b/imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py @@ -1,5 +1,6 @@ """Tests Extended Raw Events for ULTRA L1b.""" +import numpy as np import pandas as pd import pytest @@ -7,6 +8,7 @@ get_front_x_position, get_front_y_position, get_path_length, + get_ph_tof_and_back_positions, ) @@ -59,3 +61,24 @@ def test_get_path_length(de_dataset, yf_fixture): test_yb = df_filt["Yb"].astype("float").values r = get_path_length((test_xf, test_yf), (test_xb, test_yb), d) assert r == pytest.approx(df_filt["r"].astype("float"), abs=1e-5) + + +def test_get_ph_tof_and_back_positions( + de_dataset, + events_fsw_comparison_theta_0, +): + """Tests get_ph_tof_and_back_positions function.""" + + df = pd.read_csv(events_fsw_comparison_theta_0) + df_filt = df[df["StartType"] != -1] + + _, _, ph_xb, ph_yb = get_ph_tof_and_back_positions( + de_dataset, df_filt.Xf.astype("float").values, "ultra45" + ) + + ph_indices = np.where( + (de_dataset["STOP_TYPE"] == 1) | (de_dataset["STOP_TYPE"] == 2) + )[0] + selected_rows = df_filt.iloc[ph_indices] + np.testing.assert_array_equal(ph_xb, selected_rows["Xb"].astype("float")) + np.testing.assert_array_equal(ph_yb, selected_rows["Yb"].astype("float")) \ No newline at end of file diff --git a/imap_processing/ultra/l1b/lookup_utils.py b/imap_processing/ultra/l1b/lookup_utils.py index 08673c476..46585e959 100644 --- a/imap_processing/ultra/l1b/lookup_utils.py +++ b/imap_processing/ultra/l1b/lookup_utils.py @@ -78,7 +78,9 @@ def get_norm(dn: np.ndarray, key: str, file_label: str) -> npt.NDArray: else: tdc_norm_df = _TDC_NORM_DF_ULTRA90 - return tdc_norm_df[key].values[dn] + dn_norm = tdc_norm_df[key].iloc[dn].values + + return dn_norm def get_back_position(back_index: np.ndarray, key: str, file_label: str) -> npt.NDArray: diff --git a/imap_processing/ultra/l1b/ultra_l1b_extended.py b/imap_processing/ultra/l1b/ultra_l1b_extended.py index c977981fb..501a16d23 100644 --- a/imap_processing/ultra/l1b/ultra_l1b_extended.py +++ b/imap_processing/ultra/l1b/ultra_l1b_extended.py @@ -1,9 +1,13 @@ """Calculates Extended Raw Events for ULTRA L1b.""" +from enum import Enum import numpy as np from numpy import ndarray +import xarray from imap_processing.ultra.l1b.lookup_utils import ( + get_back_position, + get_norm, get_image_params, get_y_adjust, ) @@ -18,6 +22,12 @@ # TODO: make lookup tables into config files. +class StopType(Enum): + """Stop Type: 1=Top, 2=Bottom.""" + Top = 1 + Bottom = 2 + + def get_front_x_position(start_type: ndarray, start_position_tdc: ndarray) -> ndarray: """ Calculate the front xf position. @@ -111,6 +121,108 @@ def get_front_y_position(start_type: ndarray, yb: ndarray) -> tuple[ndarray, nda return np.array(d), np.array(yf) +def get_ph_tof_and_back_positions( + de_dataset: xarray.Dataset, xf: np.array, sensor: str +) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: + """ + Calculate back xb, yb position and tof. + + An incoming particle may trigger pulses from one of the stop anodes. + If so, four pulses are produced, one each from the north, south, + east, and west sides. + + The Time Of Flight (tof) and the position of the particle at the + back of the sensor are measured using the timing of the pulses. + Further description is available on pages 32-33 of + IMAP-Ultra Flight Software Specification document + (7523-9009_Rev_-.pdf). + + Parameters + ---------- + de_dataset : xarray.Dataset + Data in xarray format. + xf : np.array + X front position in (hundredths of a millimeter). + sensor : str + Sensor name. + + Returns + ------- + tof : np.array + Time of flight (tenths of a nanosecond). + t2 : np.array + Particle time of flight from start to stop (tenths of a nanosecond). + xb : np.array + Back positions in x direction (hundredths of a millimeter). + yb : np.array + Back positions in y direction (hundredths of a millimeter). + """ + indices = np.where(np.isin(de_dataset["STOP_TYPE"], [1, 2]))[0] + xf = xf[indices] + + # There are mismatches between the stop TDCs, i.e., SpN, SpS, SpE, and SpW. + # This normalizes the TDCs + sp_n_norm = get_norm(de_dataset["STOP_NORTH_TDC"].data[indices], "SpN", sensor) + sp_s_norm = get_norm(de_dataset["STOP_SOUTH_TDC"].data[indices], "SpS", sensor) + sp_e_norm = get_norm(de_dataset["STOP_EAST_TDC"].data[indices], "SpE", sensor) + sp_w_norm = get_norm(de_dataset["STOP_WEST_TDC"].data[indices], "SpW", sensor) + + # Convert normalized TDC values into units of hundredths of a + # millimeter using lookup tables. + xb_index = sp_s_norm - sp_n_norm + 2047 + yb_index = sp_e_norm - sp_w_norm + 2047 + + # Convert xf to a tof offset + tofx = sp_n_norm + sp_s_norm + tofy = sp_e_norm + sp_w_norm + + # tof is the average of the two tofs measured in the X and Y directions, + # tofx and tofy + # Units in tenths of a nanosecond + t1 = tofx + tofy # /2 incorporated into scale + + xb = np.zeros(len(de_dataset["STOP_TYPE"])) + yb = np.zeros(len(de_dataset["STOP_TYPE"])) + + # particle_tof (t2) used later to compute etof + t2 = np.zeros(len(de_dataset["STOP_TYPE"])) + tof = np.zeros(len(de_dataset["STOP_TYPE"])) + + # Stop Type: 1=Top, 2=Bottom + # Convert converts normalized TDC values into units of + # hundredths of a millimeter using lookup tables. + index_top = indices[de_dataset["STOP_TYPE"].data[indices] == 1] + stop_type_top = de_dataset["STOP_TYPE"].data[indices] == 1 + xb[index_top] = get_back_position(xb_index[stop_type_top], "XBkTp", sensor) + yb[index_top] = get_back_position(yb_index[stop_type_top], "YBkTp", sensor) + + # Correction for the propagation delay of the start anode and other effects. + t2[index_top] = get_image_params("TOFSC") * t1[stop_type_top] + get_image_params( + "TOFTPOFF" + ) + tof[index_top] = t2[index_top] + xf[stop_type_top] * get_image_params("XFTTOF") + + index_bottom = indices[de_dataset["STOP_TYPE"].data[indices] == 2] + stop_type_bottom = de_dataset["STOP_TYPE"].data[indices] == 2 + xb[index_bottom] = get_back_position(xb_index[stop_type_bottom], "XBkBt", sensor) + yb[index_bottom] = get_back_position(yb_index[stop_type_bottom], "YBkBt", sensor) + + # Correction for the propagation delay of the start anode and other effects. + t2[index_bottom] = get_image_params("TOFSC") * t1[ + stop_type_bottom + ] + get_image_params("TOFBTOFF") # 10*ns + tof[index_bottom] = t2[index_bottom] + xf[stop_type_bottom] * get_image_params( + "XFTTOF" + ) + # Multiply by 100 to get tenths of a nanosecond. + tof = tof[indices] * 100 + t2 = t2[indices] + xb = xb[indices] + yb = yb[indices] + + return tof, t2, xb, yb + + def get_path_length(front_position: tuple, back_position: tuple, d: float) -> float: """ Calculate the path length. From 87be39acd73d66ce7e2c322733bdb4a6ca0a7206 Mon Sep 17 00:00:00 2001 From: Laura Sandoval Date: Wed, 14 Aug 2024 11:41:38 -0600 Subject: [PATCH 2/4] adding ph bottom calculations --- .../ultra/unit/test_ultra_l1b_extended.py | 7 +++-- .../ultra/l1b/ultra_l1b_extended.py | 31 ++++++++++++------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py b/imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py index aaf4be4ae..d522a66b8 100644 --- a/imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +++ b/imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py @@ -5,6 +5,7 @@ import pytest from imap_processing.ultra.l1b.ultra_l1b_extended import ( + StopType, get_front_x_position, get_front_y_position, get_path_length, @@ -77,8 +78,10 @@ def test_get_ph_tof_and_back_positions( ) ph_indices = np.where( - (de_dataset["STOP_TYPE"] == 1) | (de_dataset["STOP_TYPE"] == 2) + np.isin(de_dataset["STOP_TYPE"], [StopType.Top.value, StopType.Bottom.value]) )[0] + selected_rows = df_filt.iloc[ph_indices] + np.testing.assert_array_equal(ph_xb, selected_rows["Xb"].astype("float")) - np.testing.assert_array_equal(ph_yb, selected_rows["Yb"].astype("float")) \ No newline at end of file + np.testing.assert_array_equal(ph_yb, selected_rows["Yb"].astype("float")) diff --git a/imap_processing/ultra/l1b/ultra_l1b_extended.py b/imap_processing/ultra/l1b/ultra_l1b_extended.py index 501a16d23..17d51ff9f 100644 --- a/imap_processing/ultra/l1b/ultra_l1b_extended.py +++ b/imap_processing/ultra/l1b/ultra_l1b_extended.py @@ -1,14 +1,15 @@ """Calculates Extended Raw Events for ULTRA L1b.""" from enum import Enum + import numpy as np -from numpy import ndarray import xarray +from numpy import ndarray from imap_processing.ultra.l1b.lookup_utils import ( get_back_position, - get_norm, get_image_params, + get_norm, get_y_adjust, ) @@ -24,6 +25,7 @@ class StopType(Enum): """Stop Type: 1=Top, 2=Bottom.""" + Top = 1 Bottom = 2 @@ -122,7 +124,7 @@ def get_front_y_position(start_type: ndarray, yb: ndarray) -> tuple[ndarray, nda def get_ph_tof_and_back_positions( - de_dataset: xarray.Dataset, xf: np.array, sensor: str + de_dataset: xarray.Dataset, xf: np.ndarray, sensor: str ) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """ Calculate back xb, yb position and tof. @@ -157,8 +159,11 @@ def get_ph_tof_and_back_positions( yb : np.array Back positions in y direction (hundredths of a millimeter). """ - indices = np.where(np.isin(de_dataset["STOP_TYPE"], [1, 2]))[0] - xf = xf[indices] + indices = np.where( + np.isin(de_dataset["STOP_TYPE"], [StopType.Top.value, StopType.Bottom.value]) + )[0] + + xf_ph = xf[indices] # There are mismatches between the stop TDCs, i.e., SpN, SpS, SpE, and SpW. # This normalizes the TDCs @@ -191,8 +196,8 @@ def get_ph_tof_and_back_positions( # Stop Type: 1=Top, 2=Bottom # Convert converts normalized TDC values into units of # hundredths of a millimeter using lookup tables. - index_top = indices[de_dataset["STOP_TYPE"].data[indices] == 1] - stop_type_top = de_dataset["STOP_TYPE"].data[indices] == 1 + index_top = indices[de_dataset["STOP_TYPE"].data[indices] == StopType.Top.value] + stop_type_top = de_dataset["STOP_TYPE"].data[indices] == StopType.Top.value xb[index_top] = get_back_position(xb_index[stop_type_top], "XBkTp", sensor) yb[index_top] = get_back_position(yb_index[stop_type_top], "YBkTp", sensor) @@ -200,10 +205,12 @@ def get_ph_tof_and_back_positions( t2[index_top] = get_image_params("TOFSC") * t1[stop_type_top] + get_image_params( "TOFTPOFF" ) - tof[index_top] = t2[index_top] + xf[stop_type_top] * get_image_params("XFTTOF") + tof[index_top] = t2[index_top] + xf_ph[stop_type_top] * get_image_params("XFTTOF") - index_bottom = indices[de_dataset["STOP_TYPE"].data[indices] == 2] - stop_type_bottom = de_dataset["STOP_TYPE"].data[indices] == 2 + index_bottom = indices[ + de_dataset["STOP_TYPE"].data[indices] == StopType.Bottom.value + ] + stop_type_bottom = de_dataset["STOP_TYPE"].data[indices] == StopType.Bottom.value xb[index_bottom] = get_back_position(xb_index[stop_type_bottom], "XBkBt", sensor) yb[index_bottom] = get_back_position(yb_index[stop_type_bottom], "YBkBt", sensor) @@ -211,9 +218,11 @@ def get_ph_tof_and_back_positions( t2[index_bottom] = get_image_params("TOFSC") * t1[ stop_type_bottom ] + get_image_params("TOFBTOFF") # 10*ns - tof[index_bottom] = t2[index_bottom] + xf[stop_type_bottom] * get_image_params( + + tof[index_bottom] = t2[index_bottom] + xf_ph[stop_type_bottom] * get_image_params( "XFTTOF" ) + # Multiply by 100 to get tenths of a nanosecond. tof = tof[indices] * 100 t2 = t2[indices] From bf5af68a1f1ef1a3413059b00ce95cfacb556e2c Mon Sep 17 00:00:00 2001 From: Laura Sandoval Date: Tue, 27 Aug 2024 11:08:00 -0600 Subject: [PATCH 3/4] pr response --- .../ultra/unit/test_ultra_l1b_extended.py | 2 +- .../ultra/l1b/ultra_l1b_extended.py | 35 +++++++++---------- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py b/imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py index d522a66b8..84b027666 100644 --- a/imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +++ b/imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py @@ -77,7 +77,7 @@ def test_get_ph_tof_and_back_positions( de_dataset, df_filt.Xf.astype("float").values, "ultra45" ) - ph_indices = np.where( + ph_indices = np.nonzero( np.isin(de_dataset["STOP_TYPE"], [StopType.Top.value, StopType.Bottom.value]) )[0] diff --git a/imap_processing/ultra/l1b/ultra_l1b_extended.py b/imap_processing/ultra/l1b/ultra_l1b_extended.py index 17d51ff9f..2a4dea509 100644 --- a/imap_processing/ultra/l1b/ultra_l1b_extended.py +++ b/imap_processing/ultra/l1b/ultra_l1b_extended.py @@ -145,6 +145,7 @@ def get_ph_tof_and_back_positions( Data in xarray format. xf : np.array X front position in (hundredths of a millimeter). + Has same length as de_dataset. sensor : str Sensor name. @@ -159,18 +160,19 @@ def get_ph_tof_and_back_positions( yb : np.array Back positions in y direction (hundredths of a millimeter). """ - indices = np.where( + indices = np.nonzero( np.isin(de_dataset["STOP_TYPE"], [StopType.Top.value, StopType.Bottom.value]) )[0] + de_filtered = de_dataset.isel(epoch=indices) xf_ph = xf[indices] # There are mismatches between the stop TDCs, i.e., SpN, SpS, SpE, and SpW. # This normalizes the TDCs - sp_n_norm = get_norm(de_dataset["STOP_NORTH_TDC"].data[indices], "SpN", sensor) - sp_s_norm = get_norm(de_dataset["STOP_SOUTH_TDC"].data[indices], "SpS", sensor) - sp_e_norm = get_norm(de_dataset["STOP_EAST_TDC"].data[indices], "SpE", sensor) - sp_w_norm = get_norm(de_dataset["STOP_WEST_TDC"].data[indices], "SpW", sensor) + sp_n_norm = get_norm(de_filtered["STOP_NORTH_TDC"].data, "SpN", sensor) + sp_s_norm = get_norm(de_filtered["STOP_SOUTH_TDC"].data, "SpS", sensor) + sp_e_norm = get_norm(de_filtered["STOP_EAST_TDC"].data, "SpE", sensor) + sp_w_norm = get_norm(de_filtered["STOP_WEST_TDC"].data, "SpW", sensor) # Convert normalized TDC values into units of hundredths of a # millimeter using lookup tables. @@ -186,18 +188,18 @@ def get_ph_tof_and_back_positions( # Units in tenths of a nanosecond t1 = tofx + tofy # /2 incorporated into scale - xb = np.zeros(len(de_dataset["STOP_TYPE"])) - yb = np.zeros(len(de_dataset["STOP_TYPE"])) + xb = np.zeros(len(indices)) + yb = np.zeros(len(indices)) # particle_tof (t2) used later to compute etof - t2 = np.zeros(len(de_dataset["STOP_TYPE"])) - tof = np.zeros(len(de_dataset["STOP_TYPE"])) + t2 = np.zeros(len(indices)) + tof = np.zeros(len(indices)) # Stop Type: 1=Top, 2=Bottom # Convert converts normalized TDC values into units of # hundredths of a millimeter using lookup tables. - index_top = indices[de_dataset["STOP_TYPE"].data[indices] == StopType.Top.value] - stop_type_top = de_dataset["STOP_TYPE"].data[indices] == StopType.Top.value + index_top = np.where(de_filtered["STOP_TYPE"].data == StopType.Top.value)[0] + stop_type_top = de_filtered["STOP_TYPE"].data == StopType.Top.value xb[index_top] = get_back_position(xb_index[stop_type_top], "XBkTp", sensor) yb[index_top] = get_back_position(yb_index[stop_type_top], "YBkTp", sensor) @@ -207,10 +209,8 @@ def get_ph_tof_and_back_positions( ) tof[index_top] = t2[index_top] + xf_ph[stop_type_top] * get_image_params("XFTTOF") - index_bottom = indices[ - de_dataset["STOP_TYPE"].data[indices] == StopType.Bottom.value - ] - stop_type_bottom = de_dataset["STOP_TYPE"].data[indices] == StopType.Bottom.value + index_bottom = np.where(de_filtered["STOP_TYPE"].data == StopType.Bottom.value)[0] + stop_type_bottom = de_filtered["STOP_TYPE"].data == StopType.Bottom.value xb[index_bottom] = get_back_position(xb_index[stop_type_bottom], "XBkBt", sensor) yb[index_bottom] = get_back_position(yb_index[stop_type_bottom], "YBkBt", sensor) @@ -224,10 +224,7 @@ def get_ph_tof_and_back_positions( ) # Multiply by 100 to get tenths of a nanosecond. - tof = tof[indices] * 100 - t2 = t2[indices] - xb = xb[indices] - yb = yb[indices] + tof = tof * 100 return tof, t2, xb, yb From 3beeaf0105218724cbe746d67ee399ca44546cff Mon Sep 17 00:00:00 2001 From: Laura Sandoval Date: Tue, 27 Aug 2024 11:16:06 -0600 Subject: [PATCH 4/4] pr response --- .../ultra/l1b/ultra_l1b_extended.py | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/imap_processing/ultra/l1b/ultra_l1b_extended.py b/imap_processing/ultra/l1b/ultra_l1b_extended.py index 2a4dea509..47c25b653 100644 --- a/imap_processing/ultra/l1b/ultra_l1b_extended.py +++ b/imap_processing/ultra/l1b/ultra_l1b_extended.py @@ -198,30 +198,34 @@ def get_ph_tof_and_back_positions( # Stop Type: 1=Top, 2=Bottom # Convert converts normalized TDC values into units of # hundredths of a millimeter using lookup tables. - index_top = np.where(de_filtered["STOP_TYPE"].data == StopType.Top.value)[0] stop_type_top = de_filtered["STOP_TYPE"].data == StopType.Top.value - xb[index_top] = get_back_position(xb_index[stop_type_top], "XBkTp", sensor) - yb[index_top] = get_back_position(yb_index[stop_type_top], "YBkTp", sensor) + xb[stop_type_top] = get_back_position(xb_index[stop_type_top], "XBkTp", sensor) + yb[stop_type_top] = get_back_position(yb_index[stop_type_top], "YBkTp", sensor) # Correction for the propagation delay of the start anode and other effects. - t2[index_top] = get_image_params("TOFSC") * t1[stop_type_top] + get_image_params( - "TOFTPOFF" + t2[stop_type_top] = get_image_params("TOFSC") * t1[ + stop_type_top + ] + get_image_params("TOFTPOFF") + tof[stop_type_top] = t2[stop_type_top] + xf_ph[stop_type_top] * get_image_params( + "XFTTOF" ) - tof[index_top] = t2[index_top] + xf_ph[stop_type_top] * get_image_params("XFTTOF") - index_bottom = np.where(de_filtered["STOP_TYPE"].data == StopType.Bottom.value)[0] stop_type_bottom = de_filtered["STOP_TYPE"].data == StopType.Bottom.value - xb[index_bottom] = get_back_position(xb_index[stop_type_bottom], "XBkBt", sensor) - yb[index_bottom] = get_back_position(yb_index[stop_type_bottom], "YBkBt", sensor) + xb[stop_type_bottom] = get_back_position( + xb_index[stop_type_bottom], "XBkBt", sensor + ) + yb[stop_type_bottom] = get_back_position( + yb_index[stop_type_bottom], "YBkBt", sensor + ) # Correction for the propagation delay of the start anode and other effects. - t2[index_bottom] = get_image_params("TOFSC") * t1[ + t2[stop_type_bottom] = get_image_params("TOFSC") * t1[ stop_type_bottom ] + get_image_params("TOFBTOFF") # 10*ns - tof[index_bottom] = t2[index_bottom] + xf_ph[stop_type_bottom] * get_image_params( - "XFTTOF" - ) + tof[stop_type_bottom] = t2[stop_type_bottom] + xf_ph[ + stop_type_bottom + ] * get_image_params("XFTTOF") # Multiply by 100 to get tenths of a nanosecond. tof = tof * 100