LALBurst 2.0.7.1-eeff03c
lalburst_inj_pic.py
Go to the documentation of this file.
1##python
2#
3# Copyright (C) 2010,2013 Kipp Cannon
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the
7# Free Software Foundation; either version 2 of the License, or (at your
8# option) any later version.
9#
10# This program is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13# Public License for more details.
14#
15# You should have received a copy of the GNU General Public License along
16# with this program; if not, write to the Free Software Foundation, Inc.,
17# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
19
20#
21# =============================================================================
22#
23# Preamble
24#
25# =============================================================================
26#
27
28
29import math
30from optparse import OptionParser
31import sys
32from tqdm import tqdm
33from PIL import Image
34
35
36from igwn_ligolw import ligolw
37from igwn_ligolw import lsctables
38from igwn_ligolw import utils as ligolw_utils
39from igwn_ligolw.utils import process as ligolw_process
40import lalburst
41import lalmetaio
42import lalsimulation
43from lalburst import git_version
44
45
46class SimBurst(lalmetaio.SimBurst):
47 def __init__(self, **kwargs):
48 super(SimBurst, self).__init__()
49 for key, value in kwargs.items():
50 setattr(self, key, value)
51 @property
53 return self.time_geocent_gps.gpsNanoSeconds
54lsctables.SimBurst = lsctables.SimBurstTable.RowType = SimBurst
55
56
57__author__ = "Kipp Cannon <kipp.cannon@ligo.org>"
58__version__ = "git id %s" % git_version.id
59__date__ = git_version.date
60
61
62#
63# =============================================================================
64#
65# Command Line
66#
67# =============================================================================
68#
69
70
72 parser = OptionParser(
73 version = "Name: %%prog\n%s" % git_version.verbose_msg,
74 usage = "%prog [options] filename",
75 description = "Convert an image into a LIGO Light-Weight XML file containing a list of sine-Gaussian burst injections. When injected into data, the injections will cause a waterfall plot to display the image."
76 )
77 parser.add_option("-l", "--f-low", metavar = "Hz", type = "float", default = 64.0, help = "Set the low-frequency limit of the tiling in Hertz (default = 64).")
78 parser.add_option("-d", "--delta-f", metavar = "Hz", type = "float", default = 16.0, help = "Set the bandwidth of the pixels in Hertz (default = 16). Must be > 0. The product of --delta-f and --delta-t must be at least 2/pi.")
79 parser.add_option("-t", "--delta-t", metavar = "s", type = "float", default = 1.0 / 16, help = "Set the duration of the pixels in seconds (default = 1/16). Must be > 0. The product of --delta-f and --delta-t must be at least 2/pi.")
80 parser.add_option("-f", "--overlap-fraction", metavar = "fraction", type = "float", default = 0.25, help = "Set the fraction by which adjacent tiles overlap (default = 0.25). The pixels centres are spaced (1 - --overlap-fraction) * --delta-f apart in the frequency domain. The value must be in the range [0, 1).")
81 parser.add_option("-H", "--height", metavar = "pixels", type = "int", default = 64, help = "Set the number of pixles in the frequency domain (default = 64). The image will be scaled to this vertical size, and the number of pixels in the time domain (horizontal size) will be fixed by the image aspect ratio.")
82 parser.add_option("-o", "--output", metavar = "filename", help = "Set the name of the output file (default = stdout).")
83 parser.add_option("-s", "--gps-start-time", metavar = "seconds", help = "Set the start time of the tiling in GPS seconds (required).")
84 parser.add_option("--sample-rate", metavar = "Hz", type = "int", default = 16384, help = "Set the sample rate in Hertz of the data into which the injections will be placed (default = 16384). This information is required in order to normalize each pixel accurately. If the wrong value is used, the result will be the addition of noise to the image. The highest frequency pixel must have a centre frequency < 1/2 this frequency.")
85 parser.add_option("-n", "--hrss-scale", metavar = "hrss", type = "float", default = 1e-20, help = "Set the single-pixel hrss (root-sum-square strain) corresponding to \"white\" (default = 1e-20).")
86 parser.add_option("-v", "--verbose", action = "store_true", help = "Be verbose.")
87 parser.add_option("-T", "--time-slide-xml", metavar = "filename", help = "Associate injections with the first time slide ID in this XML file (required).")
88 options, filenames = parser.parse_args()
89
90 # save for the process_params table
91 options.options_dict = dict(options.__dict__)
92
93 if options.gps_start_time is None:
94 raise ValueError("missing required option --gps-start-time")
95 if not 0. < options.delta_f:
96 raise ValueError("--delta-f must be > 0")
97 if not 0. < options.delta_t:
98 raise ValueError("--delta-t must be > 0")
99 if options.delta_f * options.delta_t < 2. / math.pi:
100 raise ValueError("the product of --delta-t and --delta-f must be >= 2/pi")
101 if not (0. <= options.overlap_fraction < 1.):
102 raise ValueError("--overlap-fraction must be in [0, 1)")
103 if options.f_low + options.height * (1. - options.overlap_fraction) * options.delta_f >= options.sample_rate / 2.:
104 raise ValueError("total waveform bandwidth exceeds Nyquist frequency")
105 if options.time_slide_xml is None:
106 raise ValueError("missing required option time-slide-xml")
107
108 # type-cast
109 options.gps_start_time = lsctables.LIGOTimeGPS(options.gps_start_time)
110
111 return options, filenames
112
113
114#
115# =============================================================================
116#
117# Main
118#
119# =============================================================================
120#
121
122
123options, filenames = parse_command_line()
124
125
126#
127# use the time-slide file to start the output document
128#
129
130
131xmldoc = ligolw_utils.load_filename(options.time_slide_xml, verbose = options.verbose)
132
133
134#
135# add our metadata
136#
137
138
139process = ligolw_process.register_to_xmldoc(xmldoc, "lalburst_inj_pic", options.options_dict, version = git_version.version)
140
141
142#
143# use whatever time slide vector comes first in the table (lazy)
144#
145
146
147time_slide_table = lsctables.TimeSlideTable.get_table(xmldoc)
148time_slide_id = time_slide_table[0].time_slide_id
149if options.verbose:
150 print("associating injections with time slide (%d) %s" % (time_slide_id, time_slide_table.as_dict()[time_slide_id]), file = sys.stderr)
151
152
153#
154# find or add a sim_burst table
155#
156
157
158try:
159 lsctables.SimBurstTable.get_table(xmldoc)
160except ValueError:
161 # no sim_burst table in document
162 pass
163else:
164 raise ValueError("%s contains a sim_burst table. this program isn't smart enough to deal with that." % options.time_slide_xml)
165
166sim_burst_tbl = xmldoc.childNodes[-1].appendChild(lsctables.SimBurstTable.new(["process:process_id", "simulation_id", "time_slide:time_slide_id", "waveform", "waveform_number", "ra", "dec", "psi", "time_geocent_gps", "time_geocent_gps_ns", "duration", "frequency", "bandwidth", "egw_over_rsquared", "pol_ellipse_angle", "pol_ellipse_e"]))
167
168
169#
170# populate the sim_burst table with pixels
171#
172
173
174if options.verbose:
175 print("time-frequency tiles have %g degrees of freedom" % (2. * options.delta_t * options.delta_f), file = sys.stderr)
176
177for n, filename in enumerate(filenames, 1):
178 if options.verbose:
179 print("%d/%d: loading %s ..." % (n, len(filenames), filename), file = sys.stderr)
180 img = Image.open(filename)
181
182 width, height = img.size
183 width, height = int(round(width * options.height / float(height))), options.height
184 if options.verbose:
185 print("converting to %dx%d grayscale ... " % (width, height), file = sys.stderr)
186 img = img.resize((width, height)).convert("L")
187
188 progress = tqdm(desc = "computing pixels", total = width * height, disable = not options.verbose)
189 for i in range(width):
190 for j in range(height):
191 progress.update()
192 # amplitude. hrss column is ignored by waveform
193 # generation code. it is included for convenience,
194 # to record the desired pixel brightness. because
195 # band- and time-limited white-noise burst
196 # waveforms are random, the waveform's final
197 # ampltiude (in the egw_over_rsquared column) is
198 # determined by generating the burst at a canonical
199 # amplitude, measuring its hrss, then rescaling to
200 # achieve the desired value. this process requires
201 # the final sample rate to be known.
202 hrss = options.hrss_scale * img.getpixel((i, height - 1 - j)) / 255.0
203 if hrss == 0.:
204 # don't generate injections for black
205 # pixels
206 continue
207
208 # create and initialize table row
209 row = lsctables.SimBurst(
210 # metadata
211 process_id = process.process_id,
212 simulation_id = sim_burst_tbl.get_next_id(),
213 time_slide_id = time_slide_id,
214
215 # source orientation
216 ra = 0.,
217 dec = 0.,
218 psi = 0.,
219
220 # band- and time-limited white-noise burst
221 waveform = "BTLWNB",
222 waveform_number = 0,
223 pol_ellipse_e = 0.,
224 pol_ellipse_angle = 0.,
225
226 # time-frequency co-ordinates
227 time_geocent_gps = options.gps_start_time + i * options.delta_t * (1.0 - options.overlap_fraction),
228 frequency = options.f_low + j * options.delta_f * (1.0 - options.overlap_fraction),
229 bandwidth = options.delta_f,
230 duration = options.delta_t,
231
232 # brightness. hrss is ignored, set
233 # egw_over_rsquared to 1, to be re-scaled
234 # after measuring waveform's real
235 # brightness
236 hrss = hrss,
237 egw_over_rsquared = 1.
238 )
239
240 # generate waveform, then scale egw_over_rsquared
241 # to get desired brightness
242 row.egw_over_rsquared *= hrss / lalsimulation.MeasureHrss(*lalburst.GenerateSimBurst(row, 1.0 / options.sample_rate))
243
244 # put row into table
245 sim_burst_tbl.append(row)
246 progress.close()
247
248
249#
250# write output
251#
252
253
254ligolw_utils.write_filename(xmldoc, options.output, verbose = options.verbose)
def __init__(self, **kwargs)