From d47a0d70ffa145ad7f0baabd9bf81666f0653274 Mon Sep 17 00:00:00 2001
From: Aaron Viets <aaron.viets@ligo.org>
Date: Wed, 26 Apr 2017 14:41:00 -0500
Subject: [PATCH] lal_add_constant:  Now handles complex streams and multiple
 channels properly.

---
 gstlal-calibration/bin/gstlal_compute_strain  | 60 ++++++-------
 .../gst/lal/gstlal_add_constant.c             | 87 +++++++++++++++++--
 .../gst/lal/gstlal_add_constant.h             |  8 ++
 .../python/calibration_parts.py               |  6 +-
 4 files changed, 121 insertions(+), 40 deletions(-)

diff --git a/gstlal-calibration/bin/gstlal_compute_strain b/gstlal-calibration/bin/gstlal_compute_strain
index b5715e1a74..b24aa264a8 100644
--- a/gstlal-calibration/bin/gstlal_compute_strain
+++ b/gstlal-calibration/bin/gstlal_compute_strain
@@ -660,19 +660,19 @@ if options.data_source == "frames" and frame_segments is not None:
 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, compute_calib_factors_caps)
+		head_dict[key] = calibration_parts.mkresample(pipeline, head_dict[key], 0, False, compute_calib_factors_caps)
 
 if not options.no_coherence:
 	if not options.no_kappatst or not options.no_kappapu or not options.no_kappac or not options.no_fcc or not options.no_fs or not options.no_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, compute_calib_factors_caps)
+		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")
-		sus_coh = calibration_parts.mkresample(pipeline, sus_coh, 0, compute_calib_factors_caps)
+		sus_coh = calibration_parts.mkresample(pipeline, sus_coh, 0, False, compute_calib_factors_caps)
 		darm_coh = calibration_parts.caps_and_progress(pipeline, head_dict["darm_coh"], coh_caps, "darm_coh")
-		darm_coh = calibration_parts.mkresample(pipeline, darm_coh, 0, compute_calib_factors_caps)
+		darm_coh = calibration_parts.mkresample(pipeline, darm_coh, 0, False, compute_calib_factors_caps)
 	if not options.no_kappac or not options.no_fcc or not options.no_fs or not options.no_srcQ:
 		pcaly_line2_coh = calibration_parts.caps_and_progress(pipeline, head_dict["pcaly_line2_coh"], coh_caps, "pcaly_line2_coh")
-		pcaly_line2_coh = calibration_parts.mkresample(pipeline, pcaly_line2_coh, 0, compute_calib_factors_caps)
+		pcaly_line2_coh = calibration_parts.mkresample(pipeline, pcaly_line2_coh, 0, False, compute_calib_factors_caps)
 
 if not options.no_kappatst or not options.no_kappapu or not options.no_kappac or not options.no_fcc or not options.no_fs or not options.no_srcQ or (options.cancel_callines and cancel_esd_act_line):
 	tstexccaps = "audio/x-raw, format=F64LE, rate=%d" % options.tst_exc_sample_rate
@@ -1010,7 +1010,7 @@ if options.cancel_callines:
 		pcal_at_darm_act_freq = calibration_parts.demodulate(pipeline, pcal_tee, darm_act_line_freq, td, compute_calib_factors_complex_caps, integration_samples, pcal_corr_at_darm_act_freq_real, pcal_corr_at_darm_act_freq_imag)
 
 	# Reconstruct a calibrated (negative) pcal at only the ~30 Hz pcal line
-	pcaly_line1 = calibration_parts.mkresample(pipeline, pcal_at_darm_act_freq, 3, "audio/x-raw, format=Z128LE, rate=%d, channel-mask=(bitmask)0x0" % hoftsr)
+	pcaly_line1 = calibration_parts.mkresample(pipeline, pcal_at_darm_act_freq, 3, False, "audio/x-raw, format=Z128LE, rate=%d, channel-mask=(bitmask)0x0" % hoftsr)
 	pcaly_line1 = pipeparts.mkgeneric(pipeline, pcaly_line1, "lal_demodulate", line_frequency = -1.0 * darm_act_line_freq, prefactor_real = -2.0)
 	cancel_pcaly_line1, trash = calibration_parts.split_into_real(pipeline, pcaly_line1)
 	pipeparts.mkfakesink(pipeline, trash)
