From 84769329fa59500a119cdb893f3fc8c4f9dd18dd Mon Sep 17 00:00:00 2001
From: Madeline Wade <madeline.wade@ligo.org>
Date: Mon, 21 Jan 2019 14:08:05 -0800
Subject: [PATCH] Adding in new logic to calculate filter settling clock from
 set of provided channels and bitmasks.

---
 gstlal-calibration/bin/gstlal_compute_strain  | 39 ++++++++++++++++---
 .../check_calibration/statevector_plot.py     | 32 +++++++++++++++
 2 files changed, 65 insertions(+), 6 deletions(-)
 create mode 100644 gstlal-calibration/tests/check_calibration/statevector_plot.py

diff --git a/gstlal-calibration/bin/gstlal_compute_strain b/gstlal-calibration/bin/gstlal_compute_strain
index 2b71b1985d..0782e68cc2 100755
--- a/gstlal-calibration/bin/gstlal_compute_strain
+++ b/gstlal-calibration/bin/gstlal_compute_strain
@@ -232,6 +232,7 @@ hoft_sr = int(SampleRates["hoftsr"]) # Sample rate for h(t)
 calibstate_sr = int(SampleRates["calibstatesr"]) # Sample rate for the CALIB_STATE_VECTOR
 obsintent_sr = int(SampleRates["obsintentsr"]) if "obsintentsr" in SampleRates else int(SampleRates["odcsr"]) # Sample rate of the ODC channel that is read in
 lownoise_sr = int(SampleRates["lownoisesr"]) if "lownoisesr" in SampleRates else int(SampleRates["odcsr"])
+filterclock_sr_list = SampleRates["filterclocksrlist"].split(';') if "filterclocksrlist" in SampleRates else [lownoise_sr]
 hwinj_sr = int(SampleRates["hwinjsr"]) if "hwinjsr" in SampleRates else int(SampleRates["odcsr"])
 ctrl_sr = int(SampleRates["ctrlsr"]) # Sample rate of the control channel (such as DARM_CTRL or DELTAL_CTRL)
 tst_exc_sr = int(SampleRates["tstexcsr"])
@@ -246,6 +247,9 @@ ctrl_caps = "audio/x-raw, format=F64LE, rate=%d, channel-mask=(bitmask)0x0" % ct
 calibstate_caps = "audio/x-raw, format=U32LE, rate=%d, channel-mask=(bitmask)0x0" % calibstate_sr
 obsintent_caps = "audio/x-raw, format=U32LE, rate=%d, channel-mask=(bitmask)0x0" % obsintent_sr
 lownoise_caps = "audio/x-raw, format=U32LE, rate=%d, channel-mask=(bitmask)0x0" % lownoise_sr
+filterclock_caps = []
+for i in range(len(filterclock_sr_list)):
+	filterclock_caps.append("audio/x-raw, format=U32LE, rate=%d, channel-mask=(bitmask)0x0" % int(filterclock_sr_list[i]))
 hwinj_caps = "audio/x-raw, format=U32LE, rate=%d, channel-mask=(bitmask)0x0" % hwinj_sr
 coh_caps = "audio/x-raw, format=F64LE, rate=%d, channel-mask=(bitmask)0x0" % coh_sr
 ref_factors_caps = "audio/x-raw, format=F64LE, rate=%d, channel-mask=(bitmask)0x0" % epics_sr
@@ -675,6 +679,8 @@ if compute_calib_statevector:
 	obsintent_bitmask = int(Bitmasks["obsintentbitmask"])
 	lownoise_channel_name = ChannelNames["lownoisestatechannel"] if "lownoisestatechannel" in ChannelNames else ChannelNames["inputdqchannel"]
 	lownoise_bitmask = int(Bitmasks["lownoisebitmask"]) if "lownoisebitmask" in Bitmasks else int(Bitmasks["obsreadybitmask"])
