Newer
Older
Aaron Viets
committed
/*
* Copyright (C) 2009--2012,2014,2015, 2016 Kipp Cannon <kipp.cannon@ligo.org>, Madeline Wade <madeline.wade@ligo.org>, Aaron Viets <aaron.viets@ligo.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/**
* SECTION:gstlal_smoothkappas
* @short_description: Smooths the calibration factors (kappas) using a
* running median.
Aaron Viets
committed
*
* This element smooths the kappas using a running median of an array
* whose size is set by the property array-size. When a new raw value
* is entered into the array, it replaces the oldest value in the array
* (first in, first out). When this element receives a gap as input, it
* will output a default kappa value (set by the property default-kappa)
* until it receives a buffer that is not flagged as a gap.
Aaron Viets
committed
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
*/
/*
* ============================================================================
*
* Preamble
*
* ============================================================================
*/
/*
* stuff from C
*/
#include <math.h>
#include <string.h>
/*
* stuff from gobject/gstreamer
*/
#include <glib.h>
#include <gst/gst.h>
#include <gst/audio/audio.h>
#include <gst/base/gstbasetransform.h>
/*
* our own stuff
*/
#include <gstlal/gstlal.h>
#include <gstlal/gstlal_debug.h>
#include <gstlal/gstlal_audio_info.h>
#include <gstlal_smoothkappas.h>
/*
* ============================================================================
*
* Parameters
*
* ============================================================================
*/
/*
* ============================================================================
*
* GStreamer Boiler Plate
*
* ============================================================================
*/
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE(
GST_BASE_TRANSFORM_SINK_NAME,
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS(
"audio/x-raw, " \
"rate = (int) [1, MAX], " \
"channels = (int) 1, " \
"format = (string) { " GST_AUDIO_NE(F32) ", " GST_AUDIO_NE(F64) " }, " \
"layout = (string) interleaved, " \
"channel-mask = (bitmask) 0"
)
);
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE(
GST_BASE_TRANSFORM_SRC_NAME,
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS(
"audio/x-raw, " \
"rate = (int) [1, MAX], " \
"channels = (int) 1, " \
"format = (string) { " GST_AUDIO_NE(F32) ", " GST_AUDIO_NE(F64) " }, " \
"layout = (string) interleaved, " \
"channel-mask = (bitmask) 0"
)
);
#define GST_CAT_DEFAULT gstlal_smoothkappas_debug
GST_DEBUG_CATEGORY_STATIC(GST_CAT_DEFAULT);
G_DEFINE_TYPE_WITH_CODE(
GSTLALSmoothKappas,
gstlal_smoothkappas,
GST_TYPE_BASE_TRANSFORM,
GST_DEBUG_CATEGORY_INIT(GST_CAT_DEFAULT, "lal_smoothkappas", 0, "lal_smoothkappas element")
);
/*
* ============================================================================
*
* Utilities
*
* ============================================================================
*/
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
static double get_new_median(double new_element, double *fifo_array, double *current_median, gint array_size) {
static int i;
if(!i)
i = 0;
fifo_array[i] = new_element;
if(i < array_size - 1)
i++;
else
i -= (array_size - 1);
int j, number_less, number_greater, number_equal;
double first_greater, second_greater, first_less, second_less;
number_less = 0;
number_equal = 0;
number_greater = 0;
first_greater = G_MAXDOUBLE;
second_greater = G_MAXDOUBLE;
first_less = -G_MAXDOUBLE;
second_less = -G_MAXDOUBLE;
for(j = 0; j < array_size; j++) {
if(fifo_array[j] < *current_median) {
number_less++;
if(fifo_array[j] > first_less) {
second_less = first_less;
first_less = fifo_array[j];
} else if(fifo_array[j] > second_less)
second_less = fifo_array[j];
}
else if(fifo_array[j] == *current_median)
number_equal++;
else if(fifo_array[j] > *current_median) {
number_greater++;
if(fifo_array[j] < first_greater) {
second_greater = first_greater;
first_greater = fifo_array[j];
} else if(fifo_array[j] < second_greater)
second_greater = fifo_array[j];
}
else
g_assert_not_reached();
}
g_assert_cmpint(number_less + number_equal + number_greater, ==, array_size);
if((!(array_size % 2)) && (number_less == array_size / 2) && (number_greater == array_size / 2))
*current_median = (first_greater + first_less) / 2;
else if((!(array_size % 2)) && (number_greater > array_size / 2))
*current_median = (first_greater + second_greater) / 2;
else if((!(array_size % 2)) && (number_less > array_size / 2))
*current_median = (first_less + second_less) / 2;
else if((!(array_size % 2)) && (number_greater == array_size / 2) && (number_less < array_size / 2))
*current_median = (*current_median + first_greater) / 2;
else if((!(array_size % 2)) && (number_less == array_size / 2) && (number_greater < array_size / 2))
*current_median = (*current_median + first_less) / 2;
else if((array_size % 2) && (number_greater > array_size / 2))
*current_median = first_greater;
else if((array_size % 2) && (number_less > array_size / 2))
*current_median = first_less;
return *current_median;
Aaron Viets
committed
}
#define DEFINE_SMOOTH_BUFFER(DTYPE) \
static GstFlowReturn smooth_buffer_ ## DTYPE(const DTYPE *src, DTYPE *dst, gint buffer_size, double *fifo_array, double default_kappa, double *current_median, double maximum_offset, gint array_size, gboolean gap, gboolean default_to_median) { \
gint i; \
DTYPE new_element; \
for(i = 0; i < buffer_size; i++) { \
if(gap || (double) *src > default_kappa + maximum_offset || (double) *src < default_kappa - maximum_offset) { \
if(default_to_median) \
new_element = *current_median; \
else \
new_element = default_kappa; \
} else \
new_element = *src; \
*dst = (DTYPE) get_new_median((double) new_element, fifo_array, current_median, array_size); \
src++; \
dst++; \
Aaron Viets
committed
} \
return GST_FLOW_OK; \
Aaron Viets
committed
}
DEFINE_SMOOTH_BUFFER(float);
DEFINE_SMOOTH_BUFFER(double);
Aaron Viets
committed
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
/*
* ============================================================================
*
* GstBaseTransform Method Overrides
*
* ============================================================================
*/
/*
* get_unit_size()
*/
static gboolean get_unit_size(GstBaseTransform *trans, GstCaps *caps, gsize *size)
{
GSTLALSmoothKappas *element = GSTLAL_SMOOTHKAPPAS(trans);
GstAudioInfo info;
gboolean success = gstlal_audio_info_from_caps(&info, caps);
if(success) {
*size = GST_AUDIO_INFO_BPF(&info);
element->unit_size = *size;
} else
GST_WARNING_OBJECT(trans, "unable to parse caps %" GST_PTR_FORMAT, caps);
return success;
}
/*
* set_caps()
*/
static gboolean set_caps(GstBaseTransform *trans, GstCaps *incaps, GstCaps *outcaps)
{
GSTLALSmoothKappas *element = GSTLAL_SMOOTHKAPPAS(trans);
GstAudioInfo info;
gboolean success = TRUE;
/*
* parse the caps
*/
success = gstlal_audio_info_from_caps(&info, incaps);
if(!success)
GST_ERROR_OBJECT(element, "unable to parse caps %" GST_PTR_FORMAT, incaps);
Aaron Viets
committed
/*
* set the unit size
Aaron Viets
committed
*/
if(success) {
switch(GST_AUDIO_INFO_FORMAT(&info)) {
case GST_AUDIO_FORMAT_F32:
element->unit_size = 4;
break;
case GST_AUDIO_FORMAT_F64:
element->unit_size = 8;
break;
default:
GST_ERROR_OBJECT(element, "unsupported format %" GST_PTR_FORMAT, incaps);
Aaron Viets
committed
success = FALSE;
break;
}
}
return success;
}
/*
* start()
*/
static gboolean start(GstBaseTransform *trans)
{
GSTLALSmoothKappas *element = GSTLAL_SMOOTHKAPPAS(trans);
element->current_median = element->default_kappa;
int i;
element->fifo_array = g_malloc(sizeof(double) * element->array_size);
for(i = 0; i < element->array_size; i++, (element->fifo_array)++)
*(element->fifo_array) = element->default_kappa;
(element->fifo_array) -= element->array_size;
return TRUE;
}
Aaron Viets
committed
/*
* transform()
*/
static GstFlowReturn transform(GstBaseTransform *trans, GstBuffer *inbuf, GstBuffer *outbuf)
{
GSTLALSmoothKappas *element = GSTLAL_SMOOTHKAPPAS(trans);
GstMapInfo inmap, outmap;
GstFlowReturn result;
GST_INFO_OBJECT(element, "processing %s%s buffer %p spanning %" GST_BUFFER_BOUNDARIES_FORMAT, GST_BUFFER_FLAG_IS_SET(inbuf, GST_BUFFER_FLAG_GAP) ? "gap" : "nongap", GST_BUFFER_FLAG_IS_SET(inbuf, GST_BUFFER_FLAG_DISCONT) ? "+discont" : "", inbuf, GST_BUFFER_BOUNDARIES_ARGS(inbuf));
Aaron Viets
committed
gst_buffer_map(outbuf, &outmap, GST_MAP_WRITE);
gboolean gap = GST_BUFFER_FLAG_IS_SET(inbuf, GST_BUFFER_FLAG_GAP);
gst_buffer_map(inbuf, &inmap, GST_MAP_READ);
Aaron Viets
committed
g_assert_cmpuint(inmap.size % element->unit_size, ==, 0);
g_assert_cmpuint(outmap.size % element->unit_size, ==, 0);
g_assert_cmpuint(inmap.size, ==, outmap.size);
if(element->unit_size == 4) {
gint buffer_size = outmap.size / element->unit_size;
result = smooth_buffer_float((const float *) inmap.data, (float *) outmap.data, buffer_size, element->fifo_array, element->default_kappa, &element->current_median, element->maximum_offset, element->array_size, gap, element->default_to_median);
Aaron Viets
committed
} else if(element->unit_size == 8) {
gint buffer_size = outmap.size / element->unit_size;
result = smooth_buffer_double((const double *) inmap.data, (double *) outmap.data, buffer_size, element->fifo_array, element->default_kappa, &element->current_median, element->maximum_offset, element->array_size, gap, element->default_to_median);
Aaron Viets
committed
} else {
g_assert_not_reached();
}
GST_BUFFER_FLAG_UNSET(outbuf, GST_BUFFER_FLAG_GAP);
Aaron Viets
committed
gst_buffer_unmap(inbuf, &inmap);
gst_buffer_unmap(outbuf, &outmap);
/*
* done
*/
return result;
Aaron Viets
committed
}
/*
* ============================================================================
*
* GObject Method Overrides
*
* ============================================================================
*/
/*
* properties
*/
enum property {
ARG_ARRAY_SIZE = 1,
ARG_DEFAULT_KAPPA,
ARG_MAXIMUM_OFFSET,
ARG_DEFAULT_TO_MEDIAN
Aaron Viets
committed
};
static void set_property(GObject *object, enum property prop_id, const GValue *value, GParamSpec *pspec)
{
GSTLALSmoothKappas *element = GSTLAL_SMOOTHKAPPAS(object);
GST_OBJECT_LOCK(element);
switch (prop_id) {
case ARG_ARRAY_SIZE:
element->array_size = g_value_get_int(value);
Aaron Viets
committed
break;
case ARG_DEFAULT_KAPPA:
element->default_kappa = g_value_get_double(value);
break;
case ARG_MAXIMUM_OFFSET:
element->maximum_offset = g_value_get_double(value);
break;
case ARG_DEFAULT_TO_MEDIAN:
element->default_to_median = g_value_get_boolean(value);
break;
Aaron Viets
committed
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
GST_OBJECT_UNLOCK(element);
}
static void get_property(GObject *object, enum property prop_id, GValue *value, GParamSpec *pspec)
{
GSTLALSmoothKappas *element = GSTLAL_SMOOTHKAPPAS(object);
GST_OBJECT_LOCK(element);
switch (prop_id) {
case ARG_ARRAY_SIZE:
g_value_set_int(value, element->array_size);
Aaron Viets
committed
break;
case ARG_DEFAULT_KAPPA:
g_value_set_double(value, element->default_kappa);
break;
case ARG_MAXIMUM_OFFSET:
g_value_set_double(value, element->maximum_offset);
break;
case ARG_DEFAULT_TO_MEDIAN:
g_value_set_boolean(value, element->default_to_median);
break;
Aaron Viets
committed
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
GST_OBJECT_UNLOCK(element);
}
/*
* finalize()
*/
static void finalize(GObject *object)
{
GSTLALSmoothKappas *element = GSTLAL_SMOOTHKAPPAS(object);
g_free(element->fifo_array);
element->fifo_array = NULL;
G_OBJECT_CLASS(gstlal_smoothkappas_parent_class)->finalize(object);
}
Aaron Viets
committed
/*
* class_init()
*/
static void gstlal_smoothkappas_class_init(GSTLALSmoothKappasClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
GstBaseTransformClass *transform_class = GST_BASE_TRANSFORM_CLASS(klass);
gst_element_class_set_details_simple(
element_class,
"Smooth Calibration Factors",
"Filter/Audio",
"Smooths the calibration factors with a running median.",
Aaron Viets
committed
"Madeline Wade <madeline.wade@ligo.org>, Aaron Viets <aaron.viets@ligo.org>"
);
gobject_class->set_property = GST_DEBUG_FUNCPTR(set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR(get_property);
gobject_class->finalize = GST_DEBUG_FUNCPTR(finalize);
Aaron Viets
committed
transform_class->get_unit_size = GST_DEBUG_FUNCPTR(get_unit_size);
transform_class->set_caps = GST_DEBUG_FUNCPTR(set_caps);
transform_class->start = GST_DEBUG_FUNCPTR(start);
Aaron Viets
committed
transform_class->transform = GST_DEBUG_FUNCPTR(transform);
gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&src_factory));
gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&sink_factory));
g_object_class_install_property(
gobject_class,
ARG_ARRAY_SIZE,
Aaron Viets
committed
g_param_spec_int(
"array-size",
"Median array size",
"Size of the array of values from which the median is calculated",
Aaron Viets
committed
G_MININT, G_MAXINT, 2048,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT
)
);
g_object_class_install_property(
gobject_class,
ARG_DEFAULT_KAPPA,
Aaron Viets
committed
g_param_spec_double(
"default-kappa",
"Default kappa value",
"Default kappa value to be used if there is a gap in the incoming buffer, or if no input values pass kappa-offset criteria. All elements of the fifo array are initialized to this value.",
-G_MAXDOUBLE, G_MAXDOUBLE, 1.0,
Aaron Viets
committed
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT
)
);
g_object_class_install_property(
gobject_class,
ARG_MAXIMUM_OFFSET,
Aaron Viets
committed
g_param_spec_double(
"maximum-offset",
"Maximum acceptable kappa offset",
"Maximum acceptable offset of unsmoothed kappa from default-kappa to be entered into array from which median is calculated.",
0, G_MAXDOUBLE, G_MAXDOUBLE,
Aaron Viets
committed
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT
)
);
g_object_class_install_property(
gobject_class,
ARG_DEFAULT_TO_MEDIAN,
g_param_spec_boolean(
"default-to-median",
"Default to median",
"If set to false (default), gaps (or times where input values do not pass kappa-offset criteria) are filled in by entering default-kappa into the fifo array. If set to true, gaps are filled in by entering the current median value into the fifo array.",
FALSE,
Aaron Viets
committed
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT
)
);
}
/*
* init()
*/
static void gstlal_smoothkappas_init(GSTLALSmoothKappas *element)
{
element->unit_size = 0;
element->array_size = 0;
element->fifo_array = NULL;
Aaron Viets
committed
gst_base_transform_set_qos_enabled(GST_BASE_TRANSFORM(element), TRUE);
gst_base_transform_set_gap_aware(GST_BASE_TRANSFORM(element), TRUE);