@@ -1019,7 +1019,7 @@ if options.cancel_callines:
 	if options.no_kappac and options.no_fcc and options.no_srcQ and options.no_fs:
 		pcal_at_opt_gain_freq = calibration_parts.demodulate(pipeline, pcaltee, opt_gain_fcc_line_freq, td, compute_calib_factors_complex_caps, integration_samples, pcal_corr_at_opt_gain_fcc_freq_real, pcal_corr_at_opt_gain_fcc_freq_imag)
 	# Reconstruct a calibrated (negative) pcal at only the ~300 Hz pcal line
-	pcaly_line2 = calibration_parts.mkresample(pipeline, pcal_at_opt_gain_freq, 3, "audio/x-raw, format=Z128LE, rate=%d, channel-mask=(bitmask)0x0" % hoftsr)
+	pcaly_line2 = calibration_parts.mkresample(pipeline, pcal_at_opt_gain_freq, 3, False, "audio/x-raw, format=Z128LE, rate=%d, channel-mask=(bitmask)0x0" % hoftsr)
 	pcaly_line2 = pipeparts.mkgeneric(pipeline, pcaly_line2, "lal_demodulate", line_frequency = -1.0 * opt_gain_fcc_line_freq, prefactor_real = -2.0)
 	cancel_pcaly_line2, trash = calibration_parts.split_into_real(pipeline, pcaly_line2)
 	pipeparts.mkfakesink(pipeline, trash)
@@ -1041,7 +1041,7 @@ if options.cancel_callines:
 		if options.apply_kappatst:
 			# Multiply by the real part of kappa_tst
 			esd_act_line = calibration_parts.mkmultiplier(pipeline, calibration_parts.list_srcs(pipeline, [esd_act_line, long_queue], [pipeparts.mktogglecomplex(pipeline, pipeparts.mkmatrixmixer(pipeline, smooth_ktstRtee, matrix=[[1.0, 0.0]])), short_queue]))
-		esd_act_line = calibration_parts.mkresample(pipeline, esd_act_line, 3, "audio/x-raw, format=Z128LE, rate=%d, channel-mask=(bitmask)0x0" % hoftsr)
+		esd_act_line = calibration_parts.mkresample(pipeline, esd_act_line, 3, False, "audio/x-raw, format=Z128LE, rate=%d, channel-mask=(bitmask)0x0" % hoftsr)
 		esd_act_line_cancel = pipeparts.mkgeneric(pipeline, esd_act_line, "lal_demodulate", line_frequency = -1.0 * esd_act_line_freq, prefactor_real = -2.0)
 		esd_act_line_cancel, trash = calibration_parts.split_into_real(pipeline, esd_act_line_cancel)
 		pipeparts.mkfakesink(pipeline, trash)
@@ -1052,7 +1052,7 @@ if options.cancel_callines:
 		# Demodulate pcal at the ~1kHz pcal line
 		pcaly_line3 = calibration_parts.demodulate(pipeline, pcaltee, high_pcal_line_freq, td, compute_calib_factors_complex_caps, integration_samples, pcal_corr_at_high_line_freq_real, pcal_corr_at_high_line_freq_imag)
 		# Reconstruct a calibrated (negative) pcal at only the ~1kHz pcal line