+	filterclock_channel_list = ChannelNames["filterclockchannellist"].split(';') if "filterclockchannellist" in ChannelNames else [lownoise_channel_name]
+	filterclock_bitmask_list = Bitmasks["filterclockbitmasklist"].split(';') if "filterclockbitmasklist" in Bitmasks else [lownoise_bitmask]
 	hwinj_channel_name = ChannelNames["hwinjchannel"] if "hwinjchannel" in ChannelNames else ChannelNames["inputdqchannel"]
 	cbchwinj_bitmask = int(Bitmasks["cbchwinjbitmask"]) if "cbchwinjbitmask" in Bitmasks else 0
 	cbchwinj_offbitmask = int(Bitmasks["cbchwinjoffbitmask"]) if "cbchwinjoffbitmask" in Bitmasks else 0
@@ -694,6 +700,17 @@ if compute_calib_statevector:
 		channel_list.append((instrument, hwinj_channel_name))
 		headkeys.append("hwinj")
 
+	if len(filterclock_bitmask_list) != len(filterclock_channel_list) or len(filterclock_bitmask_list) != len(filterclock_sr_list):
+		raise ValueError("FilterClockChannelList, FilterClockBitmaskList, and FilterClockSRList must all be the same length.")
+	for i in range(len(filterclock_channel_list)):
+		filterclock_channel_list[i] = filterclock_channel_list[i].split(',')
+		for j in range(len(filterclock_channel_list[i])):
+			if filterclock_channel_list[i][j] != obsintent_channel_name and filterclock_channel_list[i][j] != lownoise_channel_name and filterclock_channel_list[i][j] != hwinj_channel_name:
+				channel_list.append((instrument, filterclock_channel_list[i][j]))
+				headkeys.append(filterclock_channel_list[i][j])
+		filterclock_sr_list[i] = int(filterclock_sr_list[i])
+		filterclock_bitmask_list[i] = int(filterclock_bitmask_list[i])
+
 # 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_pum, kappa_uim, kappa_pu, kappa_c, f_cc, f_s, and Q
 if not factors_from_filters_file or compute_calib_statevector:
@@ -867,8 +884,8 @@ else:
 	witness_channel_list = None
 
 # If we are gating the noise or 60 Hz line subtraction with something other than the ODC state vector, add that channel
-noisesub_gate_channel = ChannelNames["noisesubgatechannel"] if "noisesubgatechannel" in ChannelNames else ChannelNames["inputdqchannel"]
-noisesub_gate_bitmask = int(Bitmasks["noisesubgatebitmask"]) if "noisesubgatebitmask" in Bitmasks else int(Bitmasks["obsreadybitmask"])
+noisesub_gate_channel = ChannelNames["noisesubgatechannel"] if "noisesubgatechannel" in ChannelNames else lownoise_channel_name 
+noisesub_gate_bitmask = int(Bitmasks["noisesubgatebitmask"]) if "noisesubgatebitmask" in Bitmasks else lownoise_bitmask
 if compute_calib_statevector and (remove_power_lines or witness_channel_list is not None) and noisesub_gate_channel != obsintent_channel_name and noisesub_gate_channel != lownoise_channel_name and noisesub_gate_channel != hwinj_channel_name and noisesub_gate_bitmask >= 0:
 	channel_list.append((instrument, noisesub_gate_channel))
 	headkeys.append("noisesubgatechannel")
@@ -2015,10 +2032,20 @@ if compute_calib_statevector:
 	# FILTERS-OK BIT BRANCH
 	#
 	
-	# Set the FILTERS-OK bit based on observation-ready transitions
-	filtersok = pipeparts.mkbitvectorgen(pipeline, obsintenttee, bit_vector = pow(2,3), threshold=2)
+	# Set the FILTERS-OK bit based on step-before transition to GRD-IFO_OK.
+	# Take in a channel list and a corresponding bitmask list to use as determinator for when to start the clock for filter settling time
+	filterclock_channels = []
+	for i in range(len(filterclock_channel_list)):
+		for key in headkeys:
+			if key in filterclock_channel_list[i]:
+				filterclock_channels.append(calibration_parts.mkqueue(pipeline, pipeparts.mkcapsfilter(pipeline, pipeparts.mkgeneric(pipeline, calibration_parts.caps_and_progress(pipeline, head_dict[key], filterclock_caps[i],key), "lal_logicalundersample", required_on = filterclock_bitmask_list[i], status_out = 1), calibstate_caps)))
+	filtersok = pipeparts.mkadder(pipeline, tuple(filterclock_channels))
+	filtersok = pipeparts.mkbitvectorgen(pipeline, filtersok, bit_vector = 1, threshold=len(filterclock_channels))
 	filtersok = pipeparts.mkcapsfilter(pipeline, filtersok, calibstate_caps)
