diff --git a/gstlal-calibration/gst/lal/gstlal_adaptivefirfilt.c b/gstlal-calibration/gst/lal/gstlal_adaptivefirfilt.c index a8ac24be1f04c48cd8df65f4e3e3855317b67862..39d5ec71eb345393a91e86ad984b921da51954d3 100644 --- a/gstlal-calibration/gst/lal/gstlal_adaptivefirfilt.c +++ b/gstlal-calibration/gst/lal/gstlal_adaptivefirfilt.c @@ -107,6 +107,7 @@ enum property { ARG_PHASE_MEASUREMENT_FREQUENCY, ARG_STATIC_FILTER, ARG_VARIABLE_FILTER_LENGTH, + ARG_MINIMIZE_FILTER_LENGTH, ARG_ADAPTIVE_FILTER, ARG_ADAPTIVE_FILTER_LENGTH, ARG_TUKEY_PARAM, @@ -193,7 +194,43 @@ static void write_filter(double *filter, char *element_name, char *filter_type, } -static void convolve(double *filter1, gint64 length1, double *filter2, gint64 length2, double *convolved_filter, gint64 convolved_length) { +double *convolve(double *filter1, int length1, double *filter2, int length2, double *convolved_filter) { + int i, j; + if(!convolved_filter) + convolved_filter = g_malloc((length1 + length2 - 1) * sizeof(*convolved_filter)); + if(convolved_filter == filter1) { + /* We are putting the result into the memory location of filter1, so we need to make a copy of filter1 */ + double *filter1_copy = g_malloc(length1 * sizeof(*filter1)); + memcpy(filter1_copy, filter1, length1 * sizeof(*filter1)); + memset(convolved_filter, 0, (length1 + length2 - 1) * sizeof(*convolved_filter)); + for(i = 0; i < length1; i++) { + for(j = 0; j < length2; j++) + convolved_filter[i + j] += filter1_copy[i] * filter2[j]; + } + g_free(filter1_copy); + } else if(convolved_filter == filter2) { + /* We are putting the result into the memory location of filter2, so we need to make a copy of filter2 */ + double *filter2_copy = g_malloc(length2 * sizeof(*filter2)); + memcpy(filter2_copy, filter2, length2 * sizeof(*filter2)); + memset(convolved_filter, 0, (length1 + length2 - 1) * sizeof(*convolved_filter)); + for(i = 0; i < length1; i++) { + for(j = 0; j < length2; j++) + convolved_filter[i + j] += filter1[i] * filter2_copy[j]; + } + g_free(filter2_copy); + } else { + /* All three memory locations are different */ + memset(convolved_filter, 0, (length1 + length2 - 1) * sizeof(*convolved_filter)); + for(i = 0; i < length1; i++) { + for(j = 0; j < length2; j++) + convolved_filter[i + j] += filter1[i] * filter2[j]; + } + } + return convolved_filter; +} + + +static void convolve_and_chop(double *filter1, gint64 length1, double *filter2, gint64 length2, double *convolved_filter, gint64 convolved_length) { gint64 i, j, chop_samples, j_start, j_stop; @@ -257,16 +294,16 @@ static gboolean update_variable_filter(complex double *variable_filter, gint64 v for(n = 0; n < fd_variable_filter_length; n++) { /* variable zeros */ for(m = 0; m < num_zeros; m++) - variable_filter[n] *= n * df - input_average[m]; + variable_filter[n] *= 1.0 + I * n * df / input_average[m]; /* variable poles */ for(m = num_zeros; m < num_zeros + num_poles; m++) - variable_filter[n] /= n * df - input_average[m]; + variable_filter[n] /= 1.0 + I * n * df / input_average[m]; /* static zeros */ for(m = 0; m < num_static_zeros; m++) - variable_filter[n] *= n * df - static_zeros[m]; + variable_filter[n] *= 1.0 + I * n * df / static_zeros[m]; /* static poles */ for(m = 0; m < num_static_poles; m++) - variable_filter[n] /= n * df - static_poles[m]; + variable_filter[n] /= 1.0 + I * n * df / static_poles[m]; } /* Make sure the DC and Nyquist components are purely real */ @@ -285,6 +322,54 @@ static gboolean update_variable_filter(complex double *variable_filter, gint64 v } +static gboolean update_variable_filter_minimized(double *variable_filter, int variable_filter_length, int filter_sample_rate, complex double *input_average, int num_zeros, gboolean filter_has_gain, complex double *static_zeros, int num_static_zeros) { + + int i; + + if(num_zeros) { + /* There is at least one zero computed from inputs. We compute a two-tap filter based on the first of these */ + *variable_filter = 0.5 + filter_sample_rate / (2.0 * M_PI * creal(*input_average)); + variable_filter[1] = 0.5 - filter_sample_rate / (2.0 * M_PI * creal(*input_average)); + } else if(num_static_zeros) { + /* There are no inputs that are zeros, but we wish to compute a two-tap filter from a static zero instead */ + *variable_filter = 0.5 + filter_sample_rate / (2.0 * M_PI * creal(*input_average)); + variable_filter[1] = 0.5 - filter_sample_rate / (2.0 * M_PI * creal(*input_average)); + } else { + /* There are no zeros, only a gain */ + *variable_filter = 1.0; + } + + if(num_zeros + num_static_zeros > 1) { + /* We will convolve the variable filter with more two-tap filters to complete it */ + double *tempfilt = g_malloc(2 * sizeof(*variable_filter)); + for(i = 1; i < num_zeros; i++) { + *tempfilt = 0.5 + filter_sample_rate / (2.0 * M_PI * creal(input_average[i])); + tempfilt[1] = 0.5 - filter_sample_rate / (2.0 * M_PI * creal(input_average[i])); + convolve(variable_filter, i + 1, tempfilt, 2, variable_filter); + } + for(i = num_zeros ? num_zeros : 1; i < num_zeros + num_static_zeros; i++) { + *tempfilt = 0.5 + filter_sample_rate / (2.0 * M_PI * creal(static_zeros[i - num_zeros])); + tempfilt[1] = 0.5 - filter_sample_rate / (2.0 * M_PI * creal(static_zeros[i - num_zeros])); + convolve(variable_filter, i + 1, tempfilt, 2, variable_filter); + } + g_free(tempfilt); + } + + /* Include a gain factor if there is one */ + if(filter_has_gain) { + for(i = 0; i < variable_filter_length; i++) + variable_filter[i] *= creal(input_average[num_zeros]); + } + + /* Check if the filter has sain values in it */ + gboolean success = TRUE; + for(i = 0; i < variable_filter_length; i++) + success &= isnormal(((double *) variable_filter)[i]); + + return success; +} + + #define DEFINE_AVERAGE_INPUT_DATA(DTYPE) \ static void average_input_data_ ## DTYPE(GSTLALAdaptiveFIRFilt *element, complex DTYPE *src, guint64 src_size, guint64 pts) { \ \ @@ -315,9 +400,14 @@ static void average_input_data_ ## DTYPE(GSTLALAdaptiveFIRFilt *element, complex element->input_average[j] /= element->num_in_avg; \ \ /* Update variable portion of adaptive FIR filter */ \ - if(update_variable_filter(element->variable_filter, element->variable_filter_length, element->filter_sample_rate, element->variable_filter_plan, element->input_average, element->num_zeros, element->num_poles, element->filter_has_gain, element->static_zeros, element->num_static_zeros, element->static_poles, element->num_static_poles, element->phase_measurement_frequency)) { \ + gboolean success; \ + if(element->minimize_filter_length) \ + success = update_variable_filter_minimized((double *) element->variable_filter, (int) element->variable_filter_length, element->filter_sample_rate, element->input_average, element->num_zeros, element->filter_has_gain, element->static_zeros, element->num_static_zeros); \ + else \ + success = update_variable_filter(element->variable_filter, element->variable_filter_length, element->filter_sample_rate, element->variable_filter_plan, element->input_average, element->num_zeros, element->num_poles, element->filter_has_gain, element->static_zeros, element->num_static_zeros, element->static_poles, element->num_static_poles, element->phase_measurement_frequency); \ + if(success) { \ /* Convolve the variable filter with the static filter */ \ - convolve((double *) element->variable_filter, element->variable_filter_length, element->static_filter, element->static_filter_length, element->adaptive_filter, element->adaptive_filter_length); \ + convolve_and_chop((double *) element->variable_filter, element->variable_filter_length, element->static_filter, element->static_filter_length, element->adaptive_filter, element->adaptive_filter_length); \ \ /* Apply the Tukey window so that the filter falls off smoothly at the edges */ \ for(i = 0; i < element->tukey_length; i++) { \ @@ -403,9 +493,15 @@ static gboolean start(GstBaseSink *sink) { *element->static_filter = 1.0; } - /* Sanity check */ + /* Sanity checks */ if(element->average_samples > element->update_samples) GST_ERROR_OBJECT(element, "average_samples cannot be greater than update_samples"); + if(element->minimize_filter_length) { + if(element->num_poles || element->num_static_poles) + GST_WARNING_OBJECT(element, "Cannot set option minimize_filter_length = True if there are poles. Keeping variable_filter_length as is."); + else + element->variable_filter_length = element->num_zeros + element->num_static_zeros + 1; + } return TRUE; } @@ -482,7 +578,7 @@ static gboolean set_caps(GstBaseSink *sink, GstCaps *caps) { for(i = 0; i < element->tukey_length; i++) element->tukey[i] = pow(sin(M_PI * i / 2.0 / element->tukey_length), 2.0); - if(!element->variable_filter) { + if(!element->variable_filter && !element->minimize_filter_length) { /* Allocate memory for fftw to do an inverse Fourier transform of the filter. */ gstlal_fftw_lock(); @@ -496,6 +592,9 @@ static gboolean set_caps(GstBaseSink *sink, GstCaps *caps) { gstlal_fftw_unlock(); + } else if (!element->variable_filter) { + /* Allocate memory for the variable filter, but in this case, no fft's are necessary */ + element->variable_filter = g_malloc(element->variable_filter_length * sizeof(*element->variable_filter) / 2); } return success; @@ -653,6 +752,10 @@ static void set_property(GObject *object, enum property id, const GValue *value, element->variable_filter_length = g_value_get_int64(value); break; + case ARG_MINIMIZE_FILTER_LENGTH: + element->minimize_filter_length = g_value_get_boolean(value); + break; + case ARG_ADAPTIVE_FILTER: if(element->adaptive_filter) { g_free(element->adaptive_filter); @@ -742,6 +845,10 @@ static void get_property(GObject *object, enum property id, GValue *value, GPara g_value_set_int64(value, element->variable_filter_length); break; + case ARG_MINIMIZE_FILTER_LENGTH: + g_value_set_boolean(value, element->minimize_filter_length); + break; + case ARG_ADAPTIVE_FILTER: if(element->adaptive_filter) g_value_take_boxed(value, gstlal_g_value_array_from_doubles(element->adaptive_filter, element->adaptive_filter_length)); @@ -835,11 +942,11 @@ static void gstlal_adaptivefirfilt_class_init(GSTLALAdaptiveFIRFiltClass *klass) "frequency, used to compute a gain and a frequency-independent time delay or\n\t\t\t " "advance. The filter applied is therefore:\n\t\t\t " "\n\t\t\t " - "\t product_i[f - f_i]\n\t\t\t " - " F(f) = ------------------ * K * exp(2 * pi * i * f * t_adv)\n\t\t\t " - "\t product_j[f - f_j]\n\t\t\t " + "\t product_m[1 + i * f / f0_m]\n\t\t\t " + " F(f) = --------------------------- * K * exp(2 * pi * i * f * t_adv)\n\t\t\t " + "\t product_n[1 + i * f / fp_n]\n\t\t\t " "\n\t\t\t " - "where the zeros f_i, poles f_j, gain K and time advance t_adv are computed\n\t\t\t " + "where the zeros f0_m, poles fp_n, gain K and time advance t_adv are computed\n\t\t\t " "internally from the input channels. If static zeros and/or poles are\n\t\t\t " "provided to this element, they will be included in the filter. Additionally,\n\t\t\t " "the element can convolve a provided static filter with the computed adaptive\n\t\t\t " @@ -952,6 +1059,15 @@ static void gstlal_adaptivefirfilt_class_init(GSTLALAdaptiveFIRFiltClass *klass) 1, G_MAXINT64, 16384, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT ); + properties[ARG_MINIMIZE_FILTER_LENGTH] = g_param_spec_boolean( + "minimize-filter-length", + "Minimize Filter Length", + "If true, the adaptive FIR filter will have the minimum number of taps needed\n\t\t\t" + "to model the given zeros (one more than the number of zeros). This cannot be\n\t\t\t" + "done if there are poles.", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT + ); properties[ARG_ADAPTIVE_FILTER] = g_param_spec_value_array( "adaptive-filter", "Adaptive Filter", @@ -1056,6 +1172,11 @@ static void gstlal_adaptivefirfilt_class_init(GSTLALAdaptiveFIRFiltClass *klass) ARG_VARIABLE_FILTER_LENGTH, properties[ARG_VARIABLE_FILTER_LENGTH] ); + g_object_class_install_property( + gobject_class, + ARG_MINIMIZE_FILTER_LENGTH, + properties[ARG_MINIMIZE_FILTER_LENGTH] + ); g_object_class_install_property( gobject_class, ARG_ADAPTIVE_FILTER, diff --git a/gstlal-calibration/gst/lal/gstlal_adaptivefirfilt.h b/gstlal-calibration/gst/lal/gstlal_adaptivefirfilt.h index a26a4979dd3ca298763b9d82a4bee0b94a9af673..b0699988123d311b9dd8d16f6c81ab3778868ebb 100644 --- a/gstlal-calibration/gst/lal/gstlal_adaptivefirfilt.h +++ b/gstlal-calibration/gst/lal/gstlal_adaptivefirfilt.h @@ -91,6 +91,7 @@ struct _GSTLALAdaptiveFIRFilt { double *static_filter; gint64 static_filter_length; gint64 variable_filter_length; + gboolean minimize_filter_length; double *adaptive_filter; gint64 adaptive_filter_length; double tukey_param;