34 gi.require_version(
'Gst',
'1.0')
35 from gi.repository
import GObject, Gst, GstAudio
36 GObject.threads_init()
40 from ligo
import segments
42 from gstlal
import bottle
43 from gstlal
import pipeparts
44 from gstlal
import reference_psd
45 from gstlal
import datasource
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 >> sys.stderr,
"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" 69 def mkwhitened_multirate_src(pipeline, src, rates, instrument, psd = None, psd_fft_length = 32, ht_gate_threshold = float(
"inf"), veto_segments =
None, nxydump_segment =
None, track_psd =
False, block_duration = 1 * Gst.SECOND, zero_pad =
None, width = 64, unit_normalize =
True, statevector =
None, dqvector =
None, fir_whiten_reference_psd =
None):
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 * rates: a list of the requested sample rates, e.g., [512,1024]. 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)
167 elif psd_fft_length % 4:
168 raise ValueError(
"default whitener zero-padding requires psd_fft_length to be multiple of 4")
170 zero_pad = psd_fft_length // 4
186 head = pipeparts.mkcapsfilter(pipeline, src,
"audio/x-raw, rate=[%d,MAX]" % max(rates))
187 head = pipeparts.mkinterpolator(pipeline, head)
188 head = pipeparts.mkaudioconvert(pipeline, head)
189 head = pipeparts.mkchecktimestamps(pipeline, head,
"%s_timestamps_%d_hoft" % (instrument, max(rates)))
199 if veto_segments
is not None:
200 short_veto_segments = segments.segmentlist([seg
for seg
in veto_segments
if abs(seg) < 1.0]).protract(0.25).coalesce()
201 head = pipeparts.mkdeglitcher(pipeline, head, short_veto_segments)
208 head = pipeparts.mktee(pipeline, head)
209 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)
210 pipeparts.mkfakesink(pipeline, whiten)
213 kernel = reference_psd.one_second_highpass_kernel(max(rates), cutoff = 12)
214 block_stride = block_duration * max(rates) // Gst.SECOND
215 assert len(kernel) % 2 == 1,
"high-pass filter length is not odd" 216 head = pipeparts.mkfirbank(pipeline, head, fir_matrix = numpy.array(kernel, ndmin = 2), block_stride = block_stride, time_domain =
False, latency = (len(kernel) - 1) // 2)
219 head = pipeparts.mktdwhiten(pipeline, head, kernel = numpy.zeros(1 + max(rates) * psd_fft_length, dtype=numpy.float64), latency = 0)
222 def set_fir_psd(whiten, pspec, firelem, psd_fir_kernel):
223 psd_data = numpy.array(whiten.get_property(
"mean-psd"))
224 psd = lal.CreateREAL8FrequencySeries(
226 epoch = lal.LIGOTimeGPS(0),
228 deltaF = whiten.get_property(
"delta-f"),
229 sampleUnits = lal.Unit(whiten.get_property(
"psd-units")),
230 length = len(psd_data)
232 psd.data.data = psd_data
233 kernel, latency, sample_rate = psd_fir_kernel.psd_to_linear_phase_whitening_fir_kernel(psd)
234 kernel, phase = psd_fir_kernel.linear_phase_fir_kernel_to_minimum_phase_whitening_fir_kernel(kernel, sample_rate)
235 firelem.set_property(
"kernel", kernel)
236 firkernel = reference_psd.PSDFirKernel()
237 if fir_whiten_reference_psd
is not None:
238 assert fir_whiten_reference_psd.f0 == 0.
242 if psd_fft_length != round(1. / fir_whiten_reference_psd.deltaF):
243 fir_whiten_reference_psd = reference_psd.interpolate_psd(fir_whiten_reference_psd, 1. / psd_fft_length)
247 assert (psd_fft_length * max(rates)) // 2 + 1 <= len(fir_whiten_reference_psd.data.data),
"fir_whiten_reference_psd Nyquist too low" 248 if (psd_fft_length * max(rates)) // 2 + 1 < len(fir_whiten_reference_psd.data.data):
249 fir_whiten_reference_psd = lal.CutREAL8FrequencySeries(fir_whiten_reference_psd, 0, (psd_fft_length * max(rates)) // 2 + 1)
251 firkernel.set_phase(fir_whiten_reference_psd)
252 whiten.connect_after(
"notify::mean-psd", set_fir_psd, head, firkernel)
266 if statevector
is not None or dqvector
is not None:
267 head = pipeparts.mkqueue(pipeline, head, max_size_buffers = 0, max_size_bytes = 0, max_size_time = Gst.SECOND * (psd_fft_length + 2))
268 if statevector
is not None:
269 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 = -max(rates), attack_length = -max(rates) * (psd_fft_length + 1))
270 if dqvector
is not None:
271 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 = -max(rates), attack_length = -max(rates) * (psd_fft_length + 1))
272 head = pipeparts.mkchecktimestamps(pipeline, head,
"%s_timestamps_fir" % instrument)
295 head = pipeparts.mkreblock(pipeline, head, block_duration = block_duration)
297 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)
300 head = pipeparts.mkreblock(pipeline, head, block_duration = block_duration)
306 whiten.set_property(
"psd-mode", 0
if track_psd
else 1)
315 def psd_units_or_resolution_changed(elem, pspec, psd):
317 units = lal.Unit(elem.get_property(
"psd-units"))
318 if units == lal.DimensionlessUnit:
320 scale = float(psd.sampleUnits / units)
322 delta_f = elem.get_property(
"delta-f")
323 n = int(round(elem.get_property(
"f-nyquist") / delta_f) + 1)
325 psd = reference_psd.interpolate_psd(psd, delta_f)
326 elem.set_property(
"mean-psd", psd.data.data[:n] * scale)
327 whiten.connect_after(
"notify::f-nyquist", psd_units_or_resolution_changed, psd)
328 whiten.connect_after(
"notify::delta-f", psd_units_or_resolution_changed, psd)
329 whiten.connect_after(
"notify::psd-units", psd_units_or_resolution_changed, psd)
335 head = pipeparts.mkaudioconvert(pipeline, head)
337 head = pipeparts.mkcapsfilter(pipeline, head,
"audio/x-raw, rate=%d, format=%s" % (max(rates), GstAudio.AudioFormat.to_string(GstAudio.AudioFormat.F64)))
339 head = pipeparts.mkcapsfilter(pipeline, head,
"audio/x-raw, rate=%d, format=%s" % (max(rates), GstAudio.AudioFormat.to_string(GstAudio.AudioFormat.F32)))
341 raise ValueError(
"invalid width: %d" % width)
342 head = pipeparts.mkchecktimestamps(pipeline, head,
"%s_timestamps_%d_whitehoft" % (instrument, max(rates)))
348 if veto_segments
is not None:
349 long_veto_segments = segments.segmentlist(veto_segments).protract(0.25).coalesce()
350 head = pipeparts.mkdeglitcher(pipeline, head, long_veto_segments)
360 ht_gate_window = max(max(rates) // 4, 1)
361 head = datasource.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)
363 head.set_property(
"emit-signals",
True)
369 head = {max(rates): pipeparts.mktee(pipeline, head)}
396 for rate
in sorted(set(rates))[:-1]:
403 head[rate] = pipeparts.mkaudioamplify(pipeline, head[max(rates)], 1. / math.sqrt(pipeparts.audioresample_variance_gain(10, max(rates), rate)))
405 head[rate] = head[max(rates)]
406 head[rate] = pipeparts.mkcapsfilter(pipeline, pipeparts.mkinterpolator(pipeline, head[rate]), caps =
"audio/x-raw, rate=%d" % rate)
407 head[rate] = pipeparts.mkchecktimestamps(pipeline, head[rate],
"%s_timestamps_%d_whitehoft" % (instrument, rate))
409 head[rate] = pipeparts.mktee(pipeline, head[rate])