PURIFY
Next-generation radio interferometric imaging
uvfits.cc
Go to the documentation of this file.
1 #include "purify/uvfits.h"
2 #include "purify/config.h"
3 #include <fitsio.h>
4 #include <iostream>
5 #include <memory>
6 #include <string>
7 #include "purify/logging.h"
8 
9 namespace purify {
10 namespace pfitsio {
11 
12 utilities::vis_params read_uvfits(const std::vector<std::string> &names, const bool flag,
13  const stokes pol) {
14  utilities::vis_params output = read_uvfits(names.at(0), flag, pol);
15  if (names.size() == 1) return output;
16  for (int i = 1; i < names.size(); i++) output = read_uvfits(names.at(i), output);
17  return output;
18 }
19 
20 utilities::vis_params read_uvfits(const std::string &vis_name2, const utilities::vis_params &uv1,
21  const bool flag, const stokes pol) {
23  const bool w_term = not uv1.w.isZero(0);
24  const auto uv2 = read_uvfits(vis_name2, flag, pol);
25  if (std::abs(uv1.ra - uv2.ra) > 1e-6)
26  throw std::runtime_error(vis_name2 + ": wrong RA in pointing.");
27  if (std::abs(uv1.dec - uv2.dec) > 1e-6)
28  throw std::runtime_error(vis_name2 + ": wrong DEC in pointing.");
29  uv.ra = uv1.ra;
30  uv.dec = uv1.dec;
31  uv.u = Vector<t_real>::Zero(uv1.size() + uv2.size());
32  uv.v = Vector<t_real>::Zero(uv1.size() + uv2.size());
33  uv.w = Vector<t_real>::Zero(uv1.size() + uv2.size());
34  uv.vis = Vector<t_complex>::Zero(uv1.size() + uv2.size());
35  uv.weights = Vector<t_complex>::Zero(uv1.size() + uv2.size());
36  uv.u.segment(0, uv1.size()) = uv1.u;
37  uv.v.segment(0, uv1.size()) = uv1.v;
38  uv.w.segment(0, uv1.size()) = uv1.w;
39  uv.vis.segment(0, uv1.size()) = uv1.vis;
40  uv.weights.segment(0, uv1.size()) = uv1.weights;
41  uv.u.segment(uv1.size(), uv2.size()) = uv2.u;
42  uv.v.segment(uv1.size(), uv2.size()) = uv2.v;
43  uv.w.segment(uv1.size(), uv2.size()) = uv2.w;
44  uv.vis.segment(uv1.size(), uv2.size()) = uv2.vis;
45  uv.weights.segment(uv1.size(), uv2.size()) = uv2.weights;
46  return uv;
47 }
48 
49 utilities::vis_params read_uvfits(const std::string &filename, const bool flag, const stokes pol) {
50  fitsfile *fptr;
51  int status = 0;
52  int hdupos;
53  int baselines;
54  int naxes;
55  int pcount;
56  t_real ra = 0;
57  t_real dec = 0;
58  std::shared_ptr<char> comment =
59  std::shared_ptr<char>(new char[FLEN_CARD], [](char *ptr) { delete[] ptr; });
60  PURIFY_MEDIUM_LOG("Reading uvfits {}", filename);
61  if (fits_open_file(&fptr, filename.c_str(), READONLY, &status))
62  throw std::runtime_error("Could not open file " + filename);
63 
64  int hdutype;
65  if (fits_movabs_hdu(fptr, 1, &hdutype, &status)) throw std::runtime_error("Error changing HDU.");
66  if (hdutype != IMAGE_HDU)
67  throw std::runtime_error("HDU 1 not expected type " + std::to_string(hdutype));
68 
69  fits_read_key(fptr, TDOUBLE, "CRVAL5", &ra, comment.get(), &status);
70  fits_read_key(fptr, TDOUBLE, "CRVAL6", &dec, comment.get(), &status);
71  fits_read_key(fptr, TINT, "GCOUNT", &baselines, comment.get(), &status);
72  fits_read_key(fptr, TINT, "NAXIS", &naxes, comment.get(), &status);
73  fits_read_key(fptr, TINT, "PCOUNT", &pcount, comment.get(), &status);
74  if (naxes == 0) throw std::runtime_error("No axes in header... ");
75  if (pcount == 0) throw std::runtime_error("No uvw or time coordinates in header... ");
76  t_uint total = 1;
77  std::vector<int> naxis(naxes, 0);
78  // filling up axis sizes
79  for (int i = 1; i < naxes; i++) {
80  int n;
81  std::string key = "NAXIS" + std::to_string(i + 1);
82  fits_read_key(fptr, TINT, key.c_str(), &n, comment.get(), &status);
83  naxis[i] = n;
84  PURIFY_LOW_LOG("NAXIS {} Size: {}", i, n);
85  total *= n;
86  }
87  const t_uint channels = naxis.at(3);
88  const t_uint pols = naxis.at(2);
89  const t_uint ifs = naxis.at(4);
90  const t_uint pointings_num = naxis.at(5);
91  PURIFY_LOW_LOG("RA: {} deg, Dec.: {} deg", ra, dec);
92  PURIFY_MEDIUM_LOG("Pointings: {}", pointings_num);
93  PURIFY_MEDIUM_LOG("IFs: {}", ifs);
94  PURIFY_MEDIUM_LOG("Baselines: {}", baselines);
95  PURIFY_MEDIUM_LOG("Channels: {}", channels);
96  PURIFY_MEDIUM_LOG("Polarisations: {}", pols);
98  "Purify currently assumes uvfits files are in XX YY XY YX format. Also will only read Stokes "
99  "I for now.");
100  PURIFY_MEDIUM_LOG("Total data per baseline: {}", total);
101  if (pointings_num > 1) throw std::runtime_error("More than one pointing is not supported.");
102  if (ifs > 1) throw std::runtime_error("More than one IF is not supported.");
103  PURIFY_LOW_LOG("Reading Data...");
104 
105  const Matrix<t_real> coords = read_uvfits_coords(fptr, &status, baselines, pcount);
106 
107  // (real, imag, weight, pol, frequency) is the order
108  const Vector<t_real> data = read_uvfits_data(fptr, &status, naxis, baselines);
109  const Vector<t_real> frequencies = read_uvfits_freq(fptr, &status, 4);
110  if (frequencies.size() != channels)
111  throw std::runtime_error("Number of frequencies doesn't match number of channels. " +
112  std::to_string(frequencies.size()) + "!=" + std::to_string(channels));
113  int pol_index1;
114  int pol_index2;
115  Vector<t_complex> stokes_transform = Vector<t_complex>::Zero(2);
116  switch (pol) {
117  case stokes::I:
118  pol_index1 = 0;
119  pol_index2 = 1;
120  stokes_transform(0) = 1. / 2;
121  stokes_transform(1) = 1. / 2;
122  break;
123  case stokes::Q:
124  throw std::runtime_error("Polarisation not supported for reading uvfits.");
125  pol_index1 = 0;
126  pol_index2 = 1;
127  stokes_transform(0) = 1. / 2;
128  stokes_transform(1) = -1. / 2;
129  break;
130  case stokes::U:
131  throw std::runtime_error("Polarisation not supported for reading uvfits.");
132  pol_index1 = 0;
133  pol_index2 = 1;
134  stokes_transform(0) = 1. / 2 * t_complex(0, -1);
135  stokes_transform(1) = 1. / 2 * t_complex(0, -1);
136  break;
137  case stokes::V:
138  throw std::runtime_error("Polarisation not supported for reading uvfits.");
139  pol_index1 = 0;
140  pol_index2 = 1;
141  stokes_transform(0) = 1. / 2 * t_complex(0, -1);
142  stokes_transform(1) = -1. / 2 * t_complex(0, -1);
143  break;
144  default:
145  throw std::runtime_error("Polarisation not supported for reading uvfits.");
146  pol_index1 = 0;
147  pol_index2 = 1;
148  stokes_transform(0) = 1. / 2;
149  stokes_transform(1) = 1. / 2;
150  break;
151  }
152  fits_close_file(fptr, &status);
153  if (status) { /* print any error messages */
154  fits_report_error(stderr, status);
155  throw std::runtime_error("Error reading fits file...");
156  }
157  auto uv_data = read_polarisation_with_flagging(
158  data, coords, frequencies, pol_index1, pol_index2, pols, baselines, channels,
159  stokes_transform,
160  [flag](const t_complex vis1, const t_complex weight1, const t_complex vis2,
161  const t_complex weight2) {
162  if (flag)
163  return (weight1.real() > 0.) and (weight2.real() > 0.) and (std::abs(vis1) > 1e-20) and
164  (std::abs(vis2) > 1e-20) and
165  (!std::isnan(vis1.real()) and !std::isnan(vis1.imag())) and
166  (!std::isnan(vis2.real()) and !std::isnan(vis2.imag()));
167  else
168  return true;
169  });
170  uv_data.ra = ra;
171  uv_data.dec = dec;
172  return uv_data;
173 }
174 
176  const utilities::vis_params &input, const utilities::vis_params &input2,
177  const Vector<t_complex> &stokes_transform,
178  const std::function<bool(t_real, t_real, t_real, t_complex, t_complex, t_real, t_real, t_real,
179  t_complex, t_complex)> &filter) {
180  t_uint size = 0;
181  if (stokes_transform.size() != 2)
182  throw std::runtime_error("stokes transform is not the right size (!= 2).");
183  if (stokes_transform.isZero()) throw std::runtime_error("stokes transform not valid.");
184  if (input.size() != input2.size())
185  throw std::runtime_error("Cannot apply filter to each data set, sizes do not match.");
186  for (int i = 0; i < input.size(); i++) {
187  if (filter(input.u(i), input.v(i), input.w(i), input.vis(i), input.weights(i), input2.u(i),
188  input2.v(i), input2.w(i), input2.vis(i), input2.weights(i))) {
189  if (std::abs(input.u(i) - input2.u(i)) > 1e-6)
190  throw std::runtime_error("baselines don't match for filter.");
191  if (std::abs(input.v(i) - input2.v(i)) > 1e-6)
192  throw std::runtime_error("baselines don't match for filter.");
193  if (std::abs(input.w(i) - input2.w(i)) > 1e-6)
194  throw std::runtime_error("baselines don't match for filter.");
195  size++;
196  }
197  }
198 
199  PURIFY_LOW_LOG("Applying flags: Keeping {} out of {} data points.", size, input.size());
200  utilities::vis_params output(Vector<t_real>::Zero(size), Vector<t_real>::Zero(size),
201  Vector<t_real>::Zero(size), Vector<t_complex>::Zero(size),
202  Vector<t_complex>::Zero(size), input.units, input.ra, input.dec,
203  input.average_frequency);
204  output.time = Vector<t_real>::Zero(size);
205  output.baseline = Vector<t_uint>::Zero(size);
206  output.frequencies = input.frequencies;
207  t_uint count = 0;
208  for (t_uint i = 0; i < input.size(); i++)
209  if (filter(input.u(i), input.v(i), input.w(i), input.vis(i), input.weights(i), input2.u(i),
210  input2.v(i), input2.w(i), input2.vis(i), input2.weights(i))) {
211  output.u(count) = input.u(i);
212  output.v(count) = input.v(i);
213  output.w(count) = input.w(i);
214  output.vis(count) = input.vis(i) * stokes_transform(0) + input2.vis(i) * stokes_transform(1);
215  output.weights(count) = 1. / std::sqrt(1. / input.weights(i) * stokes_transform(0) +
216  1. / input2.weights(i) * stokes_transform(1));
217  output.baseline(count) = input.baseline(i);
218  output.time(count) = input.time(i);
219  count++;
220  }
221 
222  return output;
223 }
224 
226  const Vector<t_real> &data, const Matrix<t_real> &coords, const Vector<t_real> &frequencies,
227  const t_uint pol_index1, const t_uint pol_index2, const t_uint pols, const t_uint baselines,
228  const t_uint channels, const Vector<t_complex> stokes_transform,
229  const std::function<bool(t_complex, t_complex, t_complex, t_complex)> &filter) {
230  t_uint count = 0;
231  for (t_uint c = 0; c < channels; c++)
232  for (t_uint b = 0; b < baselines; b++) {
233  t_complex const weight1 =
234  read_weight_from_data(data, pol_index1, pols, c, channels, b, baselines);
235  t_complex const weight2 =
236  read_weight_from_data(data, pol_index2, pols, c, channels, b, baselines);
237  t_complex const vis1 = read_vis_from_data(data, pol_index1, pols, c, channels, b, baselines);
238  t_complex const vis2 = read_vis_from_data(data, pol_index2, pols, c, channels, b, baselines);
239  if (filter(vis1, weight1, vis2, weight2)) count++;
240  }
241  const t_uint stride = count;
242  utilities::vis_params uv_data;
243  uv_data.u = Vector<t_real>::Zero(stride);
244  uv_data.v = Vector<t_real>::Zero(stride);
245  uv_data.w = Vector<t_real>::Zero(stride);
246  uv_data.time = Vector<t_real>::Zero(stride);
247  uv_data.baseline = Vector<t_uint>::Zero(stride);
248  uv_data.vis = Vector<t_complex>::Zero(stride);
249  uv_data.weights = Vector<t_complex>::Zero(stride);
250  uv_data.average_frequency = frequencies.array().mean();
251  if (pol_index1 >= pols) throw std::runtime_error("Polarisation index out of bounds.");
252  if (pol_index2 >= pols) throw std::runtime_error("Polarisation index out of bounds.");
253  PURIFY_LOW_LOG("Applying flags: Keeping {} out of {} data points.", stride, channels * baselines);
254  // reading in data
255  count = 0;
256  for (t_uint c = 0; c < channels; c++)
257  for (t_uint b = 0; b < baselines; b++) {
258  t_complex const weight1 =
259  read_weight_from_data(data, pol_index1, pols, c, channels, b, baselines);
260  t_complex const weight2 =
261  read_weight_from_data(data, pol_index2, pols, c, channels, b, baselines);
262  t_complex const vis1 = read_vis_from_data(data, pol_index1, pols, c, channels, b, baselines);
263  t_complex const vis2 = read_vis_from_data(data, pol_index2, pols, c, channels, b, baselines);
264  // checking flags of both polarisations
265  if (filter(vis1, weight1, vis2, weight2)) {
266  uv_data.vis(count) = uv_data.vis(count) =
267  vis1 * stokes_transform(0) + vis2 * stokes_transform(1);
268  uv_data.weights(count) =
269  1. / std::sqrt(1. / weight1 * stokes_transform(0) + 1. / weight2 * stokes_transform(1));
270  uv_data.u(count) = coords(0, b) * frequencies(c);
271  uv_data.v(count) = -coords(1, b) * frequencies(c);
272  uv_data.w(count) = coords(2, b) * frequencies(c);
273  uv_data.baseline(count) = static_cast<t_uint>(coords(3, b));
274  uv_data.time(count) = coords(4, b);
275  count++;
276  }
277  }
278  assert(count == stride);
279  uv_data.frequencies = frequencies;
281  PURIFY_MEDIUM_LOG("All Data Read!");
282  return uv_data;
283 }
284 utilities::vis_params read_polarisation(const Vector<t_real> &data, const Matrix<t_real> &coords,
285  const Vector<t_real> &frequencies, const t_uint pol_index1,
286  const t_uint pols, const t_uint baselines,
287  const t_uint channels) {
288  const t_uint stride = channels * baselines;
289  utilities::vis_params uv_data;
290  uv_data.u = Vector<t_real>::Zero(stride);
291  uv_data.v = Vector<t_real>::Zero(stride);
292  uv_data.w = Vector<t_real>::Zero(stride);
293  uv_data.time = Vector<t_real>::Zero(stride);
294  uv_data.baseline = Vector<t_uint>::Zero(stride);
295  uv_data.vis = Vector<t_complex>::Zero(stride);
296  uv_data.weights = Vector<t_complex>::Zero(stride);
297  if (pol_index1 >= pols) throw std::runtime_error("Polarisation index out of bounds.");
298  // reading in data
299  t_uint count = 0;
300  for (t_uint c = 0; c < channels; c++)
301  for (t_uint b = 0; b < baselines; b++) {
302  uv_data.vis(count) = read_vis_from_data(data, pol_index1, pols, c, channels, b, baselines);
303  uv_data.weights(count) =
304  read_weight_from_data(data, pol_index1, pols, c, channels, b, baselines);
305  count++;
306  }
307  uv_data.average_frequency = frequencies.array().mean();
308  for (t_uint i = 0; i < frequencies.size(); i++) {
309  uv_data.u.segment(i * baselines, baselines) = coords.row(0) * frequencies(i);
310  uv_data.v.segment(i * baselines, baselines) = -coords.row(1) * frequencies(i);
311  uv_data.w.segment(i * baselines, baselines) = coords.row(2) * frequencies(i);
312  uv_data.baseline.segment(i * baselines, baselines) = coords.row(3).cast<t_uint>();
313  uv_data.time.segment(i * baselines, baselines) = coords.row(4);
314  }
315  uv_data.frequencies = frequencies;
317  return uv_data;
318 }
319 
320 Vector<t_real> read_uvfits_freq(fitsfile *fptr, int *status, const int &col) {
321  Vector<t_real> output;
322  read_uvfits_freq(fptr, status, output, col);
323  return output;
324 }
325 
326 void read_uvfits_freq(fitsfile *fptr, int *status, Vector<t_real> &output, const int &col) {
327  t_real cfreq;
328  t_real dfreq;
329  int crpix;
330  int nfreq;
331  std::shared_ptr<char> comment =
332  std::shared_ptr<char>(new char[FLEN_CARD], [](char *ptr) { delete[] ptr; });
333  std::string key = "CRVAL" + std::to_string(col);
334  fits_read_key(fptr, TDOUBLE, key.c_str(), &cfreq, comment.get(), status);
335  key = "CDELT" + std::to_string(col);
336  fits_read_key(fptr, TDOUBLE, key.c_str(), &dfreq, comment.get(), status);
337  key = "CRPIX" + std::to_string(col);
338  fits_read_key(fptr, TINT, key.c_str(), &crpix, comment.get(), status);
339  key = "NAXIS" + std::to_string(col);
340  fits_read_key(fptr, TINT, key.c_str(), &nfreq, comment.get(), status);
341  if (nfreq == 0) throw std::runtime_error("Wrong number of channels read from header.");
342  output = Vector<t_real>::Zero(nfreq);
343  for (int i = 0; i < output.size(); i++) output(i) = (i - nfreq * 0.5) * dfreq + cfreq;
344 }
345 
346 Vector<t_real> read_uvfits_data(fitsfile *fptr, int *status, const std::vector<int> &naxis,
347  const int &baselines) {
348  Vector<t_real> output;
349  read_uvfits_data(fptr, status, naxis, baselines, output);
350  return output;
351 }
352 
353 void read_uvfits_data(fitsfile *fptr, int *status, const std::vector<int> &naxis,
354  const int &baselines, Vector<t_real> &output) {
355  long nelements = 1;
356  for (int i = 2; i < naxis.size(); i++) {
357  nelements *= static_cast<long>(naxis.at(i));
358  }
359  if (nelements == 0) throw std::runtime_error("Zero number of elements.");
360  output = Vector<t_real>::Zero(naxis.at(1) * nelements * baselines);
361  int nulval = 0;
362  int anynul = 0;
363  fits_read_col(fptr, TDOUBLE, 2, 1, 1, static_cast<long>(output.size()), &nulval, output.data(),
364  &anynul, status);
365 }
366 
367 Matrix<t_real> read_uvfits_coords(fitsfile *fptr, int *status, const int &groups,
368  const int &pcount) {
369  Matrix<t_real> output;
370  read_uvfits_coords(fptr, status, pcount, groups, output);
371  return output;
372 }
373 
374 t_real read_value_from_data(const Vector<t_real> &data, const t_uint col, const t_uint pol,
375  const t_uint pols, const t_uint chan, const t_uint chans,
376  const t_uint baseline, const t_uint baselines) {
377  const t_uint index = col + 3 * (pol + pols * (chan + chans * baseline));
378  return data(index);
379 }
380 
381 t_complex read_vis_from_data(const Vector<t_real> &data, const t_uint pol, const t_uint pols,
382  const t_uint chan, const t_uint chans, const t_uint baseline,
383  const t_uint baselines) {
384  return t_complex(read_value_from_data(data, 0, pol, pols, chan, chans, baseline, baselines),
385  read_value_from_data(data, 1, pol, pols, chan, chans, baseline, baselines));
386 }
387 
388 t_complex read_weight_from_data(const Vector<t_real> &data, const t_uint pol, const t_uint pols,
389  const t_uint chan, const t_uint chans, const t_uint baseline,
390  const t_uint baselines) {
391  return t_complex(read_value_from_data(data, 2, pol, pols, chan, chans, baseline, baselines), 0);
392 }
393 
394 void read_uvfits_coords(fitsfile *fptr, int *status, const int &pcount, const int &groups,
395  Matrix<t_real> &output) {
396  output = Matrix<t_real>::Zero(pcount, groups);
397  int nulval = 0;
398  int anynul;
399  // reading in parameters per baseline
400  for (int i = 0; i < groups; i++)
401  fits_read_col(fptr, TDOUBLE, 1, 1 + i, 1, pcount, &nulval, output.col(i).data(), &anynul,
402  status);
403 }
404 
405 void read_fits_keys(fitsfile *fptr, int *status) {
406  std::shared_ptr<char> card =
407  std::shared_ptr<char>(new char[FLEN_CARD], [](char *ptr) { delete[] ptr; });
408  int nkeys;
409  fits_get_hdrspace(fptr, &nkeys, NULL, status);
410 
411  for (int ii = 1; ii <= nkeys; ii++) {
412  fits_read_record(fptr, ii, card.get(), status); /* read keyword */
413  std::printf("%s\n", card.get());
414  }
415  std::printf("END\n\n"); /* terminate listing with END */
416 }
417 
418 } // namespace pfitsio
419 } // namespace purify
#define PURIFY_LOW_LOG(...)
Low priority message.
Definition: logging.h:207
#define PURIFY_MEDIUM_LOG(...)
Medium priority message.
Definition: logging.h:205
const t_real c
speed of light in vacuum
Definition: types.h:72
utilities::vis_params filter_and_combine(const utilities::vis_params &input, const utilities::vis_params &input2, const Vector< t_complex > &stokes_transform, const std::function< bool(t_real, t_real, t_real, t_complex, t_complex, t_real, t_real, t_real, t_complex, t_complex)> &filter)
Remove visibilities with zero weighting.
Definition: uvfits.cc:175
utilities::vis_params read_polarisation_with_flagging(const Vector< t_real > &data, const Matrix< t_real > &coords, const Vector< t_real > &frequencies, const t_uint pol_index1, const t_uint pol_index2, const t_uint pols, const t_uint baselines, const t_uint channels, const Vector< t_complex > stokes_transform, const std::function< bool(t_complex, t_complex, t_complex, t_complex)> &filter)
read polarisation with flaggging
Definition: uvfits.cc:225
t_real read_value_from_data(const Vector< t_real > &data, const t_uint col, const t_uint pol, const t_uint pols, const t_uint chan, const t_uint chans, const t_uint baseline, const t_uint baselines)
read value from data
Definition: uvfits.cc:374
t_complex read_weight_from_data(const Vector< t_real > &data, const t_uint pol, const t_uint pols, const t_uint chan, const t_uint chans, const t_uint baseline, const t_uint baselines)
return weight for given baseline
Definition: uvfits.cc:388
void read_fits_keys(fitsfile *fptr, int *status)
Read uvfits keys out.
Definition: uvfits.cc:405
Matrix< t_real > read_uvfits_coords(fitsfile *fptr, int *status, const int &groups, const int &pcount)
read coordinates from uvfits file
Definition: uvfits.cc:367
utilities::vis_params read_uvfits(const std::vector< std::string > &names, const bool flag, const stokes pol)
Read uvfits files from name of vector.
Definition: uvfits.cc:12
Vector< t_real > read_uvfits_freq(fitsfile *fptr, int *status, const int &col)
read frequencies for each channel
Definition: uvfits.cc:320
Vector< t_real > read_uvfits_data(fitsfile *fptr, int *status, const std::vector< int > &naxis, const int &baselines)
read data from uvfits file
Definition: uvfits.cc:346
utilities::vis_params read_polarisation(const Vector< t_real > &data, const Matrix< t_real > &coords, const Vector< t_real > &frequencies, const t_uint pol_index1, const t_uint pols, const t_uint baselines, const t_uint channels)
read polarisation data from uvfits data
Definition: uvfits.cc:284
t_complex read_vis_from_data(const Vector< t_real > &data, const t_uint pol, const t_uint pols, const t_uint chan, const t_uint chans, const t_uint baseline, const t_uint baselines)
return visibility for given baseline
Definition: uvfits.cc:381
stokes
Definition: types.h:44
Vector< t_complex > vis
Definition: uvw_utilities.h:22
t_uint size() const
return number of measurements
Definition: uvw_utilities.h:54
Vector< t_complex > weights
Definition: uvw_utilities.h:23
Vector< t_real > frequencies
Definition: uvw_utilities.h:21