-		pcaly_line3 = calibration_parts.mkresample(pipeline, pcaly_line3, 3, "audio/x-raw, format=Z128LE, rate=%d, channel-mask=(bitmask)0x0" % hoftsr)
+		pcaly_line3 = calibration_parts.mkresample(pipeline, pcaly_line3, 3, False, "audio/x-raw, format=Z128LE, rate=%d, channel-mask=(bitmask)0x0" % hoftsr)
 		pcaly_line3 = pipeparts.mkgeneric(pipeline, pcaly_line3, "lal_demodulate", line_frequency = -1.0 * high_pcal_line_freq, prefactor_real = -2.0)
 		cancel_pcaly_line3, trash = calibration_parts.split_into_real(pipeline, pcaly_line3)
 		pipeparts.mkfakesink(pipeline, trash)
@@ -1063,7 +1063,7 @@ if options.cancel_callines:
 		# Demodulate pcal at the ~3kHz pcal line
 		pcaly_line4 = calibration_parts.demodulate(pipeline, pcaltee, roaming_pcal_line_freq, td, compute_calib_factors_complex_caps, integration_samples, pcal_corr_at_roaming_line_real, pcal_corr_at_roaming_line_imag)
 		# Reconstruct a calibrated (negative) pcal at only the ~3kHz pcal line
-		pcaly_line4 = calibration_parts.mkresample(pipeline, pcaly_line4, 3, "audio/x-raw, format=Z128LE, rate=%d, channel-mask=(bitmask)0x0" % hoftsr)
+		pcaly_line4 = calibration_parts.mkresample(pipeline, pcaly_line4, 3, False, "audio/x-raw, format=Z128LE, rate=%d, channel-mask=(bitmask)0x0" % hoftsr)
 		pcaly_line4 = pipeparts.mkgeneric(pipeline, pcaly_line4, "lal_demodulate", line_frequency = -1.0 * roaming_pcal_line_freq, prefactor_real = -2.0)
 		cancel_pcaly_line4, trash = calibration_parts.split_into_real(pipeline, pcaly_line4)
 		pipeparts.mkfakesink(pipeline, trash)
@@ -1075,7 +1075,7 @@ if options.cancel_callines:
 		if options.no_fs and options.no_srcQ:
 			pcal_at_src_freq = calibration_parts.demodulate(pipeline, pcaltee, src_pcal_line_freq, td, compute_calib_factors_complex_caps, integration_samples, pcal_corr_at_src_freq_real, pcal_corr_at_src_freq_imag)
 		# Reconstruct a calibrated (negative) pcal at only the ~3kHz pcal line
-		pcaly_line0 = calibration_parts.mkresample(pipeline, pcal_at_src_freq, 3, "audio/x-raw, format=Z128LE, rate=%d, channel-mask=(bitmask)0x0" % hoftsr)
+		pcaly_line0 = calibration_parts.mkresample(pipeline, pcal_at_src_freq, 3, False, "audio/x-raw, format=Z128LE, rate=%d, channel-mask=(bitmask)0x0" % hoftsr)
 		pcaly_line0 = pipeparts.mkgeneric(pipeline, pcaly_line0, "lal_demodulate", line_frequency = -1.0 * src_pcal_line_freq, prefactor_real = -2.0)
 		cancel_pcaly_line0, trash = calibration_parts.split_into_real(pipeline, pcaly_line0)
 		pipeparts.mkfakesink(pipeline, trash)
@@ -1165,12 +1165,12 @@ if tstchainsr != pumuimchainsr or options.apply_kappapu or options.apply_kappats
 # apply kappa_tst
 if options.apply_kappatst:
 	# Only apply the real part of \kappa_tst as a correction to A_tst
-	ktst_for_tst = calibration_parts.mkresample(pipeline, smooth_ktstRtee, 3, hoft_caps)
+	ktst_for_tst = calibration_parts.mkresample(pipeline, smooth_ktstRtee, 3, False, hoft_caps)
 	tst = calibration_parts.mkmultiplier(pipeline, calibration_parts.list_srcs(pipeline, [ktst_for_tst, ktst_queue_length], [tst, tst_queue_length]))
 # apply kappa_pu
 if options.apply_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, hoft_caps)
