diff --git a/gstlal-burst/gst/lal/gstlal_string_triggergen.c b/gstlal-burst/gst/lal/gstlal_string_triggergen.c index 526872bc3a7e28916a687a080dc414c45887ca2a..670ff08a7d62371fa99929252900e175a888ef6a 100644 --- a/gstlal-burst/gst/lal/gstlal_string_triggergen.c +++ b/gstlal-burst/gst/lal/gstlal_string_triggergen.c @@ -153,6 +153,36 @@ static int setup_bankfile_input(GSTLALStringTriggergen *element, char *bank_file } +static int exists_unrealized_triggers(GSTLALStringTriggergen *element) +{ + gint channel; + + for(channel = 0; channel < element->num_templates; channel++) + if(element->bank[channel].snr != 0.) + return TRUE; + + return FALSE; +} + + +/* + * return min(time of offset, min(times of realized triggers)) + */ + + +static GstClockTime buffer_pts(GSTLALStringTriggergen *element, guint64 offset) +{ + gint channel; + /* time of first SNR sample that can be a trigger */ + GstClockTime t = element->t0 + gst_util_uint64_scale_int_round(offset + (autocorrelation_length(element->autocorrelation_matrix) - 1) / 2 - element->offset0, GST_SECOND, GST_AUDIO_INFO_RATE(&element->audio_info)); + /* scan for unrealized triggers with an earlier time */ + for(channel = 0; channel < element->num_templates; channel++) + if(element->bank[channel].snr > 0.0 && (GstClockTime) XLALGPSToINT8NS(&element->bank[channel].peak_time) < t) + t = XLALGPSToINT8NS(&element->bank[channel].peak_time); + return t; +} + + /* * compute autocorrelation norms --- the expectation value in noise. */ @@ -198,50 +228,24 @@ static gsl_vector_float *gstlal_autocorrelation_chi2_compute_norms_string(const */ -static GstClockTime buffer_pts(GSTLALStringTriggergen *element, guint64 offset) -{ - gint channel; - /* start time of SNR used to construct buffer */ - GstClockTime t = element->t0 + gst_util_uint64_scale_int_round(offset + (autocorrelation_length(element->autocorrelation_matrix) - 1) / 2 - element->offset0, GST_SECOND, GST_AUDIO_INFO_RATE(&element->audio_info)); - /* pin buffer start time at earliest unrealized trigger */ - for(channel = 0; channel < element->num_templates; channel++) - if(element->bank[channel].snr > 0.0 && (GstClockTime) XLALGPSToINT8NS(&element->bank[channel].peak_time) < t) - t = XLALGPSToINT8NS(&element->bank[channel].peak_time); - return t; -} - - static GstFlowReturn trigger_generator(GSTLALStringTriggergen *element, GstBuffer *outbuf) { float *snrdata; float *snrsample; SnglBurst *triggers = NULL; guint ntriggers = 0; - guint64 offset; - guint64 length; + guint64 offset = gst_audioadapter_offset(element->adapter); + guint64 length = get_available_samples(element); guint sample; gint channel; - g_mutex_lock(&element->bank_lock); - - /* check that autocorrelation vector has odd number of samples. - * NOTE: autocorrelation_length() returns 0 if the - * autocorrelation_matrix is not set, so this g_assert also tests - * for a missing autocorrelation_matrix. in set_property(), if the - * conversion from GValueArray fails the matrix will be left set to - * NULL, so this is also catching those failures. */ - g_assert(autocorrelation_length(element->autocorrelation_matrix) & 1); + /* + * obtain PTS and DURATION of output buffer. must be done before + * generating triggers + */ - /* do we have enough SNR to search for triggers? */ - offset = gst_audioadapter_offset(element->adapter); - length = get_available_samples(element); - if(length < autocorrelation_length(element->autocorrelation_matrix)) { - GST_BUFFER_PTS(outbuf) = buffer_pts(element, offset); - GST_BUFFER_DURATION(outbuf) = 0; - GST_BUFFER_OFFSET_END(outbuf) = GST_BUFFER_OFFSET(outbuf) + ntriggers; - g_mutex_unlock(&element->bank_lock); - return GST_FLOW_OK; - } + GST_BUFFER_PTS(outbuf) = buffer_pts(element, offset); + GST_BUFFER_DURATION(outbuf) = element->t0 + gst_util_uint64_scale_int_round(offset + length - (autocorrelation_length(element->autocorrelation_matrix) - 1) / 2 - element->offset0, GST_SECOND, GST_AUDIO_INFO_RATE(&element->audio_info)) - GST_BUFFER_PTS(outbuf); /* copy samples */ snrsample = snrdata = g_malloc(length * element->num_templates * sizeof(*snrdata)); @@ -267,32 +271,31 @@ static GstFlowReturn trigger_generator(GSTLALStringTriggergen *element, GstBuffe float snr = fabsf(*snrsample); if(snr >= element->threshold) { /* - * If there was a discontinuity (e.g. gap) that made this sample and the last sample above threshold larger than - * the clustering time window, pass the previous trigger and reset the SNR so as to start with a new trigger. + * If this is the first sample above + * threshold (i.e. snr of trigger is + * (re)set to 0), record the start time. */ - if(element->bank[channel].snr > element->threshold && XLALGPSDiff(&t, &element->last_time[channel]) > element->cluster) { - triggers = g_renew(SnglBurst, triggers, ntriggers + 1); - triggers[ntriggers++] = element->bank[channel]; - element->bank[channel].snr = 0.0; - element->bank[channel].chisq = 0.0; - element->bank[channel].chisq_dof = 0.0; - } - /* - * If this is the first sample above threshold (i.e. snr of trigger is (re)set to 0), record the start time. - */ if(element->bank[channel].snr < element->threshold) element->bank[channel].start_time = t; + /* - * Keep track of last time above threshold and the duration. - * For duration add a sample of fuzz on both sides (like in lalapps_StringSearch). + * Keep track of last time above threshold + * and the duration. For duration add a + * sample of fuzz on both sides (like in + * lalapps_StringSearch). FIXME: do we + * need the fuzz? */ + element->last_time[channel] = t; - element->bank[channel].duration = XLALGPSDiff(&element->last_time[channel], &element->bank[channel].start_time) + (float) 2.0 / GST_AUDIO_INFO_RATE(&element->audio_info); + element->bank[channel].duration = XLALGPSDiff(&t, &element->bank[channel].start_time) + (float) 2.0 / GST_AUDIO_INFO_RATE(&element->audio_info); + + /* + * if this sample is the highest SNR, + * update the trigger + */ + if(snr > element->bank[channel].snr) { - /* - * Higher SNR than the "current winner". Update. - */ const float *autocorrelation = (const float *) gsl_matrix_float_const_ptr(element->autocorrelation_matrix, channel, 0); const float *autocorrelation_end = autocorrelation + autocorrelation_length(element->autocorrelation_matrix); float *snrseries = snrsample - (autocorrelation_length(element->autocorrelation_matrix) - 1) / 2 * element->num_templates; @@ -309,9 +312,12 @@ static GstFlowReturn trigger_generator(GSTLALStringTriggergen *element, GstBuffe } } else if(element->bank[channel].snr != 0. && XLALGPSDiff(&t, &element->last_time[channel]) > element->cluster) { /* - * Trigger is ready to be passed. - * Push trigger to buffer, and reset it. + * there is a trigger ready to be pushed, + * and we have been below threshold for + * longer than the clustering time, so make + * a new trigger and reset this template */ + triggers = g_renew(SnglBurst, triggers, ntriggers + 1); triggers[ntriggers++] = element->bank[channel]; element->bank[channel].snr = 0.0; @@ -320,7 +326,6 @@ static GstFlowReturn trigger_generator(GSTLALStringTriggergen *element, GstBuffe } } } - g_mutex_unlock(&element->bank_lock); g_free(snrdata); gst_audioadapter_flush_samples(element->adapter, length - (autocorrelation_length(element->autocorrelation_matrix) - 1)); @@ -330,13 +335,6 @@ static GstFlowReturn trigger_generator(GSTLALStringTriggergen *element, GstBuffe else gst_buffer_remove_all_memory(outbuf); - /* - * obtain PTS and DURATION of output buffer. - */ - - GST_BUFFER_PTS(outbuf) = buffer_pts(element, offset); - GST_BUFFER_DURATION(outbuf) = element->t0 + gst_util_uint64_scale_int_round(offset + length - (autocorrelation_length(element->autocorrelation_matrix) - 1) / 2 - element->offset0, GST_SECOND, GST_AUDIO_INFO_RATE(&element->audio_info)) - GST_BUFFER_PTS(outbuf); - GST_BUFFER_OFFSET_END(outbuf) = GST_BUFFER_OFFSET(outbuf) + ntriggers; return GST_FLOW_OK; @@ -481,7 +479,6 @@ static gboolean start(GstBaseTransform *trans) element->t0 = GST_CLOCK_TIME_NONE; element->offset0 = GST_BUFFER_OFFSET_NONE; element->next_in_offset = GST_BUFFER_OFFSET_NONE; - element->next_out_offset = GST_BUFFER_OFFSET_NONE; element->need_discont = TRUE; } @@ -511,43 +508,102 @@ static GstFlowReturn transform(GstBaseTransform *trans, GstBuffer *inbuf, GstBuf { GSTLALStringTriggergen *element = GSTLAL_STRING_TRIGGERGEN(trans); guint64 length; + guint64 offset; GstFlowReturn result; + /* FIXME: this code isn't setting the output offset to anything meaningful */ + g_assert(GST_BUFFER_PTS_IS_VALID(inbuf)); g_assert(GST_BUFFER_DURATION_IS_VALID(inbuf)); g_assert(GST_BUFFER_OFFSET_IS_VALID(inbuf)); g_assert(GST_BUFFER_OFFSET_END_IS_VALID(inbuf)); - + if(GST_BUFFER_IS_DISCONT(inbuf) || GST_BUFFER_OFFSET(inbuf) != element->next_in_offset || !GST_CLOCK_TIME_IS_VALID(element->t0)) { gst_audioadapter_clear(element->adapter); element->t0 = GST_BUFFER_PTS(inbuf); element->offset0 = GST_BUFFER_OFFSET(inbuf); - element->next_out_offset = GST_BUFFER_OFFSET(inbuf); } else if(!gst_audioadapter_is_empty(element->adapter)) g_assert_cmpuint(GST_BUFFER_PTS(inbuf), ==, gst_audioadapter_expected_timestamp(element->adapter)); element->next_in_offset = GST_BUFFER_OFFSET_END(inbuf); + /* check that autocorrelation vector has odd number of samples. + * NOTE: autocorrelation_length() returns 0 if the + * autocorrelation_matrix is not set, so this g_assert also tests + * for a missing autocorrelation_matrix. in set_property(), if the + * conversion from GValueArray fails the matrix will be left set to + * NULL, so this is also catching those failures. */ + g_assert(autocorrelation_length(element->autocorrelation_matrix) & 1); + /* * gap logic */ gst_buffer_ref(inbuf); gst_audioadapter_push(element->adapter, inbuf); - if (!GST_BUFFER_FLAG_IS_SET(inbuf, GST_BUFFER_FLAG_GAP)) { - /* not gaps */ + length = get_available_samples(element); + offset = gst_audioadapter_offset(element->adapter); + g_mutex_lock(&element->bank_lock); + if(length < autocorrelation_length(element->autocorrelation_matrix)) { + /* not enough SNR to search for triggers */ + GST_BUFFER_PTS(outbuf) = buffer_pts(element, offset); + GST_BUFFER_DURATION(outbuf) = 0; + GST_BUFFER_OFFSET_END(outbuf) = GST_BUFFER_OFFSET(outbuf) + 0 /* ntriggers */; + result = GST_FLOW_OK; + } else if(!GST_BUFFER_FLAG_IS_SET(inbuf, GST_BUFFER_FLAG_GAP)) { + /* have enough SNR to make triggers, and this buffer is not a gap */ result = trigger_generator(element, outbuf); - } else { - /* gaps */ - length = get_available_samples(element); - element->next_out_offset += length; - gst_audioadapter_flush_samples(element->adapter, length); - GST_BUFFER_PTS(outbuf) = element->t0 + gst_util_uint64_scale_int_round(element->next_out_offset - element->offset0, GST_SECOND, GST_AUDIO_INFO_RATE(&element->audio_info)); - GST_BUFFER_DURATION(outbuf) = gst_util_uint64_scale_int_round(length, GST_SECOND, GST_AUDIO_INFO_RATE(&element->audio_info)); - /* we get no triggers, so outbuf offset is unchanged */ - GST_BUFFER_OFFSET_END(outbuf) = GST_BUFFER_OFFSET(outbuf); + } else if(!exists_unrealized_triggers(element)) { + /* this is a gap, there are no unrealized triggers waiting */ + gst_audioadapter_flush_samples(element->adapter, length - (autocorrelation_length(element->autocorrelation_matrix) - 1)); + GST_BUFFER_PTS(outbuf) = buffer_pts(element, offset); + GST_BUFFER_DURATION(outbuf) = element->t0 + gst_util_uint64_scale_int_round(offset + length - (autocorrelation_length(element->autocorrelation_matrix) - 1) / 2 - element->offset0, GST_SECOND, GST_AUDIO_INFO_RATE(&element->audio_info)) - GST_BUFFER_PTS(outbuf); + GST_BUFFER_OFFSET_END(outbuf) = GST_BUFFER_OFFSET(outbuf) + 0 /* ntriggers */; GST_BUFFER_FLAG_SET(outbuf, GST_BUFFER_FLAG_GAP); result = GST_FLOW_OK; + } else { + /* this is a gap, there are still unrealized triggers waiting */ + /* FIXME: there are really two cases: we have unrealized + * triggers waiting and the input gap buffer will push them + * all beyond the clustering time (forcing them all to be + * cleared), or it isn't (some might remain for next time). + * in the former case, we can produce two buffers: a + * non-gap buffer containing triggers, followed by a gap + * buffer representing the period of input gap data which + * is guaranteed not to contain triggers. for now we are + * lazy and just produce one non-gap buffer, which might or + * might not contain triggers */ + SnglBurst *triggers = NULL; + guint ntriggers = 0; + LIGOTimeGPS t; + + /* set output buffer properties. must be done before clearing triggers */ + GST_BUFFER_PTS(outbuf) = buffer_pts(element, offset); + GST_BUFFER_DURATION(outbuf) = element->t0 + gst_util_uint64_scale_int_round(offset + length - (autocorrelation_length(element->autocorrelation_matrix) - 1) / 2 - element->offset0, GST_SECOND, GST_AUDIO_INFO_RATE(&element->audio_info)) - GST_BUFFER_PTS(outbuf); + + /* has the clustering time passed for any of the unrealized triggers? */ + XLALINT8NSToGPS(&t, GST_BUFFER_PTS(outbuf) + GST_BUFFER_DURATION(outbuf)); + for(gint channel = 0; channel < element->num_templates; channel++) { + if(element->bank[channel].snr != 0. && XLALGPSDiff(&t, &element->last_time[channel]) > element->cluster){ + /* this channel has made a trigger */ + triggers = g_renew(SnglBurst, triggers, ntriggers + 1); + triggers[ntriggers++] = element->bank[channel]; + element->bank[channel].snr = 0.0; + element->bank[channel].chisq = 0.0; + element->bank[channel].chisq_dof = 0.0; + } + } + + gst_audioadapter_flush_samples(element->adapter, length - (autocorrelation_length(element->autocorrelation_matrix) - 1)); + + if(ntriggers) + gst_buffer_replace_all_memory(outbuf, gst_memory_new_wrapped(GST_MEMORY_FLAG_PHYSICALLY_CONTIGUOUS, triggers, ntriggers * sizeof(*triggers), 0, ntriggers * sizeof(*triggers), triggers, g_free)); + else + gst_buffer_remove_all_memory(outbuf); + + GST_BUFFER_OFFSET_END(outbuf) = GST_BUFFER_OFFSET(outbuf) + ntriggers; + result = GST_FLOW_OK; } + g_mutex_unlock(&element->bank_lock); /* * done @@ -739,6 +795,7 @@ static void gstlal_string_triggergen_class_init(GSTLALStringTriggergenClass *kla transform_class->transform = GST_DEBUG_FUNCPTR(transform); transform_class->transform_caps = GST_DEBUG_FUNCPTR(transform_caps); transform_class->start = GST_DEBUG_FUNCPTR(start); + /* FIXME: add a stop method to push any final unrealized triggers */ transform_class->prepare_output_buffer = GST_DEBUG_FUNCPTR(prepare_output_buffer); g_object_class_install_property( diff --git a/gstlal-burst/gst/lal/gstlal_string_triggergen.h b/gstlal-burst/gst/lal/gstlal_string_triggergen.h index 3f13ceec70e42d8c9650ab589754ff89bef3f378..245f9fbc150352bde39ca4153d58f3449bbb2645 100644 --- a/gstlal-burst/gst/lal/gstlal_string_triggergen.h +++ b/gstlal-burst/gst/lal/gstlal_string_triggergen.h @@ -54,7 +54,6 @@ typedef struct { GstClockTime t0; guint64 offset0; guint64 next_in_offset; - guint64 next_out_offset; gboolean need_discont; /*