DP3
PieceWisePhaseFitter.h
Go to the documentation of this file.
1 // Copyright (C) 2020 ASTRON (Netherlands Institute for Radio Astronomy)
2 // SPDX-License-Identifier: GPL-3.0-or-later
3 
4 #ifndef DP3_DDECAL_PIECE_WISE_PHASE_FITTER_H_
5 #define DP3_DDECAL_PIECE_WISE_PHASE_FITTER_H_
6 
7 #include <algorithm>
8 #include <iterator>
9 #include <cmath>
10 
11 namespace dp3 {
12 namespace ddecal {
13 
15  public:
16  PieceWisePhaseFitter() : _chunkSize(0) {}
17 
22  PieceWisePhaseFitter(size_t chunkSize) : _chunkSize(chunkSize) {}
23 
24  size_t ChunkSize() const { return _chunkSize; }
25  void SetChunkSize(size_t chunkSize) { _chunkSize = chunkSize; }
26 
30  template <typename Iter>
31  static void Unwrap(Iter first, Iter last) {
32  for (Iter iter = first + 1; iter != last; ++iter) {
33  while (*iter - (*(iter - 1)) > M_PI) *iter -= 2.0 * M_PI;
34  while ((*(iter - 1) - *iter) > M_PI) *iter += 2.0 * M_PI;
35  }
36  }
37 
38  static size_t CalculateChunkSize(const std::vector<double>& frequencies) {
40  const double nOctaves =
41  (log(frequencies.back()) - log(frequencies.front())) / M_LN2;
42  if (nOctaves > 0.0)
43  return std::min<size_t>(frequencies.size(), std::ceil(frequencies.size() /
44  (10.0 * nOctaves)));
45  else
46  return frequencies.size();
47  }
48 
60  void PieceWiseFit(const std::vector<double>& nu,
61  const std::vector<double>& data,
62  std::vector<double>& fittedData) {
63  size_t chunkSize = std::min(data.size(), _chunkSize);
64  if (chunkSize <= 1)
65  fittedData = data;
66  else {
67  for (size_t ch = 0; ch < data.size(); ch += chunkSize) {
68  size_t pos = ch;
69  if (pos > data.size() - chunkSize) pos = data.size() - chunkSize;
70  fitChunk(chunkSize, pos, nu, data, fittedData);
71  }
72  }
73  }
74 
75  void PieceWiseFit(const std::vector<double>& nu,
76  const std::vector<double>& data, const double* weights,
77  std::vector<double>& fittedData) {
78  size_t chunkSize = std::min(data.size(), _chunkSize);
79  if (chunkSize <= 1)
80  fittedData = data;
81  else {
82  for (size_t ch = 0; ch < data.size(); ch += chunkSize) {
83  size_t pos = ch;
84  if (pos > data.size() - chunkSize) pos = data.size() - chunkSize;
85  fitChunk(chunkSize, pos, nu, data, weights, fittedData);
86  }
87  }
88  }
89 
94  void SlidingFit(const double* nu, const double* data, double* fittedData,
95  size_t n) {
96  const size_t chunkSize = std::min(_chunkSize, n);
97  if (chunkSize <= 1)
98  std::copy(data, data + n, fittedData);
99  else {
100  const size_t leftEdge = chunkSize / 2,
101  rightEdge = n - chunkSize + chunkSize / 2;
102 
103  double a, b;
104  PieceWisePhaseFitter::fitSlope(&data[0], &nu[0], chunkSize, a, b);
105  for (size_t ch = 0; ch != leftEdge; ++ch) fittedData[ch] = a + b * nu[ch];
106 
107  for (size_t ch = 0; ch != n - chunkSize; ++ch) {
108  PieceWisePhaseFitter::fitSlope(&data[ch], &nu[ch], chunkSize, a, b);
109  fittedData[ch + chunkSize / 2] = a + b * nu[ch + chunkSize / 2];
110  }
111 
112  PieceWisePhaseFitter::fitSlope(&data[n - chunkSize], &nu[n - chunkSize],
113  chunkSize, a, b);
114  for (size_t ch = rightEdge; ch != n; ++ch)
115  fittedData[ch] = a + b * nu[ch];
116  }
117  }
118 
136  void SlidingFit(const double* nu, const double* data, const double* weights,
137  double* fittedData, size_t n) {
138  const size_t chunkSize = std::min(_chunkSize, n);
139  if (chunkSize <= 1)
140  std::copy(data, data + n, fittedData);
141  else {
142  const size_t leftEdge = chunkSize / 2,
143  rightEdge = n - chunkSize + chunkSize / 2;
144 
145  double a, b;
146  PieceWisePhaseFitter::fitSlope(&data[0], &nu[0], &weights[0], chunkSize,
147  a, b);
148  for (size_t ch = 0; ch != leftEdge; ++ch) fittedData[ch] = a + b * nu[ch];
149 
150  for (size_t ch = 0; ch != n - chunkSize; ++ch) {
151  PieceWisePhaseFitter::fitSlope(&data[ch], &nu[ch], &weights[ch],
152  chunkSize, a, b);
153  fittedData[ch + chunkSize / 2] = a + b * nu[ch + chunkSize / 2];
154  }
155 
156  PieceWisePhaseFitter::fitSlope(&data[n - chunkSize], &nu[n - chunkSize],
157  &weights[n - chunkSize], chunkSize, a, b);
158  for (size_t ch = rightEdge; ch != n; ++ch)
159  fittedData[ch] = a + b * nu[ch];
160  }
161  }
162 
163  size_t SlidingFitWithBreak(const double* nu, const double* data,
164  const double* weights, double* fittedData,
165  size_t n) {
166  if (std::min(_chunkSize, n) <= 1) {
167  std::copy(data, data + n, fittedData);
168  return 0;
169  } else {
170  SlidingFit(nu, data, weights, fittedData, n);
171  size_t bp = BreakPoint(data, weights, fittedData, n);
172  SlidingFit(nu, data, weights, fittedData, bp);
173  SlidingFit(nu + bp, data + bp, weights + bp, fittedData + bp, n - bp);
174  return bp;
175  }
176  }
177 
189  static double WeightedMedian(std::vector<std::pair<double, double>>& values) {
190  if (values.empty()) return 0.0;
191  std::sort(values.begin(), values.end());
193  double sum = 0.0;
194  for (std::pair<double, double>& v : values) sum += v.second;
195 
196  int index = 0;
198  double prefixSum = sum - values[0].second;
199  while (prefixSum > sum / 2) {
200  ++index;
201  prefixSum -= values[index].second;
202  }
203  return values[index].first;
204  }
205 
206  size_t BreakPoint(const double* data, const double* weights,
207  const double* fittedData, size_t n) {
208  double largest = 0.0;
209  size_t index = 0;
210  size_t ni = std::min(n, _chunkSize);
211  for (size_t i = 0; i != n - ni; ++i) {
212  double curSum = 0.0;
213  for (size_t j = 0; j != ni; ++j) {
214  double dist = data[i + j] - fittedData[i + j];
215  curSum += std::abs(dist) * weights[i + j];
216  }
217  if (curSum > largest) {
218  largest = curSum;
219  index = i + _chunkSize / 2;
220  }
221  }
222  return index;
223  }
224 
225  private:
226  size_t _chunkSize;
230  std::vector<double> _tempArray;
231  std::vector<std::pair<double, double>> _tempWeightedArray;
232 
236  void fitSlope(const double* data, const double* frequencies, size_t n,
237  double& a, double& b) {
238  double sum_x = 0.0, sum_xSq = 0.0, mean_y = 0.0, ss_xy = 0.0;
239  std::vector<double>& d = _tempArray;
240  d.assign(data, data + n);
241  size_t qCounts[4] = {0, 0, 0, 0};
242  for (double& v : d) {
243  if (v > M_PI * 1.5)
244  ++qCounts[3];
245  else if (v > M_PI * 1.0)
246  ++qCounts[2];
247  else if (v > M_PI * 0.5)
248  ++qCounts[1];
249  else
250  ++qCounts[0];
251  }
252  size_t maxQ = 0;
253  for (size_t i = 1; i != 4; ++i) {
254  if (qCounts[i] > qCounts[maxQ]) maxQ = i;
255  }
256  double wrapTo = (maxQ * 0.5 + 0.25) * M_PI;
257  for (double& v : d) {
258  if (v - wrapTo > M_PI)
259  v -= 2.0 * M_PI;
260  else if (v - wrapTo < -M_PI)
261  v += 2.0 * M_PI;
262  }
263  std::nth_element(d.data(), d.data() + n / 2, d.data() + n);
264  wrapTo = d[n / 2];
265  for (size_t i = 0; i != n; ++i) {
266  double x = data[i];
267  if (x - wrapTo > M_PI)
268  x -= 2.0 * M_PI;
269  else if (x - wrapTo < -M_PI)
270  x += 2.0 * M_PI;
271  sum_x += frequencies[i];
272  sum_xSq += frequencies[i] * frequencies[i];
273  mean_y += x;
274  ss_xy += frequencies[i] * x;
275  }
276  mean_y /= n;
277  double mean_x = sum_x / n;
278  double ss_xx = sum_xSq - mean_x * sum_x;
279  ss_xy -= sum_x * mean_y;
280  b = ss_xy / ss_xx;
281  a = mean_y - b * mean_x;
282  }
283 
284  template <typename Iter1, typename Iter2>
285  double weightedMedian(Iter1 valBegin, Iter1 valEnd, Iter2 weightBegin) {
286  _tempWeightedArray.resize(std::distance(valBegin, valEnd));
287  auto wIter = _tempWeightedArray.begin();
288  while (valBegin != valEnd) {
289  *wIter = std::make_pair(*valBegin, *weightBegin);
290  ++wIter;
291  ++valBegin;
292  ++weightBegin;
293  }
294  return WeightedMedian(_tempWeightedArray);
295  }
296 
300  void fitSlope(const double* data, const double* frequencies,
301  const double* weights, size_t n, double& a, double& b) {
302  size_t qCounts[4] = {0, 0, 0, 0};
303  for (size_t i = 0; i != n; ++i) {
304  if (data[i] > M_PI * 1.5)
305  qCounts[3] += weights[i];
306  else if (data[i] > M_PI * 1.0)
307  qCounts[2] += weights[i];
308  else if (data[i] > M_PI * 0.5)
309  qCounts[1] += weights[i];
310  else
311  qCounts[0] += weights[i];
312  }
313  size_t maxQ = 0;
314  for (size_t i = 1; i != 4; ++i) {
315  if (qCounts[i] > qCounts[maxQ]) maxQ = i;
316  }
317  double wrapTo = (maxQ * 0.5 + 0.25) * M_PI;
318  std::vector<double>& d = _tempArray;
319  d.resize(n);
320  for (size_t i = n / 4; i != n * 3 / 4; ++i) {
321  double v = data[i];
322  if (v - wrapTo > M_PI)
323  v -= 2.0 * M_PI;
324  else if (v - wrapTo < -M_PI)
325  v += 2.0 * M_PI;
326  d[i] = v;
327  }
328  wrapTo =
329  weightedMedian(d.data() + n / 4, d.data() + n * 3 / 4, weights + n / 4);
330 
331  double sum_x = 0.0, sum_xSq = 0.0, mean_y = 0.0, ss_xy = 0.0,
332  sum_weight = 0.0;
333 
334  for (size_t i = 0; i != n; ++i) {
335  double x = data[i];
336  if (x - wrapTo > M_PI)
337  x -= 2.0 * M_PI;
338  else if (x - wrapTo < -M_PI)
339  x += 2.0 * M_PI;
340  sum_x += frequencies[i] * weights[i];
341  sum_xSq += frequencies[i] * frequencies[i] * weights[i];
342  mean_y += x * weights[i];
343  ss_xy += frequencies[i] * x * weights[i];
344  sum_weight += weights[i];
345  }
346  if (sum_weight == 0.0) {
347  a = 0.0;
348  b = 0.0;
349  } else {
350  mean_y /= sum_weight;
351  double mean_x = sum_x / sum_weight;
352  double ss_xx = sum_xSq - mean_x * sum_x;
353  ss_xy -= sum_x * mean_y;
354  b = ss_xy / ss_xx;
355  a = mean_y - b * mean_x;
356  }
357  }
358 
359  void fitChunk(size_t chunkSize, size_t pos, const std::vector<double>& nu,
360  const std::vector<double>& data,
361  std::vector<double>& fittedData) {
362  double a, b;
363  PieceWisePhaseFitter::fitSlope(&data[pos], &nu[pos], chunkSize, a, b);
364  for (size_t ch = 0; ch != chunkSize; ++ch)
365  fittedData[ch + pos] = a + b * nu[ch + pos];
366  }
367 
368  void fitChunk(size_t chunkSize, size_t pos, const std::vector<double>& nu,
369  const std::vector<double>& data, const double* weights,
370  std::vector<double>& fittedData) {
371  double a, b;
372  PieceWisePhaseFitter::fitSlope(&data[pos], &nu[pos], &weights[pos],
373  chunkSize, a, b);
374  for (size_t ch = 0; ch != chunkSize; ++ch)
375  fittedData[ch + pos] = a + b * nu[ch + pos];
376  }
377 
382  static bool removeDeviations(std::vector<double>& data,
383  std::vector<double>& frequencies, double a,
384  double b, double phaseThreshold) {
385  if (data.empty()) return false;
386  double maxDev = 0.0;
387  size_t index = 0;
388  for (size_t i = 0; i != data.size(); ++i) {
389  double deviation = std::abs(data[i] - (a + b * frequencies[i]));
390  if (deviation >= maxDev) {
391  maxDev = deviation;
392  index = i;
393  }
394  }
395  if (maxDev > phaseThreshold) {
396  data.erase(data.begin() + index);
397  frequencies.erase(frequencies.begin() + index);
398  return true;
399  } else
400  return false;
401  }
402 };
403 
404 } // namespace ddecal
405 } // namespace dp3
406 
407 #endif
Definition: PieceWisePhaseFitter.h:14
void PieceWiseFit(const std::vector< double > &nu, const std::vector< double > &data, const double *weights, std::vector< double > &fittedData)
Definition: PieceWisePhaseFitter.h:75
void PieceWiseFit(const std::vector< double > &nu, const std::vector< double > &data, std::vector< double > &fittedData)
Definition: PieceWisePhaseFitter.h:60
void SlidingFit(const double *nu, const double *data, const double *weights, double *fittedData, size_t n)
Definition: PieceWisePhaseFitter.h:136
static double WeightedMedian(std::vector< std::pair< double, double >> &values)
Definition: PieceWisePhaseFitter.h:189
PieceWisePhaseFitter()
Definition: PieceWisePhaseFitter.h:16
void SetChunkSize(size_t chunkSize)
Definition: PieceWisePhaseFitter.h:25
static void Unwrap(Iter first, Iter last)
Definition: PieceWisePhaseFitter.h:31
size_t SlidingFitWithBreak(const double *nu, const double *data, const double *weights, double *fittedData, size_t n)
Definition: PieceWisePhaseFitter.h:163
void SlidingFit(const double *nu, const double *data, double *fittedData, size_t n)
Definition: PieceWisePhaseFitter.h:94
static size_t CalculateChunkSize(const std::vector< double > &frequencies)
Definition: PieceWisePhaseFitter.h:38
size_t ChunkSize() const
Definition: PieceWisePhaseFitter.h:24
size_t BreakPoint(const double *data, const double *weights, const double *fittedData, size_t n)
Definition: PieceWisePhaseFitter.h:206
PieceWisePhaseFitter(size_t chunkSize)
Definition: PieceWisePhaseFitter.h:22
This file has generic helper routines for testing steps.
Definition: AntennaConfig.h:53