+	kpu_for_pu = calibration_parts.mkresample(pipeline, smooth_kpuRtee, 3, False, hoft_caps)
 	pumuim = calibration_parts.mkmultiplier(pipeline, calibration_parts.list_srcs(pipeline, [kpu_for_pu, kpu_queue_length], [pumuim, pumuim_queue_length]))
 
 # Add the TST and PUM/UIM chains together to form the full actuation chain
@@ -1212,7 +1212,7 @@ if options.dewhitening:
 
 # Apply factors to actuation and sensing chains, if applicable
 if options.apply_kappac:
-	kc_modify_res = calibration_parts.mkresample(pipeline, smooth_kctee, 3, hoft_caps)
+	kc_modify_res = calibration_parts.mkresample(pipeline, smooth_kctee, 3, False, hoft_caps)
 	res = calibration_parts.mkmultiplier(pipeline, calibration_parts.list_srcs(pipeline, [res, reskc_queue_length], [pipeparts.mkpow(pipeline, kc_modify_res, exponent = -1.0), kc_queue_length]))
 
 filter_settle_time = max(res_filter_settle_time, tst_filter_settle_time, pumuim_filter_settle_time)
@@ -1506,78 +1506,78 @@ record_kappa_caps = "audio/x-raw, format=F32LE, rate=%d" % options.record_factor
 if not options.no_kappapu:
 
 	kpuRout = pipeparts.mkaudioconvert(pipeline, smooth_kpuRtee)
-	kpuRout = calibration_parts.mkresample(pipeline, kpuRout, 1, record_kappa_caps)
+	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, record_kappa_caps)
+	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, record_kappa_caps)
+	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, record_kappa_caps)
+	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 not options.no_kappatst:
 
 	ktstRout = pipeparts.mkaudioconvert(pipeline, smooth_ktstRtee)
-	ktstRout = calibration_parts.mkresample(pipeline, ktstRout, 1, record_kappa_caps)
+	ktstRout = calibration_parts.mkresample(pipeline, ktstRout, 1, False, record_kappa_caps)
 	ktstRout = pipeparts.mkprogressreport(pipeline, ktstRout, "progress_kappa_tst_real_%s" % instrument)
 
 	ktstIout = pipeparts.mkaudioconvert(pipeline, smooth_ktstItee)
-	ktstIout = calibration_parts.mkresample(pipeline, ktstIout, 1, record_kappa_caps)
+	ktstIout = calibration_parts.mkresample(pipeline, ktstIout, 1, False, record_kappa_caps)
 	ktstIout = pipeparts.mkprogressreport(pipeline, ktstIout, "progress_kappa_tst_imag_%s" % instrument)
 
 	smooth_ktstR_nogate = pipeparts.mkaudioconvert(pipeline, smooth_ktstR_nogate)
-	smooth_ktstR_nogate = calibration_parts.mkresample(pipeline, smooth_ktstR_nogate, 1, record_kappa_caps)
+	smooth_ktstR_nogate = calibration_parts.mkresample(pipeline, smooth_ktstR_nogate, 1, False, record_kappa_caps)
 	smooth_ktstR_nogate = pipeparts.mkprogressreport(pipeline, smooth_ktstR_nogate, "progress_kappa_tst_real_nogate_%s" % instrument)
 
 	smooth_ktstI_nogate = pipeparts.mkaudioconvert(pipeline, smooth_ktstI_nogate)
-	smooth_ktstI_nogate = calibration_parts.mkresample(pipeline, smooth_ktstI_nogate, 1, record_kappa_caps)
+	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_c channels at the specified recording sample rate and change it to a single precision channel
 if not options.no_kappac:
 	kcout = pipeparts.mkaudioconvert(pipeline, smooth_kctee)
-	kcout = calibration_parts.mkresample(pipeline, kcout, 1, record_kappa_caps)
+	kcout = calibration_parts.mkresample(pipeline, kcout, 1, False, record_kappa_caps)
 	kcout = pipeparts.mkprogressreport(pipeline, kcout, "progress_kappa_c_%s" % instrument)
 
 	smooth_kc_nogate = pipeparts.mkaudioconvert(pipeline, smooth_kc_nogate)
