From dd056b0aa48dc4b3bd982886cc85dfcfadf72e52 Mon Sep 17 00:00:00 2001
From: Aaron Viets <aaron.viets@ligo.org>
Date: Thu, 17 Jan 2019 11:23:04 -0600
Subject: [PATCH] lal_logicalundersample:  added properties required-off and
 invert-result to make more versatile.

---
 .../gst/lal/gstlal_logicalundersample.c       | 122 +++++++++++++-----
 .../gst/lal/gstlal_logicalundersample.h       |   7 +-
 .../tests/lal_logicalundersample_test.py      |  16 +--
 3 files changed, 102 insertions(+), 43 deletions(-)

diff --git a/gstlal-calibration/gst/lal/gstlal_logicalundersample.c b/gstlal-calibration/gst/lal/gstlal_logicalundersample.c
index 3a8005a443..16a93d21ee 100644
--- a/gstlal-calibration/gst/lal/gstlal_logicalundersample.c
+++ b/gstlal-calibration/gst/lal/gstlal_logicalundersample.c
@@ -58,42 +58,39 @@
 
 
 #define DEFINE_UNDERSAMPLE_FUNC(sign,width) \
-static void logical_op_g ## sign ## width(const g ## sign ## width *src, guint64 src_size, guint ## width *dst, guint64 dst_size, guint cadence, guint64 *remainder, g ## sign ## width *leftover, g ## sign ## width *required_on, guint ## width status_out) \
+static void logical_op_g ## sign ## width(const g ## sign ## width *src, guint64 src_size, g ## sign ## width *dst, guint64 dst_size, guint cadence, guint *remainder, gboolean *leftover, g ## sign ## width *required_on, g ## sign ## width *required_on_xor_off, guint ## width status_out, gboolean invert_result) \
 { \
-	g ## sign ## width inbits[*remainder + src_size]; \
+	guint i, k, k_start, k_end = 0; \
+	gboolean fail; \
  \
-	for(unsigned int i = 0; i < *remainder; i++) \
-		inbits[i] = *leftover; \
- \
-	for(unsigned int j = *remainder; j < *remainder + src_size; j++, src++) \
-		inbits[j] = *src; \
- \
-	for(unsigned int k_start = 0; k_start < dst_size; k_start++, dst++) { \
-		g ## sign ## width cadence_samples = *required_on; \
-		for(unsigned int k = cadence * k_start; k < cadence * (k_start + 1); k++) \
-			cadence_samples = cadence_samples & inbits[k]; \
-		if(cadence_samples == *required_on) \
-			*dst = status_out; \
+	for(i = 0; i < dst_size; i++, dst++) { \
+		fail = i ? FALSE : *leftover; \
+		k_start = i ? i * cadence - *remainder : 0; \
+		k_end = k_start + cadence; \
+		for(k = k_start; k < k_end; k++) \
+			fail = (src[k] ^ *required_on) & *required_on_xor_off ? TRUE : fail; \
+		if(fail == invert_result) \
+			*dst = (g ## sign ## width) status_out; \
 		else \
 			*dst = 0x00; \
 	} \
  \
-	unsigned int first_leftover_index = src_size + *remainder - ((src_size + *remainder) % cadence); \
 	*remainder = (src_size + *remainder) % cadence; \
-	if(*remainder != 0) { \
-		*leftover = *required_on; \
-		for(unsigned int m = first_leftover_index; m < first_leftover_index + *remainder; m++) \
-			*leftover = *leftover & inbits[m]; \
-	} else \
-		*leftover = 0; \
+	*leftover = i ? FALSE : *leftover; \
+	for(k = k_end; k < src_size; k++) \
+		*leftover = (src[k] ^ *required_on) & *required_on_xor_off ? TRUE : *leftover; \
 }
 
 
+DEFINE_UNDERSAMPLE_FUNC(int,8)
+DEFINE_UNDERSAMPLE_FUNC(uint,8)
+DEFINE_UNDERSAMPLE_FUNC(int,16)
+DEFINE_UNDERSAMPLE_FUNC(uint,16)
 DEFINE_UNDERSAMPLE_FUNC(int,32)
 DEFINE_UNDERSAMPLE_FUNC(uint,32)
 
 
-static void undersample(const void *src, guint64 src_size, void *dst, guint64 dst_size, guint unit_size, gboolean sign, guint cadence, guint64 *remainder, void *leftover, void *required_on, guint32 status_out)
+static void undersample(const void *src, guint64 src_size, void *dst, guint64 dst_size, guint unit_size, gboolean sign, guint cadence, guint *remainder, gboolean *leftover, void *required_on, void *required_on_xor_off, guint32 status_out, gboolean invert_result)
 {
 	g_assert_cmpuint(src_size % unit_size, ==, 0);
 	g_assert_cmpuint(dst_size % unit_size, ==, 0);
@@ -104,8 +101,14 @@ static void undersample(const void *src, guint64 src_size, void *dst, guint64 ds
 	if(sign) {
 
 		switch(unit_size) {
+		case 1:
+			logical_op_gint8(src, src_size, dst, dst_size, cadence, remainder, leftover, required_on, required_on_xor_off, status_out, invert_result);
+			break;
+		case 2:
+			logical_op_gint16(src, src_size, dst, dst_size, cadence, remainder, leftover, required_on, required_on_xor_off, status_out, invert_result);
+			break;
 		case 4:
-			logical_op_gint32(src, src_size, dst, dst_size, cadence, remainder, leftover, required_on, status_out);
+			logical_op_gint32(src, src_size, dst, dst_size, cadence, remainder, leftover, required_on, required_on_xor_off, status_out, invert_result);
 			break;
 		default:
 			g_assert_not_reached();
@@ -113,8 +116,14 @@ static void undersample(const void *src, guint64 src_size, void *dst, guint64 ds
 	} else {
 
 		switch(unit_size) {
+		case 1:
+			logical_op_guint8(src, src_size, dst, dst_size, cadence, remainder, leftover, required_on, required_on_xor_off, status_out, invert_result);
+			break;
+		case 2:
+			logical_op_guint16(src, src_size, dst, dst_size, cadence, remainder, leftover, required_on, required_on_xor_off, status_out, invert_result);
+			break;
 		case 4:
-			logical_op_guint32(src, src_size, dst, dst_size, cadence, remainder, leftover, required_on, status_out);
+			logical_op_guint32(src, src_size, dst, dst_size, cadence, remainder, leftover, required_on, required_on_xor_off, status_out, invert_result);
 			break;
 		default:
 			g_assert_not_reached();
@@ -163,7 +172,7 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE(
 		"audio/x-raw, " \
 		"rate = " GST_AUDIO_RATE_RANGE ", " \
 		"channels = (int) 1, " \
-		"format = (string) {" GST_AUDIO_NE(S32) ", " GST_AUDIO_NE(U32) "}, " \
+		"format = (string) {" GST_AUDIO_NE(S8) ", " GST_AUDIO_NE(U8) ", " GST_AUDIO_NE(S16) ", " GST_AUDIO_NE(U16) ", " GST_AUDIO_NE(S32) ", " GST_AUDIO_NE(U32) "}, " \
 		"layout = (string) interleaved, " \
 		"channel-mask = (bitmask) 0"
 	)
@@ -178,7 +187,7 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE(
 		"audio/x-raw, " \
 		"rate = " GST_AUDIO_RATE_RANGE ", " \
 		"channels = (int) 1, " \
-		"format = (string) " GST_AUDIO_NE(U32) ", " \
+		"format = (string) {" GST_AUDIO_NE(S8) ", " GST_AUDIO_NE(U8) ", " GST_AUDIO_NE(S16) ", " GST_AUDIO_NE(U16) ", " GST_AUDIO_NE(S32) ", " GST_AUDIO_NE(U32) "}, " \
 		"layout = (string) interleaved, " \
 		"channel-mask = (bitmask) 0"
 	)
@@ -257,6 +266,8 @@ static GstCaps *transform_caps(GstBaseTransform *trans, GstPadDirection directio
 
 			GstStructure *otherstr = gst_caps_get_structure(othercaps, i);
 
+			gst_structure_set(otherstr, "channels", G_TYPE_INT, 1, NULL);
+
 			if(GST_VALUE_HOLDS_INT_RANGE(v))
 				gst_structure_set(otherstr, "rate", GST_TYPE_INT_RANGE, gst_value_get_int_range_min(v), G_MAXINT, NULL);
 			else if(G_VALUE_HOLDS_INT(v))
@@ -284,7 +295,6 @@ static GstCaps *transform_caps(GstBaseTransform *trans, GstPadDirection directio
 			GstStructure *otherstr = gst_caps_get_structure(othercaps, i);
 
 			gst_structure_set(otherstr, "channels", G_TYPE_INT, 1, NULL);
-			gst_structure_set(otherstr, "format", G_TYPE_STRING, GST_AUDIO_NE(U32), NULL);
 
 			if(GST_VALUE_HOLDS_INT_RANGE(v)) {
 				if(gst_value_get_int_range_max(v) == 1)
@@ -332,8 +342,8 @@ static gboolean set_caps(GstBaseTransform *trans, GstCaps *incaps, GstCaps *outc
 	gint rate_in, rate_out;
 	gsize unit_size;
 	const gchar *format;
-	static char *formats[] = {"S32LE", "S32BE", "U32LE", "U32BE"};
-	gboolean sign[] = {TRUE, TRUE, FALSE, FALSE};
+	static char *formats[] = {"S8LE", "S8BE", "U8LE", "U8BE", "S16LE", "S16BE", "U16LE", "U16BE", "S32LE", "S32BE", "U32LE", "U32BE"};
+	gboolean sign[] = {TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE};
 
 	/*
 	 * parse the caps
@@ -492,7 +502,11 @@ static gboolean start(GstBaseTransform *trans)
 	element->need_discont = TRUE;
 
 	element->remainder = 0;
-	element->leftover = 0;
+	element->leftover = FALSE;
+
+	if(element->required_on & element->required_off)
+		GST_WARNING_OBJECT(element, "One or more bits are requested to be required both on and off. These bits will be ignored.");
+	element->required_on_xor_off = element->required_on ^ element->required_off;
 
 	return TRUE;
 }
@@ -534,7 +548,7 @@ static GstFlowReturn transform(GstBaseTransform *trans, GstBuffer *inbuf, GstBuf
 
 		gst_buffer_map(inbuf, &inmap, GST_MAP_READ);
 		gst_buffer_map(outbuf, &outmap, GST_MAP_WRITE);
-		undersample(inmap.data, inmap.size, outmap.data, outmap.size, element->unit_size, element->sign, element->rate_in / element->rate_out, &element->remainder, &element->leftover, &element->required_on, element->status_out);
+		undersample(inmap.data, inmap.size, outmap.data, outmap.size, element->unit_size, element->sign, element->rate_in / element->rate_out, &element->remainder, &element->leftover, &element->required_on, &element->required_on_xor_off, element->status_out, element->invert_result);
 		set_metadata(element, outbuf, outmap.size / element->unit_size, FALSE);
 		gst_buffer_unmap(outbuf, &outmap);
 		gst_buffer_unmap(inbuf, &inmap);
@@ -574,7 +588,9 @@ static GstFlowReturn transform(GstBaseTransform *trans, GstBuffer *inbuf, GstBuf
 
 enum property {
 	ARG_REQUIRED_ON = 1,
-	ARG_STATUS_OUT
+	ARG_REQUIRED_OFF,
+	ARG_STATUS_OUT,
+	ARG_INVERT_RESULT
 };
 
 
@@ -589,10 +605,18 @@ static void set_property(GObject *object, enum property prop_id, const GValue *v
 		element->required_on = g_value_get_uint(value);
 		break;
 
+	case ARG_REQUIRED_OFF:
+		element->required_off = g_value_get_uint(value);
+		break;
+
 	case ARG_STATUS_OUT:
 		element->status_out = g_value_get_uint(value);
 		break;
 
+	case ARG_INVERT_RESULT:
+		element->invert_result = g_value_get_boolean(value);
+		break;
+
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
 		break;
@@ -613,10 +637,18 @@ static void get_property(GObject *object, enum property prop_id, GValue *value,
 		g_value_set_uint(value, element->required_on);
 		break;
 
+	case ARG_REQUIRED_OFF:
+		g_value_set_uint(value, element->required_off);
+		break;
+
 	case ARG_STATUS_OUT:
 		g_value_set_uint(value, element->status_out);
 		break;
 
+	case ARG_INVERT_RESULT:
+		g_value_set_boolean(value, element->invert_result);
+		break;
+
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
 		break;
@@ -674,17 +706,41 @@ static void gstlal_logicalundersample_class_init(GSTLALLogicalUnderSampleClass *
 			G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT
 		)
 	);
+	g_object_class_install_property(
+		gobject_class,
+		ARG_REQUIRED_OFF,
+		g_param_spec_uint(
+			"required-off",
+			"Off bits",
+			"Bit mask setting the bits that must be off in the incoming stream.  Note:  if the\n\t\t\t"
+			"mask is wider than the input stream, the high-order bits should be 0 or the off\n\t\t\t"
+			"condition will never be met.",
+			0, G_MAXUINT, 0x0,
+			G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT
+		)
+	);
 	g_object_class_install_property(
 		gobject_class,
 		ARG_STATUS_OUT,
 		g_param_spec_uint(
 			"status-out",
 			"Out bits",
-			"Value of output if required-on mask is true.",
+			"Value of output if required-on mask is true and required-off mask is false.",
 			0, G_MAXUINT, 0x1,
 			G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT
 		)
 	);
+	g_object_class_install_property(
+		gobject_class,
+		ARG_INVERT_RESULT,
+		g_param_spec_boolean(
+			"invert-result",
+			"Invert result",
+			"If true, output is status-out when conditions are not met.",
+			FALSE,
+			G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT
+		)
+	);
 }
 
 
diff --git a/gstlal-calibration/gst/lal/gstlal_logicalundersample.h b/gstlal-calibration/gst/lal/gstlal_logicalundersample.h
index 2c4c765ccc..9358a4773d 100644
--- a/gstlal-calibration/gst/lal/gstlal_logicalundersample.h
+++ b/gstlal-calibration/gst/lal/gstlal_logicalundersample.h
@@ -78,13 +78,16 @@ struct _GSTLALLogicalUnderSample {
 
 	/* internal state */
 
-	guint64 remainder;
-	gint32 leftover;
+	guint remainder;
+	gboolean leftover;
 
 	/* properties  */
 
 	gint32 required_on;
+	gint32 required_off;
+	gint32 required_on_xor_off;
 	guint32 status_out;
+	gboolean invert_result;
 };
 
 
diff --git a/gstlal-calibration/tests/lal_logicalundersample_test.py b/gstlal-calibration/tests/lal_logicalundersample_test.py
index 9ee39f7f6a..690872277c 100644
--- a/gstlal-calibration/tests/lal_logicalundersample_test.py
+++ b/gstlal-calibration/tests/lal_logicalundersample_test.py
@@ -55,13 +55,13 @@ def lal_logicalundersample_01(pipeline, name):
 
 	src = test_common.int_test_src(pipeline, buffer_length = buffer_length, rate = in_rate, width=32, test_duration = test_duration)
 #	src = pipeparts.mkaudiotestsrc(pipeline, num_buffers = int(test_duration / buffer_length))
-#	capsfilter1 = pipeparts.mkcapsfilter(pipeline, src, "audio/x-raw, format=S32LE, rate=%d" % int(in_rate))
-	tee1 = pipeparts.mktee(pipeline, src)
-	pipeparts.mknxydumpsink(pipeline, pipeparts.mkqueue(pipeline, tee1), "%s_in.dump" % name)
-	undersample = pipeparts.mkgeneric(pipeline, tee1, "lal_logicalundersample")
-	capsfilter2 = pipeparts.mkcapsfilter(pipeline, undersample, "audio/x-raw, format=U32LE, rate=%d" % int(out_rate))
+	capsfilter1 = pipeparts.mkcapsfilter(pipeline, src, "audio/x-raw, format=S32LE, rate=%d" % int(in_rate))
+	tee1 = pipeparts.mktee(pipeline, capsfilter1)
+	pipeparts.mknxydumpsink(pipeline, tee1, "%s_in.txt" % name)
+	undersample = pipeparts.mkgeneric(pipeline, tee1, "lal_logicalundersample", required_on = 1, required_off = 2, invert_result = True)
+	capsfilter2 = pipeparts.mkcapsfilter(pipeline, undersample, "audio/x-raw, format=S32LE, rate=%d" % int(out_rate))
 	#checktimestamps = pipeparts.mkchecktimestamps(pipeline, capsfilter2)
-	pipeparts.mknxydumpsink(pipeline, pipeparts.mkqueue(pipeline, capsfilter2), "%s_out.dump" % name)
+	pipeparts.mknxydumpsink(pipeline, capsfilter2, "%s_out.txt" % name)
 
 	#
 	# done
@@ -137,6 +137,6 @@ def lal_logicalundersample_03(pipeline, name):
 #
 
 
-#test_common.build_and_run(lal_logicalundersample_01, "lal_logicalundersample_01")
-test_common.build_and_run(lal_logicalundersample_02, "lal_logicalundersample_02")
+test_common.build_and_run(lal_logicalundersample_01, "lal_logicalundersample_01")
+#test_common.build_and_run(lal_logicalundersample_02, "lal_logicalundersample_02")
 #test_common.build_and_run(lal_logicalundersample_03, "lal_logicalundersample_03")
-- 
GitLab