diff --git a/gstlal-burst/bin/gstlal_cs_triggergen b/gstlal-burst/bin/gstlal_cs_triggergen index 74039dbcba03ba3d6186fc7841c4e334ec87165d..928ba095ae58386cde2e0ec64aa4de71a9918837 100755 --- a/gstlal-burst/bin/gstlal_cs_triggergen +++ b/gstlal-burst/bin/gstlal_cs_triggergen @@ -1,7 +1,6 @@ #!/usr/bin/env python import sys -import math import numpy import gi gi.require_version('Gst','1.0') @@ -10,9 +9,10 @@ GObject.threads_init() from gi.repository import Gst Gst.init(None) -from gstlal import simplehandler -from gstlal import pipeparts from gstlal import datasource +from gstlal import pipeio +from gstlal import pipeparts +from gstlal import simplehandler from gstlal import snglbursttable from lal import LIGOTimeGPS from optparse import OptionParser @@ -22,8 +22,10 @@ from glue.ligolw import lsctables from glue.ligolw import utils as ligolw_utils from glue.ligolw.utils import process as ligolw_process +import lal import lalsimulation + # # ================================================================================ # @@ -53,6 +55,11 @@ def parse_command_line(): options, filenames = parser.parse_args() + required_options = ["sample_rate", "frame_cache", "output", "channel", "template_bank", "gps_start_time", "gps_end_time", "threshold", "cluster_events"] + missing_options = [option for option in required_options if getattr(options, option) is None] + if missing_options: + raise ValueError, "missing required options %s" % ", ".join(sorted("--%s" % option.replace("_", "-") for option in missing_options)) + return options, filenames # @@ -70,13 +77,78 @@ def parse_command_line(): options, filenames = parse_command_line() +# +# handler for obtaining psd +# + +class PSDHandler(simplehandler.Handler): + def __init__(self, mainloop, pipeline, firbank): + simplehandler.Handler.__init__(self,mainloop, pipeline) + self.firbank = firbank + + def do_on_message(self, bus, message): + if message.type == Gst.MessageType.ELEMENT and message.get_structure().get_name() == "spectrum": + instrument = message.src.get_name().split("_")[-1] + psd = pipeio.parse_spectrum_message(message) + timestamp = psd.epoch + stability = float(message.src.get_property("n-samples")) / message.src.get_property("average-samples") + + if stability > 0.3: + template_bank = [None] * len(template_bank_table) + template_t = [None] * len(template_bank_table) + autocorr = [None] * len(template_bank_table) + # make templates, whiten, put into firbank + for i, row in enumerate(template_bank_table): + # linearly polarized, so just use plus mode time series + template_bank[i], _ = lalsimulation.GenerateStringCusp(1.0,row.central_freq,1.0/options.sample_rate) + # zero-pad it to 32 seconds to obtain same deltaF as the PSD + template_bank[i] = lal.ResizeREAL8TimeSeries(template_bank[i],-int(32*options.sample_rate - template_bank[i].data.length)//2,int(32*options.sample_rate)) + # setup of frequency domain + length = template_bank[i].data.length + duration = float(length) / options.sample_rate + epoch = -(length-1) // 2 /options.sample_rate + template_f = lal.CreateCOMPLEX16FrequencySeries("template_freq", LIGOTimeGPS(epoch), psd.f0, 1.0/duration, lal.Unit("strain s"), length // 2 + 1) + fplan = lal.CreateForwardREAL8FFTPlan(length,0) + # FFT to frequency domain + lal.REAL8TimeFreqFFT(template_f,template_bank[i],fplan) + # set DC and Nyquist to zero + template_f.data.data[0] = 0.0 + template_f.data.data[template_f.data.length-1] = 0.0 + # whiten + template_f = lal.WhitenCOMPLEX16FrequencySeries(template_f,psd) + # obtain autocorrelation time series by + # squaring the template and inverse FFTing it + template_f_squared = lal.CreateCOMPLEX16FrequencySeries("whitened template_freq squared", LIGOTimeGPS(epoch), psd.f0, 1.0/duration, lal.Unit("s"), length // 2 + 1) + autocorr[i] = lal.CreateREAL8TimeSeries("autocorr_time", LIGOTimeGPS(epoch), psd.f0, 1.0 / options.sample_rate, lal.Unit("strain"), length) + rplan = lal.CreateReverseREAL8FFTPlan(length,0) + template_f_squared.data.data = abs(template_f.data.data)**2 + lal.REAL8FreqTimeFFT(autocorr[i],template_f_squared,rplan) + # normalize autocorrelation by central (maximum) value + autocorr[i].data.data /= numpy.max(autocorr[i].data.data) + # Inverse FFT template bank back to time domain + template_t[i] = lal.CreateREAL8TimeSeries("whitened template time", LIGOTimeGPS(epoch), psd.f0, 1.0 / options.sample_rate, lal.Unit("strain"), length) + lal.REAL8FreqTimeFFT(template_t[i],template_f,rplan) + # normalize + template_t[i].data.data /= numpy.sqrt(numpy.dot(template_t[i].data.data, template_t[i].data.data)) + template_t[i] = template_t[i].data.data + firbank.set_property("latency",-(len(template_t[0]) - 1) // 2) + firbank.set_property("fir_matrix", template_t) + self.firbank = firbank + else: + template, _ = lalsimulation.GenerateStringCusp(1.0,30,1.0/options.sample_rate) + firbank.set_property("fir_matrix",numpy.zeros((template.data.length,len(template_bank_table)), dtype=float)) + firbank.set_property("latency",-(template.data.length - 1) // 2) + self.firbank = firbank + return True + return False + + # # get data and insert injections if injection file is given # + pipeline = Gst.Pipeline(name="pipeline") -mainloop = GObject.MainLoop() -handler = simplehandler.Handler(mainloop, pipeline) head = pipeparts.mklalcachesrc(pipeline, options.frame_cache) head = pipeparts.mkframecppchanneldemux(pipeline, head) @@ -98,7 +170,7 @@ if options.injection_file is not None: # whiten # -head = pipeparts.mkwhiten(pipeline, head) +head = pipeparts.mkwhiten(pipeline, head, fft_length = 32) # @@ -107,6 +179,9 @@ head = pipeparts.mkwhiten(pipeline, head) head = pipeparts.mkaudioconvert(pipeline,head) head = pipeparts.mkresample(pipeline,head) +# FIXME check later if it's okay for filters that are exactly half of sample rate. +# FIXME NO hardcoding original sample rate! +head = pipeparts.mkaudioamplify(pipeline,head,1./numpy.sqrt(options.sample_rate/16384.0)) head = pipeparts.mkcapsfilter(pipeline,head,"audio/x-raw, format=F32LE, rate=%d" % options.sample_rate) head = pipeparts.mkqueue(pipeline,head) @@ -121,19 +196,14 @@ class LIGOLWContentHandler(ligolw.LIGOLWContentHandler): xmldoc = ligolw_utils.load_filename(options.template_bank, contenthandler = LIGOLWContentHandler, verbose = True) -sngl_burst_table = lsctables.SnglBurstTable.get_table(xmldoc) +template_bank_table = lsctables.SnglBurstTable.get_table(xmldoc) # # filter bank # -template_bank = [None] * len(sngl_burst_table) -for i, row in enumerate(sngl_burst_table): - template_bank[i], _ = lalsimulation.GenerateStringCusp(1.0,row.central_freq,1.0/options.sample_rate) - template_bank[i] = template_bank[i].data.data - template_bank[i] /= math.sqrt(numpy.dot(template_bank[i], template_bank[i])) -head = pipeparts.mkfirbank(pipeline, head, latency = -(len(template_bank[0]) - 1) // 2, fir_matrix = template_bank, block_stride = 4 * options.sample_rate) +head = firbank = pipeparts.mkfirbank(pipeline, head, time_domain = False, block_stride = 4 * options.sample_rate) # @@ -152,7 +222,6 @@ xmldoc.childNodes[-1].appendChild(sngl_burst_table) # trigger generator # - head = pipeparts.mkgeneric(pipeline, head, "lal_string_triggergen", threshold = options.threshold, cluster = options.cluster_events, bank_filename = options.template_bank) @@ -196,8 +265,11 @@ if pipeline.set_state(Gst.State.PLAYING) != Gst.StateChangeReturn.SUCCESS: raise RuntimeError("pipeline did not enter playing state") +mainloop = GObject.MainLoop() +handler = PSDHandler(mainloop, pipeline, firbank) mainloop.run() + # # write output to disk #