diff --git a/gstlal-ugly/bin/gstlal_feature_extractor_template_overlap b/gstlal-ugly/bin/gstlal_feature_extractor_template_overlap index 4b0a2534cb12e38f53e7c2510a98fe940cbfa620..2ae23886b1b62ed6aa7b57498537ccdc5ca0d774 100755 --- a/gstlal-ugly/bin/gstlal_feature_extractor_template_overlap +++ b/gstlal-ugly/bin/gstlal_feature_extractor_template_overlap @@ -233,7 +233,6 @@ if __name__ == '__main__': aggregator.makedir(options.output_dir) # common parameters we will use throughout - #max_samp_rate = 2048 max_samp_rate = 8192 min_samp_rate = 32 n_rates = int(numpy.log2(max_samp_rate/min_samp_rate) + 1) @@ -243,22 +242,20 @@ if __name__ == '__main__': # generate templates for each rate considered rates = [min_samp_rate*2**i for i in range(n_rates)] - waveforms = {} - templates = {} - basis_params = {} - for rate in rates: - flow = rate/4.*0.8 - fhigh = rate/2.*0.8 - qhigh = options.qhigh - qlow = 3.3166 - if options.waveform == 'sine_gaussian': - waveforms[rate] = idq_utils.SineGaussianGenerator((flow, fhigh), (qlow, qhigh), rate, mismatch = options.mismatch) - elif options.waveform == 'half_sine_gaussian': - waveforms[rate] = idq_utils.HalfSineGaussianGenerator((flow, fhigh), (qlow, qhigh), rate, mismatch = options.mismatch) - else: - raise NotImplementedError - basis_params[rate] = waveforms[rate].parameter_grid - templates[rate] = [waveform for waveform in waveforms[rate].generate_templates(quadrature=False)] + downsample_factor = 0.8 + qhigh = options.qhigh + qlow = 3.3166 + fhigh = max_samp_rate / 2. + flow = min_samp_rate / 4. + + if options.waveform == 'sine_gaussian': + waveforms = idq_utils.SineGaussianGenerator((flow, fhigh), (qlow, qhigh), rates, mismatch = options.mismatch, downsample_factor=downsample_factor) + elif options.waveform == 'half_sine_gaussian': + waveforms = idq_utils.HalfSineGaussianGenerator((flow, fhigh), (qlow, qhigh), rates, mismatch = options.mismatch, downsample_factor=downsample_factor) + else: + raise NotImplementedError + basis_params = waveforms.parameter_grid + templates = {rate: [waveform for waveform in waveforms.generate_templates(rate, quadrature=False)] for rate in rates} # get all templates and params all_templates = list(itertools.chain(*templates.values())) @@ -267,11 +264,16 @@ if __name__ == '__main__': if options.verbose: print >>sys.stderr, "Creating template overlaps..." + # zero pad templates to make them the same length + max_sample_pts = max(len(template) for template in all_templates) + all_templates = [numpy.pad(template, ((max_sample_pts - len(template)) // 2, (max_sample_pts - len(template)) // 2), 'constant') for template in all_templates] + # calculate overlap for each template and find maximum overlaps = [] for this_template in all_templates: overlaps.append(max([numpy.dot(this_template,template) for template in all_templates if not numpy.array_equal(template, this_template)])) + print >>sys.stderr, "total number of templates: %d" % len(all_templates) print >>sys.stderr, "min overlap specified: %f" % (1 - options.mismatch) print >>sys.stderr, "max template overlap: %f" % max(overlaps) print >>sys.stderr, "min template overlap: %f" % min(overlaps) @@ -291,13 +293,15 @@ if __name__ == '__main__': print >>sys.stderr, "Creating waveform plots..." for rate in rates: - for template_id, template in enumerate(random.sample(templates[rate], num_samples)): + #for template_id, template in enumerate(random.sample(templates[rate], num_samples)): + for template_id in random.sample(numpy.arange(len(templates[rate])), num_samples): waveform_params = ["%s: %.3f" % (name, param) for param, name in zip(basis_params[rate][template_id], param_names)] + template = templates[rate][template_id] if options.verbose: print >>sys.stderr, "\tCreating waveform plot with parameters: %s" % repr(waveform_params) - series_fig = plot_waveform(waveforms[rate].times, template, waveform_type, waveform_params) + series_fig = plot_waveform(waveforms.times[rate], template, waveform_type, waveform_params) fname = 'plot_%s_%s-timeseries.png' % (str(rate).zfill(4), str(template_id).zfill(4)) plot_paths.append((template_id*int(rate),fname)) series_fig.savefig(os.path.join(options.output_dir, fname)) diff --git a/gstlal-ugly/python/idq_utils.py b/gstlal-ugly/python/idq_utils.py index 424b934be49aa6780bdf630f0af0fa840de4135f..a8257fa1d079b61bc57a34b2379d2d43e1f85511 100644 --- a/gstlal-ugly/python/idq_utils.py +++ b/gstlal-ugly/python/idq_utils.py @@ -25,6 +25,8 @@ #################### +import bisect +from collections import defaultdict import glob import logging import os @@ -296,32 +298,59 @@ class HalfSineGaussianGenerator(object): """ Generates half sine gaussian templates based on a f, Q range and a sampling frequency. """ - def __init__(self, f_range, q_range, f_samp, mismatch=0.2, tolerance=5e-3): + def __init__(self, f_range, q_range, rates, mismatch=0.2, tolerance=5e-3, downsample_factor=0.8): + ### define parameter range - self.f_low, self.f_high = f_range + self.f_low, self.f_high = (downsample_factor * f_range[0], downsample_factor * f_range[1]) self.q_low, self.q_high = q_range - self.f_samp = f_samp + + ### define grid spacing and edge rolloff for templates self.mismatch = mismatch self.tol = tolerance + ### determine how to scale down templates considered + ### in frequency band to deal with low pass rolloff + self.downsample_factor = downsample_factor + + ### determine frequency bands considered + self.rates = sorted(set(rates)) + + ### determine lower frequency limits for rate conversion based on downsampling factor + self.freq_breakpoints = [self.downsample_factor * rate / 2. for rate in self.rates[:-1]] + ### define grid and template duration - self.parameter_grid = [(f, q, self.duration(f, q)) for f, q in self.generate_f_q_grid(self.f_low, self.f_high, self.q_low, self.q_high)] - self.max_duration = max(duration for f, q, duration in self.parameter_grid) + self.parameter_grid = defaultdict(list) + for rate, f, q in self.generate_f_q_grid(self.f_low, self.f_high, self.q_low, self.q_high): + self.parameter_grid[rate].append((f, q, self.duration(f, q))) + self.max_duration = {rate: max(duration for f, q, duration in parameters) for rate, parameters in self.parameter_grid.items()} + + ### determine rest of waveform properties self.phases = [0., numpy.pi/2.] - self.times = numpy.linspace(-self.max_duration, 0, int(numpy.ceil(self.max_duration*self.f_samp)), endpoint=True) - self.latency = 0 + self.times = {rate: numpy.linspace(-self.max_duration[rate], 0, self.round_to_next_odd(self.max_duration[rate]*rate), endpoint=True) for rate in self.rates} + self.latency = {rate: 0 for rate in self.rates} + + def frequency_to_rate(self, frequency): + """ + Maps a frequency to the correct sampling rate considered. + """ + idx = bisect.bisect(self.freq_breakpoints, frequency) + return self.rates[idx] + + def round_to_next_odd(self, n): + return int(numpy.ceil(n) // 2 * 2 + 1) - def generate_templates(self, quadrature = True): + def generate_templates(self, rate, quadrature = True): """ generate all half sine gaussian templates corresponding to a parameter range and template duration + for a given sampling rate. """ - for f, q in self.generate_f_q_grid(self.f_low, self.f_high, self.q_low, self.q_high): + for f, q, _ in self.parameter_grid[rate]: if quadrature: for phase in self.phases: yield self.waveform(f, q, phase) else: - yield self.waveform(f, q, self.phases[0]) + yield self.waveform(f, q, self.phases[0], rate) def duration(self, f, q): """ @@ -329,18 +358,16 @@ class HalfSineGaussianGenerator(object): """ return 0.5 * (q/(2.*numpy.pi*f)) * numpy.log(1./self.tol) - def waveform(self, f, q, phase): + def waveform(self, f, q, phase, rate): """ construct half sine gaussian waveforms that taper to tolerance at edges of window f is the central frequency of the waveform """ - dt = self.times[1] - self.times[0] - rate = 1./dt assert f < rate/2. # phi is the central frequency of the sine gaussian tau = q/(2.*numpy.pi*f) - template = numpy.cos(2.*numpy.pi*f*self.times + phase)*numpy.exp(-1.*self.times**2./tau**2.) + template = numpy.cos(2.*numpy.pi*f*self.times[rate] + phase)*numpy.exp(-1.*self.times[rate]**2./tau**2.) # normalize sine gaussians to have unit length in their vector space inner_product = numpy.sum(template*template) @@ -369,23 +396,26 @@ class HalfSineGaussianGenerator(object): def generate_f_q_grid(self, f_min, f_max, q_min, q_max): """ - Generates (f, Q) pairs based on f, Q ranges + Generates (f_samp, f, Q) pairs based on f, Q ranges """ for q in self.generate_q_values(q_min, q_max): num_f = self.num_f_templates(f_min, f_max, q) for l in range(num_f): f = f_min * (f_max/f_min)**( (0.5+l) /num_f) - if f < f_max / (1 + (numpy.sqrt(11)/q)): - yield (f, q) + rate = self.frequency_to_rate(f) + if f < (rate/2) / (1 + (numpy.sqrt(11)/q)): + yield rate, f, q + elif rate != max(self.rates): + yield (2 * rate), f, q class SineGaussianGenerator(HalfSineGaussianGenerator): """ Generates sine gaussian templates based on a f, Q range and a sampling frequency. """ - def __init__(self, f_range, q_range, f_samp, mismatch=0.2, tolerance=5e-3): - super(SineGaussianGenerator, self).__init__(f_range, q_range, f_samp, mismatch=mismatch, tolerance=tolerance) - self.times = numpy.linspace(-self.max_duration/2., self.max_duration/2., int(numpy.ceil(self.max_duration*self.f_samp)), endpoint=True) - self.latency = self.max_duration / 2. + def __init__(self, f_range, q_range, rates, mismatch=0.2, tolerance=5e-3, downsample_factor=0.8): + super(SineGaussianGenerator, self).__init__(f_range, q_range, rates, mismatch=mismatch, tolerance=tolerance, downsample_factor=0.8) + self.times = {rate: numpy.linspace(-self.max_duration[rate]/2., self.max_duration[rate]/2., self.round_to_next_odd(self.max_duration[rate]*rate), endpoint=True) for rate in self.rates} + self.latency = {rate: self.max_duration[rate] / 2. for rate in self.rates} def duration(self, f, q): """