Line data Source code
1 : //# DelayRateFFT.cc: Part of the implementation of FringeJones
2 : //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003,2011
3 : //# Associated Universities, Inc. Washington DC, USA.
4 : //#
5 : //# This library is free software; you can redistribute it and/or modify it
6 : //# under the terms of the GNU Library General Public License as published by
7 : //# the Free Software Foundation; either version 2 of the License, or (at your
8 : //# option) any later version.
9 : //#
10 : //# This library is distributed in the hope that it will be useful, but WITHOUT
11 : //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 : //# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
13 : //# License for more details.
14 : //#
15 : //# You should have received a copy of the GNU Library General Public License
16 : //# along with this library; if not, write to the Free Software Foundation,
17 : //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
18 : //#
19 : //# Correspondence concerning AIPS++ should be addressed as follows:
20 : //# Internet email: aips2-request@nrao.edu.
21 : //# Postal address: AIPS++ Project Office
22 : //# National Radio Astronomy Observatory
23 : //# 520 Edgemont Road
24 : //# Charlottesville, VA 22903-2475 USA
25 : //#
26 :
27 : #include <synthesis/MeasurementComponents/FringeJones.h>
28 :
29 : #include <synthesis/MeasurementComponents/SolveDataBuffer.h>
30 : #include <casacore/lattices/Lattices/ArrayLattice.h>
31 : #include <casacore/lattices/LatticeMath/LatticeFFT.h>
32 :
33 : #include <casacore/casa/Exceptions/Error.h>
34 :
35 :
36 : #include <gsl/gsl_rng.h>
37 : #include <gsl/gsl_vector.h>
38 : #include <gsl/gsl_multimin.h>
39 :
40 : #include <iomanip> // needed for setprecision
41 :
42 : // DEVDEBUG gates the development debugging information to standard
43 : // error; it should be set to 0 for production.
44 :
45 : #define DEVDEBUG false
46 :
47 : using namespace casa::vi;
48 : using namespace casacore;
49 :
50 : namespace casa {
51 :
52 : Complex
53 : dotMatrixWithModel(const Matrix<Complex>& data, Double k, Double l, Double offset);
54 :
55 : Complex
56 : dotMatrixWithModel2(const Matrix<Complex>& f, Double k, Double l, Double offset);
57 :
58 : Complex
59 : dotWithOffsets(const Cube<Complex>& ft, const Vector<Float>& offsets, double k, double l);
60 :
61 : tuple<Double, Double, Double, Double>
62 : multibandFFTs(const Array<Complex>& ffts, const Vector<Float>& offsets);
63 :
64 : Double
65 : multibandFFT(const Vector<Complex>& peaks, const Vector<Float>& offsets);
66 :
67 : tuple<Double, Double, Double, Double>
68 : refineSearch2(const Cube<Complex>& ft, const Vector<Float>& offsets, Double ipkt0, Double ipkch0);
69 :
70 : tuple<Double, Double, Double, Double>
71 : bruteForceDelay(const Cube<Complex>& ft, const Vector<Float>& offsets, Int ipkt, Int ipkch);
72 :
73 :
74 :
75 0 : static void unitize(Array<Complex>& vC)
76 : {
77 0 : Array<Float> vCa(amplitude(vC));
78 : // Divide by non-zero amps
79 0 : vCa(vCa<FLT_EPSILON)=1.0;
80 0 : vC /= vCa;
81 0 : }
82 :
83 0 : SDBListGridManagerCombo::SDBListGridManagerCombo(SDBList& sdbs) :
84 : SDBListGridManager(sdbs),
85 0 : nchan_(0)
86 : {
87 0 : std::set<Double> fmaxes_;
88 0 : std::set<Double> fmins_;
89 :
90 0 : if (sdbs.nSDB()==0) {
91 : // The for loop is fine with an empty list, but code below it
92 : // isn't and there's nothing to lose by bailing early!
93 : if (DEVDEBUG) {
94 : cerr << "No data I guess?";
95 : }
96 0 : return;
97 : }
98 :
99 0 : for (Int i=0; i != sdbs_.nSDB(); i++) {
100 0 : SolveDataBuffer& sdb = sdbs_(i);
101 0 : Int pspw = sdb.spectralWindow()(0);
102 0 : Double t = sdbs_(i).time()(0);
103 0 : times_.insert(t);
104 0 : if (spwins_.find(pspw) == spwins_.end()) {
105 0 : spwins_.insert(pspw);
106 0 : const Vector<Double>& fs = sdb.freqs();
107 0 : df_ = fs[1] - fs[0];
108 0 : pspwIdToFreqMap_[pspw] = &(sdb.freqs());
109 0 : nchan_ = max(nchan_, sdb.nChannels());
110 0 : fmaxes_.insert(fs(nchan_-1));
111 0 : fmins_.insert(fs(0));
112 : } else {
113 0 : continue;
114 : }
115 : }
116 0 : nt_ = times_.size();
117 : // nt_ = sdbs_.nSDB()/spwins_.size();
118 0 : tmin_ = *(times_.begin());
119 0 : tmax_ = *(times_.rbegin());
120 0 : dt_ = (tmax_ - tmin_)/(nt_ - 1);
121 0 : if (nchan_ == 1) {
122 0 : df_ = 1;
123 0 : return;
124 : }
125 0 : size_t i = 0;
126 0 : for (auto p=spwins_.begin(); p!=spwins_.end(); p++) {
127 0 : spwPMap_[*p] = i;
128 0 : i++;
129 : }
130 : }
131 :
132 :
133 :
134 :
135 : Float
136 0 : SDBListGridManagerCombo::getRefFreqFromLSPW(Int lspw) {
137 0 : auto p = spwins_.begin();
138 0 : std::advance(p, lspw);
139 0 : Int pspw = *p;
140 0 : const Vector<Double>& fs = *(pspwIdToFreqMap_[pspw]);
141 0 : return fs[0];
142 : }
143 :
144 0 : DelayRateFFT::DelayRateFFT(SDBList& sdbs, Int refant, Array<Double>& delayWindow, Array<Double>& rateWindow) :
145 0 : f0_(sdbs.centroidFreq() / 1.e9), // GHz, for delayrate calc
146 : refant_(refant),
147 : delayWindow_(delayWindow),
148 : rateWindow_(rateWindow),
149 : Vall_(),
150 : xcount_(),
151 : sumw_(),
152 : sumww_(),
153 : activeAntennas_(),
154 0 : allActiveAntennas_()
155 0 : {}
156 :
157 : DelayRateFFT *
158 0 : DelayRateFFT::makeAChild(int concat, SDBList& sdbs,
159 : Int refant,
160 : Array<Double>& delayWindow_,
161 : Array<Double>& rateWindow_)
162 : {
163 0 : if (concat) {
164 0 : return new DelayRateFFTConcat(sdbs, refant, delayWindow_, rateWindow_);
165 : } else {
166 0 : return new DelayRateFFTCombo(sdbs, refant, delayWindow_, rateWindow_);
167 : }
168 : }
169 :
170 : Matrix<Float>
171 0 : DelayRateFFT::delay() const {
172 0 : IPosition start(2, 1, 0);
173 0 : IPosition stop(2, 3*nCorr_-1, nElem_-1);
174 0 : IPosition stride(2, 3, 1);
175 0 : Slicer sl(start, stop, stride, Slicer::endIsLast);
176 0 : return param_(sl);
177 : }
178 :
179 : Matrix<Float>
180 0 : DelayRateFFT::rate() const {
181 0 : IPosition start(2, 2, 0);
182 0 : IPosition stop(2, 3*nCorr_-1, nElem_-1);
183 0 : IPosition stride(2, 3, 1);
184 0 : Slicer sl(start, stop, stride, Slicer::endIsLast);
185 0 : return param_(sl);
186 : }
187 :
188 :
189 0 : void DelayRateFFT::removeAntennasCorrelation(Int icor, std::set< Int > s) {
190 0 : std::set< Int > & as = activeAntennas_.find(icor)->second;
191 0 : for (std::set< Int >::iterator it=s.begin(); it!=s.end(); it++) {
192 0 : as.erase(*it);
193 : }
194 0 : }
195 :
196 : void
197 0 : DelayRateFFT::printActive() {
198 0 : for (Int icorr=0; icorr != nCorr_; icorr++) {
199 0 : cerr << "Antennas found for correlation " << icorr << ": ";
200 0 : for (std::set<Int>::iterator it = activeAntennas_[icorr].begin();
201 0 : it != activeAntennas_[icorr].end(); it++) {
202 0 : cerr << *it << ", ";
203 : }
204 0 : cerr << endl;
205 : }
206 0 : cerr << endl;
207 0 : }
208 :
209 :
210 :
211 : // DelayRateFFTCombo is modeled on DelayFFT in KJones.{cc|h}
212 0 : DelayRateFFTCombo::DelayRateFFTCombo(SDBList& sdbs, Int refant, Array<Double>& delayWindow,
213 0 : Array<Double>& rateWindow) :
214 : DelayRateFFT(sdbs, refant, delayWindow, rateWindow),
215 0 : gm_(sdbs)
216 : {
217 : if (DEVDEBUG) {
218 : gm_.describe();
219 : }
220 0 : nt_ = gm_.nt_;
221 0 : nChan_ = gm_.nchan_;
222 :
223 : // We might want to pad these too
224 0 : nPadFactor_ = 4;
225 0 : nPadT_ = nPadFactor_*nt_;
226 0 : nPadChan_ = nPadFactor_*nChan_;
227 0 : nspw_ = gm_.nSPW();
228 0 : dt_ = gm_.dt_;
229 0 : df_ = gm_.df_ / 1.e9;
230 :
231 0 : if (nt_ < 2) {
232 0 : throw(AipsError("Can't do a 2-dimensional FFT on a single timestep! Please consider changing solint to avoid orphan timesteps."));
233 : }
234 0 : IPosition ds = delayWindow_.shape();
235 0 : if (ds.size()!=1 || ds.nelements() != 1) {
236 0 : throw AipsError("delaywindow must be a list of length 2.");
237 : }
238 0 : IPosition rs = rateWindow_.shape();
239 0 : if (rs.size()!=1 || rs.nelements() != 1) {
240 0 : throw AipsError("ratewindow must be a list of length 2.");
241 : }
242 0 : Int nCorrOrig(sdbs(0).nCorrelations());
243 0 : nCorr_ = (nCorrOrig> 1 ? 2 : 1); // number of p-hands
244 0 : for (Int i=0; i<nCorr_; i++) {
245 0 : activeAntennas_[i].insert(refant_);
246 : }
247 : // when we get the visCubecorrected it is already
248 : // reduced to parallel hands, but there isn't a
249 : // corresponding method for flags.
250 0 : Int corrStep = (nCorrOrig > 2 ? 3 : 1); // step for p-hands
251 0 : for (Int ibuf=0; ibuf != sdbs.nSDB(); ibuf++) {
252 0 : SolveDataBuffer& s(sdbs(ibuf));
253 0 : for (Int irow=0; irow!=s.nRows(); irow++) {
254 0 : Int a1(s.antenna1()(irow)), a2(s.antenna2()(irow));
255 0 : allActiveAntennas_.insert(a1);
256 0 : allActiveAntennas_.insert(a2);
257 : }
258 : }
259 0 : nElem_ = 1 + *(allActiveAntennas_.rbegin()) ;
260 :
261 0 : IPosition aggregateDim(2, nCorr_, nElem_);
262 0 : xcount_.resize(aggregateDim);
263 0 : sumw_.resize(aggregateDim);
264 0 : sumww_.resize(aggregateDim);
265 0 : peak_.resize(aggregateDim);
266 :
267 0 : xcount_ = 0;
268 0 : sumw_ = 0.0;
269 0 : sumww_ = 0.0;
270 0 : IPosition dataSize(5, nCorr_, nElem_, nspw_, nPadT_, nPadChan_);
271 0 : Vall_.resize(dataSize);
272 0 : Int totalRows = 0;
273 0 : Int goodRows = 0;
274 :
275 : if (DEVDEBUG) {
276 : cerr << "DelayRateFFTCombo constructor: Here" << endl;
277 : }
278 0 : for (Int ibuf=0; ibuf != sdbs.nSDB(); ibuf++) {
279 0 : SolveDataBuffer& s(sdbs(ibuf));
280 0 : totalRows += s.nRows();
281 0 : if (!s.Ok())
282 0 : continue;
283 :
284 0 : Int nr = 0;
285 0 : for (Int irow=0; irow!=s.nRows(); irow++) {
286 0 : if (s.flagRow()(irow))
287 0 : continue;
288 : Int iant;
289 0 : Int a1(s.antenna1()(irow)), a2(s.antenna2()(irow));
290 0 : if (a1 == a2) { continue; } // we don't do autocorrelations
291 0 : else if (a1 == refant_) { iant = a2; }
292 0 : else if (a2 == refant_) { iant = a1; }
293 0 : else { continue; } // not a baseline to reference antenna
294 : // v has shape (nelems, ?, nrows, nchannels); can't be a reference.
295 0 : Cube<Complex> v = s.visCubeCorrected();
296 0 : const Cube<Float>& w( s.weightSpectrum() );
297 0 : const Cube<Bool>& fl( s.flagCube() );
298 0 : Int pspw = s.spectralWindow()(0);
299 0 : Int ispw = gm_.getLSPW(pspw);
300 0 : Int t_index = gm_.getTimeIndex(s.time()(0));
301 : // FIXME! ispw is the Measurement Set spectral window; it
302 : // doesn't follow that that is addressible in our array
303 : // which is dimensioned for logical spectral
304 : // window. E.g. and i.e., if we are not joining spectral
305 : // windows, the spectral-window dimension of our target
306 : // array will always have size 1
307 0 : IPosition start (5, 0, iant, ispw, t_index, 0);
308 0 : IPosition stop (5, nCorr_, 1, 1, 1, nChan_);
309 0 : IPosition stride(5, 1, 1, 1, 1, 1);
310 0 : Slicer target_slice(start, stop, stride, Slicer::endIsLength);
311 : // Slicer::endIsLast is also possible
312 : if (DEVDEBUG && false) {
313 : cerr << " nSDBs" << sdbs.nSDB()
314 : << " nspwins " << gm_.spwins_.size() << endl;
315 : cerr << "nCorr_ " << nCorr_
316 : << " iant " << iant
317 : << " ispw " << ispw
318 : << " t_index " << t_index
319 : << " s.time()(0) " << s.time()(0)
320 : << " nt_ " << nt_
321 : << " dt_ " << dt_
322 : << " nChan_ " << nChan_
323 : << " corrStep " << corrStep
324 : << " distinct times " << gm_.times_.size()
325 : << endl;
326 : }
327 0 : Slicer source_slice(IPosition(3, 0, 0, irow),
328 0 : IPosition(3, nCorr_, nChan_, 1),
329 0 : IPosition(3, corrStep, 1, 1), Slicer::endIsLength);
330 :
331 0 : Slicer flagSlice(IPosition(3, 0, 0, irow),
332 0 : IPosition(3, nCorr_, nChan_, 1),
333 0 : IPosition(3, corrStep, 1, 1), Slicer::endIsLength);
334 0 : nr++;
335 0 : Array<Complex>rhs( v(source_slice).nonDegenerate(1) );
336 0 : const Array<Float>& weights( w(source_slice).nonDegenerate(1) );
337 0 : unitize(rhs);
338 0 : Vall_(target_slice).nonDegenerate(1) = rhs * weights;
339 0 : const Array<Bool>& flagged( fl(flagSlice).nonDegenerate(1) );
340 :
341 : // Zero flagged entries.
342 0 : Vall_(target_slice).nonDegenerate(1)(flagged) = Complex(0.0);
343 :
344 0 : if (!allTrue(flagged)) {
345 0 : for (Int icorr=0; icorr<nCorr_; ++icorr) {
346 : // Book keeping now done per spw!
347 0 : IPosition p(3, icorr, iant, ispw);
348 0 : Bool actually = false;
349 0 : activeAntennas_[icorr].insert(iant);
350 0 : for (Int ichan=0; ichan != (Int) nChan_; ichan++) {
351 0 : IPosition pchan(2, icorr, ichan);
352 0 : if (!flagged(pchan)) {
353 0 : Float wv = weights(pchan);
354 0 : xcount_(p)++;
355 0 : sumw_(p) += wv;
356 0 : sumww_(p) += wv*wv;
357 0 : actually = true;
358 : }
359 : }
360 0 : if (actually) {
361 0 : activeAntennas_[icorr].insert(iant);
362 0 : goodRows++;
363 : }
364 : }
365 : }
366 : }
367 : }
368 0 : }
369 :
370 :
371 : // What even *is* this?
372 0 : DelayRateFFTCombo::DelayRateFFTCombo(Array<Complex>& data, Float f0, Float df, Float dt, SDBList& s,
373 0 : Array<Double>& delayWindow, Array<Double>& rateWindow) :
374 : DelayRateFFT(s, -1, delayWindow, rateWindow),
375 0 : gm_(s)
376 : {
377 0 : dt_ = dt;
378 0 : df_ = df;
379 0 : IPosition shape = data.shape();
380 0 : nCorr_ = shape(0);
381 0 : nElem_ = shape(1);
382 0 : nspw_ = shape(2);
383 0 : nt_ = shape(3);
384 0 : nChan_ = shape(4);
385 0 : IPosition dataSize(5, nCorr_, nElem_, nspw_, nt_, nChan_);
386 0 : Vall_.resize(dataSize);
387 :
388 0 : IPosition start(5, 0, 0, 0, 0, 0);
389 0 : IPosition stop(5, nCorr_, nElem_, nspw_, nt_, nChan_);
390 0 : IPosition stride(5, 1, 1, 1, 1);
391 0 : Slicer target_slice(start, stop, stride, Slicer::endIsLength);
392 0 : Vall_(target_slice) = data;
393 :
394 0 : unitize(Vall_);
395 :
396 0 : }
397 :
398 : void
399 0 : DelayRateFFTCombo::FFT() {
400 : if (DEVDEBUG) {
401 : cerr << "DelayRateFFTCombo::FFT()" << endl;
402 : }
403 : // Axes are 0: correlation (i.e., hand of polarization), 1: antenna, 2: spectral window, 3: time, 4: channel
404 : // the machinery is there to say that we only want to FFT the last two axes
405 0 : Vector<Bool> ax(5, false);
406 0 : ax(3) = true;
407 0 : ax(4) = true;
408 : // Also copied from DelayFFT in KJones.
409 : // we make a copy to FFT in place.
410 0 : ArrayLattice<Complex> c(Vall_);
411 0 : LatticeFFT::cfft0(c, ax, true);
412 : if (DEVDEBUG) {
413 : cerr << "FFT transformed" << endl;
414 : }
415 0 : }
416 :
417 : // In the new paradigm, this is where the new stuff happens. Instead of
418 : // interpolating the peaks on a single big grid, we have to synthesise
419 : // our estimate from separately FFT-ed spectral windows, using the new
420 : // off-grid peak formalism
421 : void
422 0 : DelayRateFFTCombo::searchPeak() {
423 : if (DEVDEBUG) {
424 : cerr << "DelayRateFFTCombo::searchPeak()" << endl;
425 : }
426 :
427 : // Recall param_ -> [phase, delay, rate] for each correlation
428 0 : param_.resize(3*nCorr_, nElem_); // This might be better done elsewhere.
429 0 : param_.set(0.0);
430 0 : flag_.resize(3*nCorr_, nElem_);
431 0 : flag_.set(true); // all flagged initially
432 :
433 0 : Double bw = Float(nChan_)*df_;
434 0 : for (Int icorr=0; icorr<nCorr_; ++icorr) {
435 0 : flag_(icorr*3 + 0, refant()) = false;
436 0 : flag_(icorr*3 + 1, refant()) = false;
437 0 : flag_(icorr*3 + 2, refant()) = false;
438 0 : for (Int ielem=0; ielem<nElem_; ++ielem) {
439 0 : if (ielem==refant()) {
440 0 : continue;
441 : }
442 0 : if (activeAntennas_[icorr].find(ielem)==activeAntennas_[icorr].end()) {
443 0 : continue;
444 : }
445 :
446 : // Below is the gory details for turning delay window into index range
447 0 : Int sgn = (ielem < refant()) ? 1 : -1;
448 0 : Double d0 = sgn*delayWindow_(IPosition(1, 0));
449 0 : Double d1 = sgn*delayWindow_(IPosition(1, 1));
450 0 : if (d0 > d1) std::swap(d0, d1);
451 0 : d0 = max(d0, -0.5/df_);
452 0 : d1 = min(d1, (0.5-1/nChan_)/df_);
453 :
454 : // It's simpler to keep the ranges as signed integers and
455 : // handle the wrapping of the FFT in the loop over
456 : // indices. Recall that the FFT result returned has indices
457 : // that run from 0 to nChan_/2 -1 and then from
458 : // -nChan/2 to -1, so far as our delay is concerned.
459 0 : Int i0 = bw*d0;
460 0 : Int i1 = bw*d1;
461 0 : i0 *= nPadFactor_;
462 0 : i1 *= nPadFactor_;
463 0 : if (i1==i0) i1++;
464 : // Now for the gory details for turning rate window into index range
465 0 : Double width = nt_*dt_*1e9*f0_;
466 0 : Double r0 = sgn*rateWindow_(IPosition(1,0));
467 0 : Double r1 = sgn*rateWindow_(IPosition(1,1));
468 0 : if (r0 > r1) std::swap(r0, r1);
469 0 : r0 = max(r0, -0.5/(dt_*1e9*f0_));
470 0 : r1 = min(r1, (0.5 - 1/nt_)/(dt_*1e9*f0_));
471 :
472 0 : Int j0 = width*r0;
473 0 : Int j1 = width*r1;
474 0 : j0 *= nPadFactor_;
475 0 : j1 *= nPadFactor_;
476 0 : if (j1==j0) j1++;
477 : Array<Complex> blVis(
478 0 : Vall_(Slicer(
479 0 : IPosition(5, icorr, ielem, 0, 0, 0),
480 0 : IPosition(5, 1, 1, nspw_, nPadT_, nPadChan_),
481 0 : IPosition(5, 1, 1, 1, 1, 1),
482 0 : Slicer::endIsLength)).nonDegenerate(IPosition(1,2)));
483 :
484 0 : Vector<Float> offsets(nspw_);
485 0 : Double bw = gm_.nchan_ * gm_.df_;
486 0 : for (size_t lspw=0; lspw!=size_t(nspw_); lspw++) {
487 0 : Double f = gm_.getRefFreqFromLSPW(lspw);
488 0 : Double f0 = gm_.getRefFreqFromLSPW(0);
489 0 : offsets(lspw) = (f - f0)/bw;
490 : }
491 :
492 : // Get the DFT estimates of peak location instead
493 0 : tuple<Double, Double, Double, Double> mp = multibandFFTs(blVis , offsets);
494 0 : Double mpkt = std::get<0>(mp);
495 0 : Double mpkch = std::get<1>(mp);
496 0 : Double mpeak = std::get<2>(mp);
497 0 : Double marg = std::get<3>(mp);
498 :
499 0 : Complex c0 = dotWithOffsets(blVis, offsets, mpkt, mpkch);
500 : if (DEVDEBUG) {
501 : cerr << "DelayRateFFTCombo: abs(c1) " << abs(c0) << endl;
502 : }
503 0 : tuple<Double, Double, Double, Double> p = refineSearch(blVis, offsets, mpkt, mpkch);
504 0 : Double pkt = std::get<0>(p);
505 0 : Double pkch = std::get<1>(p);
506 0 : Double peak = std::get<2>(p);
507 0 : Double phase = std::get<3>(p);
508 :
509 : if (DEVDEBUG) {
510 : cerr << "[DelayRateFFTCombo::SearchPeak] " << "icorr " << icorr << " ielem " << ielem
511 : << " from (" << mpkt << ", " << mpkch << ", peak " << abs(c0)
512 : << ", angle " << arg(c0) << ")"
513 : << " to (" << pkt << ", " << pkch << ", peak " << peak << ", angle " << phase << ")"
514 : << endl;
515 : }
516 0 : peak_(IPosition(2, icorr, ielem)) = peak;
517 0 : param_(icorr*3 + 0, ielem) = sgn*phase;
518 0 : Double delay = (pkch)/Double(nChan_);
519 0 : if (delay > 0.5) delay -= 1.0; // fold
520 0 : delay /= df_; // nsec
521 0 : param_(icorr*3 + 1, ielem) = sgn*delay;
522 0 : Double rate = (pkt)/Double(nt_);
523 0 : if (rate > 0.5) rate -= 1.0;
524 0 : Double rate0 = rate/dt_;
525 0 : Double rate1 = rate0/(1e9 * f0_);
526 0 : param_(icorr*3 + 2, ielem) = Double(sgn*rate1);
527 : if (DEVDEBUG) {
528 : cerr << "delay " << sgn*delay << " rate1 " << sgn*rate1 << endl;
529 : }
530 : // Set 3 flags.
531 0 : flag_(icorr*3 + 0, ielem)=false;
532 0 : flag_(icorr*3 + 1, ielem)=false;
533 0 : flag_(icorr*3 + 2, ielem)=false;
534 : if (DEVDEBUG) {
535 : cerr << "End of this element/correlation combination" << endl;
536 : //cerr << "Set everything " << endl;
537 : }
538 : }
539 : }
540 0 : }
541 :
542 :
543 : tuple<Double, Double, Double, Double>
544 0 : multibandFFTs(const Array<Complex>& ffts, const Vector<Float>& offsets) {
545 : if (DEVDEBUG) {
546 : cerr << "multibandFFTs() " << endl;
547 : }
548 : // We take the individual band phases from the peaks of the SPWs
549 : // and assume they are the peculiar phases for their SPW; then we
550 : // calculate the Direct Discrete FT of those phases on their
551 : // offsets and read the peak off of that
552 :
553 : // This code is based on multibandFFT below, which we were using to
554 : // combine the peak values of individual SPW ffts; this version
555 : // combines *all* the values, because it turns out we can't
556 : // reliably get the peaks first.
557 :
558 0 : IPosition shp = ffts.shape();
559 0 : Int nspw = shp(0);
560 0 : Int nt = shp(1);
561 0 : Int nchan = shp(2);
562 :
563 0 : Int binScale = 1;
564 0 : size_t nbins= size_t(binScale*max(offsets)+1+0.5);
565 0 : Vector<Double> freqs(nbins);
566 0 : freqs = 0;
567 0 : for (size_t k=0; k!=nbins; k++) {
568 0 : freqs[k] = (Double(k) - nbins/2)/float(nbins);
569 : }
570 : // We need to store out results in an (nt, nchan, nbins) array this time
571 : // We do the loop manually because array arithmetic in Casacore is beyond me
572 0 : Array<Complex> X(IPosition(3, nbins, nt, nchan));
573 0 : X = 0;
574 :
575 0 : for (size_t ispw=0; ispw!=size_t(nspw); ispw++) {
576 0 : for (size_t it=0; it != nt; it++) {
577 0 : for (size_t ichan=0; ichan != nchan; ichan++) {
578 0 : Double x = abs(ffts(IPosition(3, ispw, 0, 0)));
579 0 : if (std::isnan(x)) {cerr << "Skipping FFT " << ispw << endl;
580 0 : continue;
581 : }
582 0 : for (size_t ibin=0; ibin!=nbins; ibin++) {
583 0 : Complex rotation = exp(-Complex(0,1)*Complex(C::_2pi*offsets(ispw)*freqs(ibin)));
584 0 : X(IPosition(3, ibin, it, ichan)) += ffts(IPosition(3, ispw, it, ichan))*rotation;
585 : }
586 : }
587 : }
588 : }
589 : /*
590 : // New!
591 : Array<Complex> rateAveraged(IPosition(2, nbins, nchan));
592 : rateAveraged = 0;
593 : for (size_t ibin; ibin!=nbins; ibin++) {
594 : for (size_t ichan=0; ichan != nchan; ichan++) {
595 : for (size_t it=0; it != nt; it++) {
596 : rateAveraged(IPosition(2, ibin, ichan)) += abs(ffts(IPosition(3, ibin, it, ichan)));
597 : }
598 : }
599 : }
600 :
601 : // Search for a peak with delay in the average first
602 : Int ichanmax1 = -1;
603 : Int ibinmax1 = -1;
604 : Double zmax1 = -1.0;
605 : for (Int ichan=0; ichan != nchan; ichan++) {
606 : for (Int ibin=0; ibin != nbins; ibin++) {
607 : Complex c = rateAveraged(IPosition(2, ibin, ichan));
608 : Double a = abs(c);
609 : if (a>zmax1) {
610 : ichanmax1 = ichan;
611 : ibinmax1 = ibin;
612 : zmax1 = a;
613 : }
614 : }
615 : }
616 : // Then we search the time dimension
617 : zmax1 = -1.0;
618 : Int itmax1 = -1;
619 : for (Int it = 0; it != nt; it++) {
620 : Complex c = X(IPosition(3, ibinmax1, it, ichanmax1));
621 : Double a = abs(c);
622 : if (a>zmax1) {
623 : itmax1 = it;
624 : }
625 : }
626 : cerr << "Averaged stacked DFT peaks at " << itmax1 << "/" << nt << ", "
627 : << ichanmax1 << "/" << nchan << "; Bin " << ibinmax1
628 : << " Peak " << abs(X(IPosition(3, ibinmax1, itmax1, ichanmax1)))
629 : << endl;
630 : */
631 : // We search for the maximum ourself, by brute force.
632 0 : Int itmax = -1;
633 0 : Int ichanmax = -1;
634 0 : Int ibinmax = -1;
635 0 : Double zmax = -1.0;
636 0 : for (Int ibin=0; ibin != nbins; ibin++) {
637 0 : for (Int it = 0; it != nt; it++) {
638 0 : for (Int ichan=0; ichan != nchan; ichan++) {
639 0 : Complex c = X(IPosition(3, ibin, it, ichan));
640 0 : Double a = abs(c);
641 0 : if (a>zmax) {
642 0 : itmax = it;
643 0 : ichanmax = ichan;
644 0 : ibinmax = ibin;
645 0 : zmax = a;
646 : }
647 : }
648 : }
649 : }
650 : //cerr << "Unaveraged stacked DFT peaks at " << itmax << "/" << nt << ", "
651 : // << ichanmax << "/" << nchan << "; Bin " << ibinmax
652 : // << " Peak " << abs(X(IPosition(3, ibinmax, itmax, ichanmax)))
653 : // << endl;
654 : //for (Int i=0; i < nspw; i++) {
655 : // cerr << "Peak for bin " << i << " = " << abs(ffts(IPosition(3, i, itmax, ichanmax))) << endl;
656 : //}
657 :
658 0 : Complex c = X(IPosition(3, ibinmax, itmax, ichanmax));
659 0 : Double dpkch = freqs(ibinmax);
660 :
661 0 : tuple<Double, Double, Double, Double> t;
662 0 : t = std::make_tuple(Double(itmax), Double(ichanmax) + dpkch, abs(c), arg(c));
663 : // t = std::make_tuple(Double(itmax), Double(ichanmax), abs(c), arg(c));
664 : if (DEVDEBUG) {
665 : cerr << "itmax " << itmax << " ichanmax " << ichanmax << " ibinmax " << ibinmax
666 : << " peak " << abs(c) << endl;
667 : cerr << "MultibandFFTs dpkch=" << dpkch << endl;
668 : cerr << "multibandFFTs() finished" << endl;
669 : }
670 0 : return t;
671 : }
672 :
673 : Double
674 0 : multibandFFT(const Vector<Complex>& peaks, const Vector<Float>& offsets) {
675 : // We take the individual band phases from the peaks of the SPWs
676 : // and assume they are the peculiar phases for their SPW; then we
677 : // calculate the Direct Discrete FT of those phases on their
678 : // offsets and read the peak off of that
679 0 : Int nspw = peaks.size();
680 :
681 0 : Vector<Float> amps( amplitude(peaks) );
682 : Int len;
683 0 : amps.shape(len);
684 :
685 0 : Float eps = 1e-10;
686 0 : std::set<size_t> badInds;
687 0 : for (size_t i=0; i!=size_t(len); i++) {
688 0 : if (amps[i] < eps) {
689 : if (DEVDEBUG) {
690 : cerr << " blacklisting " << i << endl;
691 : }
692 0 : badInds.insert(i);
693 : }
694 : }
695 0 : Vector<Complex> phasors( peaks/amplitude(peaks) );
696 :
697 : // I want to print angles but I can't get the API to do that
698 : // in a reasonable way; arg(phasors) doesn't work
699 : // Vector<Float> angles ( arg(phasors) );
700 : if (DEVDEBUG) {
701 : cerr << "phasors " << phasors << endl;
702 : }
703 : // FIXME: the last offset is at the *beginning* of the subband!
704 : // size_t nbins=2*size_t(max(offsets)+1+0.5);
705 0 : size_t nbins= size_t(max(offsets)+1+0.5);
706 :
707 0 : Vector<Float> freqs(nbins);
708 0 : freqs = 0;
709 0 : for (size_t k=0; k!=nbins; k++) {
710 0 : freqs[k] = (Float(k) - nbins/2)/float(nbins);
711 : }
712 0 : Vector<Complex> X(nbins);
713 0 : X = 0;
714 0 : for (size_t n=0; n!=size_t(nspw); n++) {
715 0 : if (badInds.find(n) != badInds.end()) {
716 : if (DEVDEBUG) {
717 : cerr << " skipping " << n << endl;
718 : }
719 0 : continue;
720 : }
721 0 : for (size_t k=0; k!=nbins; k++) {
722 : // FIXME: also swap signs here!
723 0 : X[k] += peaks[n]*exp(-Complex(0,1)*Complex(C::_2pi*offsets(n)*freqs(k)));
724 : // X[k] += phasors[n]*exp(-Complex(0,1)*Complex(C::_2pi*offsets(n)*freqs(k)));
725 : }
726 : }
727 0 : Int k_max = -1;
728 0 : Float zmax = -1.0;
729 0 : for (size_t k=0; k!=nbins; k++) {
730 0 : Complex z = X[k];
731 0 : Float a = abs(z);
732 : if (DEVDEBUG) {
733 : cerr << "DFT Bin " << k << " freq " << freqs(k) << " coeff " << a << endl;
734 : }
735 0 : if (a>zmax) {
736 0 : k_max = k;
737 0 : zmax = a;
738 : }
739 : }
740 : if (DEVDEBUG) {
741 : cerr << "k_max = " << k_max
742 : << "; peak at k_max= " << abs(X[k_max])
743 : << "; freqs(k_max) = " << freqs(k_max) << endl;
744 : }
745 0 : return freqs(k_max);
746 : }
747 :
748 : // Auxilliary function
749 : Complex
750 0 : dotWithOffsets(const Cube<Complex>& ft, const Vector<Float>& offsets, double k, double l) {
751 0 : Int nspw = ft.nrow();
752 0 : Int ni = ft.ncolumn();
753 0 : Int nj = ft.nplane();
754 :
755 0 : if (k<0) k += (ni);
756 0 : if (l<0) l += (nj);
757 0 : Complex p(0.0, 0.0);
758 0 : for (Int s=0; s!=nspw; s++) {
759 0 : const Matrix<Complex>& ft_s = ft.yzPlane(s);
760 0 : Double f_off = offsets(s);
761 0 : Complex r = dotMatrixWithModel(ft_s, k, l, f_off);
762 0 : p += r;
763 : }
764 0 : return p;
765 : }
766 :
767 : // We need a function to minimize, which has to return a real value,
768 : // but when we're done we need to find the peak and also its argument,
769 : // so we factor out the complex version...
770 : Complex
771 0 : c_peak_fn(const gsl_vector *x, void *vparams) {
772 0 : std::pair< Cube<Complex> const *, Vector<Float> const * > *params =
773 : (std::pair< Cube<Complex> const *, Vector<Float> const * > *)(vparams );
774 :
775 0 : double k = gsl_vector_get(x, 0);
776 0 : double l = gsl_vector_get(x, 1);
777 :
778 0 : const Cube<Complex>& ft ( *(params->first ));
779 0 : const Vector<Float>& offsets ( *(params->second));
780 0 : return dotWithOffsets(ft, offsets, k, l);
781 : }
782 :
783 : // ... and then call it from a real version that we can give to the
784 : // optimizer (with a sign change; by tradition these are minimizer
785 : // routines and we want a maximum)
786 : double
787 0 : my_peak_fn(const gsl_vector *x, void *vparams) {
788 0 : Complex p = c_peak_fn(x, vparams);
789 0 : return -abs(p);
790 : }
791 :
792 : tuple<Double, Double, Double, Double>
793 0 : bruteForceDelay(const Cube<Complex>& ft, const Vector<Float>& offsets, Int ipkt, Int ipkch) {
794 0 : Double k(ipkt);
795 0 : Double l(ipkch);
796 0 : Int n = 10;
797 :
798 0 : Double l_p = l;
799 0 : Complex peak = Complex(0, 0);
800 0 : for (Int i=-n; i!=n+1; i++) {
801 0 : Double dch = double(i)/(2*n);
802 0 : Complex p = dotWithOffsets(ft, offsets, k, l+dch);
803 0 : if (abs(p) > abs(peak)) {
804 : if (DEVDEBUG) {
805 : cerr << "[bruteForceDelay] l " << l+dch << " peak " << abs(p) << endl;
806 : }
807 0 : peak = p;
808 0 : l_p = l+dch;
809 :
810 : }
811 : }
812 0 : tuple<Double, Double, Double, Double> t;
813 0 : t = std::make_tuple(k, l_p, abs(peak), arg(peak));
814 0 : return t;
815 : }
816 :
817 : tuple<Double, Double, Double, Double>
818 0 : DelayRateFFTCombo::refineSearch(const Cube<Complex>& ft, const Vector<Float>& offsets, Double pkt, Double pkch) {
819 : // small@jive.eu: I borrowed most of this code from the GSL documentation of multimin:
820 : // <https://www.gnu.org/software/gsl/doc/html/multimin.html>
821 : if (DEVDEBUG) {
822 : cerr << "refineSearch" << endl;
823 : }
824 0 : const gsl_multimin_fminimizer_type *T = gsl_multimin_fminimizer_nmsimplex2;
825 : /* Starting point */
826 0 : gsl_vector *x = gsl_vector_alloc (2);
827 0 : gsl_vector_set(x, 0, pkt);
828 0 : gsl_vector_set(x, 1, pkch);
829 : if (DEVDEBUG) {
830 : cerr << "pkch starts at " << pkch << endl;
831 : }
832 : /* Set initial step sizes */
833 : /* Fixme! I need to think harder about this value! */
834 0 : gsl_vector* steps = gsl_vector_alloc (2);
835 0 : gsl_vector_set_all(steps, 0.01);
836 :
837 : /* Initialize method and iterate */
838 :
839 : gsl_multimin_function minex_func;
840 0 : std::pair< Cube<Complex> const * , Vector<Float> const * > par = make_pair(&ft, &offsets);
841 :
842 0 : minex_func.n = 2;
843 0 : minex_func.f = my_peak_fn;
844 0 : minex_func.params = ∥
845 :
846 0 : gsl_multimin_fminimizer *s = gsl_multimin_fminimizer_alloc(T, 2);
847 0 : gsl_multimin_fminimizer_set (s, &minex_func, x, steps);
848 :
849 0 : float minstep = 1e-5;
850 : int status;
851 0 : size_t iter = 0;
852 0 : do {
853 : if (DEVDEBUG) {
854 : cerr << "Starting iteration" << endl;
855 : }
856 :
857 0 : iter++;
858 0 : status = gsl_multimin_fminimizer_iterate(s);
859 0 : if (status) break;
860 0 : double size = gsl_multimin_fminimizer_size(s);
861 0 : status = gsl_multimin_test_size(size, minstep);
862 0 : if (status == GSL_SUCCESS) {
863 0 : printf ("converged to minimum at\n");
864 : }
865 : if (DEVDEBUG) {
866 : printf("%5zd ipkt %12.5e ipkch %12.5e f() = %12.5g size = %12.5f\n",
867 : iter,
868 : gsl_vector_get(s->x, 0),
869 : gsl_vector_get(s->x, 1),
870 : s->fval,
871 : size);
872 : }
873 0 : } while (status == GSL_CONTINUE && iter < 5);
874 : // while (status == GSL_CONTINUE && iter < 100);
875 0 : tuple<Double, Double, Double, Double> p;
876 : // if (status == GSL_SUCCESS) {
877 : if (1) {
878 : // Double peak = gsl_multimin_fminimizer_minimum(s);
879 0 : Double ipkt = gsl_vector_get(s->x, 0);
880 0 : Double ipkch = gsl_vector_get(s->x, 1);
881 0 : Complex c = c_peak_fn(s->x, &par);
882 0 : p = std::make_tuple(ipkt, ipkch, abs(c), arg(c));
883 : } else {
884 : p = std::make_tuple(pkt, pkch, 0.0, 0.0);
885 : }
886 0 : gsl_vector_free(steps);
887 0 : gsl_vector_free(x);
888 0 : gsl_multimin_fminimizer_free(s);
889 :
890 0 : return p;
891 : }
892 :
893 : Float
894 0 : DelayRateFFTCombo::snr(Int icorr, Int ielem, Float delay, Float rate) {
895 : // We calculate a signal-to-noise ration for the 2D FFT fringefit
896 : // using a formula transcribed from AIPS FRING.
897 : //
898 : // Have to convert delay and rate back into indices on the padded 2D grid.
899 :
900 0 : IPosition p(2, icorr, ielem);
901 0 : Float peak = peak_(p);
902 0 : if (peak > 0.999*sumw_(p)) {
903 : //cerr << "Clipping peak for element " << ielem << " from " << peak << " to " << 0.999*sumw_(p) << endl;
904 0 : peak=0.999*sumw_(p);
905 : }
906 : // xcount is number of data points for baseline to ielem
907 : // sumw is sum of weights,
908 : // sumww is sum of squares of weights
909 : Float cwt;
910 0 : if (fabs(sumw_(p))<FLT_EPSILON) {
911 0 : cwt = 0;
912 : if (DEVDEBUG) {
913 : cerr << "Correlation " << icorr << " antenna " << ielem << ": sum of weights is zero." << endl;
914 : }
915 : } else {
916 0 : Float x = C::pi/2*peak/sumw_(p);
917 : // The magic numbers in the following formula are from AIPS FRING
918 0 : cwt = (pow(tan(x), 1.163) * sqrt(sumw_(p)/sqrt(sumww_(p)/xcount_(p))));
919 : if (DEVDEBUG) {
920 : cerr << "Correlation " << icorr << " antenna " << ielem
921 : << " peak=" << peak << "; xang=" << x << "; xcount=" << xcount_(p) << "; sumw=" << sumw_(p) << "; sumww=" << sumww_(p)
922 : << " snr " << cwt << endl;
923 : }
924 : }
925 0 : return cwt;
926 : }
927 :
928 :
929 : // There isn't a usable sinc lying around that I can see
930 0 : Double sinc(Double x) {
931 0 : Double p = C::pi*x;
932 0 : return sin(p)/p;
933 : }
934 :
935 : // This implements a Fancy Sinc strategy for finding the peak of a
936 : // single SPW. I no longer know if this can be extended to multiple
937 : // SPWs.
938 : tuple<Double, Double, Double, Double>
939 0 : refineSearch2(const Cube<Complex>& ft, const Vector<Float>& offsets, Int ipkt0, Int ipkch0) {
940 : Double imax, jmax;
941 : // FIXME! We need to do a single SPW first
942 0 : if (ft.nrow() > 1) {
943 0 : throw AipsError("Only one spw for now");
944 : }
945 0 : Int s = 0;
946 0 : const Matrix<Complex>& ft_s(ft.yzPlane(s));
947 0 : size_t ni = ft_s.nrow();
948 0 : size_t nj = ft_s.ncolumn();
949 : // we need to do a roll left/down and sum but Casacore doesn't have a matrix roll so we may as well do it by hand
950 0 : Float pmax = -1;
951 0 : size_t ipkt(0);
952 0 : size_t ipkch(0);
953 0 : Matrix<Float> sumAbs(ni, nj);
954 0 : for (size_t i=0; i!=ni; i++) {
955 : // FIXME: The variable i1 is unused!
956 : // Either use it or lose it!
957 : // size_t i1 = (i==ni-1) ? 0 : i+1;
958 0 : for (size_t j=0; j!=nj; j++) {
959 0 : size_t j1 = (j==nj-1) ? 0 : j+1;
960 0 : Float sumAbs = (abs(ft_s(i, j)) + abs(ft_s(i, j1)));
961 0 : if ((sumAbs) > pmax) {
962 0 : ipkt = i;
963 0 : ipkch = j;
964 0 : pmax = sumAbs;
965 : }
966 : }
967 : }
968 0 : Double y0 = abs(ft_s(ipkt, ipkch));
969 0 : Double y1 = abs(ft_s(ipkt, (ipkch==nj-1)? 0 : ipkch+1));
970 0 : Double ym1 = abs(ft_s(ipkt, (ipkch==0)? nj-1 : ipkch-1));
971 :
972 0 : Double z1 = abs(ft_s((ipkt==ni-1) ? 0 : ipkt+1, ipkch));
973 0 : Double zm1 = abs(ft_s((ipkt==0) ? ni-1 : ipkt-1, ipkch));
974 :
975 :
976 0 : Double d0 = y1/(y0+y1);
977 0 : Double peak( y0/sinc(d0) );
978 : if (DEVDEBUG) {
979 : cerr << "y0 " << y0 << " y1 " << y1 << " ym1 " << ym1 << " d0 " << d0 << " sinc(d0) "
980 : << sinc(d0) << " peak " << peak << endl;
981 : cerr << "y0 " << y0 << " z1 " << z1 << " zm1 " << zm1 << endl;
982 : cerr << real(abs(ft_s.row(ipkt)))/y0 << endl;
983 : }
984 0 : Double phase( 0.0 );
985 0 : imax = Double(ipkt);
986 0 : jmax = Double(ipkch) + d0;
987 0 : tuple<Double, Double, Double, Double> p = std::make_tuple(imax, jmax, peak, phase);
988 0 : return p;
989 : }
990 :
991 : Complex
992 0 : dotMatrixWithModel(const Matrix<Complex>& data, Double k, Double l, Double offset)
993 : {
994 0 : size_t ni = data.nrow();
995 0 : size_t nj = data.ncolumn();
996 0 : Matrix<Complex> model(ni, nj);
997 :
998 : // Note that k is the time index of the array, l is the frequency index.
999 : // Offsets correspond to frequencies
1000 0 : Double eps = 1e-8;
1001 0 : Int k_int = floor(k);
1002 0 : Double k_del = k - k_int;
1003 0 : Bool k_flag = (fabs(k_del) < eps);
1004 0 : Int l_int = floor(l);
1005 0 : Double l_del = l - l_int;
1006 0 : Bool l_flag = (fabs(l_del) < eps);
1007 0 : Complex t0;
1008 0 : Complex t1;
1009 : // FIXME! We're messing with reversing signs
1010 0 : for (size_t i=0; i!=ni; i++) {
1011 0 : if (k_flag) {
1012 0 : t0 = Complex(i==k);
1013 : } else { // if k isn't an integer!
1014 0 : t0 = ( (1-exp(Complex(0, -1.0*C::_2pi*(k-i))))/
1015 0 : (1-exp(Complex(0, -1.0*C::_2pi*(k-i)/Double(ni)))) );
1016 0 : t0 /= ni;
1017 : }
1018 0 : for (size_t j=0; j!=nj; j++) {
1019 0 : if (l_flag) {
1020 0 : t1 = Complex(j==l);
1021 : } else { // if l isn't an integer!
1022 0 : t1 = ( (1-exp(Complex(0, -1.0*C::_2pi*(l-j))))/
1023 0 : (1-exp(Complex(0, -1.0*C::_2pi*(l-j)/Double(nj)))) );
1024 0 : t1 /= nj;
1025 : }
1026 0 : model(i, j) = exp(Complex(0, -1.0*C::_2pi*offset*l_del))*t0*t1;
1027 : }
1028 : }
1029 0 : Complex t2 = sum(data*model);
1030 0 : return t2;
1031 : }
1032 :
1033 :
1034 : Complex
1035 0 : dotMatrixWithModel2(const Matrix<Complex>& data, Double k0, Double l0, Double offset)
1036 : {
1037 0 : size_t ni = data.nrow();
1038 0 : size_t nj = data.ncolumn();
1039 0 : Matrix<Complex> model(ni, nj);
1040 0 : model = 0.0;
1041 :
1042 0 : Double eps = 1e-8;
1043 0 : Int k_int = Int(ceil(k0));
1044 0 : Int l_int = Int(ceil(l0));
1045 :
1046 0 : Double d0 = k_int - k0;
1047 0 : Double d1 = l_int - l0;
1048 :
1049 0 : Bool k_flag = (fabs(d0) < eps);
1050 0 : Bool l_flag = (fabs(d1) < eps);
1051 :
1052 0 : Complex c0, c1;
1053 :
1054 0 : Complex s (0.0);
1055 0 : Complex t0 = Complex(sin(C::pi*d0)/C::pi)*exp(Complex(0, C::pi*d0));
1056 0 : Complex t1 = Complex(sin(C::pi*d1)/C::pi)*exp(Complex(0, C::pi*d1));
1057 0 : for (Int dk=-2; dk!=2; dk++) {
1058 0 : size_t k = (k_int + dk + ni) % ni;
1059 0 : if (k_flag) {
1060 0 : c0 = Complex(dk==0);
1061 : } else { // if k isn't an integer!
1062 0 : c0 = Complex(1/(d0+dk))*t0;
1063 : }
1064 0 : for (Int dl=-2; dl!=2; dl++) {
1065 0 : size_t l = (l_int+dl+nj) % nj;
1066 0 : if (l_flag) {
1067 0 : c1 = Complex(dl==0);
1068 : } else { // if l isn't an integer!
1069 0 : c1 = Complex(1/(d1+dl))*t1;
1070 : }
1071 0 : Complex d = data(k, l);
1072 0 : Complex m = c0*c1;
1073 0 : s += d*m;
1074 : }
1075 : }
1076 0 : return s;
1077 : }
1078 :
1079 :
1080 0 : SDBListGridManagerConcat::SDBListGridManagerConcat(SDBList& sdbs) :
1081 0 : SDBListGridManager(sdbs)
1082 : {
1083 0 : std::set<Double> fmaxes;
1084 0 : std::set<Double> fmins;
1085 0 : Float dfn(0.0);
1086 0 : Int totalChans0(0) ;
1087 0 : Int nchan(0);
1088 :
1089 0 : if (sdbs_.nSDB()==0) {
1090 : // The for loop is fine with an empty list, but code below it
1091 : // isn't and there's nothing to lose by bailing early!
1092 0 : return;
1093 : }
1094 :
1095 0 : for (Int i=0; i != sdbs_.nSDB(); i++) {
1096 0 : SolveDataBuffer& sdb = sdbs_(i);
1097 0 : Int spw = sdb.spectralWindow()(0);
1098 0 : Double t = sdbs_(i).time()(0);
1099 0 : times_.insert(t);
1100 0 : if (spwins_.find(spw) == spwins_.end()) {
1101 0 : spwins_.insert(spw);
1102 0 : const Vector<Double>& fs = sdb.freqs();
1103 0 : spwIdToFreqMap_[spw] = &(sdb.freqs());
1104 0 : nchan = sdb.nChannels();
1105 0 : fmaxes.insert(fs(nchan-1));
1106 0 : fmins.insert(fs(0));
1107 : // We assume they're all at the same time.
1108 :
1109 0 : totalChans0 += nchan;
1110 0 : Float df0 = fs(1) - fs(0);
1111 0 : dfn = (fs(nchan-1) - fs(0))/(nchan-1);
1112 : if (DEVDEBUG) {
1113 : cerr << "Spectral window " << spw << " has " << nchan << " channels" << endl;
1114 : cerr << "df0 "<< df0 << "; " << "dfn " << dfn << endl;
1115 : }
1116 : } else {
1117 0 : continue;
1118 : }
1119 : }
1120 : // nt_ = sdbs_.nSDB()/spwins_.size();
1121 0 : nt_ = times_.size();
1122 0 : tmin_ = *(times_.begin());
1123 0 : tmax_ = *(times_.rbegin());
1124 0 : dt_ = (tmax_ - tmin_)/(nt_ - 1);
1125 0 : nSPWChan_ = nchan;
1126 0 : if (nchan == 1) {
1127 0 : nSPWChan_ = nchan;
1128 0 : fmin_ = *(fmins.begin());
1129 0 : fmax_ = fmin_;
1130 0 : totalChans_ = 1;
1131 0 : df_ = 1;
1132 0 : return;
1133 : }
1134 :
1135 0 : fmin_ = *(fmins.begin());
1136 0 : fmax_ = *(fmaxes.rbegin());
1137 0 : totalChans_ = round((fmax_ - fmin_)/dfn + 1);
1138 0 : df_ = (fmax_ - fmin_)/(totalChans_-1);
1139 : if (DEVDEBUG) {
1140 : cerr << "Global fmin " << fmin_ << " global max " << fmax_ << endl;
1141 : cerr << "nt " << nt_ << " dt " << dt_ << endl;
1142 : cerr << "tmin " << tmin_ << " tmax " << tmax_ << endl;
1143 : cerr << "Global dt " << tmax_ - tmin_ << endl;
1144 : cerr << "Global df " << df_ << endl;
1145 : cerr << "I guess we'll need " << totalChans_ << " freq points in total." << endl;
1146 : cerr << "Compared to " << totalChans0 << " with simple-minded concatenation." << endl;
1147 : cerr << "spwins_.size() " << spwins_.size() << endl;
1148 : cerr << "nSPW() " << nSPW() << endl;
1149 : }
1150 : }
1151 :
1152 : // checkAllGridPoints is a diagnostic funtion that should not be called
1153 : // in production releases, but it doesn't do any harm to have it
1154 : // latent.
1155 : void
1156 0 : SDBListGridManagerConcat::checkAllGridpoints() {
1157 0 : map<Int, Vector<Double> const *>::iterator it;
1158 0 : for (it = spwIdToFreqMap_.begin(); it != spwIdToFreqMap_.end(); it++) {
1159 0 : Int spwid = it->first;
1160 0 : Vector<Double> const* fs = it->second;
1161 : Int length;
1162 0 : fs->shape(length);
1163 0 : for (Int i=0; i!=length; i++) {
1164 0 : Double f = (*fs)(i);
1165 0 : Int j = bigFreqGridIndex(f);
1166 : if (DEVDEBUG) {
1167 : cerr << "spwid, i = (" << spwid << ", " << i << ") => " << j << " (" << f << ")" << endl;
1168 : }
1169 : }
1170 : }
1171 : if (DEVDEBUG) {
1172 : cerr << "[1] spwins.size() " << nSPW() << endl;
1173 : }
1174 0 : }
1175 :
1176 : Int
1177 0 : SDBListGridManagerConcat::swStartIndex(Int spw) {
1178 0 : Vector<Double> const* fs = spwIdToFreqMap_[spw];
1179 0 : Double f0 = (*fs)(0);
1180 0 : return bigFreqGridIndex(f0);
1181 : }
1182 :
1183 0 : DelayRateFFTConcat::DelayRateFFTConcat(SDBList& sdbs, Int refant, Array<Double>& delayWindow,
1184 0 : Array<Double>& rateWindow) :
1185 : DelayRateFFT(sdbs, refant, delayWindow, rateWindow),
1186 0 : gm_(sdbs)
1187 : {
1188 : if (DEVDEBUG) {
1189 : gm_.describe();
1190 : cerr << "gm_.nSPW() " << gm_.nSPW() << endl;
1191 : }
1192 0 : nPadFactor_= max(2, 8 / gm_.nSPW());
1193 0 : nt_ = gm_.nt_;
1194 0 : nPadT_ = nPadFactor_ * nt_;
1195 0 : nChan_ = gm_.nChannels();
1196 0 : nPadChan_ = nPadFactor_*nChan_;
1197 0 : dt_ = gm_.dt_;
1198 0 : f0_ = sdbs.centroidFreq() / 1.e9; // GHz, for delayrate calc
1199 0 : df_ = gm_.df_ / 1.e9;
1200 0 : df_all_ = gm_.fmax_ - gm_.fmin_;
1201 : // This check should be commented out in production:
1202 : // gm_.checkAllGridpoints();
1203 0 : if (nt_ < 2) {
1204 0 : throw(AipsError("Can't do a 2-dimensional FFT on a single timestep! Please consider changing solint to avoid orphan timesteps."));
1205 : }
1206 0 : IPosition ds = delayWindow_.shape();
1207 0 : if (ds.size()!=1 || ds.nelements() != 1) {
1208 0 : throw AipsError("delaywindow must be a list of length 2.");
1209 : }
1210 0 : IPosition rs = rateWindow_.shape();
1211 0 : if (rs.size()!=1 || rs.nelements() != 1) {
1212 0 : throw AipsError("ratewindow must be a list of length 2.");
1213 : }
1214 0 : Int nCorrOrig(sdbs(0).nCorrelations());
1215 0 : nCorr_ = (nCorrOrig> 1 ? 2 : 1); // number of p-hands
1216 0 : for (Int i=0; i<nCorr_; i++) {
1217 0 : activeAntennas_[i].insert(refant_);
1218 : }
1219 :
1220 : // when we get the visCubecorrected it is already
1221 : // reduced to parallel hands, but there isn't a
1222 : // corresponding method for flags.
1223 0 : Int corrStep = (nCorrOrig > 2 ? 3 : 1); // step for p-hands
1224 0 : for (Int ibuf=0; ibuf != sdbs.nSDB(); ibuf++) {
1225 0 : SolveDataBuffer& s(sdbs(ibuf));
1226 0 : for (Int irow=0; irow!=s.nRows(); irow++) {
1227 0 : Int a1(s.antenna1()(irow)), a2(s.antenna2()(irow));
1228 0 : allActiveAntennas_.insert(a1);
1229 0 : allActiveAntennas_.insert(a2);
1230 : }
1231 : }
1232 0 : nElem_ = 1 + *(allActiveAntennas_.rbegin()) ;
1233 0 : IPosition aggregateDim(2, nCorr_, nElem_);
1234 0 : xcount_.resize(aggregateDim);
1235 0 : sumw_.resize(aggregateDim);
1236 0 : sumww_.resize(aggregateDim);
1237 :
1238 0 : xcount_ = 0;
1239 0 : sumw_ = 0.0;
1240 0 : sumww_ = 0.0;
1241 :
1242 : if (DEVDEBUG) {
1243 : cerr << "Filling FFT grid with " << sdbs.nSDB() << " data buffers." << endl;
1244 : }
1245 0 : IPosition paddedDataSize(4, nCorr_, nElem_, nPadT_, nPadChan_);
1246 0 : Vpad_.resize(paddedDataSize);
1247 0 : Int totalRows = 0;
1248 0 : Int goodRows = 0;
1249 :
1250 :
1251 0 : for (Int ibuf=0; ibuf != sdbs.nSDB(); ibuf++) {
1252 0 : SolveDataBuffer& s(sdbs(ibuf));
1253 0 : totalRows += s.nRows();
1254 0 : if (!s.Ok())
1255 0 : continue;
1256 :
1257 0 : Int nr = 0;
1258 0 : for (Int irow=0; irow!=s.nRows(); irow++) {
1259 0 : if (s.flagRow()(irow))
1260 0 : continue;
1261 : Int iant;
1262 0 : Int a1(s.antenna1()(irow)), a2(s.antenna2()(irow));
1263 0 : if (a1 == a2) {
1264 0 : continue;
1265 : }
1266 0 : else if (a1 == refant_) {
1267 0 : iant = a2;
1268 : }
1269 0 : else if (a2 == refant_) {
1270 0 : iant = a1;
1271 : }
1272 : else {
1273 0 : continue;
1274 : }
1275 : // OK, we're not skipping this one so we have to do something.
1276 :
1277 : // v has shape (nelems, ?, nrows, nchannels)
1278 0 : Cube<Complex> v = s.visCubeCorrected();
1279 0 : Cube<Float> w = s.weightSpectrum();
1280 0 : Cube<Bool> fl = s.flagCube();
1281 0 : Int spw = s.spectralWindow()(0);
1282 0 : Int f_index = gm_.swStartIndex(spw); // ditto!
1283 0 : Int t_index = gm_.getTimeIndex(s.time()(0));
1284 0 : Int spwchans = gm_.nSPWChan_;
1285 0 : IPosition start(4, 0, iant, t_index, f_index);
1286 0 : IPosition stop(4, nCorr_, 1, 1, spwchans);
1287 0 : IPosition stride(4, 1, 1, 1, 1);
1288 0 : Slicer sl1(start, stop, stride, Slicer::endIsLength);
1289 0 : Slicer sl2(IPosition(3, 0, 0, irow),
1290 0 : IPosition(3, nCorr_, spwchans, 1),
1291 0 : IPosition(3, corrStep, 1, 1), Slicer::endIsLength);
1292 :
1293 0 : Slicer flagSlice(IPosition(3, 0, 0, irow),
1294 0 : IPosition(3, nCorr_, spwchans, 1),
1295 0 : IPosition(3, corrStep, 1, 1), Slicer::endIsLength);
1296 0 : nr++;
1297 : if (DEVDEBUG && false) {
1298 : cerr << "nr " << nr
1299 : << " irow " << endl
1300 : << "Vpad shape " << Vpad_.shape() << endl
1301 : << "v shape " << v.shape() << endl
1302 : << "sl2 " << sl2 << endl
1303 : << "sl1 " << sl1 << endl
1304 : << "flagSlice " << flagSlice << endl;
1305 : }
1306 0 : Array<Complex> rhs = v(sl2).nonDegenerate(1);
1307 0 : Array<Float> weights = w(sl2).nonDegenerate(1);
1308 :
1309 0 : unitize(rhs);
1310 0 : Vpad_(sl1).nonDegenerate(1) = rhs * weights;
1311 :
1312 0 : Array<Bool> flagged(fl(flagSlice).nonDegenerate(1));
1313 : // Zero flagged entries.
1314 0 : Vpad_(sl1).nonDegenerate(1)(flagged) = Complex(0.0);
1315 :
1316 0 : if (!allTrue(flagged)) {
1317 0 : for (Int icorr=0; icorr<nCorr_; ++icorr) {
1318 0 : IPosition p(2, icorr, iant);
1319 0 : Bool actually = false;
1320 0 : activeAntennas_[icorr].insert(iant);
1321 0 : for (Int ichan=0; ichan != (Int) spwchans; ichan++) {
1322 0 : IPosition pchan(2, icorr, ichan);
1323 0 : if (!flagged(pchan)) {
1324 0 : Float wv = weights(pchan);
1325 0 : if (wv < 0) {
1326 0 : cerr << "spwchans " << spwchans << endl;
1327 0 : cerr << "Negative weight << (" << wv << ") on row "
1328 0 : << irow << " baseline (" << a1 << ", " << a2 << ") "
1329 0 : << " channel " << ichan << endl;
1330 0 : cerr << "pchan " << pchan << endl;
1331 0 : cerr << "Weights " << weights << endl;
1332 : }
1333 0 : xcount_(p)++;
1334 0 : sumw_(p) += wv;
1335 0 : sumww_(p) += wv*wv;
1336 0 : actually = true;
1337 : }
1338 : }
1339 0 : if (actually) {
1340 0 : activeAntennas_[icorr].insert(iant);
1341 0 : goodRows++;
1342 : }
1343 : }
1344 : }
1345 : if (DEVDEBUG && 0) {
1346 : cerr << "flagged " << flagged << endl;
1347 : cerr << "flagSlice " << flagSlice << endl
1348 : << "fl.shape() " << fl.shape() << endl
1349 : << "Vpad_.shape() " << Vpad_.shape() << endl
1350 : << "flagged.shape() " << flagged.shape() << endl
1351 : << "sl1 " << sl1 << endl;
1352 : }
1353 : }
1354 : }
1355 : if (DEVDEBUG) {
1356 : cerr << "In DelayRateFFTConcat::DelayRateFFTConcat " << endl;
1357 : printActive();
1358 : cerr << "sumw_ " << sumw_ << endl;
1359 : cerr << "Constructed a DelayRateFFTConcat object." << endl;
1360 : cerr << "totalRows " << totalRows << endl;
1361 : cerr << "goodRows " << goodRows << endl;
1362 : }
1363 :
1364 0 : }
1365 :
1366 0 : DelayRateFFTConcat::DelayRateFFTConcat(Array<Complex>& data, Int nPadFactor, Float f0, Float df, Float dt, SDBList& s,
1367 0 : Array<Double>& delayWindow, Array<Double>& rateWindow) :
1368 : DelayRateFFT(s, 0, delayWindow, rateWindow),
1369 : gm_(s),
1370 : nPadFactor_(nPadFactor),
1371 0 : Vpad_() {
1372 0 : dt_ = dt;
1373 0 : f0_ = f0;
1374 0 : df_ = df;
1375 0 : IPosition shape = data.shape();
1376 0 : nCorr_ = shape(0);
1377 0 : nElem_ = shape(1);
1378 0 : nt_ = shape(2);
1379 0 : nChan_ = shape(3);
1380 0 : nPadT_ = nPadFactor_*nt_;
1381 0 : nPadChan_ = nPadFactor_*nChan_;
1382 0 : IPosition paddedDataSize(4, nCorr_, nElem_, nPadT_, nPadChan_);
1383 0 : Vpad_.resize(paddedDataSize);
1384 :
1385 0 : IPosition start(4, 0, 0, 0, 0);
1386 0 : IPosition stop(4, nCorr_, nElem_, nt_, nChan_);
1387 0 : IPosition stride(4, 1, 1, 1, 1);
1388 0 : Slicer sl1(start, stop, stride, Slicer::endIsLength);
1389 0 : Vpad_(sl1) = data;
1390 :
1391 0 : unitize(Vpad_);
1392 :
1393 0 : }
1394 :
1395 : void
1396 0 : DelayRateFFTConcat::FFT() {
1397 : if (DEVDEBUG) {
1398 : cerr << "DelayRateFFTConcat::FFT()" << endl;
1399 : }
1400 : // Axes are 0: correlation (i.e., hand of polarization), 1: antenna, 2: time, 3: channel
1401 0 : Vector<Bool> ax(4, false);
1402 0 : ax(2) = true;
1403 0 : ax(3) = true;
1404 : // Also copied from DelayFFT in KJones.
1405 : // we make a copy to FFT in place.
1406 : if (DEVDEBUG) {
1407 : cerr << "Vpad_.shape() " << Vpad_.shape() << endl;
1408 : }
1409 0 : ArrayLattice<Complex> c(Vpad_);
1410 0 : LatticeFFT::cfft0(c, ax, true);
1411 : if (DEVDEBUG) {
1412 : cerr << "FFT transformed" << endl;
1413 : }
1414 0 : }
1415 :
1416 : std::pair<Bool, Float>
1417 0 : DelayRateFFTConcat::xinterp(Float alo, Float amax, Float ahi) {
1418 0 : if (amax > 0.0 && alo == amax && amax == ahi)
1419 0 : return std::make_pair(true, 0.5);
1420 :
1421 0 : Float denom(alo-2.0*amax+ahi);
1422 0 : Bool cond = amax>0.0 && abs(denom)>0.0 ;
1423 0 : Float fpk = cond ? 0.5-(ahi-amax)/denom : 0.0;
1424 0 : return std::make_pair(cond, fpk);
1425 : }
1426 :
1427 : void
1428 0 : DelayRateFFTConcat::searchPeak() {
1429 : if (DEVDEBUG) {
1430 : cerr << "DelayRateFFTConcat::searchPeak()" << endl;
1431 : }
1432 :
1433 : // Recall param_ -> [phase, delay, rate] for each correlation
1434 0 : param_.resize(3*nCorr_, nElem_); // This might be better done elsewhere.
1435 0 : param_.set(0.0);
1436 0 : flag_.resize(3*nCorr_, nElem_);
1437 0 : flag_.set(true); // all flagged initially
1438 : if (DEVDEBUG) {
1439 : cerr << "nt_ " << nt_ << " nPadChan_ " << nPadChan_ << endl;
1440 : cerr << "Vpad_.shape() " << Vpad_.shape() << endl;
1441 : cerr << "delayWindow_ " << delayWindow_ << endl;
1442 :
1443 : }
1444 :
1445 0 : for (Int icorr=0; icorr<nCorr_; ++icorr) {
1446 0 : flag_(icorr*3 + 0, refant()) = false;
1447 0 : flag_(icorr*3 + 1, refant()) = false;
1448 0 : flag_(icorr*3 + 2, refant()) = false;
1449 0 : for (Int ielem=0; ielem<nElem_; ++ielem) {
1450 0 : if (ielem==refant()) {
1451 0 : continue;
1452 : }
1453 : // NB: Time, Channel
1454 : // And once again we fail at slicing
1455 0 : IPosition start(4, icorr, ielem, 0, 0);
1456 0 : IPosition stop(4, 1, 1, nPadT_, nPadChan_);
1457 0 : IPosition step(4, 1, 1, 1, 1);
1458 0 : Slicer sl(start, stop, step, Slicer::endIsLength);
1459 0 : Matrix<Complex> aS = Vpad_(sl).nonDegenerate();
1460 0 : Int sgn = (ielem < refant()) ? 1 : -1;
1461 :
1462 : // Below is the gory details for turning delay window into index range
1463 0 : Double bw = Float(nPadChan_)*df_;
1464 0 : Double d0 = sgn*delayWindow_(IPosition(1, 0));
1465 0 : Double d1 = sgn*delayWindow_(IPosition(1, 1));
1466 0 : if (d0 > d1) std::swap(d0, d1);
1467 0 : d0 = max(d0, -0.5/df_);
1468 0 : d1 = min(d1, (0.5-1/nPadChan_)/df_);
1469 :
1470 : // It's simpler to keep the ranges as signed integers and
1471 : // handle the wrapping of the FFT in the loop over
1472 : // indices. Recall that the FFT result returned has indices
1473 : // that run from 0 to nPadChan_/2 -1 and then from
1474 : // -nPadChan/2 to -1, so far as our delay is concerned.
1475 0 : Int i0 = bw*d0;
1476 0 : Int i1 = bw*d1;
1477 0 : if (i1==i0) i1++;
1478 : // Now for the gory details for turning rate window into index range
1479 0 : Double width = nPadT_*dt_*1e9*f0_;
1480 0 : Double r0 = sgn*rateWindow_(IPosition(1,0));
1481 0 : Double r1 = sgn*rateWindow_(IPosition(1,1));
1482 0 : if (r0 > r1) std::swap(r0, r1);
1483 0 : r0 = max(r0, -0.5/(dt_*1e9*f0_));
1484 0 : r1 = min(r1, (0.5 - 1/nPadT_)/(dt_*1e9*f0_));
1485 :
1486 0 : Int j0 = width*r0;
1487 0 : Int j1 = width*r1;
1488 0 : if (j1==j0) j1++;
1489 : if (DEVDEBUG) {
1490 : cerr << "Checking the windows for delay and rate search." << endl;
1491 : cerr << "bw " << bw << endl;
1492 : cerr << "d0 " << d0 << " d1 " << d1 << endl;
1493 : cerr << "i0 " << i0 << " i1 " << i1 << endl;
1494 : cerr << "r0 " << r0 << " r1 " << r1 << endl;
1495 : cerr << "j0 " << j0 << " j1 " << j1 << endl;
1496 : }
1497 0 : Matrix<Float> amp(amplitude(aS));
1498 0 : Int ipkch(0);
1499 0 : Int ipkt(0);
1500 0 : Float amax(-1.0);
1501 : // Unlike KJones we have to iterate in time too
1502 0 : for (Int itime0=j0; itime0 != j1; itime0++) {
1503 0 : Int itime = (itime0 < 0) ? itime0 + nPadT_ : itime0;
1504 0 : for (Int ich0=i0; ich0 != i1; ich0++) {
1505 0 : Int ich = (ich0 < 0) ? ich0 + nPadChan_ : ich0;
1506 : // cerr << "Gridpoint " << itime << ", " << ich << "->" << amp(itime, ich) << endl;
1507 0 : if (amp(itime, ich) > amax) {
1508 0 : ipkch = ich;
1509 0 : ipkt = itime;
1510 0 : amax=amp(itime, ich);
1511 : }
1512 : }
1513 : }
1514 : // Finished grovelling. Now we have the location of the
1515 : // maximum amplitude.
1516 0 : Float alo_ch = amp(ipkt, (ipkch > 0) ? ipkch-1 : nPadChan_-1);
1517 0 : Float ahi_ch = amp(ipkt, ipkch<(nPadChan_-1) ? ipkch+1 : 0);
1518 0 : std::pair<Bool, Float> maybeFpkch = xinterp(alo_ch, amax, ahi_ch);
1519 : // We handle wrapping while looking for neighbours
1520 0 : Float alo_t = amp(ipkt > 0 ? ipkt-1 : nPadT_ -1, ipkch);
1521 0 : Float ahi_t = amp(ipkt < (nPadT_ -1) ? ipkt+1 : 0, ipkch);
1522 : if (DEVDEBUG) {
1523 : cerr << "For element " << ielem << endl;
1524 : cerr << "In channel dimension ipkch " << ipkch << " alo " << alo_ch
1525 : << " amax " << amax << " ahi " << ahi_ch << endl;
1526 : cerr << "In time dimension ipkt " << ipkt << " alo " << alo_t
1527 : << " amax " << amax << " ahi " << ahi_t << endl;
1528 : }
1529 0 : std::pair<Bool, Float> maybeFpkt = xinterp(alo_t, amax, ahi_t);
1530 :
1531 0 : if (maybeFpkch.first and maybeFpkt.first) {
1532 : // Phase
1533 0 : Complex c = aS(ipkt, ipkch);
1534 0 : Float phase = arg(c);
1535 0 : param_(icorr*3 + 0, ielem) = sgn*phase;
1536 0 : Float delay = (ipkch)/Float(nPadChan_);
1537 0 : if (delay > 0.5) delay -= 1.0; // fold
1538 0 : delay /= df_; // nsec
1539 0 : param_(icorr*3 + 1, ielem) = sgn*delay; //
1540 0 : Double rate = (ipkt)/Float(nPadT_);
1541 0 : if (rate > 0.5) rate -= 1.0;
1542 0 : Double rate0 = rate/dt_;
1543 0 : Double rate1 = rate0/(1e9 * f0_);
1544 :
1545 0 : param_(icorr*3 + 2, ielem) = Float(sgn*rate1);
1546 : if (DEVDEBUG) {
1547 : cerr << "maybeFpkch.second=" << maybeFpkch.second
1548 : << ", df_= " << df_
1549 : << " fpkch=" << (ipkch + maybeFpkch.second) << endl;
1550 : cerr << " maybeFpkt.second=" << maybeFpkt.second
1551 : << " rate0=" << rate
1552 : << " 1e9 * f0_=" << 1e9 * f0_
1553 : << ", dt_=" << dt_
1554 : << " fpkt=" << (ipkt + maybeFpkt.second) << endl;
1555 :
1556 : }
1557 : if (DEVDEBUG) {
1558 : cerr << "Found peak for element " << ielem << " correlation " << icorr
1559 : << " ipkt=" << ipkt << "/" << nPadT_ << ", ipkch=" << ipkch << "/" << nPadChan_
1560 : << " peak=" << amax
1561 : << "; delay " << delay << ", rate " << rate
1562 : << ", phase " << arg(c) << " sign= " << sgn << endl;
1563 : }
1564 : // Set 3 flags.
1565 0 : flag_(icorr*3 + 0, ielem)=false;
1566 0 : flag_(icorr*3 + 1, ielem)=false;
1567 0 : flag_(icorr*3 + 2, ielem)=false;
1568 : }
1569 : else {
1570 : if (DEVDEBUG) {
1571 : cerr << "No peak in 2D FFT for element " << ielem << " correlation " << icorr << endl;
1572 : }
1573 : }
1574 : }
1575 : }
1576 0 : }
1577 :
1578 :
1579 : Float
1580 0 : DelayRateFFTConcat::snr(Int icorr, Int ielem, Float delay, Float rate) {
1581 : if (DEVDEBUG) {
1582 : cerr << "DelayRateFFTConcat::snr"<< endl;
1583 : }
1584 : // We calculate a signal-to-noise ration for the 2D FFT fringefit
1585 : // using a formula transcribed from AIPS FRING.
1586 : //
1587 : // Have to convert delay and rate back into indices on the padded 2D grid.
1588 0 : Int sgn = (ielem < refant()) ? 1 : -1;
1589 0 : delay *= sgn*df_;
1590 0 : if (delay < 0.0) delay += 1;
1591 0 : Int ichan = Int(delay*nPadChan_ + 0.5);
1592 0 : if (ichan == nPadChan_) ichan = 0;
1593 :
1594 0 : rate *= sgn*1e9 * f0_;
1595 0 : rate *= dt_;
1596 0 : if (rate < 0.0) rate += 1;
1597 0 : Int itime = Int(rate*nPadT_ + 0.5);
1598 0 : if (itime == nPadT_) itime = 0;
1599 : // What about flags? If the datapoint closest to the computed
1600 : // delay and rate values is flagged we probably shouldn't use
1601 : // it, but what *should* we use?
1602 0 : IPosition ipos(4, icorr, ielem, itime, ichan);
1603 0 : IPosition p(2, icorr, ielem);
1604 0 : Complex v = Vpad_(ipos);
1605 0 : Float peak = abs(v);
1606 0 : if (peak > 0.999*sumw_(p)) peak=0.999*sumw_(p);
1607 : // xcount is number of data points for baseline to ielem
1608 : // sumw is sum of weights,
1609 : // sumww is sum of squares of weights
1610 :
1611 : Float cwt;
1612 0 : if (fabs(sumw_(p))<FLT_EPSILON) {
1613 0 : cwt = 0;
1614 : if (DEVDEBUG) {
1615 : cerr << "Correlation " << icorr << " antenna " << ielem << ": sum of weights is zero." << endl;
1616 : }
1617 : } else {
1618 0 : Float x = C::pi/2*peak/sumw_(p);
1619 : // The magic numbers in the following formula are from AIPS FRING
1620 0 : cwt = (pow(tan(x), 1.163) * sqrt(sumw_(p)/sqrt(sumww_(p)/xcount_(p))));
1621 : if (DEVDEBUG) {
1622 : cerr << "Correlation " << icorr << " antenna " << ielem << " ipos " << ipos
1623 : << " peak=" << peak << "; xang=" << x << "; xcount=" << xcount_(p) << "; sumw=" << sumw_(p) << "; sumww=" << sumww_(p)
1624 : << " snr " << cwt << endl;
1625 : }
1626 : }
1627 0 : return cwt;
1628 : }
1629 :
1630 :
1631 : } // end namespace
|