-	smooth_kc_nogate = calibration_parts.mkresample(pipeline, smooth_kc_nogate, 1, record_kappa_caps)
+	smooth_kc_nogate = calibration_parts.mkresample(pipeline, smooth_kc_nogate, 1, False, record_kappa_caps)
 	smooth_kc_nogate = pipeparts.mkprogressreport(pipeline, smooth_kc_nogate, "progress_kappa_c_nogate_%s" % instrument)
 
 # Resample the f_cc channels at the specified recording sample rate and change it to a single precision channel
 if not options.no_fcc:
 	fccout = pipeparts.mkaudioconvert(pipeline, smooth_fcctee)
-	fccout = calibration_parts.mkresample(pipeline, fccout, 1, record_kappa_caps)
+	fccout = calibration_parts.mkresample(pipeline, fccout, 1, False, record_kappa_caps)
 	fccout = pipeparts.mkprogressreport(pipeline, fccout, "progress_f_cc_%s" % instrument)
 
 	smooth_fcc_nogate = pipeparts.mkaudioconvert(pipeline, smooth_fcc_nogate)
-	smooth_fcc_nogate = calibration_parts.mkresample(pipeline, smooth_fcc_nogate, 1, record_kappa_caps)
+	smooth_fcc_nogate = calibration_parts.mkresample(pipeline, smooth_fcc_nogate, 1, False, record_kappa_caps)
 	smooth_fcc_nogate = pipeparts.mkprogressreport(pipeline, smooth_fcc_nogate, "progress_f_cc_nogate_%s" % instrument)
 
 # Resample the f_s channels at the specified recording sample rate and change it to a single precision channel
 if not options.no_fs:
 	fsout = pipeparts.mkaudioconvert(pipeline, smooth_fs)
-	fsout = calibration_parts.mkresample(pipeline, fsout, 1, record_kappa_caps)
+	fsout = calibration_parts.mkresample(pipeline, fsout, 1, False, record_kappa_caps)
 	fsout = pipeparts.mkprogressreport(pipeline, fsout, "progress_f_s_%s" % instrument)
 
 	smooth_fs_nogate = pipeparts.mkaudioconvert(pipeline, smooth_fs_nogate)
-	smooth_fs_nogate = calibration_parts.mkresample(pipeline, smooth_fs_nogate, 1, record_kappa_caps)
+	smooth_fs_nogate = calibration_parts.mkresample(pipeline, smooth_fs_nogate, 1, False, record_kappa_caps)
 	smooth_fs_nogate = pipeparts.mkprogressreport(pipeline, smooth_fs_nogate, "progress_f_s_nogate_%s" % instrument)
 
 # Resample the f_s channels at the specified recording sample rate and change it to a single precision channel
 if not options.no_srcQ:
 	srcQ_inv_out = pipeparts.mkaudioconvert(pipeline, smooth_srcQ_inv)
-	srcQ_inv_out = calibration_parts.mkresample(pipeline, srcQ_inv_out, 1, record_kappa_caps)
+	srcQ_inv_out = calibration_parts.mkresample(pipeline, srcQ_inv_out, 1, False, record_kappa_caps)
 	srcQ_inv_out = pipeparts.mkprogressreport(pipeline, srcQ_inv_out, "progress_SRC_Q_%s" % instrument)
 
 	smooth_srcQ_inv_nogate = pipeparts.mkaudioconvert(pipeline, smooth_srcQ_inv_nogate)
-	smooth_srcQ_inv_nogate = calibration_parts.mkresample(pipeline, smooth_srcQ_inv_nogate, 1, record_kappa_caps)
+	smooth_srcQ_inv_nogate = calibration_parts.mkresample(pipeline, smooth_srcQ_inv_nogate, 1, False, record_kappa_caps)
 	smooth_srcQ_inv_nogate = pipeparts.mkprogressreport(pipeline, smooth_srcQ_inv_nogate, "progress_SRC_Q_nogate_%s" % instrument)
 
 #
