gstlal  1.4.1
pipeio.py
Go to the documentation of this file.
1 # Copyright (C) 2009--2016 Kipp Cannon
2 # Copyright (C) 2016 Chad Hanna
3 # Copyright (C) 2016 Patrick Brockill
4 # Copyright (C) 2016 Sarah Caudill
5 # Copyright (C) 2015 Ryan Everett
6 # Copyright (C) 2010 Leo Singer
7 #
8 # This program is free software; you can redistribute it and/or modify it
9 # under the terms of the GNU General Public License as published by the
10 # Free Software Foundation; either version 2 of the License, or (at your
11 # option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful, but
14 # WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16 # Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License along
19 # with this program; if not, write to the Free Software Foundation, Inc.,
20 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 
22 
23 
24 
25 
26 #
27 # =============================================================================
28 #
29 # Preamble
30 #
31 # =============================================================================
32 #
33 
34 
35 import numpy
36 import sys
37 
38 
39 import gi
40 gi.require_version('Gst', '1.0')
41 gi.require_version('GstAudio', '1.0')
42 from gi.repository import GObject
43 from gi.repository import Gst
44 from gi.repository import GstAudio
45 GObject.threads_init()
46 Gst.init(None)
47 
48 
49 import lal
50 
51 
52 __author__ = "Kipp Cannon <kipp.cannon@ligo.org>, Chad Hanna <chad.hanna@ligo.org>, Drew Keppel <drew.keppel@ligo.org>"
53 __version__ = "FIXME"
54 __date__ = "FIXME"
55 
56 
57 #
58 # =============================================================================
59 #
60 # Properties
61 #
62 # =============================================================================
63 #
64 
65 
66 def repack_complex_array_to_real(arr):
67  """
68  Repack a complex-valued array into a real-valued array with twice
69  as many columns. Used to set complex arrays as values on elements
70  that expose them as real-valued array properties (gobject doesn't
71  understand complex numbers). The return value is a view into the
72  input array.
73  """
74  # FIXME: this function shouldn't exist, we should add complex
75  # types to gobject
76  if arr.dtype.kind != "c":
77  raise TypeError(arr)
78  assert arr.dtype.itemsize % 2 == 0
79  return arr.view(dtype = numpy.dtype("f%d" % (arr.dtype.itemsize // 2)))
80 
81 
82 def repack_real_array_to_complex(arr):
83  """
84  Repack a real-valued array into a complex-valued array with half as
85  many columns. Used to retrieve complex arrays from elements that
86  expose them as real-valued array properties (gobject doesn't
87  understand complex numbers). The return value is a view into the
88  input array.
89  """
90  # FIXME: this function shouldn't exist, we should add complex
91  # types to gobject
92  if arr.dtype.kind != "f":
93  raise TypeError(arr)
94  return arr.view(dtype = numpy.dtype("c%d" % (arr.dtype.itemsize * 2)))
95 
96 
97 #
98 # =============================================================================
99 #
100 # Buffers
101 #
102 # =============================================================================
103 #
104 
105 
106 def get_unit_size(caps):
107  struct = caps[0]
108  name = struct.get_name()
109  if name == "audio/x-raw":
110  info = GstAudio.AudioInfo()
111  info.from_caps(caps)
112  return info.bpf
113  elif name == "video/x-raw" and struct["format"] in ("RGB", "RGBA", "ARGB", "ABGR"):
114  return struct["width"] * struct["height"] * (3 if struct["format"] == "RGB" else 4)
115  raise ValueError(caps)
116 
117 
118 def numpy_dtype_from_caps(caps):
119  formats_dict = {
120  GstAudio.AudioFormat.F32: numpy.dtype("float32"),
121  GstAudio.AudioFormat.F64: numpy.dtype("float64"),
122  GstAudio.AudioFormat.S8: numpy.dtype("int8"),
123  GstAudio.AudioFormat.U8: numpy.dtype("uint8"),
124  GstAudio.AudioFormat.S16: numpy.dtype("int16"),
125  GstAudio.AudioFormat.U16: numpy.dtype("uint16"),
126  GstAudio.AudioFormat.S32: numpy.dtype("int32"),
127  GstAudio.AudioFormat.U32: numpy.dtype("uint32")
128  }
129  info = GstAudio.AudioInfo()
130  info.from_caps(caps)
131  return formats_dict[info.finfo.format]
132 
133 
134 def format_string_from_numpy_dtype(dtype):
135  formats_dict = {
136  numpy.dtype("float32"): GstAudio.AudioFormat.F32,
137  numpy.dtype("float64"): GstAudio.AudioFormat.F64,
138  numpy.dtype("int8"): GstAudio.AudioFormat.S8,
139  numpy.dtype("uint8"): GstAudio.AudioFormat.U8,
140  numpy.dtype("int16"): GstAudio.AudioFormat.S16,
141  numpy.dtype("uint16"): GstAudio.AudioFormat.U16,
142  numpy.dtype("int32"): GstAudio.AudioFormat.S32,
143  numpy.dtype("uint32"): GstAudio.AudioFormat.U32
144  }
145  return GstAudio.AudioFormat.to_string(formats_dict[dtype])
146 
147 
148 def caps_from_array(arr, rate = None):
149  return Gst.Caps.from_string("audio/x-raw, format=(string)%s, rate=(int)%d, channels=(int)%d, layout=(string)interleaved, channel-mask=(bitmask)0" % (format_string_from_numpy_dtype(arr.dtype), rate, arr.shape[1]))
150 
151 
152 def array_from_audio_sample(sample):
153  caps = sample.get_caps()
154  bufmap = sample.get_buffer().map(Gst.MapFlags.READ)[1]
155  (success, channels) = caps.get_structure(0).get_int("channels")
156  # FIXME: conditional is work-around for broken handling of
157  # zero-length buffers in numpy < 1.7. remove and use frombuffer()
158  # unconditionally when we can rely on numpy >= 1.7
159  if bufmap.size > 0:
160  a = numpy.frombuffer(bufmap.data, dtype = numpy_dtype_from_caps(caps))
161  else:
162  a = numpy.array((), dtype = numpy_dtype_from_caps(caps))
163  return numpy.reshape(a, (len(a) // channels, channels))
164 
165 
166 def audio_buffer_from_array(arr, timestamp, offset, rate):
167  buf = Gst.Buffer.new_wrapped(numpy.getbuffer(arr))
168  buf.pts = timestamp
169  buf.duration = (Gst.SECOND * arr.shape[0] + rate // 2) // rate
170  buf.offset = offset
171  buf.offset_end = offset + arr.shape[0]
172  return buf
173 
174 
175 #
176 # =============================================================================
177 #
178 # Messages
179 #
180 # =============================================================================
181 #
182 
183 
184 def parse_spectrum_message(message):
185  """
186  Parse a "spectrum" message from the lal_whiten element, return a
187  LAL REAL8FrequencySeries containing the strain spectral density.
188  """
189  s = message.get_structure()
190  psd = lal.CreateREAL8FrequencySeries(
191  name = s["instrument"] if s.has_field("instrument") else "",
192  epoch = lal.LIGOTimeGPS(0, message.timestamp),
193  f0 = 0.0,
194  deltaF = s["delta-f"],
195  sampleUnits = lal.Unit(s["sample-units"].strip()),
196  length = len(s["magnitude"])
197  )
198  psd.data.data = numpy.array(s["magnitude"])
199  return psd
200 
201 
202 #
203 # =============================================================================
204 #
205 # Tags
206 #
207 # =============================================================================
208 #
209 
210 
211 def parse_framesrc_tags(taglist):
212  try:
213  instrument = taglist["instrument"]
214  except KeyError:
215  instrument = None
216  try:
217  channel_name = taglist["channel-name"]
218  except KeyError:
219  channel_name = None
220  if "units" in taglist:
221  sample_units = lal.Unit(taglist["units"].strip())
222  else:
223  sample_units = None
224  return {
225  "instrument": instrument,
226  "channel-name": channel_name,
227  "sample-units": sample_units
228  }