From 76e902539858689ae44cf6d3173eabcac7152cd2 Mon Sep 17 00:00:00 2001
From: Aaron Viets <aaron.viets@ligo.org>
Date: Fri, 7 Sep 2018 17:36:55 -0700
Subject: [PATCH] gstlal_compute_strain:  Split the PUM and UIM stages of the
 actuation and calculate kappa_PUM and kappa_UIM separately and apply them.

---
 gstlal-calibration/bin/gstlal_compute_strain  | 1048 +++++++++++------
 .../H1DCS_AllCorrections_Cleaning.ini         |   67 +-
 ...GDS_LowLatency_AllCorrections_Cleaning.ini |   67 +-
 .../gstlal_compute_strain_config_example.ini  |   59 +-
 .../python/calibration_parts.py               |  189 ++-
 5 files changed, 1006 insertions(+), 424 deletions(-)

diff --git a/gstlal-calibration/bin/gstlal_compute_strain b/gstlal-calibration/bin/gstlal_compute_strain
index 92ed2f995a..3d8a847120 100755
--- a/gstlal-calibration/bin/gstlal_compute_strain
+++ b/gstlal-calibration/bin/gstlal_compute_strain
@@ -192,6 +192,9 @@ if (CalibrationConfigs["calibrationmode"] != "Full") and (CalibrationConfigs["ca
 if int(TDCFConfigs["recordfactorssr"]) > int(TDCFConfigs["computefactorssr"]):
 	raise ValueError("RecordFactorsSR must be less than or equal to ComputeFactorsSR")
 
+if (Config.getboolean("TDCFConfigurations", "applycomplexkappapum") or Config.getboolean("TDCFConfigurations", "applykappapum") or Config.getboolean("TDCFConfigurations", "applycomplexkappauim") or Config.getboolean("TDCFConfigurations", "applykappauim")) and (Config.getboolean("TDCFConfigurations", "applycomplexkappapu") or Config.getboolean("TDCFConfigurations", "applykappapu")):
+	raise ValueError("Cannot apply any corrections for kappa_PU if corrections are already being applied for kappa_PUM or kappa_UIM")
+
 if TDCFConfigs["factorsfromfiltersfile"] is "No" and (TDCFConfigs["computefs"] is "Yes" or TDCFConfigs["computesrcq"] is "Yes") and ((InputConfigs["datasource"] is "frames" and int(options.gps_start_time) < 1175954418) or (InputConfigs["datasource"] == "lvshm" and now() < 1175954418)):
 	raise ValueError("Cannot compute SRC detuning parameters as the needed EPICS channels are not in the frames until GPS time 1175954418. Set ComputeFs: No and ComputeSRCQ: No.")
 
@@ -222,6 +225,8 @@ calibstate_sr = int(SampleRates["calibstatesr"]) # Sample rate for the CALIB_STA
 odc_sr = int(SampleRates["odcsr"]) # Sample rate of the ODC channel that is read in
 ctrl_sr = int(SampleRates["ctrlsr"]) # Sample rate of the control channel (such as DARM_CTRL or DELTAL_CTRL)
 tst_exc_sr = int(SampleRates["tstexcsr"])
+pum_exc_sr = int(SampleRates["pumexcsr"])
+uim_exc_sr = int(SampleRates["uimexcsr"])
 coh_sr = int(SampleRates["cohsr"]) # Sample rate for the coherence uncertainty channels
 epics_sr = int(SampleRates["epicsrefsr"]) # Sample rate for EPICS records used for TDCFs
 compute_factors_sr = int(SampleRates["computefactorssr"]) # Sample rate for computing TDCFs
@@ -264,10 +269,10 @@ expected_kappatst_real = float(TDCFConfigs["expectedkappatstreal"])
 expected_kappatst_imag = float(TDCFConfigs["expectedkappatstimag"])
 expected_kappapu_real = float(TDCFConfigs["expectedkappapureal"])
 expected_kappapu_imag = float(TDCFConfigs["expectedkappapuimag"])
-expected_kappap_real = float(TDCFConfigs["expectedkappapreal"])
-expected_kappap_imag = float(TDCFConfigs["expectedkappapimag"])
-expected_kappau_real = float(TDCFConfigs["expectedkappaureal"])
-expected_kappau_imag = float(TDCFConfigs["expectedkappauimag"])
+expected_kappapum_real = float(TDCFConfigs["expectedkappapumreal"])
+expected_kappapum_imag = float(TDCFConfigs["expectedkappapumimag"])
+expected_kappauim_real = float(TDCFConfigs["expectedkappauimreal"])
+expected_kappauim_imag = float(TDCFConfigs["expectedkappauimimag"])
 expected_kappac = float(TDCFConfigs["expectedkappac"])
 expected_fcc = float(TDCFConfigs["expectedfcc"])
 expected_fs = float(TDCFConfigs["expectedfs"])
@@ -276,10 +281,10 @@ kappatst_real_var = float(TDCFConfigs["kappatstrealvar"])
 kappatst_imag_var = float(TDCFConfigs["kappatstimagvar"])
 kappapu_real_var = float(TDCFConfigs["kappapurealvar"])
 kappapu_imag_var = float(TDCFConfigs["kappapuimagvar"])
-kappap_real_var = float(TDCFConfigs["kappaprealvar"])
-kappap_imag_var = float(TDCFConfigs["kappapimagvar"])
-kappau_real_var = float(TDCFConfigs["kappaurealvar"])
-kappau_imag_var = float(TDCFConfigs["kappauimagvar"])
+kappapum_real_var = float(TDCFConfigs["kappapumrealvar"])
+kappapum_imag_var = float(TDCFConfigs["kappapumimagvar"])
+kappauim_real_var = float(TDCFConfigs["kappauimrealvar"])
+kappauim_imag_var = float(TDCFConfigs["kappauimimagvar"])
 kappac_var = float(TDCFConfigs["kappacvar"])
 fcc_var = float(TDCFConfigs["fccvar"])
 fs_var = float(TDCFConfigs["fsvar"])
@@ -304,8 +309,9 @@ chan_prefix = OutputConfigs["chanprefix"]
 # Booleans for TDCFs
 compute_kappatst = Config.getboolean("TDCFConfigurations", "computekappatst")
 compute_kappapu = Config.getboolean("TDCFConfigurations", "computekappapu")
-compute_kappap = Config.getboolean("TDCFConfigurations", "computekappap")
-compute_kappau = Config.getboolean("TDCFConfigurations", "computekappau")
+compute_kappapum = Config.getboolean("TDCFConfigurations", "computekappapum")
+compute_kappauim = Config.getboolean("TDCFConfigurations", "computekappauim")
+use_uim_line = Config.getboolean("TDCFConfigurations", "useuimline")
 compute_kappac = Config.getboolean("TDCFConfigurations", "computekappac")
 compute_fcc = Config.getboolean("TDCFConfigurations", "computefcc")
 compute_fs = Config.getboolean("TDCFConfigurations", "computefs")
@@ -314,10 +320,10 @@ apply_kappatst = Config.getboolean("TDCFConfigurations", "applykappatst")
 apply_complex_kappatst = Config.getboolean("TDCFConfigurations", "applycomplexkappatst")
 apply_kappapu = Config.getboolean("TDCFConfigurations", "applykappapu")
 apply_complex_kappapu = Config.getboolean("TDCFConfigurations", "applycomplexkappapu")
-apply_kappap = Config.getboolean("TDCFConfigurations", "applykappap")
-apply_complex_kappap = Config.getboolean("TDCFConfigurations", "applycomplexkappap")
-apply_kappau = Config.getboolean("TDCFConfigurations", "applykappau")
-apply_complex_kappau = Config.getboolean("TDCFConfigurations", "applycomplexkappau")
+apply_kappapum = Config.getboolean("TDCFConfigurations", "applykappapum")
+apply_complex_kappapum = Config.getboolean("TDCFConfigurations", "applycomplexkappapum")
+apply_kappauim = Config.getboolean("TDCFConfigurations", "applykappauim")
+apply_complex_kappauim = Config.getboolean("TDCFConfigurations", "applycomplexkappauim")
 apply_kappac = Config.getboolean("TDCFConfigurations", "applykappac")
 apply_fcc = Config.getboolean("TDCFConfigurations", "applyfcc")
 apply_srcq = Config.getboolean("TDCFConfigurations", "applysrcq")
@@ -348,19 +354,28 @@ if (not factors_from_filters_file) and remove_cal_lines and (InputConfigs["datas
 	remove_esd_act_line = True
 else:
 	remove_esd_act_line = False
+# If we are using EPICS from frames and removing calibration lines, we need EP23 and EP24 to remove the PUM line and UIM line, respectively. Otherwise, we just remove the other lines if possible.
+if (not factors_from_filters_file) and remove_cal_lines and (InputConfigs["datasource"] == "lvshm" or gps_start_time > 1220000000):
+	remove_pum_act_line = True
+	remove_uim_act_line = True
+else:
+	remove_pum_act_line = False
+	remove_uim_act_line = False
 
 # How many EPICS will we for the CALIB_STATE_VECTOR calculation? It depends on the IFO and the time we are calibrating
 if not compute_calib_statevector:
 	num_dq_epics = 0
 elif InputConfigs["datasource"] == "lvshm":
-	num_dq_epics = 14
+	num_dq_epics = 21
+elif gps_start_time > 1220000000:
+	num_dq_epics = 21
 elif instrument == "H1" and gps_start_time > 1175976256:
 	num_dq_epics = 14
 elif instrument == "L1" and gps_start_time > 1179588864:
 	num_dq_epics = 10
 else:
 	num_dq_epics = 9
-if (not factors_from_filters_file) and (compute_fs or compute_srcq) and ((InputConfigs["datasource"] is "frames" and gps_start_time < 1175954418) or (InputConfigs["datasource"] == "lvshm" and now() < 1175954418)):
+if (not factors_from_filters_file) and (compute_fs or compute_srcq) and ((InputConfigs["datasource"] is "frames" and gps_start_time < 1175954418)):
 	raise ValueError("Cannot compute SRC detuning parameters as the needed EPICS channels are not in the frames until GPS time 1175954418. Set ComputeFs: No and ComputeSRCQ: No.")
 
 #
@@ -392,6 +407,7 @@ print "\nLoading calibration filters from %s\n" % filters_paths[0]
 filters = numpy.load(filters_paths[0])
 
 # If we're reading the reference model factors from the filters file, load them
+
 if factors_from_filters_file or compute_calib_statevector:
 	try:
 		EP1_real = float(filters["EP1_real"])
@@ -422,10 +438,8 @@ if factors_from_filters_file or compute_calib_statevector:
 		EP10_imag = float(filters["EP10_imag"])
 		if factors_from_filters_file and remove_cal_lines:
 			remove_esd_act_line = True
-		else:
-			remove_esd_act_line = False
 	except:
-		if factors_from_filters_file:
+		if factors_from_filters_file and remove_cal_lines:
 			remove_esd_act_line = False
 		if num_dq_epics > 9:
 			num_dq_epics = 9
@@ -444,24 +458,74 @@ if factors_from_filters_file or compute_calib_statevector:
 		if num_dq_epics > 10:
 			num_dq_epics = 10
 
+	try:
+		EP15_real = float(filters["EP15_real"])
+		EP15_imag = float(filters["EP15_imag"])
+		EP16_real = float(filters["EP16_real"])
+		EP16_imag = float(filters["EP16_imag"])
+		EP17_real = float(filters["EP17_real"])
+		EP17_imag = float(filters["EP17_imag"])
+		EP18_real = float(filters["EP18_real"])
+		EP18_imag = float(filters["EP18_imag"])
+		EP19_real = float(filters["EP19_real"])
+		EP19_imag = float(filters["EP19_imag"])
+		EP20_real = float(filters["EP20_real"])
+		EP20_imag = float(filters["EP20_imag"])
+		EP21_real = float(filters["EP21_real"])
+		EP21_imag = float(filters["EP21_imag"])
+		EP22_real = float(filters["EP22_real"])
+		EP22_imag = float(filters["EP22_imag"])
+		EP23_real = float(filters["EP23_real"])
+		EP23_imag = float(filters["EP23_imag"])
+		EP24_real = float(filters["EP24_real"])
+		EP24_imag = float(filters["EP24_imag"])
+		if factors_from_filters_file and remove_cal_lines:
+			remove_pum_act_line = True
+			remove_uim_act_line = True
+	except:
+		if factors_from_filters_file and remove_cal_lines:
+			remove_pum_act_line = False
+			remove_uim_act_line = False
+		if factors_from_filters_file and (compute_kappapum or compute_kappauim):
+			raise ValueError("Cannot compute kappa_P or kappa_U, as the needed EPICS are not contained in the specified filters file.")
+		if num_dq_epics > 14:
+			num_dq_epics = 14
+
 # Load all of the kappa dewhitening and correction factors
-darm_act_line_freq = float(filters["ka_pcal_line_freq"])
-pcal_corr_at_darm_act_freq_real = float(filters["ka_pcal_corr_re"])
-pcal_corr_at_darm_act_freq_imag = float(filters["ka_pcal_corr_im"])
-pu_act_esd_line_freq = float(filters["ka_esd_line_freq"])
+act_pcal_line_freq = float(filters["ka_pcal_line_freq"])
+pcal_corr_at_act_freq_real = float(filters["ka_pcal_corr_re"])
+pcal_corr_at_act_freq_imag = float(filters["ka_pcal_corr_im"])
+try:
+	darm_ctrl_line_freq = float(filters["ka_esd_line_freq"])
+except:
+	if(compute_kappapu or (compute_kappauim and not use_uim_line)):
+		raise ValueError("Cannot compute kappa_U or kappa_PU using DARM ctrl line, because the specified filters do not have the DARM ctrl line frequency")
 opt_gain_fcc_line_freq = float(filters["kc_pcal_line_freq"])
 pcal_corr_at_opt_gain_fcc_freq_real = float(filters["kc_pcal_corr_re"])
 pcal_corr_at_opt_gain_fcc_freq_imag = float(filters["kc_pcal_corr_im"])
 esd_act_line_freq = float(filters["ktst_esd_line_freq"])
+try:
+	pum_act_line_freq = float(filters["pum_act_line_freq"])
+except:
+	remove_pum_act_line = False
+	if compute_kappapum:
+		raise ValueError("Cannot compute kappa_P, as the specified filters file does not contain the needed calibration line frequency")
+try:
+	uim_act_line_freq = float(filters["uim_act_line_freq"])
+except:
+	remove_uim_act_line = False
+	if compute_kappauim and use_uim_line:
+		raise ValueError("Cannot compute kappa_U, as the specified filters file does not contain the needed calibration line frequency")
+
 pcal_line_removal_dict = {}
 if remove_cal_lines:
-	pcal_line_removal_dict["pcal1"] = [darm_act_line_freq, pcal_corr_at_darm_act_freq_real, pcal_corr_at_darm_act_freq_imag, None]
+	pcal_line_removal_dict["pcal1"] = [act_pcal_line_freq, pcal_corr_at_act_freq_real, pcal_corr_at_act_freq_imag, None]
 	pcal_line_removal_dict["pcal2"] = [opt_gain_fcc_line_freq, pcal_corr_at_opt_gain_fcc_freq_real, pcal_corr_at_opt_gain_fcc_freq_imag, None]
 try:
 	src_pcal_line_freq = float(filters["src_pcal_line_freq"])
 	pcal_corr_at_src_freq_real = float(filters["src_pcal_corr_re"])
 	pcal_corr_at_src_freq_imag = float(filters["src_pcal_corr_im"])
-	if src_pcal_line_freq > 10.0 and remove_cal_lines and src_pcal_line_freq != darm_act_line_freq:
+	if src_pcal_line_freq > 10.0 and remove_cal_lines and src_pcal_line_freq != act_pcal_line_freq:
 		pcal_line_removal_dict["pcal4"] = [src_pcal_line_freq, pcal_corr_at_src_freq_real, pcal_corr_at_src_freq_imag, None]
 except:
 	if compute_srcq or compute_fs:
@@ -517,16 +581,24 @@ if dewhitening:
 if CalibrationConfigs["calibrationmode"] == "Partial":
 	reschaindelay = int(filters["res_corr_delay"])
 	reschainfilt = filters["res_corr_filter"]
-	tstdelay = pumuimdelay = int(filters["ctrl_corr_delay"])
-	tstfilt = pumuimfilt = filters["ctrl_corr_filter"]
-	tstchainsr = pumuimchainsr = int(filters["ctrl_corr_sr"])
+	tstdelay = pumdelay = uimdelay = pumuimdelay = int(filters["ctrl_corr_delay"])
+	tstfilt = pumfilt = uimfilt = pumuimfilt = filters["ctrl_corr_filter"]
+	tstchainsr = pumchainsr = uimchainsr = pumuimchainsr = int(filters["ctrl_corr_sr"])
 	if dewhitening:
 		tstdewhitensr = int(filters["deltal_tst_dewhiten_sr"])
-		pumuimdewhitensr = int(filters["deltal_pumuim_dewhiten_sr"])
 		tstdewhitendelay = int(filters["deltal_tst_dewhiten_delay"])
-		pumuimdewhitendelay = int(filters["deltal_pumuim_dewhiten_delay"])
 		tstdewhiten = filters["deltal_tst_dewhiten"]
-		pumuimdewhiten = filters["deltal_pumuim_dewhiten"]
+		if apply_kappapum or apply_kappauim or apply_complex_kappapum or apply_complex_kappauim:
+			pumdewhitensr = int(filters["deltal_pum_dewhiten_sr"])
+			pumdewhitendelay = int(filters["deltal_pum_dewhiten_delay"])
+			pumdewhiten = filters["deltal_pum_dewhiten"]
+			uimdewhitensr = int(filters["deltal_uim_dewhiten_sr"])
+			uimdewhitendelay = int(filters["deltal_uim_dewhiten_delay"])
+			uimdewhiten = filters["deltal_uim_dewhiten"]
+		else:
+			pumuimdewhitensr = int(filters["deltal_pumuim_dewhiten_sr"])
+			pumuimdewhitendelay = int(filters["deltal_pumuim_dewhiten_delay"])
+			pumuimdewhiten = filters["deltal_pumuim_dewhiten"]
 		resdewhitendelay = int(filters["deltal_res_dewhiten_delay"])
 		resdewhiten = filters["deltal_res_dewhiten"]
 
@@ -545,11 +617,25 @@ if CalibrationConfigs["calibrationmode"] == "Partial":
 # If we're performing full calibration, load the actuation, sensing filters
 if CalibrationConfigs["calibrationmode"] == "Full":
 	tstchainsr = int(filters["actuation_tst_sr"])
-	pumuimchainsr = int(filters["actuation_pumuim_sr"])
 	tstdelay = int(filters["actuation_tst_delay"])
-	pumuimdelay = int(filters["actuation_pumuim_delay"])
 	tstfilt = filters["actuation_tst"]
-	pumuimfilt = filters["actuation_pumuim"]
+	try:
+		pumchainsr = int(filters["actuation_pum_sr"])
+		pumdelay = int(filters["actuation_pum_delay"])
+		pumfilt = filters["actuation_pum"]
+		uimchainsr = int(filters["actuation_uim_sr"])
+		uimdelay = int(filters["actuation_uim_delay"])
+		uimfilt = filters["actuation_uim"]
+	except:
+		if apply_kappapum or apply_kappauim or apply_complex_kappapum or apply_complex_kappauim:
+			raise ValueError("PUM and UIM actuation filters are needed since the PUM and UIM stages are being split")
+	try:
+		pumuimchainsr = int(filters["actuation_pumuim_sr"])
+		pumuimdelay = int(filters["actuation_pumuim_delay"])
+		pumuimfilt = filters["actuation_pumuim"]
+	except:
+		if not (apply_kappapum or apply_kappauim or apply_complex_kappapum or apply_complex_kappauim):
+			raise ValueError("PUM/UIM actuation filter is needed since the PUM and UIM stages are not being split")
 	reschaindelay = int(filters["inv_sens_delay"])
 	reschainfilt = filters["inv_sensing"]
 	if dewhitening:
@@ -586,31 +672,63 @@ if compute_calib_statevector:
 
 # If we are using the front-end EPICS records to either compute the TDCFs or the CALIB_STATE_VECTOR, we need to add those channels
 # Needed for kappa_tst, kappa_pu, kappa_c and f_cc
-if not factors_from_filters_file and (compute_kappatst or compute_kappapu or compute_kappap or compute_kappau or compute_kappac or compute_fcc or compute_fs or compute_srcq) or num_dq_epics > 0:
-	channel_list.extend(((instrument, ChannelNames["ep1realchannel"]), (instrument, ChannelNames["ep1imagchannel"])))
-	headkeys.extend(("EP1_real", "EP1_imag"))
-# These are needed for kappa_pu and kappa_c and f_cc
-if not factors_from_filters_file and (compute_kappac or compute_fcc or compute_kappapu or compute_fs or compute_srcq) or num_dq_epics > 3:
-	channel_list.extend(((instrument, ChannelNames["ep2realchannel"]), (instrument, ChannelNames["ep2imagchannel"]), (instrument, ChannelNames["ep3realchannel"]), (instrument, ChannelNames["ep3imagchannel"]), (instrument, ChannelNames["ep4realchannel"]), (instrument, ChannelNames["ep4imagchannel"])))
-	headkeys.extend(("EP2_real", "EP2_imag", "EP3_real", "EP3_imag", "EP4_real", "EP4_imag"))
-# If we are computing either kappa_c or f_cc, we need some more EPICS records
-if not factors_from_filters_file and (compute_kappac or compute_fcc or compute_fs or compute_srcq) or num_dq_epics > 8:
-	channel_list.extend(((instrument, ChannelNames["ep6realchannel"]), (instrument, ChannelNames["ep6imagchannel"]), (instrument, ChannelNames["ep7realchannel"]), (instrument, ChannelNames["ep7imagchannel"]), (instrument, ChannelNames["ep8realchannel"]), (instrument, ChannelNames["ep8imagchannel"]), (instrument, ChannelNames["ep9realchannel"]), (instrument, ChannelNames["ep9imagchannel"])))
-	headkeys.extend(("EP6_real", "EP6_imag", "EP7_real", "EP7_imag", "EP8_real", "EP8_imag", "EP9_real", "EP9_imag"))
-
-# EP10 is needed to remove the ESD line
-if not factors_from_filters_file and remove_esd_act_line or num_dq_epics > 9:
-	channel_list.extend(((instrument, ChannelNames["ep10realchannel"]), (instrument, ChannelNames["ep10imagchannel"])))
-	headkeys.extend(("EP10_real", "EP10_imag"))
-
-# These are needed if we compute the optical spring frequency and/or Q-factor of the Signal Recycling Cavity (SRC)
-if not factors_from_filters_file and (compute_fs or compute_srcq) or num_dq_epics > 13:
-	channel_list.extend(((instrument, ChannelNames["ep11realchannel"]), (instrument, ChannelNames["ep11imagchannel"]), (instrument, ChannelNames["ep12realchannel"]), (instrument, ChannelNames["ep12imagchannel"]), (instrument, ChannelNames["ep13realchannel"]), (instrument, ChannelNames["ep13imagchannel"]), (instrument, ChannelNames["ep14realchannel"]), (instrument, ChannelNames["ep14imagchannel"])))
-	headkeys.extend(("EP11_real", "EP11_imag", "EP12_real", "EP12_imag", "EP13_real", "EP13_imag", "EP14_real", "EP14_imag"))
-
-# If we are using pre-computed coherence to gate kappas
+if not factors_from_filters_file or compute_calib_statevector:
+	if (compute_kappatst or compute_kappapu or compute_kappapum or compute_kappauim or compute_kappac or compute_fcc or compute_fs or compute_srcq):
+		channel_list.extend(((instrument, ChannelNames["ep1realchannel"]), (instrument, ChannelNames["ep1imagchannel"])))
+		headkeys.extend(("EP1_real", "EP1_imag"))
+	# These are needed for kappa_P
+	if compute_kappapum or (compute_kappauim and (not use_uim_line or compute_kappac or compute_fcc or compute_fs or compute_srcq)):
+		channel_list.extend(((instrument, ChannelNames["ep15realchannel"]), (instrument, ChannelNames["ep15imagchannel"])))
+		headkeys.extend(("EP15_real", "EP15_imag"))
+	# These are needed for kappa_U
+	if compute_kappauim or (compute_kappapum and (compute_kappac or compute_fcc or compute_fs or compute_srcq)):
+		if use_uim_line:
+			channel_list.extend(((instrument, ChannelNames["ep22realchannel"]), (instrument, ChannelNames["ep22imagchannel"])))
+			headkeys.extend(("EP22_real", "EP22_imag"))
+		else:
+			channel_list.extend(((instrument, ChannelNames["ep4realchannel"]), (instrument, ChannelNames["ep4imagchannel"]), (instrument, ChannelNames["ep16realchannel"]), (instrument, ChannelNames["ep16imagchannel"]), (instrument, ChannelNames["ep17realchannel"]), (instrument, ChannelNames["ep17imagchannel"])))
+			headkeys.extend(("EP4_real", "EP4_imag", "EP16_real", "EP16_imag", "EP17_real", "EP17_imag"))
+	# These are needed for kappa_PU
+	if (compute_kappapu or ((compute_kappac or compute_fcc or compute_fs or compute_srcq) and not (compute_kappapum or compute_kappauim))):
+		channel_list.extend(((instrument, ChannelNames["ep2realchannel"]), (instrument, ChannelNames["ep2imagchannel"]), (instrument, ChannelNames["ep3realchannel"]), (instrument, ChannelNames["ep3imagchannel"])))
+		headkeys.extend(("EP2_real", "EP2_imag", "EP3_real", "EP3_imag"))
+		# Additionally, if kappa_U is not calculated using the DARM line, we need these
+		if use_uim_line or not (compute_kappauim or (compute_kappapum and (compute_kappac or compute_fcc or compute_fs or compute_srcq))):
+			channel_list.extend(((instrument, ChannelNames["ep4realchannel"]), (instrument, ChannelNames["ep4imagchannel"])))
+			headkeys.extend(("EP4_real", "EP4_imag"))
+	# These are needed for kappa_C and f_cc
+	if compute_kappac or compute_fcc or compute_fs or compute_srcq:
+		if compute_kappapum or compute_kappauim:
+			channel_list.extend(((instrument, ChannelNames["ep6realchannel"]), (instrument, ChannelNames["ep6imagchannel"]), (instrument, ChannelNames["ep7realchannel"]), (instrument, ChannelNames["ep7imagchannel"]), (instrument, ChannelNames["ep8realchannel"]), (instrument, ChannelNames["ep8imagchannel"]), (instrument, ChannelNames["ep18realchannel"]), (instrument, ChannelNames["ep18imagchannel"]), (instrument, ChannelNames["ep19realchannel"]), (instrument, ChannelNames["ep19imagchannel"])))
+			headkeys.extend(("EP6_real", "EP6_imag", "EP7_real", "EP7_imag", "EP8_real", "EP8_imag", "EP18_real", "EP18_imag", "EP19_real", "EP19_imag"))
+		else:
+			channel_list.extend(((instrument, ChannelNames["ep6realchannel"]), (instrument, ChannelNames["ep6imagchannel"]), (instrument, ChannelNames["ep7realchannel"]), (instrument, ChannelNames["ep7imagchannel"]), (instrument, ChannelNames["ep8realchannel"]), (instrument, ChannelNames["ep8imagchannel"]), (instrument, ChannelNames["ep9realchannel"]), (instrument, ChannelNames["ep9imagchannel"])))
+			headkeys.extend(("EP6_real", "EP6_imag", "EP7_real", "EP7_imag", "EP8_real", "EP8_imag", "EP9_real", "EP9_imag"))
+	# EP10 is needed to remove the ESD line
+	if remove_esd_act_line:
+		channel_list.extend(((instrument, ChannelNames["ep10realchannel"]), (instrument, ChannelNames["ep10imagchannel"])))
+		headkeys.extend(("EP10_real", "EP10_imag"))
+
+	# These are needed if we compute the optical spring frequency and/or Q-factor of the Signal Recycling Cavity (SRC)
+	if compute_fs or compute_srcq:
+		if compute_kappapum or compute_kappauim:
+			channel_list.extend(((instrument, ChannelNames["ep11realchannel"]), (instrument, ChannelNames["ep11imagchannel"]), (instrument, ChannelNames["ep12realchannel"]), (instrument, ChannelNames["ep12imagchannel"]), (instrument, ChannelNames["ep13realchannel"]), (instrument, ChannelNames["ep13imagchannel"]), (instrument, ChannelNames["ep20realchannel"]), (instrument, ChannelNames["ep20imagchannel"]), (instrument, ChannelNames["ep21realchannel"]), (instrument, ChannelNames["ep21imagchannel"])))
+			headkeys.extend(("EP11_real", "EP11_imag", "EP12_real", "EP12_imag", "EP13_real", "EP13_imag", "EP20_real", "EP20_imag", "EP21_real", "EP21_imag"))
+		else:
+			channel_list.extend(((instrument, ChannelNames["ep11realchannel"]), (instrument, ChannelNames["ep11imagchannel"]), (instrument, ChannelNames["ep12realchannel"]), (instrument, ChannelNames["ep12imagchannel"]), (instrument, ChannelNames["ep13realchannel"]), (instrument, ChannelNames["ep13imagchannel"]), (instrument, ChannelNames["ep14realchannel"]), (instrument, ChannelNames["ep14imagchannel"])))
+			headkeys.extend(("EP11_real", "EP11_imag", "EP12_real", "EP12_imag", "EP13_real", "EP13_imag", "EP14_real", "EP14_imag"))
+	# These are needed to remove the PUM line
+	if remove_pum_act_line:
+		channel_list.extend(((instrument, ChannelNames["ep23realchannel"]), (instrument, ChannelNames["ep23imagchannel"])))
+		headkeys.extend(("EP23_real", "EP23_imag"))
+	# These are needed to remove the UIM line
+	if remove_uim_act_line:
+		channel_list.extend(((instrument, ChannelNames["ep24realchannel"]), (instrument, ChannelNames["ep24imagchannel"])))
+		headkeys.extend(("EP24_real", "EP24_imag"))
+
+	# If we are using pre-computed coherence to gate kappas
 if use_coherence:
-	if compute_kappatst or compute_kappapu or compute_kappac or compute_fcc or compute_fs or compute_srcq:
+	if compute_kappatst or compute_kappapum or compute_kappauim or compute_kappapu or compute_kappac or compute_fcc or compute_fs or compute_srcq:
 		channel_list.extend(((instrument, ChannelNames["cohuncsusline1channel"]), (instrument, ChannelNames["cohuncpcalyline1channel"]), (instrument, ChannelNames["cohuncdarmline1channel"])))
 		headkeys.extend(("pcaly_line1_coh", "sus_coh", "darm_coh"))
 	if compute_kappac or compute_fcc or compute_fs or compute_srcq:
@@ -618,20 +736,26 @@ if use_coherence:
 		headkeys.append("pcaly_line2_coh")
 
 # We also need excitation channels for computing kappas
-if compute_kappatst or compute_kappapu or compute_kappac or compute_fcc or compute_fs or compute_srcq or remove_esd_act_line:
+if compute_kappatst or compute_kappapum or compute_kappauim or compute_kappapu or compute_kappac or compute_fcc or compute_fs or compute_srcq or remove_esd_act_line:
 	channel_list.append((instrument, ChannelNames["tstexcchannel"]))
 	headkeys.append("tstexc")
-if compute_kappac or compute_fcc or compute_fs or compute_srcq or compute_kappapu:
+#if compute_kappapum or (compute_kappauim and (compute_kappac or compute_fcc or compute_fs or compute_srcq)):
+	channel_list.append((instrument, ChannelNames["pumexcchannel"]))
+	headkeys.append("pumexc")
+if use_uim_line and (compute_kappauim or compute_kappapum and (compute_kappac or compute_fcc or compute_fs or compute_srcq)):
+	channel_list.append((instrument, ChannelNames["uimexcchannel"]))
+	headkeys.append("uimexc")
+if compute_kappapu or ((not use_uim_line or not (compute_kappapum or compute_kappauim)) and (compute_kappauim or compute_kappac or compute_fcc or compute_fs or compute_srcq)):
 	channel_list.append((instrument, ChannelNames["darmexcchannel"]))
-	headkeys.append("exc")
+	headkeys.append("darmexc")
 
 # We need the PCAL channel if we are computing \kappas or removing the calibration lines
-if compute_kappatst or compute_kappapu or compute_kappac or compute_fcc or compute_fs or compute_srcq or remove_cal_lines:
+if compute_kappatst or compute_kappapum or compute_kappauim or compute_kappapu or compute_kappac or compute_fcc or compute_fs or compute_srcq or remove_cal_lines:
 	channel_list.append((instrument, ChannelNames["pcalchannel"]))
 	headkeys.append("pcal")
 
 # We also need DARM_ERR if we are computing the kappas
-if (compute_kappatst or compute_kappapu or compute_kappac or compute_fcc or compute_fs or compute_srcq) and CalibrationConfigs["calibrationmode"] == "Partial":
+if (compute_kappatst or compute_kappapum or compute_kappauim or compute_kappapu or compute_kappac or compute_fcc or compute_fs or compute_srcq) and CalibrationConfigs["calibrationmode"] == "Partial":
 		channel_list.append((instrument, ChannelNames["darmerrchannel"]))
 		headkeys.append("darm_err")
 
@@ -716,14 +840,30 @@ for key, chan in zip(headkeys, channel_list):
 # TIME-VARYING FACTORS COMPUTATIONS
 #
 
+A_epics_dict = {}
+D_epics_dict = {}
+C_epics_dict = {}
+misc_epics_dict = {}
 for key in headkeys:
-	if key.startswith("EP"):
-		head_dict[key] = calibration_parts.caps_and_progress(pipeline, head_dict[key], ref_factors_caps, key)
-		head_dict[key] = calibration_parts.mkresample(pipeline, head_dict[key], 0, False, compute_calib_factors_caps)
-		head_dict[key] = pipeparts.mktee(pipeline, head_dict[key])
+	if key.startswith("EP6") or key.startswith("EP11"):
+		C_epics_dict[key] = calibration_parts.caps_and_progress(pipeline, head_dict[key], ref_factors_caps, key)
+		C_epics_dict[key] = calibration_parts.mkresample(pipeline, C_epics_dict[key], 0, False, compute_calib_factors_caps)
+		C_epics_dict[key] = (pipeparts.mktee(pipeline, C_epics_dict[key]), float(filters[key]))
+	elif key.startswith("EP7") or key.startswith("EP12"):
+		D_epics_dict[key] = calibration_parts.caps_and_progress(pipeline, head_dict[key], ref_factors_caps, key)
+		D_epics_dict[key] = calibration_parts.mkresample(pipeline, D_epics_dict[key], 0, False, compute_calib_factors_caps)
+		D_epics_dict[key] = (pipeparts.mktee(pipeline, D_epics_dict[key]), float(filters[key]))
+	elif key.startswith("EP1") or key.startswith("EP2") or key.startswith("EP15") or key.startswith("EP22"):
+		misc_epics_dict[key] = calibration_parts.caps_and_progress(pipeline, head_dict[key], ref_factors_caps, key)
+		misc_epics_dict[key] = calibration_parts.mkresample(pipeline, misc_epics_dict[key], 0, False, compute_calib_factors_caps)
+		misc_epics_dict[key] = (pipeparts.mktee(pipeline, misc_epics_dict[key]), float(filters[key]))
+	elif key.startswith("EP"):
+		A_epics_dict[key] = calibration_parts.caps_and_progress(pipeline, head_dict[key], ref_factors_caps, key)
+		A_epics_dict[key] = calibration_parts.mkresample(pipeline, A_epics_dict[key], 0, False, compute_calib_factors_caps)
+		A_epics_dict[key] = (pipeparts.mktee(pipeline, A_epics_dict[key]), float(filters[key]))
 
 if use_coherence:
-	if compute_kappatst or compute_kappapu or compute_kappac or compute_fcc or compute_fs or compute_srcq:
+	if compute_kappatst or compute_kappapum or compute_kappauim or compute_kappapu or compute_kappac or compute_fcc or compute_fs or compute_srcq:
 		pcaly_line1_coh = calibration_parts.caps_and_progress(pipeline, head_dict["pcaly_line1_coh"], coh_caps, "pcaly_line1_coh")
 		pcaly_line1_coh = calibration_parts.mkresample(pipeline, pcaly_line1_coh, 0, False, compute_calib_factors_caps)
 		sus_coh = calibration_parts.caps_and_progress(pipeline, head_dict["sus_coh"], coh_caps, "sus_coh")
@@ -738,14 +878,19 @@ if use_coherence:
 		pcaly_line2_coh = calibration_parts.mkresample(pipeline, pcaly_line2_coh, 0, False, compute_calib_factors_caps)
 		pcaly_line2_coh = pipeparts.mktee(pipeline, pcaly_line2_coh)
 
-if compute_kappatst or compute_kappapu or compute_kappac or compute_fcc or compute_fs or compute_srcq or remove_esd_act_line:
+if compute_kappatst or compute_kappapum or compute_kappauim or compute_kappapu or compute_kappac or compute_fcc or compute_fs or compute_srcq or remove_esd_act_line:
 	tstexccaps = "audio/x-raw, format=F64LE, rate=%d" % tst_exc_sr
 	tstexc = calibration_parts.caps_and_progress(pipeline, head_dict["tstexc"], tstexccaps, "tstexc")
-
-if compute_kappac or compute_fcc or compute_kappapu or compute_fs or compute_srcq:
-	exc = calibration_parts.caps_and_progress(pipeline, head_dict["exc"], hoft_caps, "exc")
-
-# Set up computations for \kappa_tst,\kappa_c, \kappa_pu, f_cc, if applicable
+if compute_kappapum or (compute_kappauim and (compute_kappac or compute_fcc or compute_fs or compute_srcq)):
+	pumexccaps = "audio/x-raw, format=F64LE, rate=%d" % pum_exc_sr
+	pumexc = calibration_parts.caps_and_progress(pipeline, head_dict["pumexc"], pumexccaps, "pumexc")
+if use_uim_line and (compute_kappauim or compute_kappapum and (compute_kappac or compute_fcc or compute_fs or compute_srcq)):
+	uimexccaps = "audio/x-raw, format=F64LE, rate=%d" % uim_exc_sr
+	uimexc = calibration_parts.caps_and_progress(pipeline, head_dict["uimexc"], uimexccaps, "uimexc")
+if compute_kappapu or ((not use_uim_line or not (compute_kappapum or compute_kappauim)) and (compute_kappauim or compute_kappac or compute_fcc or compute_fs or compute_srcq)):
+	darmexc = calibration_parts.caps_and_progress(pipeline, head_dict["darmexc"], hoft_caps, "darmexc")
+
+# Set up computations for kappa_tst, kappa_pum, kappa_uim, kappa_pu,kappa_c, f_cc, f_s, and Q, if applicable
 if compute_kappac or compute_fcc or compute_kappatst or compute_kappapu or compute_srcq or compute_fs:
 
 	# pcal excitation channel, which will be demodulated
@@ -760,18 +905,18 @@ if compute_kappac or compute_fcc or compute_kappatst or compute_kappapu or compu
 	derrtee = pipeparts.mktee(pipeline, darm_err)
 
 	# demodulate the PCAL channel and apply the PCAL correction factor at the DARM actuation line frequency
-	pcal_at_darm_act_freq = calibration_parts.demodulate(pipeline, pcaltee, darm_act_line_freq, td, compute_factors_sr, demodulation_filter_time, filter_latency_factor, prefactor_real = pcal_corr_at_darm_act_freq_real, prefactor_imag = pcal_corr_at_darm_act_freq_imag)
-	pcal_at_darm_act_freq = pipeparts.mktee(pipeline, pcal_at_darm_act_freq)
+	pcal_at_act_pcal_freq = calibration_parts.demodulate(pipeline, pcaltee, act_pcal_line_freq, td, compute_factors_sr, demodulation_filter_time, filter_latency_factor, prefactor_real = pcal_corr_at_act_freq_real, prefactor_imag = pcal_corr_at_act_freq_imag)
+	pcal_at_act_pcal_freq = pipeparts.mktee(pipeline, pcal_at_act_pcal_freq)
 	if remove_cal_lines:
 		# This will save having to demodulate it again
-		pcal_line_removal_dict["pcal1"][3] = pcal_at_darm_act_freq
+		pcal_line_removal_dict["pcal1"][3] = pcal_at_act_pcal_freq
 
-	# demodulate DARM_ERR at the DARM actuation line frequency
-	derr_at_darm_act_freq = calibration_parts.demodulate(pipeline, derrtee, darm_act_line_freq, td, compute_factors_sr, demodulation_filter_time, filter_latency_factor)
+	# demodulate DARM_ERR at the ~30 Hz pcal line frequency
+	derr_at_act_pcal_freq = calibration_parts.demodulate(pipeline, derrtee, act_pcal_line_freq, td, compute_factors_sr, demodulation_filter_time, filter_latency_factor)
 	if dewhitening:
-		# dewhiten DARM_ERR at the DARM actuation line frequency
-		derr_at_darm_act_freq = calibration_parts.complex_audioamplify(pipeline, derr_at_darm_act_freq, derr_dewhiten_at_darm_act_freq_real, derr_dewhiten_at_darm_act_freq_imag)
-	derr_at_darm_act_freq = pipeparts.mktee(pipeline, derr_at_darm_act_freq)
+		# dewhiten DARM_ERR at the ~30 Hz pcal line frequency
+		derr_at_act_pcal_freq = calibration_parts.complex_audioamplify(pipeline, derr_at_act_pcal_freq, derr_dewhiten_at_darm_act_freq_real, derr_dewhiten_at_darm_act_freq_imag)
+	derr_at_act_pcal_freq = pipeparts.mktee(pipeline, derr_at_act_pcal_freq)
 
 	# demodulate the TST excitation channel at the ESD actuation line frequency
 	tstexc_at_esd_act_freq = calibration_parts.demodulate(pipeline, tstexc, esd_act_line_freq, td, compute_factors_sr, demodulation_filter_time, filter_latency_factor)
@@ -786,10 +931,10 @@ if compute_kappac or compute_fcc or compute_kappatst or compute_kappapu or compu
 
 	# compute kappa_tst, either using reference factors from the filters file or reading them from EPICS channels
 	if not factors_from_filters_file:
-		EP1 = calibration_parts.merge_into_complex(pipeline, head_dict["EP1_real"], head_dict["EP1_imag"])
-		ktst = calibration_parts.compute_kappatst(pipeline, derr_at_esd_act_freq, tstexc_at_esd_act_freq, pcal_at_darm_act_freq, derr_at_darm_act_freq, EP1)
+		EP1 = calibration_parts.merge_into_complex(pipeline, misc_epics_dict["EP1_real"][0], misc_epics_dict["EP1_imag"][0])
+		ktst = calibration_parts.compute_kappatst(pipeline, derr_at_esd_act_freq, tstexc_at_esd_act_freq, pcal_at_act_pcal_freq, derr_at_act_pcal_freq, EP1)
 	elif factors_from_filters_file:
-		ktst = calibration_parts.compute_kappatst_from_filters_file(pipeline, derr_at_esd_act_freq, tstexc_at_esd_act_freq, pcal_at_darm_act_freq, derr_at_darm_act_freq, EP1_real, EP1_imag)
+		ktst = calibration_parts.compute_kappatst_from_filters_file(pipeline, derr_at_esd_act_freq, tstexc_at_esd_act_freq, pcal_at_act_pcal_freq, derr_at_act_pcal_freq, EP1_real, EP1_imag)
 
 	ktst = pipeparts.mktee(pipeline, ktst)
 
@@ -811,7 +956,7 @@ if compute_kappac or compute_fcc or compute_kappatst or compute_kappapu or compu
 
 		else:
 			# Smooth kappa_tst
-			smooth_ktst = calibration_parts.smooth_complex_kappas_no_coherence(pipeline, ktst, kappatst_real_ok_var, kappatst_imag_ok_var, expected_kappatst_real, expected_kappatst_imag, median_smoothing_samples, factors_average_samples, tdcf_default_to_median, filter_latency_factor)
+			smooth_ktst = calibration_parts.smooth_complex_kappas_no_coherence(pipeline, ktst, kappatst_real_var, kappatst_imag_var, expected_kappatst_real, expected_kappatst_imag, median_smoothing_samples, factors_average_samples, tdcf_default_to_median, filter_latency_factor)
 
 		smooth_ktsttee = pipeparts.mktee(pipeline, smooth_ktst)
 		smooth_ktstR, smooth_ktstI = calibration_parts.split_into_real(pipeline, smooth_ktsttee)
@@ -819,30 +964,136 @@ if compute_kappac or compute_fcc or compute_kappatst or compute_kappapu or compu
 		smooth_ktstRtee = pipeparts.mktee(pipeline, smooth_ktstR)
 		smooth_ktstItee = pipeparts.mktee(pipeline, smooth_ktstI)
 
-# If we're also computing \kappa_pu, \kappa_c, f_cc, f_s, or Q, keep going
-if compute_kappapu or compute_kappac or compute_fcc or compute_fs or compute_srcq:
-	# demodulate excitation channel at PU actuation line frequency
-	exc_at_pu_act_freq = calibration_parts.demodulate(pipeline, exc, pu_act_esd_line_freq, td, compute_factors_sr, demodulation_filter_time, filter_latency_factor)
+# Check if we need to compute kappa_pum
+if compute_kappapum or compute_kappauim and (not use_uim_line or compute_kappac or compute_fcc or compute_fs or compute_srcq):
+	# demodulate the PUM excitation channel at the PUM actuation line frequency
+	pumexc_at_pum_act_freq = calibration_parts.demodulate(pipeline, pumexc, pum_act_line_freq, td, compute_factors_sr, demodulation_filter_time, filter_latency_factor)
+	if remove_pum_act_line:
+		pumexc_at_pum_act_freq = pipeparts.mktee(pipeline, pumexc_at_pum_act_freq)
 
-	# demodulate DARM_ERR at PU actuation line frequency
-	derr_at_pu_act_freq = calibration_parts.demodulate(pipeline, derrtee, pu_act_esd_line_freq, td, compute_factors_sr, demodulation_filter_time, filter_latency_factor)
-	if dewhitening:
-		# dewhiten DARM_ERR at the PU actuation line frequency
-		derr_at_pu_act_freq = calibration_parts.complex_audioamplify(pipeline, derr_at_pu_act_freq, derr_dewhiten_at_pu_act_freq_real, derr_dewhiten_at_pu_act_freq_imag)
+	# demodulate DARM_ERR at the PUM actuation line frequency
+	derr_at_pum_act_freq = calibration_parts.demodulate(pipeline, derrtee, pum_act_line_freq, td, compute_factors_sr, demodulation_filter_time, filter_latency_factor)
 
-	# compute the factor Afctrl that will be used in the computation of kappa_pu and kappa_a, either using reference factors from the filters file or reading them from EPICS channels
+	# Compute kappa_pum, either using reference factors from the filters file or reading them from EPICS channels
 	if not factors_from_filters_file:
-		EP2 = calibration_parts.merge_into_complex(pipeline, head_dict["EP2_real"], head_dict["EP2_imag"])
-		EP3 = calibration_parts.merge_into_complex(pipeline, head_dict["EP3_real"], head_dict["EP3_imag"])
-		EP4 = calibration_parts.merge_into_complex(pipeline, head_dict["EP4_real"], head_dict["EP4_imag"])
-		afctrl = calibration_parts.compute_afctrl(pipeline, derr_at_pu_act_freq, exc_at_pu_act_freq, pcal_at_darm_act_freq, derr_at_darm_act_freq, EP2)
-	elif factors_from_filters_file:
-		afctrl = calibration_parts.compute_afctrl_from_filters_file(pipeline, derr_at_pu_act_freq, exc_at_pu_act_freq, pcal_at_darm_act_freq, derr_at_darm_act_freq, EP2_real, EP2_imag)
+		EP15 = calibration_parts.merge_into_complex(pipeline, misc_epics_dict["EP15_real"][0], misc_epics_dict["EP15_imag"][0])
+		kpum = calibration_parts.compute_kappapum(pipeline, derr_at_pum_act_freq, pumexc_at_pum_act_freq, pcal_at_act_pcal_freq, derr_at_act_pcal_freq, EP15)
+	else:
+		kpum = calibration_parts.compute_kappapum_from_filters_file(pipeline, derr_at_pum_act_freq, pumexc_at_pum_act_freq, pcal_at_act_pcal_freq, derr_at_act_pcal_freq, EP15_real, EP15_imag)
+
+	kpum = pipeparts.mktee(pipeline, kpum)
+
+	# Now apply the gating and smoothing to kappa_pum
+	if compute_kappapum:
+		smooth_kpum_nogate = pipeparts.mkgeneric(pipeline, kpum, "lal_smoothkappas", default_kappa_re = expected_kappapum_real, default_kappa_im = expected_kappapum_imag, array_size = median_smoothing_samples, avg_array_size = factors_average_samples, default_to_median = tdcf_default_to_median, filter_latency = filter_latency_factor)
+		smooth_kpumR_nogate, smooth_kpumI_nogate = calibration_parts.split_into_real(pipeline, smooth_kpum_nogate)
+
+		if use_coherence:
+			# Gate kappa_pum with the coherence of the PCALY_line1 line
+			kpum_gated = calibration_parts.mkgate(pipeline, kpum, pcaly_line1_coh, coherence_unc_threshold, attack_length = kappa_gate_attack_length, hold_length = kappa_gate_hold_length, invert_control = True)
+			# Gate kappa_pum with the coherence of the suspension line
+			kpum_gated = calibration_parts.mkgate(pipeline, kpum_gated, sus_coh, coherence_unc_threshold, attack_length = kappa_gate_attack_length, hold_length = kappa_gate_hold_length, invert_control = True)
+			# Gate kappa_pum with the coherence of the DARM line
+			kpum_gated = calibration_parts.mkgate(pipeline, kpum_gated, darm_coh, coherence_unc_threshold, attack_length = kappa_gate_attack_length, hold_length = kappa_gate_hold_length, invert_control = True)
+
+			# Smooth kappa_pum
+			smooth_kpum = calibration_parts.smooth_complex_kappas(pipeline, kpum_gated, expected_kappapum_real, expected_kappapum_imag, median_smoothing_samples, factors_average_samples, tdcf_default_to_median, filter_latency_factor)
+
+		else:
+			# Smooth kappa_pum
+			smooth_kpum = calibration_parts.smooth_complex_kappas_no_coherence(pipeline, kpum, kappapum_real_var, kappapum_imag_var, expected_kappapum_real, expected_kappapum_imag, median_smoothing_samples, factors_average_samples, tdcf_default_to_median, filter_latency_factor)
+
+		smooth_kpumtee = pipeparts.mktee(pipeline, smooth_kpum)
+		smooth_kpumR, smooth_kpumI = calibration_parts.split_into_real(pipeline, smooth_kpumtee)
+
+		smooth_kpumRtee = pipeparts.mktee(pipeline, smooth_kpumR)
+		smooth_kpumItee = pipeparts.mktee(pipeline, smooth_kpumI)
+
+# Check if we need to compute kappa_uim
+if compute_kappauim or (compute_kappapum and (compute_kappac or compute_fcc or compute_fs or compute_srcq)):
+	if use_uim_line:
+		# Demodulate DARM_ERR and the UIM excitation channel at the UIM actuation line frequency
+		derr_at_uim_act_freq = calibration_parts.demodulate(pipeline, derrtee, uim_act_line_freq, td, compute_factors_sr, demodulation_filter_time, filter_latency_factor)
+		uimexc_at_uim_act_freq = calibration_parts.demodulate(pipeline, uimexc, uim_act_line_freq, td, compute_factors_sr, demodulation_filter_time, filter_latency_factor)
+		if remove_uim_act_line:
+			uimexc_at_uim_act_freq = pipeparts.mktee(pipeline, uimexc_at_uim_act_freq)
+
+		# Compute kappa_uim, either using reference factors from the filters file or reading them from EPICS channels
+		if not factors_from_filters_file:
+			EP22 = calibration_parts.merge_into_complex(pipeline, misc_epics_dict["EP22_real"][0], misc_epics_dict["EP22_imag"][0])
+			kuim = calibration_parts.compute_kappauim_uim_line(pipeline, derr_at_uim_act_freq, uimexc_at_uim_act_freq, pcal_at_act_pcal_freq, derr_at_act_pcal_freq, EP22)
+		else:
+			kuim = calibration_parts.compute_kappauim_from_filters_file_uim_line(pipeline, derr_at_uim_act_freq, uimexc_at_uim_act_freq, pcal_at_act_pcal_freq, derr_at_act_pcal_freq, EP22_real, EP22_imag)
 
-	# \kappa_pu calcuation, which needs to happen for any of the other kappas to be computed
+	else:
+		# Demodulate DARM_ERR and the darm excitation channel at the darm actuation line frequency
+		derr_at_darm_act_freq = calibration_parts.demodulate(pipeline, derrtee, darm_ctrl_line_freq, td, compute_factors_sr, demodulation_filter_time, filter_latency_factor)
+		darmexc_at_darm_act_freq = calibration_parts.demodulate(pipeline, darmexc, darm_ctrl_line_freq, td, compute_factors_sr, demodulation_filter_time, filter_latency_factor)
+		if compute_kappapu:
+			derr_at_darm_act_freq = pipeparts.mktee(pipeline, derr_at_darm_act_freq)
+			darmexc_at_darm_act_freq = pipeparts.mktee(pipeline, darmexc_at_darm_act_freq)
+
+		# Compute the factor A(fctrl) that will be used in the computation of kappa_uim, either using reference factors from the filters file or reading them from EPICS channels
+		if not factors_from_filters_file:
+			EP2 = calibration_parts.merge_into_complex(pipeline, misc_epics_dict["EP2_real"][0], misc_epics_dict["EP2_imag"][0])
+			EP4 = calibration_parts.merge_into_complex(pipeline, misc_epics_dict["EP4_real"][0], misc_epics_dict["EP4_imag"][0])
+			EP16 = calibration_parts.merge_into_complex(pipeline, misc_epics_dict["EP16_real"][0], misc_epics_dict["EP16_imag"][0])
+			EP17 = calibration_parts.merge_into_complex(pipeline, misc_epics_dict["EP17_real"][0], misc_epics_dict["EP17_imag"][0])
+			afctrl = calibration_parts.compute_afctrl(pipeline, derr_at_darm_act_freq, darmexc_at_darm_act_freq, pcal_at_act_pcal_freq, derr_at_act_pcal_freq, EP2)
+			kuim = calibration_parts.compute_kappauim(pipeline, EP16, afctrl, ktst, EP4, kpum, EP17)
+		else:
+			afctrl = calibration_parts.compute_afctrl_from_filters_file(pipeline, derr_at_darm_act_freq, darmexc_at_darm_act_freq, pcal_at_act_pcal_freq, derr_at_act_pcal_freq, EP2_real, EP2_imag)
+			kuim = calibration_parts.compute_kappauim_from_filters_file(pipeline, EP16_real, EP16_imag, afctrl, ktst, EP4_real, EP4_imag, kpum, EP17_real, EP17_imag)
+
+	kuim = pipeparts.mktee(pipeline, kuim)
+
+	# Now apply the gating and smoothing to kappa_uim
+	if compute_kappauim:
+		smooth_kuim_nogate = pipeparts.mkgeneric(pipeline, kuim, "lal_smoothkappas", default_kappa_re = expected_kappauim_real, default_kappa_im = expected_kappauim_imag, array_size = median_smoothing_samples, avg_array_size = factors_average_samples, default_to_median = tdcf_default_to_median, filter_latency = filter_latency_factor)
+		smooth_kuimR_nogate, smooth_kuimI_nogate = calibration_parts.split_into_real(pipeline, smooth_kuim_nogate)
+
+		if use_coherence:
+			# Gate kappa_uim with the coherence of the PCALY_line1 line
+			kuim_gated = calibration_parts.mkgate(pipeline, kuim, pcaly_line1_coh, coherence_unc_threshold, attack_length = kappa_gate_attack_length, hold_length = kappa_gate_hold_length, invert_control = True)
+			# Gate kappa_uim with the coherence of the suspension line
+			kuim_gated = calibration_parts.mkgate(pipeline, kuim_gated, sus_coh, coherence_unc_threshold, attack_length = kappa_gate_attack_length, hold_length = kappa_gate_hold_length, invert_control = True)
+			# Gate kappa_uim with the coherence of the DARM line
+			kuim_gated = calibration_parts.mkgate(pipeline, kuim_gated, darm_coh, coherence_unc_threshold, attack_length = kappa_gate_attack_length, hold_length = kappa_gate_hold_length, invert_control = True)
+
+			# Smooth kappa_uim
+			smooth_kuim = calibration_parts.smooth_complex_kappas(pipeline, kuim_gated, expected_kappauim_real, expected_kappauim_imag, median_smoothing_samples, factors_average_samples, tdcf_default_to_median, filter_latency_factor)
+
+		else:
+			# Smooth kappa_uim
+			smooth_kuim = calibration_parts.smooth_complex_kappas_no_coherence(pipeline, kuim, kappauim_real_var, kappauim_imag_var, expected_kappauim_real, expected_kappauim_imag, median_smoothing_samples, factors_average_samples, tdcf_default_to_median, filter_latency_factor)
+
+		smooth_kuimtee = pipeparts.mktee(pipeline, smooth_kuim)
+		smooth_kuimR, smooth_kuimI = calibration_parts.split_into_real(pipeline, smooth_kuimtee)
+
+		smooth_kuimRtee = pipeparts.mktee(pipeline, smooth_kuimR)
+		smooth_kuimItee = pipeparts.mktee(pipeline, smooth_kuimI)
+
+# Check if we need to compute kappa_PU
+if compute_kappapu or (compute_kappac or compute_fcc or compute_fs or compute_srcq) and not (compute_kappapum or compute_kappauim):
+	if use_uim_line or not (compute_kappauim or (compute_kappapum and (compute_kappac or compute_fcc or compute_fs or compute_srcq))):
+		# demodulate excitation channel at darm actuation line frequency
+		darmexc_at_darm_act_freq = calibration_parts.demodulate(pipeline, darmexc, darm_ctrl_line_freq, td, compute_factors_sr, demodulation_filter_time, filter_latency_factor)
+
+		# demodulate DARM_ERR at the darm actuation line frequency
+		derr_at_darm_act_freq = calibration_parts.demodulate(pipeline, derrtee, darm_ctrl_line_freq, td, compute_factors_sr, demodulation_filter_time, filter_latency_factor)
+	if dewhitening:
+		# dewhiten DARM_ERR at the darm actuation line frequency
+		derr_at_darm_act_freq = calibration_parts.complex_audioamplify(pipeline, derr_at_darm_act_freq, derr_dewhiten_at_pu_act_freq_real, derr_dewhiten_at_pu_act_freq_imag)
+
+	# compute the factor A(fctrl) that will be used in the computation of kappa_pu, either using reference factors from the filters file or reading them from EPICS channels
 	if not factors_from_filters_file:
+		EP2 = calibration_parts.merge_into_complex(pipeline, misc_epics_dict["EP2_real"][0], misc_epics_dict["EP2_imag"][0])
+		EP3 = calibration_parts.merge_into_complex(pipeline, A_epics_dict["EP3_real"][0], A_epics_dict["EP3_imag"][0])
+		EP4 = calibration_parts.merge_into_complex(pipeline, A_epics_dict["EP4_real"][0], A_epics_dict["EP4_imag"][0])
+		afctrl = calibration_parts.compute_afctrl(pipeline, derr_at_darm_act_freq, darmexc_at_darm_act_freq, pcal_at_act_pcal_freq, derr_at_act_pcal_freq, EP2)
 		kpu = calibration_parts.compute_kappapu(pipeline, EP3, afctrl, ktst, EP4)
-	elif factors_from_filters_file:
+	else:
+		afctrl = calibration_parts.compute_afctrl_from_filters_file(pipeline, derr_at_darm_act_freq, darmexc_at_darm_act_freq, pcal_at_act_pcal_freq, derr_at_act_pcal_freq, EP2_real, EP2_imag)
 		kpu = calibration_parts.compute_kappapu_from_filters_file(pipeline, EP3_real, EP3_imag, afctrl, ktst, EP4_real, EP4_imag)
 
 	kpu = pipeparts.mktee(pipeline, kpu)
@@ -864,7 +1115,7 @@ if compute_kappapu or compute_kappac or compute_fcc or compute_fs or compute_src
 
 		else:
 			# Smooth kappa_pu
-			smooth_kpu = calibration_parts.smooth_complex_kappas_no_coherence(pipeline, kpu, kappapu_real_ok_var, kappapu_imag_ok_var, expected_kappapu_real, expected_kappapu_imag, median_smoothing_samples, factors_average_samples, tdcf_default_to_median, filter_latency_factor)
+			smooth_kpu = calibration_parts.smooth_complex_kappas_no_coherence(pipeline, kpu, kappapu_real_var, kappapu_imag_var, expected_kappapu_real, expected_kappapu_imag, median_smoothing_samples, factors_average_samples, tdcf_default_to_median, filter_latency_factor)
 
 		smooth_kputee = pipeparts.mktee(pipeline, smooth_kpu)
 		smooth_kpuR, smooth_kpuI = calibration_parts.split_into_real(pipeline, smooth_kputee)
@@ -888,17 +1139,31 @@ if compute_kappac or compute_fcc or compute_fs or compute_srcq:
 		derr_at_opt_gain_freq = calibration_parts.complex_audioamplify(pipeline, derr_at_opt_gain_freq, derr_dewhiten_at_opt_gain_fcc_freq_real, derr_dewhiten_at_opt_gain_fcc_freq_imag)
 
 	# Compute the factor S which will be used for the kappa_c and f_cc calculations
-	# \kappa_tst and \kappa_pu need to be evaluated at the higher pcal line frequency
+	# Tha actuation kappas need to be evaluated at the higher pcal line frequency
 	ktst_at_opt_gain_freq = pipeparts.mkgeneric(pipeline, ktst, "lpshiftfreq", frequency_ratio = opt_gain_fcc_line_freq / esd_act_line_freq)
-	kpu_at_opt_gain_freq = pipeparts.mkgeneric(pipeline, kpu, "lpshiftfreq", frequency_ratio = opt_gain_fcc_line_freq / pu_act_esd_line_freq)
 	if not factors_from_filters_file:
-		EP6 = calibration_parts.merge_into_complex(pipeline, head_dict["EP6_real"], head_dict["EP6_imag"])
-		EP7 = calibration_parts.merge_into_complex(pipeline, head_dict["EP7_real"], head_dict["EP7_imag"])
-		EP8 = calibration_parts.merge_into_complex(pipeline, head_dict["EP8_real"], head_dict["EP8_imag"])
-		EP9 = calibration_parts.merge_into_complex(pipeline, head_dict["EP9_real"], head_dict["EP9_imag"])
-		S = calibration_parts.compute_S(pipeline, EP6, pcal_at_opt_gain_freq, derr_at_opt_gain_freq, EP7, ktst_at_opt_gain_freq, EP8, kpu_at_opt_gain_freq, EP9)
+		EP6 = calibration_parts.merge_into_complex(pipeline, C_epics_dict["EP6_real"][0], C_epics_dict["EP6_imag"][0])
+		EP7 = calibration_parts.merge_into_complex(pipeline, D_epics_dict["EP7_real"][0], D_epics_dict["EP7_imag"][0])
+		EP8 = calibration_parts.merge_into_complex(pipeline, A_epics_dict["EP8_real"][0], A_epics_dict["EP8_imag"][0])
+		if compute_kappapum or compute_kappauim:
+			kpum_at_opt_gain_freq = pipeparts.mkgeneric(pipeline, kpum, "lpshiftfreq", frequency_ratio = opt_gain_fcc_line_freq / pum_act_line_freq)
+			kuim_at_opt_gain_freq = pipeparts.mkgeneric(pipeline, kuim, "lpshiftfreq", frequency_ratio = opt_gain_fcc_line_freq / (uim_act_line_freq if use_uim_line else darm_ctrl_line_freq))
+			EP18 = calibration_parts.merge_into_complex(pipeline, A_epics_dict["EP18_real"][0], A_epics_dict["EP18_imag"][0])
+			EP19 = calibration_parts.merge_into_complex(pipeline, A_epics_dict["EP19_real"][0], A_epics_dict["EP19_imag"][0])
+			S = calibration_parts.compute_S_split_act(pipeline, EP6, pcal_at_opt_gain_freq, derr_at_opt_gain_freq, EP7, ktst_at_opt_gain_freq, EP8, kpum_at_opt_gain_freq, EP18, kuim_at_opt_gain_freq, EP19)
+		else:
+			kpu_at_opt_gain_freq = pipeparts.mkgeneric(pipeline, kpu, "lpshiftfreq", frequency_ratio = opt_gain_fcc_line_freq / darm_ctrl_line_freq)
+			EP9 = calibration_parts.merge_into_complex(pipeline, A_epics_dict["EP9_real"][0], A_epics_dict["EP9_imag"][0])
+			S = calibration_parts.compute_S(pipeline, EP6, pcal_at_opt_gain_freq, derr_at_opt_gain_freq, EP7, ktst_at_opt_gain_freq, EP8, kpu_at_opt_gain_freq, EP9)
+
 	elif factors_from_filters_file:
-		S = calibration_parts.compute_S_from_filters_file(pipeline, EP6_real, EP6_imag, pcal_at_opt_gain_freq, derr_at_opt_gain_freq, EP7_real, EP7_imag, ktst_at_opt_gain_freq, EP8_real, EP8_imag, kpu_at_opt_gain_freq, EP9_real, EP9_imag)
+		if compute_kappapum or compute_kappauim:
+			kpum_at_opt_gain_freq = pipeparts.mkgeneric(pipeline, kpum, "lpshiftfreq", frequency_ratio = opt_gain_fcc_line_freq / pum_act_line_freq)
+			kuim_at_opt_gain_freq = pipeparts.mkgeneric(pipeline, kuim, "lpshiftfreq", frequency_ratio = opt_gain_fcc_line_freq / (uim_act_line_freq if use_uim_line else darm_ctrl_line_freq))
+			S = calibration_parts.compute_S_from_filters_file_split_act(pipeline, EP6_real, EP6_imag, pcal_at_opt_gain_freq, derr_at_opt_gain_freq, EP7_real, EP7_imag, ktst_at_opt_gain_freq, EP8_real, EP8_imag, kpum_at_opt_gain_freq, EP18_real, EP18_imag, kuim_at_opt_gain_freq, EP19_real, EP19_imag)
+		else:
+			kpu_at_opt_gain_freq = pipeparts.mkgeneric(pipeline, kpu, "lpshiftfreq", frequency_ratio = opt_gain_fcc_line_freq / darm_ctrl_line_freq)
+			S = calibration_parts.compute_S_from_filters_file(pipeline, EP6_real, EP6_imag, pcal_at_opt_gain_freq, derr_at_opt_gain_freq, EP7_real, EP7_imag, ktst_at_opt_gain_freq, EP8_real, EP8_imag, kpu_at_opt_gain_freq, EP9_real, EP9_imag)
 
 	S = pipeparts.mktee(pipeline, S)
 
@@ -960,12 +1225,12 @@ if compute_kappac or compute_fcc or compute_fs or compute_srcq:
 # compute f_s and Q
 if compute_fs or compute_srcq:
 	expected_Xi = complex((fs_default * fs_default - 1j * src_pcal_line_freq * fs_default / srcQ_default) / (src_pcal_line_freq * src_pcal_line_freq))
-	Xi_real_ok_var = float((pow(fs_default + fs_var, 2) - pow(fs_default, 2.0)) / pow(src_pcal_line_freq, 2))
-	Xi_imag_ok_var = float(fs_var / (srcQ_default * src_pcal_line_freq))
+	Xi_real_var = float((pow(fs_default + fs_var, 2) - pow(fs_default, 2.0)) / pow(src_pcal_line_freq, 2))
+	Xi_imag_var = float(fs_var / (srcQ_default * src_pcal_line_freq))
 
 	# demodulate PCAL channel and apply the PCAL correction factor at SRC detuning line frequency
-	if src_pcal_line_freq == darm_act_line_freq:
-		pcal_at_src_freq = pcal_at_darm_act_freq
+	if src_pcal_line_freq == act_pcal_line_freq:
+		pcal_at_src_freq = pcal_at_act_pcal_freq
 	else:
 		pcal_at_src_freq = calibration_parts.demodulate(pipeline, pcaltee, src_pcal_line_freq, td, compute_factors_sr, demodulation_filter_time, filter_latency_factor, prefactor_real = pcal_corr_at_src_freq_real, prefactor_imag = pcal_corr_at_src_freq_imag)
 		pcal_at_src_freq = pipeparts.mktee(pipeline, pcal_at_src_freq)
@@ -974,23 +1239,36 @@ if compute_fs or compute_srcq:
 		pcal_line_removal_dict["pcal4"][3] = pcal_at_src_freq
 
 	# demodulate DARM_ERR at SRC detuning line frequency
-	if src_pcal_line_freq == darm_act_line_freq:
-		derr_at_src_freq = derr_at_darm_act_freq
+	if src_pcal_line_freq == act_pcal_line_freq:
+		derr_at_src_freq = derr_at_act_pcal_freq
 	else:
 		derr_at_src_freq = calibration_parts.demodulate(pipeline, derrtee, src_pcal_line_freq, td, compute_factors_sr, demodulation_filter_time, filter_latency_factor)
 
 	# Compute the factor Xi which will be used for the f_s and src_Q calculations
-	# \kappa_tst and \kappa_pu need to be evaluated at the SRC pcal line frequency
+	# The actuation kappas need to be evaluated at the SRC pcal line frequency
 	ktst_at_src_freq = pipeparts.mkgeneric(pipeline, ktst, "lpshiftfreq", frequency_ratio = src_pcal_line_freq / esd_act_line_freq)
-	kpu_at_src_freq = pipeparts.mkgeneric(pipeline, kpu, "lpshiftfreq", frequency_ratio = src_pcal_line_freq / pu_act_esd_line_freq)
 	if not factors_from_filters_file:
-		EP11 = calibration_parts.merge_into_complex(pipeline, head_dict["EP11_real"], head_dict["EP11_imag"])
-		EP12 = calibration_parts.merge_into_complex(pipeline, head_dict["EP12_real"], head_dict["EP12_imag"])
-		EP13 = calibration_parts.merge_into_complex(pipeline, head_dict["EP13_real"], head_dict["EP13_imag"])
-		EP14 = calibration_parts.merge_into_complex(pipeline, head_dict["EP14_real"], head_dict["EP14_imag"])
-		Xi = calibration_parts.compute_Xi(pipeline, pcal_at_src_freq, derr_at_src_freq, src_pcal_line_freq, EP11, EP12, EP13, EP14, ktst_at_src_freq, kpu_at_src_freq, kc, fcc)
-	elif factors_from_filters_file:
-		Xi = calibration_parts.compute_Xi_from_filters_file(pipeline, pcal_at_src_freq, derr_at_src_freq, src_pcal_line_freq, EP11_real, EP11_imag, EP12_real, EP12_imag, EP13_real, EP13_imag, EP14_real, EP14_imag, ktst_at_src_freq, kpu_at_src_freq, kc, fcc)
+		EP11 = calibration_parts.merge_into_complex(pipeline, C_epics_dict["EP11_real"][0], C_epics_dict["EP11_imag"][0])
+		EP12 = calibration_parts.merge_into_complex(pipeline, D_epics_dict["EP12_real"][0], D_epics_dict["EP12_imag"][0])
+		EP13 = calibration_parts.merge_into_complex(pipeline, A_epics_dict["EP13_real"][0], A_epics_dict["EP13_imag"][0])
+		if compute_kappapum or compute_kappauim:
+			kpum_at_src_freq = pipeparts.mkgeneric(pipeline, kpum, "lpshiftfreq", frequency_ratio = src_pcal_line_freq / pum_act_line_freq)
+			kuim_at_src_freq = pipeparts.mkgeneric(pipeline, kuim, "lpshiftfreq", frequency_ratio = src_pcal_line_freq / (uim_act_line_freq if use_uim_line else darm_ctrl_line_freq))
+			EP20 = calibration_parts.merge_into_complex(pipeline, A_epics_dict["EP20_real"][0], A_epics_dict["EP20_imag"][0])
+			EP21 = calibration_parts.merge_into_complex(pipeline, A_epics_dict["EP21_real"][0], A_epics_dict["EP21_imag"][0])
+			Xi = calibration_parts.compute_Xi_split_act(pipeline, pcal_at_src_freq, derr_at_src_freq, src_pcal_line_freq, EP11, EP12, EP13, EP20, EP21, ktst_at_src_freq, kpum_at_src_freq, kuim_at_src_freq, kc, fcc)
+		else:
+			kpu_at_src_freq = pipeparts.mkgeneric(pipeline, kpu, "lpshiftfreq", frequency_ratio = src_pcal_line_freq / darm_ctrl_line_freq)
+			EP14 = calibration_parts.merge_into_complex(pipeline, A_epics_dict["EP14_real"][0], A_epics_dict["EP14_imag"][0])
+			Xi = calibration_parts.compute_Xi(pipeline, pcal_at_src_freq, derr_at_src_freq, src_pcal_line_freq, EP11, EP12, EP13, EP14, ktst_at_src_freq, kpu_at_src_freq, kc, fcc)
+	else:
+		if compute_kappapum or compute_kappauim:
+			kpum_at_src_freq = pipeparts.mkgeneric(pipeline, kpum, "lpshiftfreq", frequency_ratio = src_pcal_line_freq / pum_act_line_freq)
+			kuim_at_src_freq = pipeparts.mkgeneric(pipeline, kuim, "lpshiftfreq", frequency_ratio = src_pcal_line_freq / (uim_act_line_freq if use_uim_line else darm_ctrl_line_freq))
+			Xi = calibration_parts.compute_Xi_from_filters_file_split_act(pipeline, pcal_at_src_freq, derr_at_src_freq, src_pcal_line_freq, EP11_real, EP11_imag, EP12_real, EP12_imag, EP13_real, EP13_imag, EP20_real, EP20_imag, EP21_real, EP21_imag, ktst_at_src_freq, kpum_at_src_freq, kuim_at_src_freq, kc, fcc)
+		else:
+			kpu_at_src_freq = pipeparts.mkgeneric(pipeline, kpu, "lpshiftfreq", frequency_ratio = src_pcal_line_freq / darm_ctrl_line_freq)
+			Xi = calibration_parts.compute_Xi_from_filters_file(pipeline, pcal_at_src_freq, derr_at_src_freq, src_pcal_line_freq, EP11_real, EP11_imag, EP12_real, EP12_imag, EP13_real, EP13_imag, EP14_real, EP14_imag, ktst_at_src_freq, kpu_at_src_freq, kc, fcc)
 
 	Xi = pipeparts.mktee(pipeline, Xi)
 	smooth_Xi_nogate = pipeparts.mkgeneric(pipeline, Xi, "lal_smoothkappas", default_kappa_re = float(numpy.real(expected_Xi)), default_kappa_im = float(numpy.imag(expected_Xi)), array_size = median_smoothing_samples, avg_array_size = factors_average_samples, default_to_median = tdcf_default_to_median, filter_latency = filter_latency_factor)
@@ -1005,7 +1283,7 @@ if compute_fs or compute_srcq:
 		smooth_Xi = calibration_parts.smooth_complex_kappas(pipeline, Xi_gated, float(numpy.real(expected_Xi)), float(numpy.imag(expected_Xi)), median_smoothing_samples, factors_average_samples, tdcf_default_to_median, filter_latency_factor)
 
 	else:
-		smooth_Xi = calibration_parts.smooth_complex_kappas_no_coherence(pipeline, Xi, Xi_real_ok_var, Xi_real_ok_var, float(numpy.real(expected_Xi)), float(numpy.imag(expected_Xi)), median_smoothing_samples, factors_average_samples, tdcf_default_to_median, filter_latency_factor)
+		smooth_Xi = calibration_parts.smooth_complex_kappas_no_coherence(pipeline, Xi, Xi_real_var, Xi_real_var, float(numpy.real(expected_Xi)), float(numpy.imag(expected_Xi)), median_smoothing_samples, factors_average_samples, tdcf_default_to_median, filter_latency_factor)
 
 	if not compute_srcq:
 		# the imaginary part is only used to compute Q
@@ -1048,14 +1326,22 @@ if compute_fs or compute_srcq:
 # TIME-VARYING FACTORS COMPENSATIONS
 #
 
-if apply_complex_kappapu:
-	# We will apply an adaptive FIR filter to the PUM/UIM component of the actuation that includes time-dependence in the gain and computational time delay
-	adaptive_pumuim_filter = pipeparts.mkgeneric(pipeline, smooth_kputee, "lal_adaptivefirfilt", update_samples = int(actuation_filter_update_time * compute_factors_sr), average_samples = int(actuation_filter_averaging_time * compute_factors_sr), phase_measurement_frequency = pu_act_esd_line_freq, static_filter = pumuimfilt, variable_filter_length = len(pumuimfilt), adaptive_filter_length = len(pumuimfilt), tukey_param = 0.5, filter_sample_rate = pumuimchainsr)
-
 if apply_complex_kappatst:
 	# We will apply an adaptive FIR filter to the TST component of the actuation that includes time-dependence in the gain and computational time delay
 	adaptive_tst_filter = pipeparts.mkgeneric(pipeline, smooth_ktsttee, "lal_adaptivefirfilt", update_samples = int(actuation_filter_update_time * compute_factors_sr), average_samples = int(actuation_filter_averaging_time * compute_factors_sr), phase_measurement_frequency = esd_act_line_freq, static_filter = tstfilt, variable_filter_length = len(tstfilt), adaptive_filter_length = len(tstfilt), tukey_param = 0.5, filter_sample_rate = tstchainsr)
 
+if apply_complex_kappapum:
+	# We will apply an adaptive FIR filter to the PUM component of the actuation that includes time-dependence in the gain and computational time delay
+	adaptive_pum_filter = pipeparts.mkgeneric(pipeline, smooth_kpumtee, "lal_adaptivefirfilt", update_samples = int(actuation_filter_update_time * compute_factors_sr), average_samples = int(actuation_filter_averaging_time * compute_factors_sr), phase_measurement_frequency = esd_act_line_freq, static_filter = pumfilt, variable_filter_length = len(pumfilt), adaptive_filter_length = len(pumfilt), tukey_param = 0.5, filter_sample_rate = pumchainsr)
+
+if apply_complex_kappauim:
+	# We will apply an adaptive FIR filter to the UIM component of the actuation that includes time-dependence in the gain and computational time delay
+	adaptive_uim_filter = pipeparts.mkgeneric(pipeline, smooth_kuimtee, "lal_adaptivefirfilt", update_samples = int(actuation_filter_update_time * compute_factors_sr), average_samples = int(actuation_filter_averaging_time * compute_factors_sr), phase_measurement_frequency = esd_act_line_freq, static_filter = uimfilt, variable_filter_length = len(uimfilt), adaptive_filter_length = len(uimfilt), tukey_param = 0.5, filter_sample_rate = uimchainsr)
+
+if apply_complex_kappapu:
+	# We will apply an adaptive FIR filter to the PUM/UIM component of the actuation that includes time-dependence in the gain and computational time delay
+	adaptive_pumuim_filter = pipeparts.mkgeneric(pipeline, smooth_kputee, "lal_adaptivefirfilt", update_samples = int(actuation_filter_update_time * compute_factors_sr), average_samples = int(actuation_filter_averaging_time * compute_factors_sr), phase_measurement_frequency = darm_ctrl_line_freq, static_filter = pumuimfilt, variable_filter_length = len(pumuimfilt), adaptive_filter_length = len(pumuimfilt), tukey_param = 0.5, filter_sample_rate = pumuimchainsr)
+
 if apply_fcc or apply_fs or apply_srcq:
 	# We will apply an adaptive FIR filter to DARM_ERR that allows corrections for poles, zeros, and gain
 	# We need to track the number of time-dependent and static zeros and poles in the adaptive filter
@@ -1157,9 +1443,15 @@ if apply_fcc or apply_fs or apply_srcq:
 # zero out filter settling samples
 tst_filter_settle_time = 0.0
 tst_filter_latency = 0.0
+pum_filter_settle_time = 0.0
+pum_filter_latency = 0.0
+uim_filter_settle_time = 0.0
+uim_filter_latency = 0.0
 pumuim_filter_settle_time = 0.0
 pumuim_filter_latency = 0.0
 
+actsr = max(tstchainsr, pumchainsr, uimchainsr) if (apply_kappapum or apply_kappauim or apply_complex_kappapum or apply_complex_kappauim) else max (tstchainsr, pumuimchainsr)
+
 # The reverse of the filters will be used in all filtering below due to the definition of the filtering procedure employed by lal_firbank
 if CalibrationConfigs["calibrationmode"] == "Partial":
 	# enforce caps on actuation channels and set up progress report if verbose is on
@@ -1170,19 +1462,30 @@ if CalibrationConfigs["calibrationmode"] == "Partial":
 	uim = calibration_parts.caps_and_progress(pipeline, head_dict["uim"], ctrl_caps, "uim")
 	uimtee = pipeparts.mktee(pipeline, uim)
 
-	# add together the PUM and UIM actuation channels; this may change in the future...
-	pumuim = calibration_parts.mkadder(pipeline, calibration_parts.list_srcs(pipeline, pumtee, uimtee))
+	# If processing the PUM and UIM actuation channels together, add them here
+	if not (apply_kappapum or apply_kappauim or apply_complex_kappapum or apply_complex_kappauim):
+		pumuim = calibration_parts.mkadder(pipeline, calibration_parts.list_srcs(pipeline, pumtee, uimtee))
 
 	# if you need to, dewhiten the TST and PUM/UIM chains
 	if dewhitening:
-		pumuim = calibration_parts.mkresample(pipeline, pumuim, 5, False, "audio/x-raw, format=F64LE, rate=%d" % pumuimdewhitensr) 
-		pumuim = pipeparts.mkfirbank(pipeline, pumuim, latency = int(pumuimdewhitendelay), fir_matrix = [pumuimdewhiten[::-1]], time_domain = td)
-		pumuim_filter_settle_time += float(len(pumuimdewhiten)-pumuimdewhitendelay)/pumuimdewhitensr
-		pumuim_filter_latency += float(pumuimdewhitendelay)/pumuimdewhitensr
 		tst = calibration_parts.mkresample(pipeline, tsttee, 5, False, "audio/x-raw, format=F64LE, rate=%d" % tstdewhitensr) 
 		tst = pipeparts.mkfirbank(pipeline, tst, latency = int(tstdewhitendelay), fir_matrix = [tstdewhiten[::-1]], time_domain = td)
 		tst_filter_settle_time += float(len(tstdewhiten)-tstdewhitendelay)/tstdewhitensr
 		tst_filter_latency += float(tstdewhitendelay)/tstdewhitensr
+		if apply_kappapum or apply_kappauim or apply_complex_kappapum or apply_complex_kappauim:
+			pum = calibration_parts.mkresample(pipeline, pum, 5, False, "audio/x-raw, format=F64LE, rate=%d" % pumdewhitensr)
+			pum = pipeparts.mkfirbank(pipeline, pum, latency = int(pumdewhitendelay), fir_matrix = [pumdewhiten[::-1]], time_domain = td)
+			pum_filter_settle_time += float(len(pumdewhiten)-pumdewhitendelay)/pumdewhitensr
+			pum_filter_latency += float(pumdewhitendelay)/pumdewhitensr
+			uim = calibration_parts.mkresample(pipeline, uim, 5, False, "audio/x-raw, format=F64LE, rate=%d" % uimdewhitensr)
+			uim = pipeparts.mkfirbank(pipeline, uim, latency = int(uimdewhitendelay), fir_matrix = [uimdewhiten[::-1]], time_domain = td)
+			uim_filter_settle_time += float(len(uimdewhiten)-uimdewhitendelay)/uimdewhitensr
+			uim_filter_latency += float(uimdewhitendelay)/uimdewhitensr
+		else:
+			pumuim = calibration_parts.mkresample(pipeline, pumuim, 5, False, "audio/x-raw, format=F64LE, rate=%d" % pumuimdewhitensr)
+			pumuim = pipeparts.mkfirbank(pipeline, pumuim, latency = int(pumuimdewhitendelay), fir_matrix = [pumuimdewhiten[::-1]], time_domain = td)
+			pumuim_filter_settle_time += float(len(pumuimdewhiten)-pumuimdewhitendelay)/pumuimdewhitensr
+			pumuim_filter_latency += float(pumuimdewhitendelay)/pumuimdewhitensr
 	else:
 		tst = tsttee
 
@@ -1197,13 +1500,21 @@ if CalibrationConfigs["calibrationmode"] == "Full":
 		ctrl = pipeparts.mkfirbank(pipeline, ctrl, latency = int(ctrldewhitendelay), fir_matrix = [ctrldewhiten[::-1]], time_domain = td)
 		tst_filter_settle_time += float(len(ctrldewhiten)-ctrldewhitendelay)/ctrldewhitensr
 		tst_filter_latency += float(ctrldewhitendelay)/ctrldewhitensr
-		pumuim_filter_settle_time += float(len(ctrldewhiten)-ctrldewhitendelay)/ctrldewhitensr
-		pumuim_filter_latency += float(ctrldewhitendelay)/ctrldewhitensr
-		# tee off DARM_CTRL to be filtered with PUM/UIM and TST filters separately
+		if apply_kappapum or apply_kappauim or apply_complex_kappapum or apply_complex_kappauim:
+			pum_filter_settle_time += float(len(ctrldewhiten)-ctrldewhitendelay)/ctrldewhitensr
+			pum_filter_latency += float(ctrldewhitendelay)/ctrldewhitensr
+			uim_filter_settle_time += float(len(ctrldewhiten)-ctrldewhitendelay)/ctrldewhitensr
+			uim_filter_latency += float(ctrldewhitendelay)/ctrldewhitensr
+		else:
+			pumuim_filter_settle_time += float(len(ctrldewhiten)-ctrldewhitendelay)/ctrldewhitensr
+			pumuim_filter_latency += float(ctrldewhitendelay)/ctrldewhitensr
+		# tee DARM_CTRL, which will be filtered with PUM, UIM, and TST filters separately
 		ctrltee = pipeparts.mktee(pipeline, ctrl)
 	else:
-		ctrltee = pipeparts.mktee(pipeline, darmctrltee)
+		ctrltee = darmctrltee
 	tst = ctrltee
+	pumtee = ctrltee
+	uimtee = ctrltee
 	pumuim = ctrltee
 
 # resample what will become the TST actuation chain to the TST FIR filter sample rate
@@ -1237,48 +1548,112 @@ if apply_kappatst and not apply_complex_kappatst:
 	tst = calibration_parts.mkmultiplier(pipeline, calibration_parts.list_srcs(pipeline, ktst_for_tst, tst))
 
 # resample the TST actuation chain if necessary
-if tstchainsr < pumuimchainsr:
-	tst = calibration_parts.mkresample(pipeline, tst, 5, False, pumuimchainsr)
-
-# resample what will become the PUM/UIM actuation chain to the PUM/UIM FIR filter sample rate
-pumuim = calibration_parts.mkresample(pipeline, pumuim, 5, False, "audio/x-raw, format=F64LE, rate=%d" % pumuimchainsr)
-# Remove any DC component
-if remove_dc:
-	pumuim = calibration_parts.removeDC(pipeline, pumuim, pumuimchainsr)
-# High-pass filter the PUM/UIM chain
-if any(act_highpass):
-	pumuim = pipeparts.mkfirbank(pipeline, pumuim, latency = act_highpass_delay, fir_matrix = [act_highpass[::-1]], time_domain = td)
-	pumuim_filter_settle_time += float(len(act_highpass)-act_highpass_delay)/pumuimchainsr
-	pumuim_filter_latency += float(act_highpass_delay)/pumuimchainsr
+if tstchainsr < actsr:
+	tst = calibration_parts.mkresample(pipeline, tst, 5, False, actsr)
+
+# Check whether we need to filter the PUM and UIM stages separately or together
+if apply_kappapum or apply_kappauim or apply_complex_kappapum or apply_complex_kappauim:
+	# resample what will become the PUM and UIM actuation paths to the PUM and UIM FIR filter sample rates
+	pum = calibration_parts.mkresample(pipeline, pumtee, 5, False, "audio/x-raw, format=F64LE, rate=%d" % pumchainsr)
+	uim = calibration_parts.mkresample(pipeline, uimtee, 5, False, "audio/x-raw, format=F64LE, rate=%d" % uimchainsr)
+	# Remove any DC component
+	if remove_dc:
+		pum = calibration_parts.removeDC(pipeline, pum, pumchainsr)
+		uim = calibration_parts.removeDC(pipeline, uim, uimchainsr)
+	# High-pass filter the PUM and UIM paths
+	if any(act_highpass):
+		pum = pipeparts.mkfirbank(pipeline, pum, latency = act_highpass_delay, fir_matrix = [act_highpass[::-1]], time_domain = td)
+		pum_filter_settle_time += float(len(act_highpass)-act_highpass_delay)/pumchainsr
+		pum_filter_latency += float(act_highpass_delay)/pumchainsr
+		uim = pipeparts.mkfirbank(pipeline, uim, latency = act_highpass_delay, fir_matrix = [act_highpass[::-1]], time_domain = td) 
+		uim_filter_settle_time += float(len(act_highpass)-act_highpass_delay)/uimchainsr
+		uim_filter_latency += float(act_highpass_delay)/uimchainsr
+
+	if apply_complex_kappapum:
+		# Filter the PUM chain with an adaptive PUM actuation filter that includes a linear-phase correction from kappa_pum
+		pum = pipeparts.mkgeneric(pipeline, pum, "lal_tdwhiten", kernel = pumfilt[::-1], latency = pumdelay, taper_length = actuation_filter_taper_length)
+		# Hook up the adaptive filter from lal_adaptivefirfilt to lal_tdwhiten so that the filter gets updated
+		adaptive_pum_filter.connect("notify::adaptive-filter", calibration_parts.update_filter, pum, "adaptive-filter", "kernel")
+	else:
+		# Filter the PUM chain with the static PUM actuation filter
+		pum = pipeparts.mkfirbank(pipeline, pum, latency = pumdelay, fir_matrix = [pumfilt[::-1]], time_domain = td)
+
+	if apply_complex_kappauim:
+		# Filter the UIM chain with an adaptive UIM actuation filter that includes a linear-phase correction from kappa_uim
+		uim = pipeparts.mkgeneric(pipeline, uim, "lal_tdwhiten", kernel = uimfilt[::-1], latency = uimdelay, taper_length = actuation_filter_taper_length)
+		# Hook up the adaptive filter from lal_adaptivefirfilt to lal_tdwhiten so that the filter gets updated
+		adaptive_uim_filter.connect("notify::adaptive-filter", calibration_parts.update_filter, uim, "adaptive-filter", "kernel")
+	else:
+		# Filter the UIM chain with the static UIM actuation filter
+		uim = pipeparts.mkfirbank(pipeline, uim, latency = uimdelay, fir_matrix = [uimfilt[::-1]], time_domain = td)
+
+	pum_filter_settle_time += float(len(pumfilt)-pumdelay)/pumchainsr
+	pum_filter_latency += float(pumdelay)/pumchainsr
+	uim_filter_settle_time += float(len(uimfilt)-uimdelay)/uimchainsr
+	uim_filter_latency += float(uimdelay)/uimchainsr
+
+	# apply kappa_pum if we haven't already
+	if apply_kappapum and not apply_complex_kappapum:
+		# Only apply the real part of kappa_pum as a correction to A_pum
+		kpum_for_pum = calibration_parts.mkresample(pipeline, smooth_kpumRtee, 3, False, pumchainsr)
+		pum = calibration_parts.mkmultiplier(pipeline, calibration_parts.list_srcs(pipeline, kpum_for_pum, pum))
+	# apply kappa_uim if we haven't already
+	if apply_kappauim and not apply_complex_kappauim:
+		# Only apply the real part of kappa_uim as a correction to A_uim
+		kuim_for_uim = calibration_parts.mkresample(pipeline, smooth_kuimRtee, 3, False, uimchainsr)
+		uim = calibration_parts.mkmultiplier(pipeline, calibration_parts.list_srcs(pipeline, kuim_for_uim, uim))
+
+	# resample the PUM actuation path if necessary
+	if pumchainsr < actsr:
+		pum = calibration_parts.mkresample(pipeline, pum, 5, False, actsr)
+
+	# resample the UIM actuation path if necessary
+	if uimchainsr < actsr:
+		uim = calibration_parts.mkresample(pipeline, uim, 5, False, actsr)
+
+	# Add the TST, PUM, and UIM paths together to form the full actuation path
+	ctrl = calibration_parts.mkadder(pipeline, calibration_parts.list_srcs(pipeline, tst, pum, uim))
+else:
+	# resample what will become the PUM/UIM actuation chain to the PUM/UIM FIR filter sample rate
+	pumuim = calibration_parts.mkresample(pipeline, pumuim, 5, False, "audio/x-raw, format=F64LE, rate=%d" % pumuimchainsr)
+	# Remove any DC component
+	if remove_dc:
+		pumuim = calibration_parts.removeDC(pipeline, pumuim, pumuimchainsr)
+	# High-pass filter the PUM/UIM chain
+	if any(act_highpass):
+		pumuim = pipeparts.mkfirbank(pipeline, pumuim, latency = act_highpass_delay, fir_matrix = [act_highpass[::-1]], time_domain = td)
+		pumuim_filter_settle_time += float(len(act_highpass)-act_highpass_delay)/pumuimchainsr
+		pumuim_filter_latency += float(act_highpass_delay)/pumuimchainsr
+
+	if apply_complex_kappapu:
+		# Filter the PUM/UIM chain with an adaptive PUM/UIM actuation filter that includes a linear-phase correction from kappa_pu
+		pumuim = pipeparts.mkgeneric(pipeline, pumuim, "lal_tdwhiten", kernel = pumuimfilt[::-1], latency = pumuimdelay, taper_length = actuation_filter_taper_length)
+		# Hook up the adaptive filter from lal_adaptivefirfilt to lal_tdwhiten so that the filter gets updated
+		adaptive_pumuim_filter.connect("notify::adaptive-filter", calibration_parts.update_filter, pumuim, "adaptive-filter", "kernel")
 
-if apply_complex_kappapu:
-	# Filter the PUM/UIM chain with an adaptive PUM/UIM actuation filter that includes a linear-phase correction from kappa_pu
-	pumuim = pipeparts.mkgeneric(pipeline, pumuim, "lal_tdwhiten", kernel = pumuimfilt[::-1], latency = pumuimdelay, taper_length = actuation_filter_taper_length)
-	# Hook up the adaptive filter from lal_adaptivefirfilt to lal_tdwhiten so that the filter gets updated
-	adaptive_pumuim_filter.connect("notify::adaptive-filter", calibration_parts.update_filter, pumuim, "adaptive-filter", "kernel")
+	else:
+		# Filter the PUM/UIM chain with the static PUM/UIM actuation filter
+		pumuim = pipeparts.mkfirbank(pipeline, pumuim, latency = pumuimdelay, fir_matrix = [pumuimfilt[::-1]], time_domain = td)
 
-else:
-	# Filter the PUM/UIM chain with the static PUM/UIM actuation filter
-	pumuim = pipeparts.mkfirbank(pipeline, pumuim, latency = pumuimdelay, fir_matrix = [pumuimfilt[::-1]], time_domain = td)
+	pumuim_filter_settle_time += float(len(pumuimfilt)-pumuimdelay)/pumuimchainsr
+	pumuim_filter_latency += float(pumuimdelay)/pumuimchainsr
 
-pumuim_filter_settle_time += float(len(pumuimfilt)-pumuimdelay)/pumuimchainsr
-pumuim_filter_latency += float(pumuimdelay)/pumuimchainsr
+	# apply kappa_pu if we haven't already
+	if apply_kappapu and not apply_complex_kappapu:
+		# Only apply the real part of \kappa_pu as a correction to A_pu
+		kpu_for_pu = calibration_parts.mkresample(pipeline, smooth_kpuRtee, 3, False, pumuimchainsr)
+		pumuim = calibration_parts.mkmultiplier(pipeline, calibration_parts.list_srcs(pipeline, kpu_for_pu, pumuim))
 
-# apply kappa_pu if we haven't already
-if apply_kappapu and not apply_complex_kappapu:
-	# Only apply the real part of \kappa_pu as a correction to A_pu
-	kpu_for_pu = calibration_parts.mkresample(pipeline, smooth_kpuRtee, 3, False, pumuimchainsr)
-	pumuim = calibration_parts.mkmultiplier(pipeline, calibration_parts.list_srcs(pipeline, kpu_for_pu, pumuim))
 
+	# resample the PUM/UIM actuation chain if necessary
+	if pumuimchainsr < actsr:
+		pumuim = calibration_parts.mkresample(pipeline, pumuim, 5, False, actsr)
 
-# resample the PUM/UIM actuation chain if necessary
-if pumuimchainsr < tstchainsr:
-	pumuim = calibration_parts.mkresample(pipeline, pumuim, 5, False, tstchainsr)
+	# Add the TST and PUM/UIM chains together to form the full actuation chain
+	ctrl = calibration_parts.mkadder(pipeline, calibration_parts.list_srcs(pipeline, tst, pumuim))
 
-# Add the TST and PUM/UIM chains together to form the full actuation chain
-ctrl = calibration_parts.mkadder(pipeline, calibration_parts.list_srcs(pipeline, tst, pumuim))
-# Resample to the full h(t) sample rate
-if max(tstchainsr, pumuimchainsr) != hoft_sr:
+# Resample \DeltaL_ctrl to the full h(t) sample rate
+if actsr != hoft_sr:
 	ctrl = calibration_parts.mkresample(pipeline, ctrl, 5, False, hoft_caps)
 
 #
@@ -1458,19 +1833,19 @@ if compute_calib_statevector:
 		ktstSmoothInRange = calibration_parts.compute_kappa_bits(pipeline, smooth_ktstRtee, smooth_ktstItee, expected_kappatst_real, expected_kappatst_imag, kappatst_real_var, kappatst_imag_var, int(median_smoothing_samples / 2) + factors_average_samples, status_out_smooth = pow(2,9), starting_rate = compute_factors_sr, ending_rate = calibstate_sr)
 
 	#
-	# KAPPAP/KAPPAPU BITS BRANCH
+	# KAPPAPUM/KAPPAPU BITS BRANCH
 	#
-	if compute_kappap:
-		kpSmoothInRange = calibration_parts.compute_kappa_bits(pipeline, smooth_kpRtee, smooth_kpItee, expected_kappap_real, expected_kappap_imag, kappap_real_var, kappap_imag_var, int(median_smoothing_samples / 2) + factors_average_samples, status_out_smooth = pow(2,10), starting_rate = compute_factors_sr, ending_rate = calibstate_sr)
+	if compute_kappapum:
+		kpumSmoothInRange = calibration_parts.compute_kappa_bits(pipeline, smooth_kpumRtee, smooth_kpumItee, expected_kappapum_real, expected_kappapum_imag, kappapum_real_var, kappapum_imag_var, int(median_smoothing_samples / 2) + factors_average_samples, status_out_smooth = pow(2,10), starting_rate = compute_factors_sr, ending_rate = calibstate_sr)
 
 	elif compute_kappapu:
-		kpSmoothInRange = calibration_parts.compute_kappa_bits(pipeline, smooth_kpuRtee, smooth_kpuItee, expected_kappapu_real, expected_kappapu_imag, kappapu_real_var, kappapu_imag_var, int(median_smoothing_samples / 2) + factors_average_samples, status_out_smooth = pow(2,10), starting_rate = compute_factors_sr, ending_rate = calibstate_sr)
+		kpumSmoothInRange = calibration_parts.compute_kappa_bits(pipeline, smooth_kpuRtee, smooth_kpuItee, expected_kappapu_real, expected_kappapu_imag, kappapu_real_var, kappapu_imag_var, int(median_smoothing_samples / 2) + factors_average_samples, status_out_smooth = pow(2,10), starting_rate = compute_factors_sr, ending_rate = calibstate_sr)
 
 	#
-	# KAPPAU BITS BRANCH
+	# KAPPAUIM BITS BRANCH
 	#
-	if compute_kappau:
-		kuSmoothInRange = calibration_parts.compute_kappa_bits(pipeline, smooth_kuRtee, smooth_kuItee, expected_kappau_real, expected_kappau_imag, kappau_real_ok_var, kappau_imag_ok_var, int(median_smoothing_samples / 2) + factors_average_samples, status_out_smooth = pow(2,11), starting_rate = compute_factors_sr, ending_rate = calibstate_sr)
+	if compute_kappauim:
+		kuimSmoothInRange = calibration_parts.compute_kappa_bits(pipeline, smooth_kuimRtee, smooth_kuimItee, expected_kappauim_real, expected_kappauim_imag, kappauim_real_var, kappauim_imag_var, int(median_smoothing_samples / 2) + factors_average_samples, status_out_smooth = pow(2,11), starting_rate = compute_factors_sr, ending_rate = calibstate_sr)
 
 	#
 	# KAPPAC BITS BRANCH
@@ -1537,11 +1912,11 @@ if compute_calib_statevector:
 	if apply_kappatst or apply_complex_kappatst:
 		higherbits_list.append(ktstSmoothInRange)
 		htok_threshold += pow(2,9)
-	if apply_kappap or apply_complex_kappap or apply_kappapu or apply_complex_kappapu:
-		higherbits_list.append(kpSmoothInRange)
+	if apply_kappapum or apply_complex_kappapum or apply_kappapu or apply_complex_kappapu:
+		higherbits_list.append(kpumSmoothInRange)
 		htok_threshold += pow(2,10)
-	if apply_kappau or apply_complex_kappau:
-		higherbits_list.append(kuSmoothInRange)
+	if apply_kappauim or apply_complex_kappauim:
+		higherbits_list.append(kuimSmoothInRange)
 		htok_threshold += pow(2,11)
 	if apply_kappac:
 		higherbits_list.append(kcSmoothInRange)
@@ -1582,175 +1957,67 @@ if compute_calib_statevector:
 	# EPICS BITS
 	#
 
-	D_epics_threshold = 0.0
-	A_epics_threshold = 0.0
-	C_epics_threshold = 0.0
-	other_epics_threshold = 0.0
+	D_epics_threshold = 0.5 + len(D_epics_dict)
+	A_epics_threshold = 0.5 + len(A_epics_dict)
+	C_epics_threshold = 0.5 + len(C_epics_dict)
+	misc_epics_threshold = 0.5 + len(misc_epics_dict)
+	epics_bits_list = []
 
 	# First, check the EPICS that involve only the digital filter D, EP7 and EP12
-	if num_dq_epics > 6:
-		D_epics_threshold += 1.5
-		EP7_real_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP7_real"], 1.0 / EP7_real)
-		EP7_imag_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP7_imag"], 1.0 / EP7_imag)
-
-		# The above values should be close to one (within 1 / 10^4)
-		EP7_real_check = pipeparts.mkgeneric(pipeline, EP7_real_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-		EP7_imag_check = pipeparts.mkgeneric(pipeline, EP7_imag_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-		D_epics_check_list = [EP7_real_check, EP7_imag_check]
-
-		if num_dq_epics > 11:
-			D_epics_threshold += 2.0
-			EP12_real_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP12_real"], 1.0 / EP12_real)
-			EP12_imag_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP12_imag"], 1.0 / EP12_imag)
-
+	if(len(D_epics_dict)):
+		D_epics_check_list = []
+		for EP_key in D_epics_dict:
+			D_epics_check = pipeparts.mkaudioamplify(pipeline, D_epics_dict[EP_key][0], 1.0 / D_epics_dict[EP_key][1])
 			# The above values should be close to one (within 1 / 10^4)
-			EP12_real_check = pipeparts.mkgeneric(pipeline, EP12_real_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-			EP12_imag_check = pipeparts.mkgeneric(pipeline, EP12_imag_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-			D_epics_check_list.extend((EP12_real_check, EP12_imag_check))
-
+			D_epics_check = pipeparts.mkgeneric(pipeline, D_epics_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
+			D_epics_check_list.append(D_epics_check)
 		D_epics_check = calibration_parts.mkadder(pipeline, tuple(D_epics_check_list))
 		D_epics_bit = pipeparts.mkbitvectorgen(pipeline, D_epics_check, bit_vector = pow(2,21), threshold = D_epics_threshold)
 		D_epics_bit = pipeparts.mkgeneric(pipeline, D_epics_bit, "lal_logicalundersample", required_on = pow(2,21), status_out = pow(2,21))
 		D_epics_bit = pipeparts.mkcapsfilter(pipeline, D_epics_bit, calibstate_caps)
+		epics_bits_list.append(D_epics_bit)
 
-	# Next, check the EPICS that involve only the actuation function A, EP3, EP4, EP8, EP9, EP10, EP13, EP14
-	if num_dq_epics > 2:
-		A_epics_threshold += 1.5
-		EP3_real_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP3_real"], 1.0 / EP3_real)
-		EP3_imag_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP3_imag"], 1.0 / EP3_imag)
-
-		# The above values should be close to one (within 1 / 10^4)
-		EP3_real_check = pipeparts.mkgeneric(pipeline, EP3_real_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-		EP3_imag_check = pipeparts.mkgeneric(pipeline, EP3_imag_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-		A_epics_check_list = [EP3_real_check, EP3_imag_check]
-
-		if num_dq_epics > 3:
-			A_epics_threshold += 2.0
-			EP4_real_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP4_real"], 1.0 / EP4_real)
-			EP4_imag_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP4_imag"], 1.0 / EP4_imag)
-
-			# The above values should be close to one (within 1 / 10^4)
-			EP4_real_check = pipeparts.mkgeneric(pipeline, EP4_real_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-			EP4_imag_check = pipeparts.mkgeneric(pipeline, EP4_imag_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-			A_epics_check_list.extend((EP4_real_check, EP4_imag_check))
-
-		if num_dq_epics > 7:
-			A_epics_threshold += 2.0
-			EP8_real_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP8_real"], 1.0 / EP8_real)
-			EP8_imag_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP8_imag"], 1.0 / EP8_imag)
-
-			# The above values should be close to one (within 1 / 10^4)
-			EP8_real_check = pipeparts.mkgeneric(pipeline, EP8_real_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-			EP8_imag_check = pipeparts.mkgeneric(pipeline, EP8_imag_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-			A_epics_check_list.extend((EP8_real_check, EP8_imag_check))
-
-		if num_dq_epics > 8:
-			A_epics_threshold += 2.0
-			EP9_real_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP9_real"], 1.0 / EP9_real)
-			EP9_imag_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP9_imag"], 1.0 / EP9_imag)
-
-			# The above values should be close to one (within 1 / 10^4)
-			EP9_real_check = pipeparts.mkgeneric(pipeline, EP9_real_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-			EP9_imag_check = pipeparts.mkgeneric(pipeline, EP9_imag_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-			A_epics_check_list.extend((EP9_real_check, EP9_imag_check))
-
-		if num_dq_epics > 9:
-			A_epics_threshold += 2.0
-			EP10_real_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP10_real"], 1.0 / EP10_real)
-			EP10_imag_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP10_imag"], 1.0 / EP10_imag)
-
+	# Next, check the EPICS that involve only the actuation function A: EP3, EP4, EP8, EP9, EP10, EP13, EP14, EP16, EP17, EP18, EP19, EP20, EP21, EP23, and EP24
+	if(len(A_epics_dict)):
+		A_epics_check_list = []
+		for EP_key in A_epics_dict:
+			A_epics_check = pipeparts.mkaudioamplify(pipeline, A_epics_dict[EP_key][0], 1.0 / A_epics_dict[EP_key][1])
 			# The above values should be close to one (within 1 / 10^4)
-			EP10_real_check = pipeparts.mkgeneric(pipeline, EP10_real_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-			EP10_imag_check = pipeparts.mkgeneric(pipeline, EP10_imag_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-			A_epics_check_list.extend((EP10_real_check, EP10_imag_check))
-
-		if num_dq_epics > 12:
-			A_epics_threshold += 2.0
-			EP13_real_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP13_real"], 1.0 / EP13_real)
-			EP13_imag_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP13_imag"], 1.0 / EP13_imag)
-
-			# The above values should be close to one (within 1 / 10^4)
-			EP13_real_check = pipeparts.mkgeneric(pipeline, EP13_real_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-			EP13_imag_check = pipeparts.mkgeneric(pipeline, EP13_imag_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-			A_epics_check_list.extend((EP13_real_check, EP13_imag_check))
-
-		if num_dq_epics > 13:
-			A_epics_threshold += 2.0
-			EP14_real_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP14_real"], 1.0 / EP14_real)
-			EP14_imag_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP14_imag"], 1.0 / EP14_imag)
-
-			# The above values should be close to one (within 1 / 10^4)
-			EP14_real_check = pipeparts.mkgeneric(pipeline, EP14_real_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-			EP14_imag_check = pipeparts.mkgeneric(pipeline, EP14_imag_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-			A_epics_check_list.extend((EP14_real_check, EP14_imag_check))
-
+			A_epics_check = pipeparts.mkgeneric(pipeline, A_epics_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
+			A_epics_check_list.append(A_epics_check)
 		A_epics_check = calibration_parts.mkadder(pipeline, tuple(A_epics_check_list))
 		A_epics_bit = pipeparts.mkbitvectorgen(pipeline, A_epics_check, bit_vector = pow(2,22), threshold = A_epics_threshold)
 		A_epics_bit = pipeparts.mkgeneric(pipeline, A_epics_bit, "lal_logicalundersample", required_on = pow(2,22), status_out = pow(2,22))
 		A_epics_bit = pipeparts.mkcapsfilter(pipeline, A_epics_bit, calibstate_caps)
+		epics_bits_list.append(A_epics_bit)
 
 	# Next, check the EPICS that involve only the sensing function C, EP6 and EP11
-	if num_dq_epics > 5:
-		C_epics_threshold += 1.5
-		EP6_real_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP6_real"], 1.0 / EP6_real)
-		EP6_imag_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP6_imag"], 1.0 / EP6_imag)
-
-		# The above values should be close to one (within 1 / 10^4)
-		EP6_real_check = pipeparts.mkgeneric(pipeline, EP6_real_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-		EP6_imag_check = pipeparts.mkgeneric(pipeline, EP6_imag_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-		C_epics_check_list = [EP6_real_check, EP6_imag_check]
-
-		if num_dq_epics > 10:
-			C_epics_threshold += 2.0
-			EP11_real_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP11_real"], 1.0 / EP11_real)
-			EP11_imag_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP11_imag"], 1.0 / EP11_imag)
-
+	if(len(C_epics_dict)):
+		C_epics_check_list = []
+		for EP_key in C_epics_dict:
+			C_epics_check = pipeparts.mkaudioamplify(pipeline, C_epics_dict[EP_key][0], 1.0 / C_epics_dict[EP_key][1])
 			# The above values should be close to one (within 1 / 10^4)
-			EP11_real_check = pipeparts.mkgeneric(pipeline, EP11_real_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-			EP11_imag_check = pipeparts.mkgeneric(pipeline, EP11_imag_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-			C_epics_check_list.extend((EP11_real_check, EP11_imag_check))
-
+			C_epics_check = pipeparts.mkgeneric(pipeline, C_epics_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
+			C_epics_check_list.append(C_epics_check)
 		C_epics_check = calibration_parts.mkadder(pipeline, tuple(C_epics_check_list))
 		C_epics_bit = pipeparts.mkbitvectorgen(pipeline, C_epics_check, bit_vector = pow(2,23), threshold = C_epics_threshold)
 		C_epics_bit = pipeparts.mkgeneric(pipeline, C_epics_bit, "lal_logicalundersample", required_on = pow(2,23), status_out = pow(2,23))
 		C_epics_bit = pipeparts.mkcapsfilter(pipeline, C_epics_bit, calibstate_caps)
+		epics_bits_list.append(C_epics_bit)
 
-	# Next, check the remaining EPICS that are combinations of D, A, and C, EP1 and EP2
-	if num_dq_epics > 0:
-		other_epics_threshold += 1.5
-		EP1_real_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP1_real"], 1.0 / EP1_real)
-		EP1_imag_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP1_imag"], 1.0 / EP1_imag)
-
-		# The above values should be close to one (within 1 / 10^4)
-		EP1_real_check = pipeparts.mkgeneric(pipeline, EP1_real_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-		EP1_imag_check = pipeparts.mkgeneric(pipeline, EP1_imag_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-		other_epics_check_list = [EP1_real_check, EP1_imag_check]
-
-		if num_dq_epics > 1:
-			other_epics_threshold += 2.0
-			EP2_real_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP2_real"], 1.0 / EP2_real)
-			EP2_imag_check = pipeparts.mkaudioamplify(pipeline, head_dict["EP2_imag"], 1.0 / EP2_imag)
-
+	# Next, check the remaining EPICS that are combinations of D, A, and C: EP1, EP2, EP15, EP22
+	if(len(misc_epics_dict)):
+		misc_epics_check_list = []
+		for EP_key in misc_epics_dict:
+			misc_epics_check = pipeparts.mkaudioamplify(pipeline, misc_epics_dict[EP_key][0], 1.0 / misc_epics_dict[EP_key][1])
 			# The above values should be close to one (within 1 / 10^4)
-			EP2_real_check = pipeparts.mkgeneric(pipeline, EP2_real_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-			EP2_imag_check = pipeparts.mkgeneric(pipeline, EP2_imag_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
-			other_epics_check_list.extend((EP2_real_check, EP2_imag_check))
-
-		other_epics_check = calibration_parts.mkadder(pipeline, tuple(other_epics_check_list))
-		other_epics_bit = pipeparts.mkbitvectorgen(pipeline, other_epics_check, bit_vector = pow(2,24), threshold = other_epics_threshold)
-		other_epics_bit = pipeparts.mkgeneric(pipeline, other_epics_bit, "lal_logicalundersample", required_on = pow(2,24), status_out = pow(2,24))
-		epics_bits = pipeparts.mkcapsfilter(pipeline, other_epics_bit, calibstate_caps)
-
-		# Add the EPICS bits together
-		if num_dq_epics > 6:
-			# There are EPICS for D, A, and C
-			epics_bits = calibration_parts.mkadder(pipeline, calibration_parts.list_srcs(pipeline, epics_bits, A_epics_bit, C_epics_bit, D_epics_bit))
-		elif num_dq_epics > 5:
-			# There are EPICS for A and C
-			epics_bits = calibration_parts.mkadder(pipeline, calibration_parts.list_srcs(pipeline, epics_bits, A_epics_bit, C_epics_bit))
-		elif num_dq_epics > 2:
-			# There are EPICS for A
-			epics_bits = calibration_parts.mkadder(pipeline, calibration_parts.list_srcs(pipeline, epics_bits, A_epics_bit))
+			misc_epics_check = pipeparts.mkgeneric(pipeline, misc_epics_check, "lal_insertgap", bad_data_intervals = [0.9999, 1.0001], replace_value = 0.0, insert_gap = False)
+			misc_epics_check_list.append(misc_epics_check)
+		misc_epics_check = calibration_parts.mkadder(pipeline, tuple(misc_epics_check_list))
+		misc_epics_bit = pipeparts.mkbitvectorgen(pipeline, misc_epics_check, bit_vector = pow(2,24), threshold = misc_epics_threshold)
+		misc_epics_bit = pipeparts.mkgeneric(pipeline, misc_epics_bit, "lal_logicalundersample", required_on = pow(2,24), status_out = pow(2,24))
+		misc_epics_bit = pipeparts.mkcapsfilter(pipeline, misc_epics_bit, calibstate_caps)
+		epics_bits_list.append(misc_epics_bit)
 
 	#
 	# COMBINE ALL BITS TO MAKE GDS-CALIB_STATE_VECTOR
@@ -1761,10 +2028,10 @@ if compute_calib_statevector:
 		all_bits_list.append(coherence_bits)
 	if compute_kappatst and not apply_kappatst and not apply_complex_kappatst:
 		all_bits_list.append(ktstSmoothInRange)
-	if (compute_kappap or compute_kappapu) and not (apply_kappap or apply_complex_kappap or apply_kappapu or apply_complex_kappapu):
-		all_bits_list.append(kpSmoothInRange)
-	if compute_kappau and not (apply_kappau or apply_complex_kappau):
-		all_bits_list.append(kuSmoothInRange)
+	if (compute_kappapum or compute_kappapu) and not (apply_kappapum or apply_complex_kappapum or apply_kappapu or apply_complex_kappapu):
+		all_bits_list.append(kpumSmoothInRange)
+	if compute_kappauim and not (apply_kappauim or apply_complex_kappauim):
+		all_bits_list.append(kuimSmoothInRange)
 	if compute_kappac and not apply_kappac:
 		all_bits_list.append(kcSmoothInRange)
 	if compute_fcc and not apply_fcc:
@@ -1773,8 +2040,7 @@ if compute_calib_statevector:
 		all_bits_list.append(fsSmoothInRange)
 	if compute_srcq and not apply_srcq:
 		all_bits_list.append(srcQSmoothInRange)
-	if num_dq_epics > 0:
-		all_bits_list.append(epics_bits)
+	all_bits_list.extend(epics_bits_list)
 
 	calibstatevector = calibration_parts.mkadder(pipeline, tuple(all_bits_list))
 	calibstatevector = pipeparts.mkprogressreport(pipeline, calibstatevector, "progress_calibstatevec_%s" % instrument)
@@ -1808,13 +2074,13 @@ if remove_cal_lines:
 	# Now deal with the ESD line
 	if remove_esd_act_line:
 		# Make sure we have demodulated the ESD excitation channel at the ~30 Hz ESD line
-		if not compute_kappac and not compute_fcc and not compute_kappatst and not compute_kappapu and not compute_srcq and not compute_fs:
+		if not compute_kappac and not compute_fcc and not compute_kappatst and not compute_kappapu and not compute_kappapum and not compute_kappauim and not compute_srcq and not compute_fs:
 			tstexc_at_esd_act_freq = calibration_parts.demodulate(pipeline, tstexc, esd_act_line_freq, td, compute_factors_sr, demodulation_filter_time, filter_latency_factor)
 		if factors_from_filters_file:
 			esd_act_line = calibration_parts.complex_audioamplify(pipeline, tstexc_at_esd_act_freq, EP10_real, EP10_imag)
 		else:
 			# EP10 was read from the frames
-			EP10 = calibration_parts.merge_into_complex(pipeline, head_dict["EP10_real"], head_dict["EP10_imag"])
+			EP10 = calibration_parts.merge_into_complex(pipeline, A_epics_dict["EP10_real"], A_epics_dict["EP10_imag"])
 			esd_act_line = calibration_parts.mkmultiplier(pipeline, calibration_parts.list_srcs(pipeline, tstexc_at_esd_act_freq, EP10))
 		# Reconstruct a calibrated ESD injection at the ~30 Hz ESD line
 		if apply_complex_kappatst:
@@ -1932,25 +2198,6 @@ if compute_calib_statevector and (remove_cal_lines or remove_power_lines or witn
 
 record_kappa_caps = "audio/x-raw, format=F32LE, rate=%d" % record_factors_sr
 
-# Resample the \kappa_pu channels at the specified recording sample rate and change them to single precision channels
-if compute_kappapu:
-
-	kpuRout = pipeparts.mkaudioconvert(pipeline, smooth_kpuRtee)
-	kpuRout = calibration_parts.mkresample(pipeline, kpuRout, 1, False, record_kappa_caps)
-	kpuRout = pipeparts.mkprogressreport(pipeline, kpuRout, "progress_kappa_pu_real_%s" % instrument)
-
-	kpuIout = pipeparts.mkaudioconvert(pipeline, smooth_kpuItee)
-	kpuIout = calibration_parts.mkresample(pipeline, kpuIout, 1, False, record_kappa_caps)
-	kpuIout = pipeparts.mkprogressreport(pipeline, kpuIout, "progress_kappa_pu_imag_%s" % instrument)
-
-	smooth_kpuR_nogate = pipeparts.mkaudioconvert(pipeline, smooth_kpuR_nogate)
-	smooth_kpuR_nogate = calibration_parts.mkresample(pipeline, smooth_kpuR_nogate, 1, False, record_kappa_caps)
-	smooth_kpuR_nogate = pipeparts.mkprogressreport(pipeline, smooth_kpuR_nogate, "progress_kappa_pu_real_nogate_%s" % instrument)
-
-	smooth_kpuI_nogate = pipeparts.mkaudioconvert(pipeline, smooth_kpuI_nogate)
-	smooth_kpuI_nogate = calibration_parts.mkresample(pipeline, smooth_kpuI_nogate, 1, False, record_kappa_caps)
-	smooth_kpuI_nogate = pipeparts.mkprogressreport(pipeline, smooth_kpuI_nogate, "progress_kappa_pu_imag_nogate_%s" % instrument)
-
 # Resample the \kappa_tst channels at the specified recording sample rate and change them to single precision channels
 if compute_kappatst:
 
@@ -1970,6 +2217,63 @@ if compute_kappatst:
 	smooth_ktstI_nogate = calibration_parts.mkresample(pipeline, smooth_ktstI_nogate, 1, False, record_kappa_caps)
 	smooth_ktstI_nogate = pipeparts.mkprogressreport(pipeline, smooth_ktstI_nogate, "progress_kappa_tst_imag_nogate_%s" % instrument)
 
+# Resample the \kappa_pum channels at the specified recording sample rate and change them to single precision channels
+if compute_kappapum:
+
+	kpumRout = pipeparts.mkaudioconvert(pipeline, smooth_kpumRtee)
+	kpumRout = calibration_parts.mkresample(pipeline, kpumRout, 1, False, record_kappa_caps)
+	kpumRout = pipeparts.mkprogressreport(pipeline, kpumRout, "progress_kappa_pum_real_%s" % instrument)
+
+	kpumIout = pipeparts.mkaudioconvert(pipeline, smooth_kpumItee)
+	kpumIout = calibration_parts.mkresample(pipeline, kpumIout, 1, False, record_kappa_caps)
+	kpumIout = pipeparts.mkprogressreport(pipeline, kpumIout, "progress_kappa_pum_imag_%s" % instrument)
+
+	smooth_kpumR_nogate = pipeparts.mkaudioconvert(pipeline, smooth_kpumR_nogate)
+	smooth_kpumR_nogate = calibration_parts.mkresample(pipeline, smooth_kpumR_nogate, 1, False, record_kappa_caps)
+	smooth_kpumR_nogate = pipeparts.mkprogressreport(pipeline, smooth_kpumR_nogate, "progress_kappa_pum_real_nogate_%s" % instrument)
+
+	smooth_kpumI_nogate = pipeparts.mkaudioconvert(pipeline, smooth_kpumI_nogate)
+	smooth_kpumI_nogate = calibration_parts.mkresample(pipeline, smooth_kpumI_nogate, 1, False, record_kappa_caps)
+	smooth_kpumI_nogate = pipeparts.mkprogressreport(pipeline, smooth_kpumI_nogate, "progress_kappa_pum_imag_nogate_%s" % instrument)
+
+# Resample the \kappa_uim channels at the specified recording sample rate and change them to single precision channels
+if compute_kappauim:
+
+	kuimRout = pipeparts.mkaudioconvert(pipeline, smooth_kuimRtee)
+	kuimRout = calibration_parts.mkresample(pipeline, kuimRout, 1, False, record_kappa_caps)
+	kuimRout = pipeparts.mkprogressreport(pipeline, kuimRout, "progress_kappa_uim_real_%s" % instrument)
+
+	kuimIout = pipeparts.mkaudioconvert(pipeline, smooth_kuimItee)
+	kuimIout = calibration_parts.mkresample(pipeline, kuimIout, 1, False, record_kappa_caps)
+	kuimIout = pipeparts.mkprogressreport(pipeline, kuimIout, "progress_kappa_uim_imag_%s" % instrument)
+
+	smooth_kuimR_nogate = pipeparts.mkaudioconvert(pipeline, smooth_kuimR_nogate)
+	smooth_kuimR_nogate = calibration_parts.mkresample(pipeline, smooth_kuimR_nogate, 1, False, record_kappa_caps)
+	smooth_kuimR_nogate = pipeparts.mkprogressreport(pipeline, smooth_kuimR_nogate, "progress_kappa_uim_real_nogate_%s" % instrument)
+
+	smooth_kuimI_nogate = pipeparts.mkaudioconvert(pipeline, smooth_kuimI_nogate)
+	smooth_kuimI_nogate = calibration_parts.mkresample(pipeline, smooth_kuimI_nogate, 1, False, record_kappa_caps)
+	smooth_kuimI_nogate = pipeparts.mkprogressreport(pipeline, smooth_kuimI_nogate, "progress_kappa_uim_imag_nogate_%s" % instrument)
+
+# Resample the \kappa_pu channels at the specified recording sample rate and change them to single precision channels
+if compute_kappapu:
+
+	kpuRout = pipeparts.mkaudioconvert(pipeline, smooth_kpuRtee)
+	kpuRout = calibration_parts.mkresample(pipeline, kpuRout, 1, False, record_kappa_caps)
+	kpuRout = pipeparts.mkprogressreport(pipeline, kpuRout, "progress_kappa_pu_real_%s" % instrument)
+
+	kpuIout = pipeparts.mkaudioconvert(pipeline, smooth_kpuItee)
+	kpuIout = calibration_parts.mkresample(pipeline, kpuIout, 1, False, record_kappa_caps)
+	kpuIout = pipeparts.mkprogressreport(pipeline, kpuIout, "progress_kappa_pu_imag_%s" % instrument)
+
+	smooth_kpuR_nogate = pipeparts.mkaudioconvert(pipeline, smooth_kpuR_nogate)
+	smooth_kpuR_nogate = calibration_parts.mkresample(pipeline, smooth_kpuR_nogate, 1, False, record_kappa_caps)
+	smooth_kpuR_nogate = pipeparts.mkprogressreport(pipeline, smooth_kpuR_nogate, "progress_kappa_pu_real_nogate_%s" % instrument)
+
+	smooth_kpuI_nogate = pipeparts.mkaudioconvert(pipeline, smooth_kpuI_nogate)
+	smooth_kpuI_nogate = calibration_parts.mkresample(pipeline, smooth_kpuI_nogate, 1, False, record_kappa_caps)
+	smooth_kpuI_nogate = pipeparts.mkprogressreport(pipeline, smooth_kpuI_nogate, "progress_kappa_pu_imag_nogate_%s" % instrument)
+
 # Resample the \kappa_c channels at the specified recording sample rate and change it to a single precision channel
 if compute_kappac:
 	kcout = pipeparts.mkaudioconvert(pipeline, smooth_kctee)
@@ -2033,14 +2337,28 @@ calibration_parts.mkqueue(pipeline, strain).get_static_pad("src").link(mux.get_r
 if remove_cal_lines or remove_power_lines or witness_channel_list is not None:
 	calibration_parts.mkqueue(pipeline, clean_strain).get_static_pad("src").link(mux.get_request_pad("%s:%sCALIB_STRAIN_CLEAN%s" % (instrument, chan_prefix, chan_suffix)))
 
-# Link the real and imaginary parts of \kappa_tst to the muxer
+# Link the real and imaginary parts of kappa_tst to the muxer
 if compute_kappatst:
 	calibration_parts.mkqueue(pipeline, ktstRout).get_static_pad("src").link(mux.get_request_pad("%s:%sCALIB_KAPPA_TST_REAL%s" % (instrument, chan_prefix, chan_suffix)))
 	calibration_parts.mkqueue(pipeline, ktstIout).get_static_pad("src").link(mux.get_request_pad("%s:%sCALIB_KAPPA_TST_IMAGINARY%s" % (instrument, chan_prefix, chan_suffix)))
 	calibration_parts.mkqueue(pipeline, smooth_ktstR_nogate).get_static_pad("src").link(mux.get_request_pad("%s:%sCALIB_KAPPA_TST_REAL_NOGATE%s" % (instrument, chan_prefix, chan_suffix)))
 	calibration_parts.mkqueue(pipeline, smooth_ktstI_nogate).get_static_pad("src").link(mux.get_request_pad("%s:%sCALIB_KAPPA_TST_IMAGINARY_NOGATE%s" % (instrument, chan_prefix, chan_suffix)))
 
-# Link the real and imaginary parts of \kappa_pu to the muxer
+# Link the real and imaginary parts of kappa_pum to the muxer
+if compute_kappapum:
+	calibration_parts.mkqueue(pipeline, kpumRout).get_static_pad("src").link(mux.get_request_pad("%s:%sCALIB_KAPPA_PUM_REAL%s" % (instrument, chan_prefix, chan_suffix)))
+	calibration_parts.mkqueue(pipeline, kpumIout).get_static_pad("src").link(mux.get_request_pad("%s:%sCALIB_KAPPA_PUM_IMAGINARY%s" % (instrument, chan_prefix, chan_suffix)))
+	calibration_parts.mkqueue(pipeline, smooth_kpumR_nogate).get_static_pad("src").link(mux.get_request_pad("%s:%sCALIB_KAPPA_PUM_REAL_NOGATE%s" % (instrument, chan_prefix, chan_suffix)))
+	calibration_parts.mkqueue(pipeline, smooth_kpumI_nogate).get_static_pad("src").link(mux.get_request_pad("%s:%sCALIB_KAPPA_PUM_IMAGINARY_NOGATE%s" % (instrument, chan_prefix, chan_suffix)))
+
+# Link the real and imaginary parts of kappa_uim to the muxer
+if compute_kappauim:
+        calibration_parts.mkqueue(pipeline, kuimRout).get_static_pad("src").link(mux.get_request_pad("%s:%sCALIB_KAPPA_UIM_REAL%s" % (instrument, chan_prefix, chan_suffix)))
+        calibration_parts.mkqueue(pipeline, kuimIout).get_static_pad("src").link(mux.get_request_pad("%s:%sCALIB_KAPPA_UIM_IMAGINARY%s" % (instrument, chan_prefix, chan_suffix)))
+        calibration_parts.mkqueue(pipeline, smooth_kuimR_nogate).get_static_pad("src").link(mux.get_request_pad("%s:%sCALIB_KAPPA_UIM_REAL_NOGATE%s" % (instrument, chan_prefix, chan_suffix)))
+        calibration_parts.mkqueue(pipeline, smooth_kuimI_nogate).get_static_pad("src").link(mux.get_request_pad("%s:%sCALIB_KAPPA_UIM_IMAGINARY_NOGATE%s" % (instrument, chan_prefix, chan_suffix)))
+
+# Link the real and imaginary parts of kappa_pu to the muxer
 if compute_kappapu:
 	calibration_parts.mkqueue(pipeline, kpuRout).get_static_pad("src").link(mux.get_request_pad("%s:%sCALIB_KAPPA_PU_REAL%s" % (instrument, chan_prefix, chan_suffix)))
 	calibration_parts.mkqueue(pipeline, kpuIout).get_static_pad("src").link(mux.get_request_pad("%s:%sCALIB_KAPPA_PU_IMAGINARY%s" % (instrument, chan_prefix, chan_suffix)))
diff --git a/gstlal-calibration/config_files/H1DCS_AllCorrections_Cleaning.ini b/gstlal-calibration/config_files/H1DCS_AllCorrections_Cleaning.ini
index e9aad81253..c44697a5fe 100644
--- a/gstlal-calibration/config_files/H1DCS_AllCorrections_Cleaning.ini
+++ b/gstlal-calibration/config_files/H1DCS_AllCorrections_Cleaning.ini
@@ -9,7 +9,7 @@ SkipBadFiles: No
 ############################################
 # If reading from frames use these options #
 ############################################
-FrameCache: easy_raw_frames.cache
+FrameCache: H1_raw_frames.cache
 ###################################################
 # If reading from shared memory use these options #
 ###################################################
@@ -58,26 +58,37 @@ ComputeKappaTST: Yes
 ApplyKappaTST: No
 # Set this to have the \kappa_tst factors filter the actuation chain with an adaptive filter that corrects for both magnitude and phase errors.
 ApplyComplexKappaTST: Yes
+
 ComputeKappaPU: Yes
 ApplyKappaPU: No
 # Set this to have the \kappa_pu factors the actuation chain with an adaptive filter that corrects for both magnitude and phase errors
 ApplyComplexKappaPU: Yes
-ComputeKappaP: No
-ApplyKappaP: No
+
+ComputeKappaPUM: No
+ApplyKappaPUM: No
 # Set this to have the \kappa_p factors the actuation chain with an adaptive filter that corrects for both magnitude and phase errors.
-ApplyComplexKappaP: No
-ComputeKappaU: No
-ApplyKappaU: No
+ApplyComplexKappaPUM: No
+
+ComputeKappaUIM: No
+ApplyKappaUIM: No
 # Set this to have the \kappa_u factors the actuation chain with an adaptive filter that corrects for both magnitude and phase errors.
-ApplyComplexKappaU: No
+ApplyComplexKappaUIM: No
+
+# Set this to use a calibration line injected using the UIM stage of actuation to compute \kappa_U. Otherwise, the DARM_ctrl line is used.
+UseUIMLine: No
+
 ComputeKappaC: Yes
 ApplyKappaC: Yes
+
 ComputeFcc: Yes
 ApplyFcc: Yes
+
 ComputeSRCQ: Yes
 ApplySRCQ: Yes
+
 ComputeFs: Yes
 ApplyFs: Yes
+
 ###########################################
 # Options related to the coherence gating #
 ###########################################
@@ -111,12 +122,12 @@ FccFilterTaperLength: 32768
 ############################
 ExpectedKappaTSTReal: 1.0
 ExpectedKappaTSTImag: 0.0
+ExpectedKappaPUMReal: 1.0
+ExpectedKappaPUMImag: 0.0
+ExpectedKappaUIMReal: 1.0
+ExpectedKappaUIMImag: 0.0
 ExpectedKappaPUReal: 1.0
 ExpectedKappaPUImag: 0.0
-ExpectedKappaPReal: 1.0
-ExpectedKappaPImag: 0.0
-ExpectedKappaUReal: 1.0
-ExpectedKappaUImag: 0.0
 ExpectedKappaC: 1.0
 ExpectedFcc: 360.0
 ExpectedFs: 6.91
@@ -128,10 +139,10 @@ KappaTSTRealVar: 0.2
 KappaTSTImagVar: 0.2
 KappaPURealVar: 0.2
 KappaPUImagVar: 0.2
-KappaPRealVar: 0.2
-KappaPImagVar: 0.2
-KappaURealVar: 0.2
-KappaUImagVar: 0.2
+KappaPUMRealVar: 0.2
+KappaPUMImagVar: 0.2
+KappaUIMRealVar: 0.2
+KappaUIMImagVar: 0.2
 KappaCVar: 0.2
 FccVar: 50.0
 FsVar: 5.0
@@ -177,6 +188,8 @@ InputDQChannel: ODC-MASTER_CHANNEL_OUT_DQ
 ##################################
 DARMExcChannel: CAL-CS_LINE_SUM_DQ
 TSTExcChannel: SUS-ETMY_L3_CAL_LINE_OUT_DQ
+PUMExcChannel: SUS-ETMY_L2_CAL_LINE_OUT_DQ
+UIMExcChannel: SUS-ETMY_L1_CAL_LINE_OUT_DQ
 PCALChannel: CAL-PCALY_RX_PD_OUT_DQ
 #######################################
 # Coherence Uncertainty Channel Names #
@@ -223,6 +236,26 @@ EP13RealChannel: CAL-CS_TDEP_PCALY_LINE4_REF_A_TST_REAL
 EP13ImagChannel: CAL-CS_TDEP_PCALY_LINE4_REF_A_TST_IMAG
 EP14RealChannel: CAL-CS_TDEP_PCALY_LINE4_REF_A_USUM_REAL
 EP14ImagChannel: CAL-CS_TDEP_PCALY_LINE4_REF_A_USUM_IMAG
+EP15RealChannel: CAL-CS_TDEP_REF_INVA_CLGRATIO_PUM_REAL
+EP15Imagchannel: CAL-CS_TDEP_REF_INVA_CLGRATIO_PUM_IMAG
+EP16RealChannel: CAL-CS_TDEP_DARM_LINE1_REF_A_UIM_INV_REAL
+EP16ImagChannel: CAL-CS_TDEP_DARM_LINE1_REF_A_UIM_INV_IMAG
+EP17RealChannel: CAL-CS_TDEP_DARM_LINE1_REF_A_PUM_REAL
+EP17ImagChannel: CAL-CS_TDEP_DARM_LINE1_REF_A_PUM_IMAG
+EP18RealChannel: CAL-CS_TDEP_PCALY_LINE2_REF_A_PUM_REAL
+EP18ImagChannel: CAL-CS_TDEP_PCALY_LINE2_REF_A_PUM_IMAG
+EP19RealChannel: CAL-CS_TDEP_PCALY_LINE2_REF_A_UIM_REAL
+EP19ImagChannel: CAL-CS_TDEP_PCALY_LINE2_REF_A_UIM_IMAG
+EP20RealChannel: CAL-CS_TDEP_PCALY_LINE4_REF_A_PUM_REAL
+EP20ImagChannel: CAL-CS_TDEP_PCALY_LINE4_REF_A_PUM_IMAG
+EP21RealChannel: CAL-CS_TDEP_PCALY_LINE4_REF_A_UIM_REAL
+EP21ImagChannel: CAL-CS_TDEP_PCALY_LINE4_REF_A_UIM_IMAG
+EP22RealChannel: CAL-CS_TDEP_REF_INVA_CLGRATIO_UIM_REAL
+EP22ImagChannel: CAL-CS_TDEP_REF_INVA_CLGRATIO_UIM_IMAG
+EP23RealChannel: CAL-CS_TDEP_PUM_LINE1_REF_A_PUM_NOLOCK_REAL
+EP23ImagChannel: CAL-CS_TDEP_PUM_LINE1_REF_A_PUM_NOLOCK_IMAG
+EP24RealChannel: CAL-CS_TDEP_UIM_LINE1_REF_A_UIM_NOLOCK_REAL
+EP24ImagChannel: CAL-CS_TDEP_UIM_LINE1_REF_A_UIM_NOLOCK_IMAG
 
 [SampleRates]
 # Sample rate at which to compute h(t)
@@ -236,6 +269,10 @@ CtrlSR: 16384
 ODCSR: 16384
 # Sample rate of TST excitation channel
 TSTExcSR: 512
+# Sample rate of PUM excitation channel
+PUMExcSR: 512
+# Sample rate of UIM excitation channel
+UIMExcSR: 512
 # Sample rate of coherence channels
 CohSR: 16
 # Sample rate for the EPICS reference channels
diff --git a/gstlal-calibration/config_files/H1GDS_LowLatency_AllCorrections_Cleaning.ini b/gstlal-calibration/config_files/H1GDS_LowLatency_AllCorrections_Cleaning.ini
index 60081bf20a..0958b21776 100644
--- a/gstlal-calibration/config_files/H1GDS_LowLatency_AllCorrections_Cleaning.ini
+++ b/gstlal-calibration/config_files/H1GDS_LowLatency_AllCorrections_Cleaning.ini
@@ -9,7 +9,7 @@ SkipBadFiles: No
 ############################################
 # If reading from frames use these options #
 ############################################
-FrameCache: easy_raw_frames.cache
+FrameCache: H1_raw_frames.cache
 ###################################################
 # If reading from shared memory use these options #
 ###################################################
@@ -58,26 +58,37 @@ ComputeKappaTST: Yes
 ApplyKappaTST: No
 # Set this to have the \kappa_tst factors filter the actuation chain with an adaptive filter that corrects for both magnitude and phase errors.
 ApplyComplexKappaTST: Yes
+
 ComputeKappaPU: Yes
 ApplyKappaPU: No
 # Set this to have the \kappa_pu factors the actuation chain with an adaptive filter that corrects for both magnitude and phase errors
 ApplyComplexKappaPU: Yes
-ComputeKappaP: No
-ApplyKappaP: No
+
+ComputeKappaPUM: No
+ApplyKappaPUM: No
 # Set this to have the \kappa_p factors the actuation chain with an adaptive filter that corrects for both magnitude and phase errors.
-ApplyComplexKappaP: No
-ComputeKappaU: No
-ApplyKappaU: No
+ApplyComplexKappaPUM: No
+
+ComputeKappaUIM: No
+ApplyKappaUIM: No
 # Set this to have the \kappa_u factors the actuation chain with an adaptive filter that corrects for both magnitude and phase errors.
-ApplyComplexKappaU: No
+ApplyComplexKappaUIM: No
+
+# Set this to use a calibration line injected using the UIM stage of actuation to compute \kappa_U. Otherwise, the DARM_ctrl line is used.
+UseUIMLine: No
+
 ComputeKappaC: Yes
 ApplyKappaC: Yes
+
 ComputeFcc: Yes
 ApplyFcc: Yes
+
 ComputeSRCQ: Yes
 ApplySRCQ: Yes
+
 ComputeFs: Yes
 ApplyFs: Yes
+
 ###########################################
 # Options related to the coherence gating #
 ###########################################
@@ -111,12 +122,12 @@ FccFilterTaperLength: 32768
 ############################
 ExpectedKappaTSTReal: 1.0
 ExpectedKappaTSTImag: 0.0
+ExpectedKappaPUMReal: 1.0
+ExpectedKappaPUMImag: 0.0
+ExpectedKappaUIMReal: 1.0
+ExpectedKappaUIMImag: 0.0
 ExpectedKappaPUReal: 1.0
 ExpectedKappaPUImag: 0.0
-ExpectedKappaPReal: 1.0
-ExpectedKappaPImag: 0.0
-ExpectedKappaUReal: 1.0
-ExpectedKappaUImag: 0.0
 ExpectedKappaC: 1.0
 ExpectedFcc: 360.0
 ExpectedFs: 6.91
@@ -128,10 +139,10 @@ KappaTSTRealVar: 0.2
 KappaTSTImagVar: 0.2
 KappaPURealVar: 0.2
 KappaPUImagVar: 0.2
-KappaPRealVar: 0.2
-KappaPImagVar: 0.2
-KappaURealVar: 0.2
-KappaUImagVar: 0.2
+KappaPUMRealVar: 0.2
+KappaPUMImagVar: 0.2
+KappaUIMRealVar: 0.2
+KappaUIMImagVar: 0.2
 KappaCVar: 0.2
 FccVar: 50.0
 FsVar: 5.0
@@ -177,6 +188,8 @@ InputDQChannel: ODC-MASTER_CHANNEL_OUT_DQ
 ##################################
 DARMExcChannel: CAL-CS_LINE_SUM_DQ
 TSTExcChannel: SUS-ETMY_L3_CAL_LINE_OUT_DQ
+PUMExcChannel: SUS-ETMY_L2_CAL_LINE_OUT_DQ 
+UIMExcChannel: SUS-ETMY_L1_CAL_LINE_OUT_DQ
 PCALChannel: CAL-PCALY_RX_PD_OUT_DQ
 #######################################
 # Coherence Uncertainty Channel Names #
@@ -223,6 +236,26 @@ EP13RealChannel: CAL-CS_TDEP_PCALY_LINE4_REF_A_TST_REAL
 EP13ImagChannel: CAL-CS_TDEP_PCALY_LINE4_REF_A_TST_IMAG
 EP14RealChannel: CAL-CS_TDEP_PCALY_LINE4_REF_A_USUM_REAL
 EP14ImagChannel: CAL-CS_TDEP_PCALY_LINE4_REF_A_USUM_IMAG
+EP15RealChannel: CAL-CS_TDEP_REF_INVA_CLGRATIO_PUM_REAL
+EP15Imagchannel: CAL-CS_TDEP_REF_INVA_CLGRATIO_PUM_IMAG
+EP16RealChannel: CAL-CS_TDEP_DARM_LINE1_REF_A_UIM_INV_REAL
+EP16ImagChannel: CAL-CS_TDEP_DARM_LINE1_REF_A_UIM_INV_IMAG
+EP17RealChannel: CAL-CS_TDEP_DARM_LINE1_REF_A_PUM_REAL
+EP17ImagChannel: CAL-CS_TDEP_DARM_LINE1_REF_A_PUM_IMAG
+EP18RealChannel: CAL-CS_TDEP_PCALY_LINE2_REF_A_PUM_REAL
+EP18ImagChannel: CAL-CS_TDEP_PCALY_LINE2_REF_A_PUM_IMAG
+EP19RealChannel: CAL-CS_TDEP_PCALY_LINE2_REF_A_UIM_REAL
+EP19ImagChannel: CAL-CS_TDEP_PCALY_LINE2_REF_A_UIM_IMAG
+EP20RealChannel: CAL-CS_TDEP_PCALY_LINE4_REF_A_PUM_REAL
+EP20ImagChannel: CAL-CS_TDEP_PCALY_LINE4_REF_A_PUM_IMAG
+EP21RealChannel: CAL-CS_TDEP_PCALY_LINE4_REF_A_UIM_REAL
+EP21ImagChannel: CAL-CS_TDEP_PCALY_LINE4_REF_A_UIM_IMAG
+EP22RealChannel: CAL-CS_TDEP_REF_INVA_CLGRATIO_UIM_REAL
+EP22ImagChannel: CAL-CS_TDEP_REF_INVA_CLGRATIO_UIM_IMAG
+EP23RealChannel: CAL-CS_TDEP_PUM_LINE1_REF_A_PUM_NOLOCK_REAL
+EP23ImagChannel: CAL-CS_TDEP_PUM_LINE1_REF_A_PUM_NOLOCK_IMAG
+EP24RealChannel: CAL-CS_TDEP_UIM_LINE1_REF_A_UIM_NOLOCK_REAL
+EP24ImagChannel: CAL-CS_TDEP_UIM_LINE1_REF_A_UIM_NOLOCK_IMAG
 
 [SampleRates]
 # Sample rate at which to compute h(t)
@@ -236,6 +269,10 @@ CtrlSR: 4096
 ODCSR: 16384
 # Sample rate of TST excitation channel
 TSTExcSR: 512
+# Sample rate of PUM excitation channel
+PUMExcSR: 512
+# Sample rate of UIM excitation channel
+UIMExcSR: 512
 # Sample rate of coherence channels
 CohSR: 16
 # Sample rate for the EPICS reference channels
diff --git a/gstlal-calibration/config_files/gstlal_compute_strain_config_example.ini b/gstlal-calibration/config_files/gstlal_compute_strain_config_example.ini
index 47de4f714f..94de30cae6 100644
--- a/gstlal-calibration/config_files/gstlal_compute_strain_config_example.ini
+++ b/gstlal-calibration/config_files/gstlal_compute_strain_config_example.ini
@@ -64,15 +64,18 @@ ApplyKappaPU: No
 # Set this to have the \kappa_pu factors the actuation chain with an adaptive filter that corrects for both magnitude and phase errors
 ApplyComplexKappaPU: No
 
-ComputeKappaP: No
-ApplyKappaP: No
+ComputeKappaPUM: No
+ApplyKappaPUM: No
 # Set this to have the \kappa_p factors the actuation chain with an adaptive filter that corrects for both magnitude and phase errors.
-ApplyComplexKappaP: No
+ApplyComplexKappaPUM: No
 
-ComputeKappaU: No
-ApplyKappaU: No
+ComputeKappaUIM: No
+ApplyKappaUIM: No
 # Set this to have the \kappa_u factors the actuation chain with an adaptive filter that corrects for both magnitude and phase errors.
-ApplyComplexKappaU: No
+ApplyComplexKappaUIM: No
+
+# Set this to use a calibration line injected using the UIM stage of actuation to compute \kappa_U. Otherwise, the DARM_ctrl line is used.
+UseUIMLine: No
 
 ComputeKappaC: Yes
 ApplyKappaC: No
@@ -118,12 +121,12 @@ FccFilterTaperLength: 32768
 ############################
 ExpectedKappaTSTReal: 1.0
 ExpectedKappaTSTImag: 0.0
+ExpectedKappaPUMReal: 1.0
+ExpectedKappaPUMImag: 0.0
+ExpectedKappaUIMReal: 1.0
+ExpectedKappaUIMImag: 0.0
 ExpectedKappaPUReal: 1.0
 ExpectedKappaPUImag: 0.0
-ExpectedKappaPReal: 1.0
-ExpectedKappaPImag: 0.0
-ExpectedKappaUReal: 1.0
-ExpectedKappaUImag: 0.0
 ExpectedKappaC: 1.0
 ExpectedFcc: 360.0
 ExpectedFs: 8.0
@@ -135,10 +138,10 @@ KappaTSTRealVar: 0.2
 KappaTSTImagVar: 0.2
 KappaPURealVar: 0.2
 KappaPUImagVar: 0.2
-KappaPRealVar: 0.2
-KappaPImagVar: 0.2
-KappaURealVar: 0.2
-KappaUImagVar: 0.2
+KappaPUMRealVar: 0.2
+KappaPUMImagVar: 0.2
+KappaUIMRealVar: 0.2
+KappaUIMImagVar: 0.2
 KappaCVar: 0.2
 FccVar: 50.0
 FsVar: 10.0
@@ -184,6 +187,8 @@ InputDQChannel: ODC-MASTER_CHANNEL_OUT_DQ
 ##################################
 DARMExcChannel: CAL-CS_LINE_SUM_DQ
 TSTExcChannel: SUS-ETMY_L3_CAL_LINE_OUT_DQ
+PUMExcChannel: SUS-ETMY_L2_CAL_LINE_OUT_DQ 
+UIMExcChannel: SUS-ETMY_L1_CAL_LINE_OUT_DQ
 PCALChannel: CAL-PCALY_RX_PD_OUT_DQ
 #######################################
 # Coherence Uncertainty Channel Names #
@@ -230,6 +235,26 @@ EP13RealChannel: CAL-CS_TDEP_PCALY_LINE4_REF_A_TST_REAL
 EP13ImagChannel: CAL-CS_TDEP_PCALY_LINE4_REF_A_TST_IMAG
 EP14RealChannel: CAL-CS_TDEP_PCALY_LINE4_REF_A_USUM_REAL
 EP14ImagChannel: CAL-CS_TDEP_PCALY_LINE4_REF_A_USUM_IMAG
+EP15RealChannel: CAL-CS_TDEP_REF_INVA_CLGRATIO_PUM_REAL
+EP15Imagchannel: CAL-CS_TDEP_REF_INVA_CLGRATIO_PUM_IMAG
+EP16RealChannel: CAL-CS_TDEP_DARM_LINE1_REF_A_UIM_INV_REAL
+EP16ImagChannel: CAL-CS_TDEP_DARM_LINE1_REF_A_UIM_INV_IMAG
+EP17RealChannel: CAL-CS_TDEP_DARM_LINE1_REF_A_PUM_REAL
+EP17ImagChannel: CAL-CS_TDEP_DARM_LINE1_REF_A_PUM_IMAG
+EP18RealChannel: CAL-CS_TDEP_PCALY_LINE2_REF_A_PUM_REAL
+EP18ImagChannel: CAL-CS_TDEP_PCALY_LINE2_REF_A_PUM_IMAG
+EP19RealChannel: CAL-CS_TDEP_PCALY_LINE2_REF_A_UIM_REAL
+EP19ImagChannel: CAL-CS_TDEP_PCALY_LINE2_REF_A_UIM_IMAG
+EP20RealChannel: CAL-CS_TDEP_PCALY_LINE4_REF_A_PUM_REAL
+EP20ImagChannel: CAL-CS_TDEP_PCALY_LINE4_REF_A_PUM_IMAG
+EP21RealChannel: CAL-CS_TDEP_PCALY_LINE4_REF_A_UIM_REAL
+EP21ImagChannel: CAL-CS_TDEP_PCALY_LINE4_REF_A_UIM_IMAG
+EP22RealChannel: CAL-CS_TDEP_REF_INVA_CLGRATIO_UIM_REAL
+EP22ImagChannel: CAL-CS_TDEP_REF_INVA_CLGRATIO_UIM_IMAG
+EP23RealChannel: CAL-CS_TDEP_PUM_LINE1_REF_A_PUM_NOLOCK_REAL
+EP23ImagChannel: CAL-CS_TDEP_PUM_LINE1_REF_A_PUM_NOLOCK_IMAG
+EP24RealChannel: CAL-CS_TDEP_UIM_LINE1_REF_A_UIM_NOLOCK_REAL
+EP24ImagChannel: CAL-CS_TDEP_UIM_LINE1_REF_A_UIM_NOLOCK_IMAG
 
 [SampleRates]
 # Sample rate at which to compute h(t)
@@ -243,6 +268,10 @@ CtrlSR: 16384
 ODCSR: 16384
 # Sample rate of TST excitation channel
 TSTExcSR: 512
+# Sample rate of PUM excitation channel
+PUMExcSR: 512
+# Sample rate of UIM excitation channel
+UIMExcSR: 512
 # Sample rate of coherence channels
 CohSR: 16
 # Sample rate for the EPICS reference channels
@@ -295,6 +324,8 @@ PowerLinesTFAveragingTime: 128
 WitnessChannelFFTTime: 4.0
 # The number of ffts to take before averaging the witness -> h(t) transfer functions calculation. The average is taken after the ratio h(f) / witness(f).
 NumWitnessFFTs: 1800
+# Sets the minimum number of FFTs necessary to produce the first transfer functions and clean data after data flow starts.
+MinWitnessFFTs: 400
 # The length in seconds of the filters applied to the witness channels before subtracting from h(t)
 WitnessFIRLength: 0.5
 # The frequency resolution of the filters applied to the witness channels before subtracting from h(t). It can be advantageous to lower the frequency resolution in order to average over excess noise.
diff --git a/gstlal-calibration/python/calibration_parts.py b/gstlal-calibration/python/calibration_parts.py
index ef2fd92334..8c4222f076 100644
--- a/gstlal-calibration/python/calibration_parts.py
+++ b/gstlal-calibration/python/calibration_parts.py
@@ -554,7 +554,7 @@ def compute_kappatst_from_filters_file(pipeline, derrfesd, tstexcfesd, pcalfdarm
 
 	#	       
 	# \kappa_TST = ktstfac * (derrfesd/tstexcfesd) * (pcalfdarm/derrfdarm)
-	# ktstfac = -(1/A0fesd) * (C0fdarm/(1+G0fdarm)) * ((1+G0fesd)/C0fesd)
+	# ktstfac = EP1 = (1/A0fesd) * (C0fdarm/(1+G0fdarm)) * ((1+G0fesd)/C0fesd)
 	#
 
 	derrfdarminv = complex_inverse(pipeline, derrfdarm)
@@ -568,7 +568,7 @@ def compute_kappatst(pipeline, derrfesd, tstexcfesd, pcalfdarm, derrfdarm, ktstf
 
 	#	       
 	# \kappa_TST = ktstfac * (derrfesd/tstexcfesd) * (pcalfdarm/derrfdarm)
-	# ktstfac = -(1/A0fesd) * (C0fdarm/(1+G0fdarm)) * ((1+G0fesd)/C0fesd)
+	# ktstfac = EP1 = (1/A0fesd) * (C0fdarm/(1+G0fdarm)) * ((1+G0fesd)/C0fesd)
 	#
 
 	derrfdarminv = complex_inverse(pipeline, derrfdarm)
@@ -577,35 +577,112 @@ def compute_kappatst(pipeline, derrfesd, tstexcfesd, pcalfdarm, derrfdarm, ktstf
 
 	return ktst
 
-def compute_afctrl_from_filters_file(pipeline, derrfpu, excfpu, pcalfdarm, derrfdarm, afctrlfacR, afctrlfacI):
+def compute_kappapum_from_filters_file(pipeline, derrfpum, pumexcfpum, pcalfpcal, derrfpcal, kpumfacR, kpumfacI):
 
 	#
-	# A(f_ctrl) = -afctrlfac * (derrfpu/excfpu) * (pcalfdarm/derrfdarm)
-	# afctrlfac = C0fpcal/(1+G0fpcal) * (1+G0fctrl)/C0fctrl
+	# \kappa_PUM = kpumfac * [derr(fpum) / pumexc(fpum)] * [pcal(fpcal) / derr(fpcal)]
+	# kpumfac = EP15 = [1 / A_PUM0(fpum)] * [C0(fpcal) / (1 + G0(fpcal))] * [(1 + G0(fpum)) / C0(fpum)]
 	#
 
-	derrfdarminv = complex_inverse(pipeline, derrfdarm)
-	excfpuinv = complex_inverse(pipeline, excfpu)
-	afctrl = mkmultiplier(pipeline, list_srcs(pipeline, pcalfdarm, derrfdarminv, excfpuinv, derrfpu))
-	afctrl = complex_audioamplify(pipeline, afctrl, -1.0*afctrlfacR, -1.0*afctrlfacI)
+	pumexcfpuminv = complex_inverse(pipeline, pumexcfpum)
+	derrfpcalinv = complex_inverse(pipeline, derrfpcal)
+	kpum = mkmultiplier(pipeline, list_srcs(pipeline, derrfpum, pumexcfpuminv, pcalfpcal, derrfpcalinv))
+	kpum = complex_audioamplify(pipeline, kpum, kpumfacR, kpumfacI)
+
+	return kpum
+
+def compute_kappapum(pipeline, derrfpum, pumexcfpum, pcalfpcal, derrfpcal, kpumfac):
+
+	#
+	# \kappa_PUM = kpumfac * [derr(fpum) / pumexc(fpum)] * [pcal(fpcal) / derr(fpcal)]
+	# kpumfac = EP15 = [1 / A_PUM0(fpum)] * [C0(fpcal) / (1 + G0(fpcal))] * [(1 + G0(fpum)) / C0(fpum)]
+	#
+
+	pumexcfpuminv = complex_inverse(pipeline, pumexcfpum)
+	derrfpcalinv = complex_inverse(pipeline, derrfpcal)
+	kpum = mkmultiplier(pipeline, list_srcs(pipeline, kpumfac, derrfpum, pumexcfpuminv, pcalfpcal, derrfpcalinv))
+
+	return kpum
+
+def compute_afctrl_from_filters_file(pipeline, derrfdarm, excfdarm, pcalfpcal, derrfpcal, afctrlfacR, afctrlfacI):
+
+	#
+	# A(f_ctrl) = -afctrlfac * (derrfdarm/excfdarm) * (pcalfpcal/derrfpcal)
+	# afctrlfac = EP2 = C0fpcal/(1+G0fpcal) * (1+G0fctrl)/C0fctrl
+	#
+
+	derrfpcalinv = complex_inverse(pipeline, derrfpcal)
+	excfdarminv = complex_inverse(pipeline, excfdarm)
+	afctrl = mkmultiplier(pipeline, list_srcs(pipeline, pcalfpcal, derrfpcalinv, excfdarminv, derrfdarm))
+	afctrl = complex_audioamplify(pipeline, afctrl, -1.0 * afctrlfacR, -1.0 * afctrlfacI)
 
 	return afctrl
 	
 
-def compute_afctrl(pipeline, derrfpu, excfpu, pcalfdarm, derrfdarm, afctrlfac):
+def compute_afctrl(pipeline, derrfdarm, excfdarm, pcalfpcal, derrfpcal, afctrlfac):
 
 	#
-	# A(f_ctrl) = -afctrlfac * (derrfpu/excfpu) * (pcalfdarm/derrfdarm)
+	# A(f_ctrl) = -afctrlfac * (derrfdarm/excfdarm) * (pcalfpcal/derrfpcal)
 	# afctrlfac = EP2 = C0fpcal/(1+G0fpcal) * (1+G0fctrl)/C0fctrl
 	#
 
-	derrfdarminv = complex_inverse(pipeline, derrfdarm)
-	excfpuinv = complex_inverse(pipeline, excfpu)
-	afctrl = mkmultiplier(pipeline, list_srcs(pipeline, afctrlfac, pcalfdarm, derrfdarminv, excfpuinv, derrfpu))
+	derrfpcalinv = complex_inverse(pipeline, derrfpcal)
+	excfdarminv = complex_inverse(pipeline, excfdarm)
+	afctrl = mkmultiplier(pipeline, list_srcs(pipeline, afctrlfac, pcalfpcal, derrfpcalinv, excfdarminv, derrfdarm))
 	afctrl = complex_audioamplify(pipeline, afctrl, -1.0, 0.0)
 
 	return afctrl
 
+def compute_kappauim_from_filters_file(pipeline, EP16R, EP16I, afctrl, ktst, EP4R, EP4I, kpum, EP17R, EP17I):
+
+	#
+	# \kappa_uim = EP16 * (afctrl - ktst * EP4 - kpum * EP17)
+	#
+
+	kuim = complex_audioamplify(pipeline, mkadder(pipeline, list_srcs(pipeline, afctrl, complex_audioamplify(pipeline, ktst, -1.0 * EP4R, -1.0 * EP4I), complex_audioamplify(pipeline, kpum, -1.0 * EP17R, -1.0 * EP17I))), EP16R, EP16I)
+
+	return kuim
+
+def compute_kappauim(pipeline, EP16, afctrl, ktst, EP4, kpum, EP17):
+
+	#
+	# \kappa_uim = EP16 * (afctrl - ktst * EP4 - kpum * EP17)
+	#
+
+	ep4_kappatst = mkmultiplier(pipeline, list_srcs(pipeline, ktst, complex_audioamplify(pipeline, EP4, -1.0, 0.0)))
+	ep17_kappapum = mkmultiplier(pipeline, list_srcs(pipeline, kpum, complex_audioamplify(pipeline, EP17, -1.0, 0.0)))
+	kuim = mkadder(pipeline, list_srcs(pipeline, afctrl, ep4_kappatst, ep17_kappapum))
+	kuim = mkmultiplier(pipeline, list_srcs(pipeline, EP16, kuim))
+
+	return kuim
+
+def compute_kappauim_from_filters_file_uim_line(pipeline, derrfuim, uimexcfuim, pcalfpcal, derrfpcal, kuimfacR, kuimfacI):
+
+	#
+	# \kappa_UIM = kuimfac * [derr(fuim) / uimexc(fuim)] * [pcal(fpcal) / derr(fpcal)]
+	# kuimfac = EP22 = [1 / A_UIM0(fuim)] * [C0(fpcal) / (1 + G0(fpcal))] * [(1 + G0(fuim)) / C0(fuim)]
+	#
+
+	uimexcfuiminv = complex_inverse(pipeline, uimexcfuim)
+	derrfpcalinv = complex_inverse(pipeline, derrfpcal)
+	kuim = mkmultiplier(pipeline, list_srcs(pipeline, derrfuim, uimexcfuiminv, pcalfpcal, derrfpcalinv))
+	kuim = complex_audioamplify(pipeline, kuim, kuimfacR, kuimfacI)
+
+	return kuim
+
+def compute_kappauim_uim_line(pipeline, derrfuim, uimexcfuim, pcalfpcal, derrfpcal, kuimfac):
+
+	#
+	# \kappa_UIM = kuimfac * [derr(fuim) / uimexc(fuim)] * [pcal(fpcal) / derr(fpcal)]
+	# kuimfac = EP22 = [1 / A_UIM0(fuim)] * [C0(fpcal) / (1 + G0(fpcal))] * [(1 + G0(fuim)) / C0(fuim)]
+	#
+
+	uimexcfuiminv = complex_inverse(pipeline, uimexcfuim)
+	derrfpcalinv = complex_inverse(pipeline, derrfpcal)
+	kuim = mkmultiplier(pipeline, list_srcs(pipeline, kuimfac, derrfuim, uimexcfuiminv, pcalfpcal, derrfpcalinv))
+
+	return kuim
+
 def compute_kappapu_from_filters_file(pipeline, EP3R, EP3I, afctrl, ktst, EP4R, EP4I):
 
 	#
@@ -669,10 +746,28 @@ def compute_S_from_filters_file(pipeline, EP6R, EP6I, pcalfpcal2, derrfpcal2, EP
 	
 	return S
 
+def compute_S_from_filters_file_split_act(pipeline, EP6R, EP6I, pcalfpcal2, derrfpcal2, EP7R, EP7I, ktst, EP8R, EP8I, kpum, EP18R, EP18I, kuim, EP19R, EP19I):
+
+	#       
+	# S = (1 / EP6) * (pcalfpcal2 / derrfpcal2 - EP7 * (ktst * EP8 + kpum * EP18 + kuim * EP19))^(-1)
+	#
+
+	pcal_over_derr = complex_division(pipeline, pcalfpcal2, derrfpcal2)
+	ep8_ktst = complex_audioamplify(pipeline, ktst, EP8R, EP8I)
+	ep18_kpum = complex_audioamplify(pipeline, kpum, EP18R, EP18I)
+	ep19_kuim = complex_audioamplify(pipeline, kuim, EP19R, EP19I)
+	A_at_fpcal2 = mkadder(pipeline, list_srcs(pipeline, ep8_ktst, ep18_kpum, ep19_kuim))
+	DA_at_fpcal2 = complex_audioamplify(pipeline, A_at_fpcal2,  -1.0 * EP7R, -1.0 * EP7I)
+	Sinv = mkadder(pipeline, list_srcs(pipeline, pcal_over_derr, DA_at_fpcal2))
+	Sinv = complex_audioamplify(pipeline, Sinv, EP6R, EP6I)
+	S = complex_inverse(pipeline, Sinv)
+
+	return S
+
 def compute_S(pipeline, EP6, pcalfpcal2, derrfpcal2, EP7, ktst, EP8, kpu, EP9):
 
 	#	
-	# S = 1/EP6 * ( pcalfpcal2/derrfpcal2 - EP7*(ktst*EP8 + kpu*EP9) ) ^ (-1)
+	# S = 1/EP6 * ( pcalfpcal2/derrfpcal2 - EP7*(ktst*EP8 + kpum*EP9) ) ^ (-1)
 	#
 
 	pcal_over_derr = complex_division(pipeline, pcalfpcal2, derrfpcal2)
@@ -686,6 +781,24 @@ def compute_S(pipeline, EP6, pcalfpcal2, derrfpcal2, EP7, ktst, EP8, kpu, EP9):
 
 	return S
 
+def compute_S_split_act(pipeline, EP6, pcalfpcal2, derrfpcal2, EP7, ktst, EP8, kpum, EP18, kuim, EP19):
+
+	#       
+	# S = (1 / EP6) * (pcalfpcal2 / derrfpcal2 - EP7 * (ktst * EP8 + kpu * EP18 + kuim * EP19))^(-1)
+	#
+
+	pcal_over_derr = complex_division(pipeline, pcalfpcal2, derrfpcal2)
+	ep8_ktst = mkmultiplier(pipeline, list_srcs(pipeline, ktst, EP8))
+	ep18_kpum = mkmultiplier(pipeline, list_srcs(pipeline, kpum, EP18))
+	ep19_kuim = mkmultiplier(pipeline, list_srcs(pipeline, kuim, EP19))
+	A_at_fpcal2 = mkadder(pipeline, list_srcs(pipeline, ep8_ktst, ep18_kpum, ep19_puim))
+	DA_at_fpcal2 = mkmultiplier(pipeline, list_srcs(pipeline, complex_audioamplify(pipeline, EP7, -1.0, 0.0), A_at_fpcal2))
+	Sinv = mkadder(pipeline, list_srcs(pipeline, pcal_over_derr, DA_at_fpcal2))
+	Sinv = mkmultiplier(pipeline, list_srcs(pipeline, EP6, Sinv))
+	S = complex_inverse(pipeline, Sinv)
+
+	return S
+
 def compute_kappac(pipeline, SR, SI):
 
 	#
@@ -728,6 +841,29 @@ def compute_Xi_from_filters_file(pipeline, pcalfpcal4, darmfpcal4, fpcal4, EP11_
 
 	return Xi
 
+def compute_Xi_from_filters_file_split_act(pipeline, pcalfpcal4, darmfpcal4, fpcal4, EP11R, EP11I, EP12R, EP12I, EP13R, EP13I, EP20R, EP20I, EP21R, EP21I, ktst, kpum, kuim, kc, fcc):
+
+	#
+	# Xi = -1 + ((EP11 * kc) / (1 + i * f_src / f_cc)) * (pcalfpcal4 / derrfpcal4 - EP12 * (ktst * EP13 + kpum * EP20 + kuim * EP21))
+	#
+
+	Atst = complex_audioamplify(pipeline, ktst, EP13R, EP13I)
+	Apum = complex_audioamplify(pipeline, kpum, EP20R, EP20I)
+	Auim = complex_audioamplify(pipeline, kuim, EP21R, EP21I)
+	A = mkadder(pipeline, list_srcs(pipeline, Atst, Apum, Auim))
+	minusAD = complex_audioamplify(pipeline, A, -1.0 * EP12R, -1.0 * EP12I)
+	pcal_over_derr = complex_division(pipeline, pcalfpcal4, darmfpcal4)
+	pcal_over_derr_res = mkadder(pipeline, list_srcs(pipeline, pcal_over_derr, minusAD))
+	fpcal4_over_fcc = pipeparts.mkaudioamplify(pipeline, mkpow(pipeline, fcc, exponent = -1.0), fpcal4)
+	i_fpcal4_over_fcc = pipeparts.mktogglecomplex(pipeline, pipeparts.mkmatrixmixer(pipeline, fpcal4_over_fcc, matrix = [[0, 1]]))
+	i_fpcal4_over_fcc_plus_one = pipeparts.mkgeneric(pipeline, i_fpcal4_over_fcc, "lal_add_constant", value = 1.0)
+	i_fpcal4_over_fcc_plus_one_inv = complex_inverse(pipeline, i_fpcal4_over_fcc_plus_one)
+	kc_EP11 = pipeparts.mktogglecomplex(pipeline, pipeparts.mkmatrixmixer(pipeline, kc, matrix = [[EP11R, EP11I]]))
+	Xi_plus_one = mkmultiplier(pipeline, list_srcs(pipeline, kc_EP11, i_fpcal4_over_fcc_plus_one_inv, pcal_over_derr_res))
+	Xi = pipeparts.mkgeneric(pipeline, Xi_plus_one, "lal_add_constant", value = -1.0)
+
+	return Xi
+
 def compute_Xi(pipeline, pcalfpcal4, darmfpcal4, fpcal4, EP11, EP12, EP13, EP14, ktst, kpu, kc, fcc):
 
 	#
@@ -750,6 +886,29 @@ def compute_Xi(pipeline, pcalfpcal4, darmfpcal4, fpcal4, EP11, EP12, EP13, EP14,
 
 	return Xi
 
+def compute_Xi_split_act(pipeline, pcalfpcal4, darmfpcal4, fpcal4, EP11, EP12, EP13, EP20, EP21, ktst, kpum, kuim, kc, fcc):
+
+	#
+	# Xi = -1 + ((EP11 * kc) / (1 + i * f_src / f_cc)) * (pcalfpcal4 / derrfpcal4 - EP12 * (ktst * EP13 + kpum * EP20 + kuim * EP21))
+	#
+
+	complex_kc = pipeparts.mktogglecomplex(pipeline, pipeparts.mkmatrixmixer(pipeline, kc, matrix=[[1,0]]))
+	Atst = mkmultiplier(pipeline, list_srcs(pipeline, EP13, ktst))
+	Apum = mkmultiplier(pipeline, list_srcs(pipeline, EP20, kpum))
+	Auim = mkmultiplier(pipeline, list_srcs(pipeline, EP21, kuim))
+	A = mkadder(pipeline, list_srcs(pipeline, Atst, Apum, Auim))
+	minusAD = mkmultiplier(pipeline, list_srcs(pipeline, complex_audioamplify(pipeline, EP12, -1.0, 0.0), A))
+	pcal_over_derr = complex_division(pipeline, pcalfpcal4, darmfpcal4)
+	pcal_over_derr_res = mkadder(pipeline, list_srcs(pipeline, pcal_over_derr, minusAD))
+	fpcal4_over_fcc = pipeparts.mkaudioamplify(pipeline, mkpow(pipeline, fcc, exponent = -1.0), fpcal4)
+	i_fpcal4_over_fcc = pipeparts.mktogglecomplex(pipeline, pipeparts.mkmatrixmixer(pipeline, fpcal4_over_fcc, matrix = [[0, 1]]))
+	i_fpcal4_over_fcc_plus_one = pipeparts.mkgeneric(pipeline, i_fpcal4_over_fcc, "lal_add_constant", value = 1.0)
+	i_fpcal4_over_fcc_plus_one_inv = complex_inverse(pipeline, i_fpcal4_over_fcc_plus_one)
+	Xi_plus_one = mkmultiplier(pipeline, list_srcs(pipeline, EP11, complex_kc, i_fpcal4_over_fcc_plus_one_inv, pcal_over_derr_res))
+	Xi = pipeparts.mkgeneric(pipeline, Xi_plus_one, "lal_add_constant", value = -1.0)
+
+	return Xi
+
 def update_filter(filter_maker, arg, filter_taker, maker_prop_name, taker_prop_name):
 	firfilter = filter_maker.get_property(maker_prop_name)[::-1]
 	filter_taker.set_property(taker_prop_name, firfilter)
-- 
GitLab