diff --git a/gstlal-calibration/gst/lal/gstlal_add_constant.c b/gstlal-calibration/gst/lal/gstlal_add_constant.c
index 28073138a6..c8d098350b 100644
--- a/gstlal-calibration/gst/lal/gstlal_add_constant.c
+++ b/gstlal-calibration/gst/lal/gstlal_add_constant.c
@@ -26,6 +26,15 @@
  */
 
 
+/*
+ * stuff from C
+ */
+
+
+#include <string.h>
+#include <complex.h>
+
+
 /*
  *  stuff from gobject/gstreamer
  */
@@ -72,7 +81,7 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE(
 	GST_PAD_SINK,
 	GST_PAD_ALWAYS,
 	GST_STATIC_CAPS(
-		GST_AUDIO_CAPS_MAKE("{" GST_AUDIO_NE(F32) ", " GST_AUDIO_NE(F64) "}") ", " \
+		GST_AUDIO_CAPS_MAKE("{" GST_AUDIO_NE(F32) ", " GST_AUDIO_NE(F64) ", " GST_AUDIO_NE(Z64) ", " GST_AUDIO_NE(Z128) "}") ", " \
 		"layout = (string) interleaved, " \
 		"channel-mask = (bitmask) 0"
 	)
@@ -84,7 +93,7 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE(
 	GST_PAD_SRC,
 	GST_PAD_ALWAYS,
 	GST_STATIC_CAPS(
-		GST_AUDIO_CAPS_MAKE("{" GST_AUDIO_NE(F32) ", " GST_AUDIO_NE(F64) "}") ", " \
+		GST_AUDIO_CAPS_MAKE("{" GST_AUDIO_NE(F32) ", " GST_AUDIO_NE(F64) ", " GST_AUDIO_NE(Z64) ", " GST_AUDIO_NE(Z128) "}") ", " \
 		"layout = (string) interleaved, " \
 		"channel-mask = (bitmask) 0"
 	)
@@ -129,18 +138,24 @@ static gboolean get_unit_size(GstBaseTransform *trans, GstCaps *caps, gsize *siz
 static gboolean set_caps(GstBaseTransform *trans, GstCaps *incaps, GstCaps *outcaps)
 {
 	GSTLALAddConstant *element = GSTLAL_ADD_CONSTANT(trans);
-	gint rate_in, rate_out;
+	gint rate_in, rate_out, channels;
 	gsize unit_size;
 
 	/*
  	 * parse the caps
  	 */
 
+	GstStructure *str = gst_caps_get_structure(incaps, 0);
+	const gchar *name = gst_structure_get_string(str, "format");
+	if(!name) {
+		GST_DEBUG_OBJECT(element, "unable to parse format from %" GST_PTR_FORMAT, incaps);
+		return FALSE;
+	}
 	if(!get_unit_size(trans, incaps, &unit_size)) {
 		GST_DEBUG_OBJECT(element, "function 'get_unit_size' failed");
 		return FALSE;
 	}
-	if(!gst_structure_get_int(gst_caps_get_structure(incaps, 0), "rate", &rate_in)) {
+	if(!gst_structure_get_int(str, "rate", &rate_in)) {
 		GST_DEBUG_OBJECT(element, "unable to parse rate from %" GST_PTR_FORMAT, incaps);
 		return FALSE;
 	}
@@ -148,6 +163,10 @@ static gboolean set_caps(GstBaseTransform *trans, GstCaps *incaps, GstCaps *outc
 		GST_DEBUG_OBJECT(element, "unable to parse rate from %" GST_PTR_FORMAT, outcaps);
 		return FALSE;
 	}
+	if(!gst_structure_get_int(str, "channels", &channels)) {
+		GST_DEBUG_OBJECT(element, "unable to parse channels from %" GST_PTR_FORMAT, incaps);
+		return FALSE;
+	}
 
 	/*
  	 * require the output rate to be equal to the input rate
@@ -162,6 +181,21 @@ static gboolean set_caps(GstBaseTransform *trans, GstCaps *incaps, GstCaps *outc
  	 * record stream parameters
  	 */
 
+	if(!strcmp(name, GST_AUDIO_NE(F32))) {
+			element->data_type = GSTLAL_ADD_CONSTANT_F32;
+			g_assert_cmpuint(unit_size, ==, 4 * (guint) channels);
+		} else if(!strcmp(name, GST_AUDIO_NE(F64))) {
+			element->data_type = GSTLAL_ADD_CONSTANT_F64;
+			g_assert_cmpuint(unit_size, ==, 8 * (guint) channels);
+		} else if(!strcmp(name, GST_AUDIO_NE(Z64))) {
+			element->data_type = GSTLAL_ADD_CONSTANT_Z64;
+			g_assert_cmpuint(unit_size, ==, 8 * (guint) channels);
+		} else if(!strcmp(name, GST_AUDIO_NE(Z128))) {
+			element->data_type = GSTLAL_ADD_CONSTANT_Z128;
+			g_assert_cmpuint(unit_size, ==, 16 * (guint) channels);
+		} else
+			g_assert_not_reached();
+
 	element->rate = rate_in;
 	element->unit_size = unit_size;
 
@@ -180,12 +214,13 @@ static GstFlowReturn transform_ip(GstBaseTransform *trans, GstBuffer *buf)
 	GstMapInfo mapinfo;
 	GstFlowReturn result = GST_FLOW_OK;
 	gdouble value = element->value;
+	gdouble value_imag = element->value_imag;
 
 	GST_BUFFER_FLAG_UNSET(buf, GST_BUFFER_FLAG_GAP);
 
 	gst_buffer_map(buf, &mapinfo, GST_MAP_READWRITE);
 
-	if(element->unit_size == 4) {
+	if(element->data_type == GSTLAL_ADD_CONSTANT_F32) {
 
 		gfloat *addr, *end;
 		g_assert(mapinfo.size % sizeof(gfloat) == 0);
@@ -194,7 +229,7 @@ static GstFlowReturn transform_ip(GstBaseTransform *trans, GstBuffer *buf)
 		while(addr < end)
 			*addr++ += value;
 
-	} else if(element->unit_size == 8) {
+	} else if(element->data_type == GSTLAL_ADD_CONSTANT_F64) {
 
 		gdouble *addr, *end;
 		g_assert(mapinfo.size % sizeof(gdouble) == 0);
@@ -203,6 +238,24 @@ static GstFlowReturn transform_ip(GstBaseTransform *trans, GstBuffer *buf)
 		while(addr < end)
 			*addr++ += value;
 
+	} else if(element->data_type == GSTLAL_ADD_CONSTANT_Z64) {
+
+		complex float *addr, *end;
+		g_assert(mapinfo.size % sizeof(complex float) == 0);
+		addr = (complex float *) mapinfo.data;
+		end = (complex float *) (mapinfo.data + mapinfo.size);
+		while(addr < end)
+			*addr++ += value + I * value_imag;
+
+	} else if(element->data_type == GSTLAL_ADD_CONSTANT_Z128) {
+
+		complex double *addr, *end;
+		g_assert(mapinfo.size % sizeof(complex double) == 0);
+		addr = (complex double *) mapinfo.data;
+		end = (complex double *) (mapinfo.data + mapinfo.size);
+		while(addr < end)
+			*addr++ += value + I * value_imag;
+
 	} else {
 		g_assert_not_reached();
 	}
@@ -224,6 +277,7 @@ static GstFlowReturn transform_ip(GstBaseTransform *trans, GstBuffer *buf)
 
 enum property {
 	ARG_VALUE = 1,
+	ARG_VALUE_IMAG
 };
 
 
@@ -246,6 +300,10 @@ static void set_property(GObject *object, enum property prop_id, const GValue *v
 		element->value = g_value_get_double(value);
 		break;
 
+	case ARG_VALUE_IMAG:
+		element->value_imag = g_value_get_double(value);
+		break;
+
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
 		break;
@@ -271,6 +329,10 @@ static void get_property(GObject *object, enum property prop_id, GValue *value,
 		g_value_set_double(value, element->value);
 		break;
 
+	case ARG_VALUE_IMAG:
+		g_value_set_double(value, element->value_imag);
+		break;
+
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
 		break;
@@ -308,7 +370,18 @@ static void gstlal_add_constant_class_init(GSTLALAddConstantClass *klass)
 		g_param_spec_double(
 			"value",
 			"Value",
-			"Value to add to all samples.",
+			"Real value to add to all samples.",
+			-G_MAXDOUBLE, G_MAXDOUBLE, DEFAULT_VALUE,
+			G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT
+		)
+	);
+	g_object_class_install_property(
+		gobject_class,
+		ARG_VALUE_IMAG,
+		g_param_spec_double(
+			"value-imag",
+			"Imaginary Value",
+			"Imaginary part of value to add to all samples.",
 			-G_MAXDOUBLE, G_MAXDOUBLE, DEFAULT_VALUE,
 			G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT
 		)
diff --git a/gstlal-calibration/gst/lal/gstlal_add_constant.h b/gstlal-calibration/gst/lal/gstlal_add_constant.h
index 743d19142c..c5c20ddd69 100644
--- a/gstlal-calibration/gst/lal/gstlal_add_constant.h
+++ b/gstlal-calibration/gst/lal/gstlal_add_constant.h
@@ -79,8 +79,16 @@ struct _GSTLALAddConstant {
 	gint unit_size;
 	gint rate;
 
+	enum gstlal_add_constant_data_type {
+		GSTLAL_ADD_CONSTANT_F32 = 0,
+		GSTLAL_ADD_CONSTANT_F64,
+		GSTLAL_ADD_CONSTANT_Z64,
+		GSTLAL_ADD_CONSTANT_Z128
+	} data_type;
+
 	/* properties */
 	gdouble value;
+	gdouble value_imag;
 };
 
 
diff --git a/gstlal-calibration/python/calibration_parts.py b/gstlal-calibration/python/calibration_parts.py
index 47ae18dda2..ecf253c6bf 100644
--- a/gstlal-calibration/python/calibration_parts.py
+++ b/gstlal-calibration/python/calibration_parts.py
@@ -55,8 +55,8 @@ def mkstockresample(pipeline, head, caps):
 	head = pipeparts.mkcapsfilter(pipeline, head, caps)
 	return head
 
-def mkresample(pipeline, head, polynomial_order, caps):
-	head = pipeparts.mkgeneric(pipeline, head, "lal_resample", polynomial_order = polynomial_order)
+def mkresample(pipeline, head, quality, zero_latency, caps):
+	head = pipeparts.mkgeneric(pipeline, head, "lal_resample", quality = quality, zero_latency = zero_latency)
 	head = pipeparts.mkcapsfilter(pipeline, head, caps)
 	return head
 
@@ -273,7 +273,7 @@ def demodulate(pipeline, head, freq, td, caps, integration_samples, prefactor_re
 	# demodulate input at a given frequency freq
 
 	head = pipeparts.mkgeneric(pipeline, head, "lal_demodulate", line_frequency = freq, prefactor_real = prefactor_real, prefactor_imag = prefactor_imag)
-	head = mkresample(pipeline, head, 1, caps)
+	head = mkresample(pipeline, head, 3, True, caps)
 	head = mkcomplexfirbank(pipeline, head, fir_matrix=[numpy.hanning(integration_samples + 1) * 2 / integration_samples], time_domain = td)
 
 	return head
-- 
GitLab