gstlal  0.8.1
 All Classes Namespaces Files Functions Variables Pages
pipeio.py
1 # Copyright (C) 2009--2013 LIGO Scientific Collaboration
2 #
3 # This program is free software; you can redistribute it and/or modify it
4 # under the terms of the GNU General Public License as published by the
5 # Free Software Foundation; either version 2 of the License, or (at your
6 # option) any later version.
7 #
8 # This program is distributed in the hope that it will be useful, but
9 # WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
11 # Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License along
14 # with this program; if not, write to the Free Software Foundation, Inc.,
15 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 
17 
18 #
19 # =============================================================================
20 #
21 # Preamble
22 #
23 # =============================================================================
24 #
25 
26 
27 import numpy
28 import sys
29 
30 
31 import pygtk
32 pygtk.require("2.0")
33 import gobject
34 gobject.threads_init()
35 import pygst
36 pygst.require('0.10')
37 import gst
38 
39 
40 from pylal import datatypes as laltypes
41 
42 
43 __author__ = "Kipp Cannon <kipp.cannon@ligo.org>, Chad Hanna <chad.hanna@ligo.org>, Drew Keppel <drew.keppel@ligo.org>"
44 __version__ = "FIXME"
45 __date__ = "FIXME"
46 
47 
48 #
49 # =============================================================================
50 #
51 # Properties
52 #
53 # =============================================================================
54 #
55 
56 
57 def repack_complex_array_to_real(arr):
58  """
59  Repack a complex-valued array into a real-valued array with twice
60  as many columns. Used to set complex arrays as values on elements
61  that expose them as real-valued array properties (gobject doesn't
62  understand complex numbers). The return value is a view into the
63  input array.
64  """
65  # FIXME: this function shouldn't exist, we should add complex
66  # types to gobject
67  if arr.dtype.kind != "c":
68  raise TypeError(arr)
69  assert arr.dtype.itemsize % 2 == 0
70  return arr.view(dtype = numpy.dtype("f%d" % (arr.dtype.itemsize // 2)))
71 
72 
73 def repack_real_array_to_complex(arr):
74  """
75  Repack a real-valued array into a complex-valued array with half as
76  many columns. Used to retrieve complex arrays from elements that
77  expose them as real-valued array properties (gobject doesn't
78  understand complex numbers). The return value is a view into the
79  input array.
80  """
81  # FIXME: this function shouldn't exist, we should add complex
82  # types to gobject
83  if arr.dtype.kind != "f":
84  raise TypeError(arr)
85  return arr.view(dtype = numpy.dtype("c%d" % (arr.dtype.itemsize * 2)))
86 
87 
88 #
89 # =============================================================================
90 #
91 # Buffers
92 #
93 # =============================================================================
94 #
95 
96 
97 def get_unit_size(caps):
98  struct = caps[0]
99  name = struct.get_name()
100  if name in ("audio/x-raw-complex", "audio/x-raw-float", "audio/x-raw-int"):
101  assert struct["width"] % 8 == 0
102  return struct["channels"] * struct["width"] // 8
103  elif name == "video/x-raw-rgb":
104  assert struct["bpp"] % 8 == 0
105  return struct["width"] * struct["height"] * struct["bpp"] // 8
106  raise ValueError(caps)
107 
108 
109 def numpy_dtype_from_caps(caps):
110  struct = caps[0]
111  name = struct.get_name()
112  if name == "audio/x-raw-float":
113  assert struct["width"] % 8 == 0
114  return "f%d" % (struct["width"] // 8)
115  elif name == "audio/x-raw-int":
116  assert struct["width"] % 8 == 0
117  if struct["signed"]:
118  return "i%d" % (struct["width"] // 8)
119  else:
120  return "u%d" % (struct["width"] // 8)
121  elif name == "audio/x-raw-complex":
122  assert struct["width"] % 8 == 0
123  return "c%d" % (struct["width"] // 8)
124  raise ValueError(name)
125 
126 
127 def caps_from_numpy_dtype(dtype):
128  if dtype.char == 'f':
129  caps = gst.Caps("audio/x-raw-float, width=32")
130  elif dtype.char == 'd':
131  caps = gst.Caps("audio/x-raw-float, width=64")
132  elif dtype.char == 'b':
133  caps = gst.Caps("audio/x-raw-int, width=8, signed=true")
134  elif dtype.char == 'B':
135  caps = gst.Caps("audio/x-raw-int, width=8, signed=false")
136  elif dtype.char == 'h':
137  caps = gst.Caps("audio/x-raw-int, width=16, signed=true")
138  elif dtype.char == 'H':
139  caps = gst.Caps("audio/x-raw-int, width=16, signed=false")
140  elif dtype.char == 'i':
141  caps = gst.Caps("audio/x-raw-int, width=32, signed=true")
142  elif dtype.char == 'I':
143  caps = gst.Caps("audio/x-raw-int, width=32, signed=false")
144  elif dtype.char == 'l':
145  caps = gst.Caps("audio/x-raw-int, width=64, signed=true")
146  elif dtype.char == 'L':
147  caps = gst.Caps("audio/x-raw-int, width=64, signed=false")
148  else:
149  raise ValueError(dtype)
150  caps[0]["endianness"] = {
151  "=": 1234 if sys.byteorder == "little" else 4321,
152  "<": 1234,
153  ">": 4321
154  }[dtype.byteorder]
155  return caps
156 
157 
158 def caps_from_array(arr, rate = None):
159  caps = caps_from_numpy_dtype(arr.dtype)
160  if rate is not None:
161  caps[0]["rate"] = rate
162  caps[0]["channels"] = arr.shape[1]
163  return caps
164 
165 
166 def array_from_audio_buffer(buf):
167  channels = buf.caps[0]["channels"]
168  # FIXME: conditional is work-around for broken handling of
169  # zero-length buffers in numpy < 1.7. remove and use frombuffer()
170  # unconditionally when we can rely on numpy >= 1.7
171  if len(buf) > 0:
172  a = numpy.frombuffer(buf, dtype = numpy_dtype_from_caps(buf.caps))
173  else:
174  a = numpy.array((), dtype = numpy_dtype_from_caps(buf.caps))
175  a.shape = (len(a) // channels, channels)
176  return a
177 
178 
179 def audio_buffer_from_array(arr, timestamp, offset, rate):
180  buf = gst.Buffer(arr.data)
181  buf.caps = caps_from_array(arr, rate = rate)
182  buf.timestamp = timestamp
183  buf.duration = (gst.SECOND * arr.shape[0] + rate // 2) // rate
184  buf.offset = offset
185  buf.offset_end = offset + arr.shape[0]
186  return buf
187 
188 
189 #
190 # =============================================================================
191 #
192 # Messages
193 #
194 # =============================================================================
195 #
196 
197 
198 def parse_spectrum_message(message):
199  """
200  Parse a "spectrum" message from the lal_whiten element, return a
201  LAL REAL8FrequencySeries containing the strain spectral density.
202  """
203  s = message.structure
204  return laltypes.REAL8FrequencySeries(
205  name = s["instrument"] if s.has_field("instrument") else "",
206  epoch = laltypes.LIGOTimeGPS(0, message.timestamp),
207  f0 = 0.0,
208  deltaF = s["delta-f"],
209  sampleUnits = laltypes.LALUnit(s["sample-units"].strip()),
210  data = numpy.array(s["magnitude"])
211  )
212 
213 
214 #
215 # =============================================================================
216 #
217 # Tags
218 #
219 # =============================================================================
220 #
221 
222 
223 def parse_framesrc_tags(taglist):
224  try:
225  instrument = taglist["instrument"]
226  except KeyError:
227  instrument = None
228  try:
229  channel_name = taglist["channel-name"]
230  except KeyError:
231  channel_name = None
232  if "units" in taglist:
233  sample_units = laltypes.LALUnit(taglist["units"].strip())
234  else:
235  sample_units = None
236  return {
237  "instrument": instrument,
238  "channel-name": channel_name,
239  "sample-units": sample_units
240  }