-	filtersok = calibration_parts.mkgate(pipeline, filtersok, lownoisetee, 4, attack_length = -int(filter_settle_time * calibstate_sr), hold_length = -int(filter_latency * calibstate_sr))
+	filtersoktee = pipeparts.mktee(pipeline, filtersok)
+	filtersok = calibration_parts.mkgate(pipeline, filtersoktee, filtersoktee, 1, attack_length = -int(filter_settle_time * calibstate_sr))
+	# The "hold" on FILTERS_OK turning off is still determined by the low noise state
+	filtersok = calibration_parts.mkgate(pipeline, filtersok, lownoisetee, pow(2,2), hold_length = -int(filter_latency * calibstate_sr))
 	filtersok = pipeparts.mkbitvectorgen(pipeline, filtersok, bit_vector = pow(2,3), nongap_is_control = True)
 	filtersok = pipeparts.mkcapsfilter(pipeline, filtersok, calibstate_caps)
 
@@ -2026,7 +2053,7 @@ if compute_calib_statevector:
 	# NO-INVALID-INPUT BRANCH
 	#
 
-	# Check if the ODC state vector is present
+	# Check if the DQ state vector is present
 	nogap = pipeparts.mkbitvectorgen(pipeline, obsintentchanneltee, threshold=1, bit_vector = 1)
 	nogap = pipeparts.mkcapsfilter(pipeline, nogap, obsintent_caps)
 	nogap = pipeparts.mkgeneric(pipeline, nogap, "lal_logicalundersample", required_on = 1, status_out = 1)
diff --git a/gstlal-calibration/tests/check_calibration/statevector_plot.py b/gstlal-calibration/tests/check_calibration/statevector_plot.py
new file mode 100644
index 0000000000..25e89275b1
--- /dev/null
+++ b/gstlal-calibration/tests/check_calibration/statevector_plot.py
@@ -0,0 +1,32 @@
+import matplotlib as mpl; mpl.use('Agg')
+from gwpy.timeseries import StateVector
+import numpy
+import sys
+from optparse import OptionParser, Option
+
+parser = OptionParser()
+
+parser.add_option("--gps-start-time", metavar = "seconds", help = "Set the GPS start time.")
+parser.add_option("--gps-end-time", metavar = "seconds", help = "Set the GPS end time.")
+parser.add_option("--ifo", metavar = "name", help = "Name of the IFO")
+parser.add_option("--hoft-frames-cache", metavar = "name", help = "Frame cache file for h(t) data to be analyzed")
+#parser.add_option("--raw-frames-cache", metavar = "name", help = "Frame cache for raw data.")
+parser.add_option("--calib-state-vector-channel-name", metavar = "name", default = "GDS-CALIB_STATE_VECTOR", help = "Calibration state vector channel name (default = GDS-CALIB_STATE_VECTOR")
+#parser.add_option("--analyze-calcs-hoft", action = "store_true", help = "Set this to analyze CALCS h(t) data")
+#parser.add_option("--calcs-deltal-channel-name", metavar = "name", default = "CAL-DELTAL_EXTERNAL_DQ", help = "CALCS \delta L channel name (default = CAL-DELTAL_EXTERNAL_DQ)")
+
+options, filenames = parser.parse_args()
+
+start = int(options.gps_start_time)
+end = int(options.gps_end_time)
+ifo = options.ifo
+hoft_frames_cache = options.hoft_frames_cache
+calib_state_channel_name = options.calib_state_vector_channel_name
+
+calib_state_vector = StateVector.read(hoft_frames_cache, "%s:%s" % (ifo, calib_state_channel_name), start = start, end = end)
+
+plot = calib_state_vector.plot(format='segments')
+ax = plot.gca()
+ax.set_xscale('seconds')
+ax.set_title("Calibration state vector")
+plot.save("%s_%s_%s_calib_state_vector.pdf" % (ifo, options.gps_start_time, options.gps_end_time))
-- 
GitLab