34gi.require_version(
'Gst',
'1.0')
35gi.require_version(
"GstAudio",
"1.0")
36from gi.repository
import GObject
37from gi.repository
import Gst, GstAudio
40from ligo
import segments
42from gstlal
import kernels
43from gstlal
import pipeparts
44from gstlal.psd
import interpolate_psd
45from gstlal
import pipeio
51+------------------------------------------------+------------------------------------------+------------+
52| Names | Hash | Date |
53+================================================+==========================================+============+
54| Florent, Sathya, Duncan Me, Jolien, Kipp, Chad | 8a6ea41398be79c00bdc27456ddeb1b590b0f68e | 2014-06-18 |
55+------------------------------------------------+------------------------------------------+------------+
60 if int(os.environ[
"GSTLAL_FIR_WHITEN"]):
65 print(
"You must set the environment variable GSTLAL_FIR_WHITEN to either 0 or 1. 1 enables causal whitening. 0 is the traditional acausal whitening filter", file=sys.stderr)
69def mkcondition(pipeline, src, target_rate, instrument, psd = None, psd_fft_length = 32, ht_gate_threshold = float(
"inf"), idq_gate_threshold = float(
"inf"), veto_segments =
None, nxydump_segment =
None, track_psd =
False, block_duration = Gst.SECOND // 4, zero_pad =
None, width = 64, statevector =
None, dqvector =
None, idq_series =
None, idq_channel_name =
None, fir_whiten_reference_psd =
None, track_latency =
False):
71 Build pipeline stage to whiten and downsample h(t).
73 * pipeline: the gstreamer pipeline to add this to
74 * src: the gstreamer element that will be providing data to this
75 * target_rate: the requested sample rate.
76 * instrument: the instrument to process
77 * psd: a psd frequency series
78 * psd_fft_length: length of fft used for whitening
79 * ht_gate_threshold: gate h(t) if it crosses this value
80 * veto_segments: segments to mark as gaps after whitening
81 * track_psd: decide whether to dynamically track the spectrum or use the fixed spectrum provided
82 * width: type convert to either 32 or 64 bit float
83 * fir_whiten_reference_psd: when using FIR whitener, use this PSD to define desired desired phase response
85 **Gstreamer graph describing this function**
92 node [shape=record fontsize=10 fontname="Verdana"];
93 edge [fontsize=8 fontname="Verdana"];
102 "segmentsrcgate()" [label="segmentsrcgate() \\n [iff veto segment list provided]", style=filled, color=lightgrey];
106 htgater1 [label="htgate() \\n [iff ht gate specified]", style=filled, color=lightgrey];
110 htgater2 [label="htgate() \\n [iff ht gate specified]", style=filled, color=lightgrey];
114 htgate_rn [style=filled, color=lightgrey, label="htgate() \\n [iff ht gate specified]"];
119 "<src>" -> capsfilter1 -> audioresample;
120 audioresample -> capsfilter2;
121 capsfilter2 -> reblock;
123 whiten -> audioconvert;
124 audioconvert -> capsfilter3;
125 capsfilter3 -> "segmentsrcgate()";
126 "segmentsrcgate()" -> tee;
128 tee -> audioamplifyr1 [label="Rate 1"];
129 audioamplifyr1 -> capsfilterr1;
130 capsfilterr1 -> htgater1;
131 htgater1 -> tee1 -> "<return> 1";
133 tee -> audioamplifyr2 [label="Rate 2"];
134 audioamplifyr2 -> capsfilterr2;
135 capsfilterr2 -> htgater2;
136 htgater2 -> tee2 -> "<return> 2";
138 tee -> audioamplify_rn [label="Rate N"];
139 audioamplify_rn -> capsfilter_rn;
140 capsfilter_rn -> htgate_rn;
141 htgate_rn -> tee_n -> "<return> 3";
149 if psd
is None and not track_psd:
150 raise ValueError(
"must enable track_psd when psd is None")
151 if int(psd_fft_length) != psd_fft_length:
152 raise ValueError(
"psd_fft_length must be an integer")
153 psd_fft_length = int(psd_fft_length)
154 if int(block_duration) != block_duration:
155 raise ValueError(
"block duration must be an integer")
169 elif psd_fft_length % 4:
170 raise ValueError(
"default whitener zero-padding requires psd_fft_length to be multiple of 4")
172 zero_pad = psd_fft_length // 4
188 head = pipeparts.mkcapsfilter(pipeline, src, f
"audio/x-raw, rate=[{target_rate:d},MAX]")
189 head = pipeparts.mkinterpolator(pipeline, head)
190 head = pipeparts.mkaudioconvert(pipeline, head)
191 head = pipeparts.mkchecktimestamps(pipeline, head, f
"{instrument}_timestamps_{target_rate:d}_hoft")
198 head = pipeparts.mktee(pipeline, head)
199 whiten = pipeparts.mkwhiten(pipeline, head, fft_length = psd_fft_length, zero_pad = zero_pad, average_samples = 64, median_samples = 7, expand_gaps =
True, name =
"lal_whiten_%s" % instrument)
200 pipeparts.mkfakesink(pipeline, whiten)
203 kernel = kernels.one_second_highpass_kernel(max(rates), cutoff = 12)
204 block_stride = block_duration * target_rate // Gst.SECOND
205 assert len(kernel) % 2 == 1,
"high-pass filter length is not odd"
206 head = pipeparts.mkfirbank(pipeline, head, fir_matrix = numpy.array(kernel, ndmin = 2), block_stride = block_stride, time_domain =
False, latency = (len(kernel) - 1) // 2)
209 head = pipeparts.mktdwhiten(pipeline, head, kernel = numpy.zeros(1 + target_rate * psd_fft_length, dtype=numpy.float64), latency = 0)
212 def set_fir_psd(whiten, pspec, firelem, psd_fir_kernel):
213 psd_data = numpy.array(whiten.get_property(
"mean-psd"))
214 psd = lal.CreateREAL8FrequencySeries(
216 epoch = lal.LIGOTimeGPS(0),
218 deltaF = whiten.get_property(
"delta-f"),
219 sampleUnits = lal.Unit(whiten.get_property(
"psd-units")),
220 length = len(psd_data)
222 psd.data.data = psd_data
223 kernel, latency, sample_rate = psd_fir_kernel.psd_to_linear_phase_whitening_fir_kernel(psd)
224 kernel, phase = psd_fir_kernel.linear_phase_fir_kernel_to_minimum_phase_whitening_fir_kernel(kernel, sample_rate)
225 firelem.set_property(
"kernel", pipeio.format_property(kernel))
226 firkernel = kernels.PSDFirKernel()
227 if fir_whiten_reference_psd
is not None:
228 assert fir_whiten_reference_psd.f0 == 0.
232 if psd_fft_length != round(1. / fir_whiten_reference_psd.deltaF):
233 fir_whiten_reference_psd = interpolate_psd(fir_whiten_reference_psd, 1. / psd_fft_length)
237 assert (psd_fft_length * target_rate) // 2 + 1 <= len(fir_whiten_reference_psd.data.data),
"fir_whiten_reference_psd Nyquist too low"
238 if (psd_fft_length * target_rate) // 2 + 1 < len(fir_whiten_reference_psd.data.data):
239 fir_whiten_reference_psd = lal.CutREAL8FrequencySeries(fir_whiten_reference_psd, 0, (psd_fft_length * target_rate) // 2 + 1)
241 firkernel.set_phase(fir_whiten_reference_psd)
242 whiten.connect_after(
"notify::mean-psd", set_fir_psd, head, firkernel)
256 if statevector
is not None or dqvector
is not None:
257 head = pipeparts.mkqueue(pipeline, head, max_size_buffers = 0, max_size_bytes = 0, max_size_time = Gst.SECOND * (psd_fft_length + 2))
258 if statevector
is not None:
259 head = pipeparts.mkgate(pipeline, head, control = pipeparts.mkqueue(pipeline, statevector, max_size_buffers = 0, max_size_bytes = 0, max_size_time = 0), default_state =
False, threshold = 1, hold_length = -target_rate, attack_length = -target_rate * (psd_fft_length + 1))
260 if dqvector
is not None:
261 head = pipeparts.mkgate(pipeline, head, control = pipeparts.mkqueue(pipeline, dqvector, max_size_buffers = 0, max_size_bytes = 0, max_size_time = 0), default_state =
False, threshold = 1, hold_length = -target_rate, attack_length = -target_rate * (psd_fft_length + 1))
262 head = pipeparts.mkchecktimestamps(pipeline, head,
"%s_timestamps_fir" % instrument)
285 head = pipeparts.mkreblock(pipeline, head, block_duration = block_duration)
287 head = whiten = pipeparts.mkwhiten(pipeline, head, fft_length = psd_fft_length, zero_pad = zero_pad, average_samples = 64, median_samples = 7, expand_gaps =
True, name =
"lal_whiten_%s" % instrument)
290 head = pipeparts.mkreblock(pipeline, head, block_duration = block_duration)
296 whiten.set_property(
"psd-mode", 0
if track_psd
else 1)
305 def psd_units_or_resolution_changed(elem, pspec, psd):
307 units = lal.Unit(elem.get_property(
"psd-units"))
308 if units == lal.DimensionlessUnit:
310 scale = float(psd.sampleUnits / units)
312 delta_f = elem.get_property(
"delta-f")
313 n = int(round(elem.get_property(
"f-nyquist") / delta_f) + 1)
315 psd = interpolate_psd(psd, delta_f)
316 elem.set_property(
"mean-psd", pipeio.format_property(psd.data.data[:n] * scale))
317 whiten.connect_after(
"notify::f-nyquist", psd_units_or_resolution_changed, psd)
318 whiten.connect_after(
"notify::delta-f", psd_units_or_resolution_changed, psd)
319 whiten.connect_after(
"notify::psd-units", psd_units_or_resolution_changed, psd)
325 head = pipeparts.mkaudioconvert(pipeline, head)
327 head = pipeparts.mkcapsfilter(pipeline, head,
"audio/x-raw, rate=%d, format=%s" % (target_rate, GstAudio.AudioFormat.to_string(GstAudio.AudioFormat.F64)))
329 head = pipeparts.mkcapsfilter(pipeline, head,
"audio/x-raw, rate=%d, format=%s" % (target_rate, GstAudio.AudioFormat.to_string(GstAudio.AudioFormat.F32)))
331 raise ValueError(
"invalid width: %d" % width)
332 head = pipeparts.mkchecktimestamps(pipeline, head,
"%s_timestamps_%d_whitehoft" % (instrument, target_rate))
338 if veto_segments
is not None:
339 long_veto_segments = segments.segmentlist(veto_segments).protract(0.25).coalesce()
340 head = pipeparts.mkdeglitcher(pipeline, head, long_veto_segments)
348 idq_gate_window = max(target_rate // 4, 1)
349 if idq_series
is not None and idq_gate_threshold
is not None:
351 _, channel_type = idq_channel_name.split(
'-')
352 invert_idq_control = channel_type.split(
'_')[0] !=
'FAP'
354 control = pipeparts.mkqueue(pipeline, idq_series, max_size_time = 12 * Gst.SECOND, max_size_bytes = 0, max_size_buffers = 0)
355 head = mkhtgate(pipeline, head, control = control, threshold = idq_gate_threshold, hold_length = idq_gate_window, attack_length = idq_gate_window, name =
"%s_idq_gate" % instrument, invert_control = invert_idq_control)
365 ht_gate_window = max(target_rate // 2, 1)
366 head = mkhtgate(pipeline, head, threshold = ht_gate_threshold
if ht_gate_threshold
is not None else float(
"+inf"), hold_length = ht_gate_window, attack_length = ht_gate_window, name =
"%s_ht_gate" % instrument)
368 head.set_property(
"emit-signals",
True)
371 head = pipeparts.mklatency(pipeline, head, name =
"%s_whitening_latency" % instrument, silent =
True)
376def mkmultiband(pipeline, head, rates, instrument = None, unit_normalize = True):
378 Build pipeline stage to multiband a stream.
380 * rates: a list of the requested sample rates, e.g., [512,1024].
387 head = {max(rates): pipeparts.mktee(pipeline, head)}
414 for rate
in sorted(set(rates))[:-1]:
421 head[rate] = pipeparts.mkaudioamplify(pipeline, head[max(rates)], 1. / math.sqrt(pipeparts.audioresample_variance_gain(10, max(rates), rate)))
423 head[rate] = head[max(rates)]
424 head[rate] = pipeparts.mkcapsfilter(pipeline, pipeparts.mkinterpolator(pipeline, head[rate]), caps =
"audio/x-raw, rate=%d" % rate)
425 head[rate] = pipeparts.mkchecktimestamps(pipeline, head[rate],
"%s_timestamps_%d_whitehoft" % (instrument, rate))
427 head[rate] = pipeparts.mktee(pipeline, head[rate])
437def mkhtgate(pipeline, src, control = None, threshold = 8.0, attack_length = 128, hold_length = 128, invert_control = True, **kwargs):
439 A convenience function to provide thresholds on input data. This can
440 be used to remove large spikes / glitches etc. Of course you can use it for
441 other stuff by plugging whatever you want as input and ouput
443 NOTE: the queues constructed by this code assume the attack and
444 hold lengths combined are less than 1 second in duration.
452 node [shape=record fontsize=10 fontname="Verdana"];
458 out [label="<return>"];
459 in -> tee -> inputqueue -> lal_gate -> out;
467 control = src = pipeparts.mktee(pipeline, src)
468 src = pipeparts.mkqueue(pipeline, src, max_size_time = Gst.SECOND, max_size_bytes = 0, max_size_buffers = 0)
469 return pipeparts.mkgate(pipeline, src, control = control, threshold = threshold, attack_length = -attack_length, hold_length = -hold_length, invert_control = invert_control, **kwargs)