LCOV - code coverage report
Current view: top level - synthesis/MeasurementComponents - XJones.cc (source / functions) Hit Total Coverage
Test: casacpp_coverage.info Lines: 516 1437 35.9 %
Date: 2024-12-11 20:54:31 Functions: 28 123 22.8 %

          Line data    Source code
       1             : //# XJones.cc: Implementation of cross-hand phase calibration
       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: casa-feedback@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/XJones.h>
      28             : #include <synthesis/MeasurementComponents/CalCorruptor.h>
      29             : #include <synthesis/MeasurementComponents/MSMetaInfoForCal.h>
      30             : #include <synthesis/MeasurementComponents/SolveDataBuffer.h>
      31             : #include <msvis/MSVis/VisBuffer.h>
      32             : #include <msvis/MSVis/VisBuffAccumulator.h>
      33             : #include <synthesis/CalTables/CTIter.h>
      34             : #include <synthesis/MeasurementEquations/VisEquation.h>
      35             : #include <casacore/scimath/Fitting/LSQFit.h>
      36             : #include <casacore/scimath/Fitting/LinearFit.h>
      37             : #include <casacore/scimath/Functionals/CompiledFunction.h>
      38             : #include <casacore/scimath/Functionals/Polynomial.h>
      39             : #include <casacore/scimath/Mathematics/AutoDiff.h>
      40             : #include <casacore/casa/BasicMath/Math.h>
      41             : #include <casacore/tables/TaQL/ExprNode.h>
      42             : 
      43             : #include <casacore/casa/Arrays/ArrayMath.h>
      44             : #include <casacore/casa/Arrays/MaskArrMath.h>
      45             : #include <casacore/casa/Arrays/MatrixMath.h>
      46             : #include <casacore/casa/BasicSL/String.h>
      47             : #include <casacore/casa/Utilities/Assert.h>
      48             : #include <casacore/casa/Utilities/GenSort.h>
      49             : #include <casacore/casa/Exceptions/Error.h>
      50             : #include <casacore/casa/OS/Memory.h>
      51             : #include <casacore/casa/System/Aipsrc.h>
      52             : 
      53             : #include <sstream>
      54             : 
      55             : #include <casacore/measures/Measures/MCBaseline.h>
      56             : #include <casacore/measures/Measures/MDirection.h>
      57             : #include <casacore/measures/Measures/MEpoch.h>
      58             : #include <casacore/measures/Measures/MeasTable.h>
      59             : 
      60             : #include <casacore/casa/Logging/LogMessage.h>
      61             : #include <casacore/casa/Logging/LogSink.h>
      62             : // math.h ?
      63             : 
      64             : using namespace casacore;
      65             : namespace casa { //# NAMESPACE CASA - BEGIN
      66             : 
      67             : 
      68             : 
      69             : // **********************************************************
      70             : //  XMueller: positiona angle for circulars
      71             : //
      72             : 
      73           0 : XMueller::XMueller(VisSet& vs) :
      74             :   VisCal(vs),             // virtual base
      75             :   VisMueller(vs),         // virtual base
      76           0 :   SolvableVisMueller(vs)    // immediate parent
      77             : {
      78           0 :   if (prtlev()>2) cout << "X::X(vs)" << endl;
      79           0 : }
      80             : 
      81           0 : XMueller::XMueller(String msname,Int MSnAnt,Int MSnSpw) :
      82             :   VisCal(msname,MSnAnt,MSnSpw),             // virtual base
      83             :   VisMueller(msname,MSnAnt,MSnSpw),         // virtual base
      84           0 :   SolvableVisMueller(msname,MSnAnt,MSnSpw)    // immediate parent
      85             : {
      86           0 :   if (prtlev()>2) cout << "X::X(msname,MSnAnt,MSnSpw)" << endl;
      87           0 : }
      88             : 
      89           0 : XMueller::XMueller(const MSMetaInfoForCal& msmc) :
      90             :   VisCal(msmc),             // virtual base
      91             :   VisMueller(msmc),         // virtual base
      92           0 :   SolvableVisMueller(msmc)    // immediate parent
      93             : {
      94           0 :   if (prtlev()>2) cout << "X::X(msmc)" << endl;
      95           0 : }
      96             : 
      97           0 : XMueller::XMueller(const Int& nAnt) :
      98             :   VisCal(nAnt), 
      99             :   VisMueller(nAnt),
     100           0 :   SolvableVisMueller(nAnt)
     101             : {
     102           0 :   if (prtlev()>2) cout << "X::X(nAnt)" << endl;
     103           0 : }
     104             : 
     105           0 : XMueller::~XMueller() {
     106           0 :   if (prtlev()>2) cout << "X::~X()" << endl;
     107           0 : }
     108             : 
     109           0 : void XMueller::setApply(const Record& apply) {
     110             : 
     111           0 :   SolvableVisCal::setApply(apply);
     112             : 
     113             :   // Force calwt to false 
     114           0 :   calWt()=false;
     115             : 
     116           0 : }
     117             : 
     118             : 
     119           0 : void XMueller::setSolve(const Record& solvepar) {
     120             : 
     121           0 :   cout << "XMueller: parType() = " << this->parType() << endl;
     122             : 
     123           0 :   SolvableVisCal::setSolve(solvepar);
     124             : 
     125             :   // Force calwt to false 
     126           0 :   calWt()=false;
     127             : 
     128             :   // For X insist preavg is meaningful (5 minutes or user-supplied)
     129           0 :   if (preavg()<0.0)
     130           0 :     preavg()=300.0;
     131             : 
     132             :   
     133           0 :   cout << "ct_ = " << ct_ << endl;
     134             : 
     135             : 
     136           0 : }
     137             : 
     138           0 : void XMueller::newselfSolve(VisSet& vs, VisEquation& ve) {
     139             : 
     140           0 :   if (prtlev()>4) cout << "   M::selfSolve(ve)" << endl;
     141             : 
     142             :   // Inform logger/history
     143           0 :   logSink() << "Solving for " << typeName()
     144           0 :             << LogIO::POST;
     145             : 
     146             :   // Initialize the svc according to current VisSet
     147             :   //  (this counts intervals, sizes CalSet)
     148           0 :   Vector<Int> nChunkPerSol;
     149           0 :   Int nSol = sizeUpSolve(vs,nChunkPerSol);
     150             :   
     151             :   // Create the Caltable
     152           0 :   createMemCalTable();
     153             : 
     154             :   // The iterator, VisBuffer
     155           0 :   VisIter& vi(vs.iter());
     156           0 :   VisBuffer vb(vi);
     157             : 
     158             :   //  cout << "nSol = " << nSol << endl;
     159             :   //  cout << "nChunkPerSol = " << nChunkPerSol << endl;
     160             : 
     161           0 :   Vector<Int> slotidx(vs.numberSpw(),-1);
     162             : 
     163           0 :   Int nGood(0);
     164           0 :   vi.originChunks();
     165           0 :   for (Int isol=0;isol<nSol && vi.moreChunks();++isol) {
     166             : 
     167             :     // Arrange to accumulate
     168           0 :     VisBuffAccumulator vba(nAnt(),preavg(),false);
     169             :     
     170           0 :     for (Int ichunk=0;ichunk<nChunkPerSol(isol);++ichunk) {
     171             : 
     172             :       // Current _chunk_'s spw
     173           0 :       Int spw(vi.spectralWindow());
     174             : 
     175             :       // Abort if we encounter a spw for which a priori cal not available
     176           0 :       if (!ve.spwOK(spw))
     177           0 :         throw(AipsError("Pre-applied calibration not available for at least 1 spw. Check spw selection carefully."));
     178             : 
     179             : 
     180             :       // Collapse each timestamp in this chunk according to VisEq
     181             :       //  with calibration and averaging
     182           0 :       for (vi.origin(); vi.more(); vi++) {
     183             : 
     184             :         // Force read of the field Id
     185           0 :         vb.fieldId();
     186             : 
     187             :         // This forces the data/model/wt I/O, and applies
     188             :         //   any prior calibrations
     189           0 :         ve.collapse(vb);
     190             : 
     191             :         // If permitted/required by solvable component, normalize
     192             :         //if (normalizable())
     193             :         //          vb.normalize();
     194             : 
     195             :         // If this solve not freqdep, and channels not averaged yet, do so
     196           0 :         if (!freqDepMat() && vb.nChannel()>1)
     197           0 :           vb.freqAveCubes();
     198             : 
     199             :         // Accumulate collapsed vb in a time average
     200           0 :         vba.accumulate(vb);
     201             :       }
     202             :       // Advance the VisIter, if possible
     203           0 :       if (vi.moreChunks()) vi.nextChunk();
     204             : 
     205             :     }
     206             : 
     207             :     // Finalize the averged VisBuffer
     208           0 :     vba.finalizeAverage();
     209             : 
     210             :     // The VisBuffer to solve with
     211           0 :     VisBuffer& svb(vba.aveVisBuff());
     212             : 
     213             :     // Establish meta-data for this interval
     214             :     //  (some of this may be used _during_ solve)
     215             :     //  (this sets currSpw() in the SVC)
     216           0 :     Bool vbOk=syncSolveMeta(svb,-1);
     217             : 
     218           0 :     Int thisSpw=spwMap()(svb.spectralWindow());
     219           0 :     slotidx(thisSpw)++;
     220             : 
     221             :     // We are actually solving for all channels simultaneously
     222           0 :     solveCPar().reference(solveAllCPar());
     223           0 :     solveParOK().reference(solveAllParOK());
     224           0 :     solveParErr().reference(solveAllParErr());
     225           0 :     solveParSNR().reference(solveAllParSNR());
     226             : 
     227             :     // Fill solveCPar() with 1, nominally, and flagged
     228           0 :     solveCPar()=Complex(1.0);
     229           0 :     solveParOK()=false;
     230             :     
     231           0 :     if (vbOk && svb.nRow()>0) {
     232             : 
     233             :       // solve for the R-L phase term in the current VB
     234           0 :       solveOneVB(svb);
     235             : 
     236           0 :       if (solveParOK()(0,0,0))
     237             :         logSink() << "Position angle offset solution for " 
     238           0 :                   << msmc().fieldName(currField())
     239           0 :                   << " (spw = " << currSpw() << ") = "
     240           0 :                   << arg(solveCPar()(0,0,0))*180.0/C::pi/2.0
     241             :                   << " deg."
     242           0 :                   << LogIO::POST;
     243             :       else
     244             :         logSink() << "Position angle offset solution for " 
     245           0 :                   << msmc().fieldName(currField())
     246           0 :                   << " (spw = " << currSpw() << ") "
     247             :                   << " was not determined (insufficient data)."
     248           0 :                   << LogIO::POST;
     249             :         
     250           0 :       nGood++;
     251             :     }
     252             : 
     253           0 :     keepNCT();
     254             :     
     255           0 :   }
     256             :   
     257             :   logSink() << "  Found good "
     258           0 :             << typeName() << " solutions in "
     259             :             << nGood << " intervals."
     260           0 :             << LogIO::POST;
     261             : 
     262             :   // Store whole of result in a caltable
     263           0 :   if (nGood==0)
     264             :     logSink() << "No output calibration table written."
     265           0 :               << LogIO::POST;
     266             :   else {
     267             : 
     268             :     // Do global post-solve tinkering (e.g., phase-only, normalization, etc.)
     269             :     //  TBD
     270             :     // globalPostSolveTinker();
     271             : 
     272             :     // write the table
     273           0 :     storeNCT();
     274             : 
     275             :   }
     276             : 
     277           0 : }
     278             : 
     279           0 : void XMueller::calcAllMueller() {
     280             : 
     281             :   //  cout << "currMElem().shape() = " << currMElem().shape() << endl;
     282             : 
     283             :   // Put the phase factor into the cross-hand diagonals
     284             :   //  (1,0) for the para-hands  
     285           0 :   IPosition blc(3,0,0,0);
     286           0 :   IPosition trc(3,0,nChanMat()-1,nElem()-1);
     287           0 :   currMElem()(blc,trc)=Complex(1.0);
     288             : 
     289           0 :   blc(0)=trc(0)=1;
     290           0 :   currMElem()(blc,trc)=currCPar()(0,0,0);
     291           0 :   blc(0)=trc(0)=2;
     292           0 :   currMElem()(blc,trc)=conj(currCPar()(0,0,0));
     293             : 
     294           0 :   blc(0)=trc(0)=3;
     295           0 :   currMElem()(blc,trc)=Complex(1.0);
     296             : 
     297           0 :   currMElemOK()=true;
     298             : 
     299           0 : }
     300             : 
     301             : 
     302           0 : void XMueller::solveOneVB(const VisBuffer& vb) {
     303             : 
     304             :   // This just a simple average of the cross-hand
     305             :   //  visbilities...
     306             : 
     307           0 :   Complex d,md;
     308             :   Float wt,a;
     309           0 :   DComplex rl(0.0),lr(0.0);
     310           0 :   Double sumwt(0.0);
     311           0 :   for (Int irow=0;irow<vb.nRow();++irow) {
     312           0 :     if (!vb.flagRow()(irow) &&
     313           0 :         vb.antenna1()(irow)!=vb.antenna2()(irow)) {
     314             : 
     315           0 :       for (Int ich=0;ich<vb.nChannel();++ich) {
     316           0 :         if (!vb.flag()(ich,irow)) {
     317             :           
     318             :           // A common weight for both crosshands
     319             :           // TBD: we should probably consider this carefully...
     320             :           //  (also in D::guessPar...)
     321           0 :           wt=Double(vb.weightMat()(1,irow)+
     322           0 :                     vb.weightMat()(2,irow))/2.0;
     323             : 
     324             :           // correct weight for model normalization
     325           0 :           a=abs(vb.modelVisCube()(1,ich,irow));
     326           0 :           wt*=(a*a);
     327             :           
     328           0 :           if (wt>0.0) {
     329             :             // Cross-hands only
     330           0 :             for (Int icorr=1;icorr<3;++icorr) {
     331           0 :               md=vb.modelVisCube()(icorr,ich,irow);
     332           0 :               d=vb.visCube()(icorr,ich,irow);
     333             :               
     334           0 :               if (abs(d)>0.0) {
     335             :                 
     336           0 :                 if (icorr==1) 
     337           0 :                   rl+=DComplex(Complex(wt)*d/md);
     338             :                 else
     339           0 :                   lr+=DComplex(Complex(wt)*d/md);
     340             :                 
     341           0 :                 sumwt+=Double(wt);
     342             :                 
     343             :               } // abs(d)>0
     344             :             } // icorr
     345             :           } // wt>0
     346             :         } // !flag
     347             :       } // ich
     348             :     } // !flagRow
     349             :   } // row
     350             :   
     351             : /*
     352             :   cout << "spw = " << currSpw() << endl;
     353             :   cout << " rl = " << rl << " " << arg(rl)*180.0/C::pi << endl;
     354             :   cout << " lr = " << lr << " " << arg(lr)*180.0/C::pi << endl;
     355             : */
     356             : 
     357             :     // combine lr with rl
     358           0 :   rl+=conj(lr);
     359             :   
     360             :   // Normalize to unit amplitude
     361             :   //  (note that the phase result is insensitive to sumwt)
     362           0 :   Double amp=abs(rl);
     363           0 :   if (sumwt>0 && amp>0.0) {
     364           0 :     rl/=DComplex(amp);
     365             :     
     366           0 :     solveCPar()=Complex(rl);
     367           0 :     solveParOK()=true;
     368             :   }
     369             :   
     370           0 : }
     371             : 
     372             : 
     373             : 
     374             : // **********************************************************
     375             : //  XJones: position angle for circulars (antenna-based
     376             : //
     377             : 
     378           0 : XJones::XJones(VisSet& vs) :
     379             :   VisCal(vs),             // virtual base
     380             :   VisMueller(vs),         // virtual base
     381           0 :   SolvableVisJones(vs)    // immediate parent
     382             : {
     383           0 :   if (prtlev()>2) cout << "X::X(vs)" << endl;
     384           0 : }
     385             : 
     386           0 : XJones::XJones(String msname,Int MSnAnt,Int MSnSpw) :
     387             :   VisCal(msname,MSnAnt,MSnSpw),             // virtual base
     388             :   VisMueller(msname,MSnAnt,MSnSpw),         // virtual base
     389           0 :   SolvableVisJones(msname,MSnAnt,MSnSpw)    // immediate parent
     390             : {
     391           0 :   if (prtlev()>2) cout << "X::X(msname,MSnAnt,MSnSpw)" << endl;
     392           0 : }
     393             : 
     394          20 : XJones::XJones(const MSMetaInfoForCal& msmc) :
     395             :   VisCal(msmc),             // virtual base
     396             :   VisMueller(msmc),         // virtual base
     397          20 :   SolvableVisJones(msmc)    // immediate parent
     398             : {
     399          20 :   if (prtlev()>2) cout << "X::X(msmc)" << endl;
     400          20 : }
     401             : 
     402           0 : XJones::XJones(const Int& nAnt) :
     403             :   VisCal(nAnt), 
     404             :   VisMueller(nAnt),
     405           0 :   SolvableVisJones(nAnt)
     406             : {
     407           0 :   if (prtlev()>2) cout << "X::X(nAnt)" << endl;
     408           0 : }
     409             : 
     410          20 : XJones::~XJones() {
     411          20 :   if (prtlev()>2) cout << "X::~X()" << endl;
     412          20 : }
     413             : 
     414           1 : void XJones::setApply(const Record& apply) {
     415             : 
     416           1 :   SolvableVisCal::setApply(apply);
     417             : 
     418             :   // Force calwt to false 
     419           1 :   calWt()=false;
     420             : 
     421           1 : }
     422             : 
     423             : 
     424          11 : void XJones::setSolve(const Record& solvepar) {
     425             : 
     426          11 :   SolvableVisCal::setSolve(solvepar);
     427             : 
     428             :   // Force calwt to false 
     429          11 :   calWt()=false;
     430             : 
     431             :   // For X insist preavg is meaningful (5 minutes or user-supplied)
     432          11 :   if (preavg()<0.0)
     433           0 :     preavg()=300.0;
     434             : 
     435             :   // Force refant to none (==-1), because it is meaningless to
     436             :   //  apply a refant to an X solution
     437          11 :   if (refant()>-1) {
     438             :     logSink() << ".   (Ignoring specified refant for " 
     439           3 :               << typeName() << " solve.)"
     440           3 :               << LogIO::POST;
     441           3 :     refantlist().resize(1);
     442           3 :     refantlist()(0)=-1;
     443             :   }
     444             : 
     445          11 : }
     446             : 
     447           0 : void XJones::newselfSolve(VisSet& vs, VisEquation& ve) {
     448             : 
     449           0 :   if (prtlev()>4) cout << "   Xj::newselfSolve(ve)" << endl;
     450             : 
     451             :   // Inform logger/history
     452           0 :   logSink() << "Solving for " << typeName()
     453           0 :             << LogIO::POST;
     454             : 
     455             :   // Initialize the svc according to current VisSet
     456             :   //  (this counts intervals, sizes CalSet)
     457           0 :   Vector<Int> nChunkPerSol;
     458           0 :   Int nSol = sizeUpSolve(vs,nChunkPerSol);
     459             : 
     460             :   // Create the Caltable
     461           0 :   createMemCalTable();
     462             : 
     463             :   // The iterator, VisBuffer
     464           0 :   VisIter& vi(vs.iter());
     465           0 :   VisBuffer vb(vi);
     466             : 
     467             :   //  cout << "nSol = " << nSol << endl;
     468             :   //  cout << "nChunkPerSol = " << nChunkPerSol << endl;
     469             : 
     470           0 :   Vector<Int> slotidx(vs.numberSpw(),-1);
     471             : 
     472           0 :   Int nGood(0);
     473           0 :   vi.originChunks();
     474           0 :   for (Int isol=0;isol<nSol && vi.moreChunks();++isol) {
     475             : 
     476             :     // Arrange to accumulate
     477           0 :     VisBuffAccumulator vba(nAnt(),preavg(),false);
     478             :     
     479           0 :     for (Int ichunk=0;ichunk<nChunkPerSol(isol);++ichunk) {
     480             : 
     481             :       // Current _chunk_'s spw
     482           0 :       Int spw(vi.spectralWindow());
     483             : 
     484             :       // Abort if we encounter a spw for which a priori cal not available
     485           0 :       if (!ve.spwOK(spw))
     486           0 :         throw(AipsError("Pre-applied calibration not available for at least 1 spw. Check spw selection carefully."));
     487             : 
     488             : 
     489             :       // Collapse each timestamp in this chunk according to VisEq
     490             :       //  with calibration and averaging
     491           0 :       for (vi.origin(); vi.more(); vi++) {
     492             : 
     493             :         // Force read of the field Id
     494           0 :         vb.fieldId();
     495             : 
     496             :         // This forces the data/model/wt I/O, and applies
     497             :         //   any prior calibrations
     498           0 :         ve.collapse(vb);
     499             : 
     500             :         // If permitted/required by solvable component, normalize
     501           0 :         if (normalizable())
     502           0 :           vb.normalize();
     503             : 
     504             :         // If this solve not freqdep, and channels not averaged yet, do so
     505           0 :         if (!freqDepMat() && vb.nChannel()>1)
     506           0 :           vb.freqAveCubes();
     507             : 
     508             :         // Accumulate collapsed vb in a time average
     509           0 :         vba.accumulate(vb);
     510             :       }
     511             :       // Advance the VisIter, if possible
     512           0 :       if (vi.moreChunks()) vi.nextChunk();
     513             : 
     514             :     }
     515             : 
     516             :     // Finalize the averged VisBuffer
     517           0 :     vba.finalizeAverage();
     518             : 
     519             :     // The VisBuffer to solve with
     520           0 :     VisBuffer& svb(vba.aveVisBuff());
     521             : 
     522             :     // Establish meta-data for this interval
     523             :     //  (some of this may be used _during_ solve)
     524             :     //  (this sets currSpw() in the SVC)
     525           0 :     Bool vbOk=syncSolveMeta(svb,-1);
     526             : 
     527           0 :     Int thisSpw=spwMap()(svb.spectralWindow());
     528           0 :     slotidx(thisSpw)++;
     529             : 
     530             :     // We are actually solving for all channels simultaneously
     531           0 :     solveCPar().reference(solveAllCPar());
     532           0 :     solveParOK().reference(solveAllParOK());
     533           0 :     solveParErr().reference(solveAllParErr());
     534           0 :     solveParSNR().reference(solveAllParSNR());
     535             : 
     536             :     // Fill solveCPar() with 1, nominally, and flagged
     537           0 :     solveCPar()=Complex(1.0);
     538           0 :     solveParOK()=false;
     539             :     
     540           0 :     if (vbOk && svb.nRow()>0) {
     541             : 
     542             :       // solve for the R-L phase term in the current VB
     543           0 :       solveOneVB(svb);
     544             : 
     545           0 :       if (ntrue(solveParOK())>0) {
     546           0 :         Float ang=arg(sum(solveCPar()(solveParOK()))/Float(ntrue(solveParOK())))*180.0/C::pi;
     547             : 
     548             : 
     549             :         logSink() << "Mean CROSS-HAND PHASE solution for " 
     550           0 :                   << msmc().fieldName(currField())
     551           0 :                   << " (spw = " << currSpw() << ") = "
     552             :                   << ang
     553             :                   << " deg."
     554           0 :                   << LogIO::POST;
     555             :       }
     556             :       else
     557             :         logSink() << "CROSS-HAND PHASE solution for " 
     558           0 :                   << msmc().fieldName(currField())
     559           0 :                   << " (spw = " << currSpw() << ") "
     560             :                   << " was not determined (insufficient data)."
     561           0 :                   << LogIO::POST;
     562             :         
     563           0 :       nGood++;
     564             :     }
     565             : 
     566           0 :     keepNCT();
     567             :     
     568           0 :   }
     569             :   
     570             :   logSink() << "  Found good "
     571           0 :             << typeName() << " solutions in "
     572             :             << nGood << " intervals."
     573           0 :             << LogIO::POST;
     574             : 
     575             :   // Store whole of result in a caltable
     576           0 :   if (nGood==0)
     577             :     logSink() << "No output calibration table written."
     578           0 :               << LogIO::POST;
     579             :   else {
     580             : 
     581             :     // Do global post-solve tinkering (e.g., phase-only, normalization, etc.)
     582             :     //  TBD
     583             :     // globalPostSolveTinker();
     584             : 
     585             :     // write the table
     586           0 :     storeNCT();
     587             :   }
     588             : 
     589           0 : }
     590             : 
     591             : 
     592           4 : void XJones::calcAllJones() {
     593             : 
     594             :   //  cout << "currJElem().shape() = " << currJElem().shape() << endl;
     595             : 
     596             :   //  put the par in the first position on the diagonal
     597             :   //  [p 0]
     598             :   //  [0 1]
     599             :   
     600             : 
     601             :   // Set first element to the parameter
     602           4 :   IPosition blc(3,0,0,0);
     603           4 :   IPosition trc(3,0,nChanMat()-1,nElem()-1);
     604           4 :   currJElem()(blc,trc)=currCPar();
     605           4 :   currJElemOK()(blc,trc)=currParOK();
     606             :   
     607             :   // Set second diag element to one
     608           4 :   blc(0)=trc(0)=1;
     609           4 :   currJElem()(blc,trc)=Complex(1.0);
     610           4 :   currJElemOK()(blc,trc)=currParOK();
     611             : 
     612           4 : }
     613             : 
     614             : 
     615           0 : void XJones::solveOneVB(const VisBuffer& vb) {
     616             : 
     617             :   // This just a simple average of the cross-hand
     618             :   //  visbilities...
     619             : 
     620             :   // We are actually solving for all channels simultaneously
     621           0 :   solveCPar().reference(solveAllCPar());
     622           0 :   solveParOK().reference(solveAllParOK());
     623           0 :   solveParErr().reference(solveAllParErr());
     624           0 :   solveParSNR().reference(solveAllParSNR());
     625             :   
     626             :   // Fill solveCPar() with 1, nominally, and flagged
     627           0 :   solveCPar()=Complex(1.0);
     628           0 :   solveParOK()=false;
     629             : 
     630           0 :   Int nChan=vb.nChannel();
     631             : 
     632           0 :   Complex d /*,md*/;
     633             :   Float wt;
     634           0 :   Vector<DComplex> rl(nChan,0.0),lr(nChan,0.0);
     635           0 :   Double sumwt(0.0);
     636           0 :   for (Int irow=0;irow<vb.nRow();++irow) {
     637           0 :     if (!vb.flagRow()(irow) &&
     638           0 :         vb.antenna1()(irow)!=vb.antenna2()(irow)) {
     639             : 
     640           0 :       for (Int ich=0;ich<nChan;++ich) {
     641           0 :         if (!vb.flag()(ich,irow)) {
     642             :           
     643             :           // A common weight for both crosshands
     644             :           // TBD: we should probably consider this carefully...
     645             :           //  (also in D::guessPar...)
     646           0 :           wt=Double(vb.weightMat()(1,irow)+
     647           0 :                     vb.weightMat()(2,irow))/2.0;
     648             : 
     649             :           // correct weight for model normalization
     650             :           //      a=abs(vb.modelVisCube()(1,ich,irow));
     651             :           //      wt*=(a*a);
     652             :           
     653           0 :           if (wt>0.0) {
     654             :             // Cross-hands only
     655           0 :             for (Int icorr=1;icorr<3;++icorr) {
     656             :               //              md=vb.modelVisCube()(icorr,ich,irow);
     657           0 :               d=vb.visCube()(icorr,ich,irow);
     658             :               
     659           0 :               if (abs(d)>0.0) {
     660             :                 
     661           0 :                 if (icorr==1) 
     662           0 :                   rl(ich)+=DComplex(Complex(wt)*d);
     663             :                 //                rl(ich)+=DComplex(Complex(wt)*d/md);
     664             :                 else
     665           0 :                   lr(ich)+=DComplex(Complex(wt)*d);
     666             :                 //                lr(ich)+=DComplex(Complex(wt)*d/md);
     667             :                 
     668           0 :                 sumwt+=Double(wt);
     669             :                 
     670             :               } // abs(d)>0
     671             :             } // icorr
     672             :           } // wt>0
     673             :         } // !flag
     674             :       } // ich
     675             :     } // !flagRow
     676             :   } // row
     677             :   
     678             : 
     679             :   //  cout << "spw = " << currSpw() << endl;
     680             :   //  cout << " rl = " << rl << " " << phase(rl)*180.0/C::pi << endl;
     681             :   //  cout << " lr = " << lr << " " << phase(lr)*180.0/C::pi << endl;
     682             : 
     683             :   // Record results
     684           0 :   solveCPar()=Complex(1.0);
     685           0 :   solveParOK()=false;
     686           0 :   for (Int ich=0;ich<nChan;++ich) {
     687             :     // combine lr with rl
     688           0 :     rl(ich)+=conj(lr(ich));
     689             :   
     690             :     // Normalize to unit amplitude
     691             :     //  (note that the phase result is insensitive to sumwt)
     692           0 :     Double amp=abs(rl(ich));
     693             :     // For now, all antennas get the same solution
     694           0 :     IPosition blc(3,0,0,0);
     695           0 :     IPosition trc(3,0,0,nElem()-1);
     696           0 :     if (sumwt>0 && amp>0.0) {
     697           0 :       rl(ich)/=DComplex(amp);
     698           0 :       blc(1)=trc(1)=ich;
     699           0 :       solveCPar()(blc,trc)=Complex(rl(ich));
     700           0 :       solveParOK()(blc,trc)=true;
     701             :     }
     702           0 :   }
     703             : 
     704             :   
     705           0 :   if (ntrue(solveParOK())>0) {
     706           0 :     Float ang=arg(sum(solveCPar()(solveParOK()))/Float(ntrue(solveParOK())))*180.0/C::pi;
     707             :     
     708             :     
     709             :     logSink() << "Mean CROSS-HAND PHASE solution for " 
     710           0 :               << msmc().fieldName(currField())
     711           0 :               << " (spw = " << currSpw() << ") = "
     712             :               << ang
     713             :               << " deg."
     714           0 :               << LogIO::POST;
     715             :   }
     716             :   else
     717             :     logSink() << "CROSS-HAND PHASE solution for " 
     718           0 :               << msmc().fieldName(currField())
     719           0 :               << " (spw = " << currSpw() << ") "
     720             :               << " was not determined (insufficient data)."
     721           0 :               << LogIO::POST;
     722             :   
     723           0 : }
     724             : 
     725           0 : void XJones::solveOneSDB(SolveDataBuffer& sdb) {
     726             : 
     727             :   // This just a simple average of the cross-hand
     728             :   //  visbilities...
     729             : 
     730             :   // We are actually solving for all channels simultaneously
     731           0 :   solveCPar().reference(solveAllCPar());
     732           0 :   solveParOK().reference(solveAllParOK());
     733           0 :   solveParErr().reference(solveAllParErr());
     734           0 :   solveParSNR().reference(solveAllParSNR());
     735             :   
     736             :   // Fill solveCPar() with 1, nominally, and flagged
     737           0 :   solveCPar()=Complex(1.0);
     738           0 :   solveParOK()=false;
     739             : 
     740           0 :   Int nChan=sdb.nChannels();
     741             : 
     742           0 :   Complex d /*,md*/;
     743             :   Float wt;
     744           0 :   Vector<DComplex> rl(nChan,0.0),lr(nChan,0.0);
     745           0 :   Double sumwt(0.0);
     746           0 :   for (Int irow=0;irow<sdb.nRows();++irow) {
     747           0 :     if (!sdb.flagRow()(irow) &&
     748           0 :         sdb.antenna1()(irow)!=sdb.antenna2()(irow)) {
     749             : 
     750           0 :       for (Int ich=0;ich<nChan;++ich) {
     751           0 :         if (!sdb.flagCube()(1,ich,irow) &&
     752           0 :             !sdb.flagCube()(2,ich,irow)) {
     753             :           
     754             :           // A common weight for both crosshands
     755             :           // TBD: we should probably consider this carefully...
     756             :           //  (also in D::guessPar...)
     757           0 :           wt=Double(sdb.weightSpectrum()(1,ich,irow)+
     758           0 :                     sdb.weightSpectrum()(2,ich,irow))/2.0;
     759             : 
     760             :           // correct weight for model normalization
     761             :           //      a=abs(vb.modelVisCube()(1,ich,irow));
     762             :           //      wt*=(a*a);
     763             :           
     764           0 :           if (wt>0.0) {
     765             :             // Cross-hands only
     766           0 :             for (Int icorr=1;icorr<3;++icorr) {
     767           0 :               d=sdb.visCubeCorrected()(icorr,ich,irow);
     768             :               
     769           0 :               if (abs(d)>0.0) {
     770             :                 
     771           0 :                 if (icorr==1) 
     772           0 :                   rl(ich)+=DComplex(Complex(wt)*d);
     773             :                 else
     774           0 :                   lr(ich)+=DComplex(Complex(wt)*d);
     775             :                 
     776           0 :                 sumwt+=Double(wt);
     777             :                 
     778             :               } // abs(d)>0
     779             :             } // icorr
     780             :           } // wt>0
     781             :         } // !flag
     782             :       } // ich
     783             :     } // !flagRow
     784             :   } // row
     785             :   
     786             : 
     787             :   //  cout << "spw = " << currSpw() << endl;
     788             :   //  cout << " rl = " << rl << " " << phase(rl)*180.0/C::pi << endl;
     789             :   //  cout << " lr = " << lr << " " << phase(lr)*180.0/C::pi << endl;
     790             : 
     791             :   // Record results
     792           0 :   solveCPar()=Complex(1.0);
     793           0 :   solveParOK()=false;
     794           0 :   for (Int ich=0;ich<nChan;++ich) {
     795             :     // combine lr with rl
     796           0 :     rl(ich)+=conj(lr(ich));
     797             :   
     798             :     // Normalize to unit amplitude
     799             :     //  (note that the phase result is insensitive to sumwt)
     800           0 :     Double amp=abs(rl(ich));
     801             :     // For now, all antennas get the same solution
     802           0 :     IPosition blc(3,0,0,0);
     803           0 :     IPosition trc(3,0,0,nElem()-1);
     804           0 :     if (sumwt>0 && amp>0.0) {
     805           0 :       rl(ich)/=DComplex(amp);
     806           0 :       blc(1)=trc(1)=ich;
     807           0 :       solveCPar()(blc,trc)=Complex(rl(ich));
     808           0 :       solveParOK()(blc,trc)=true;
     809             :     }
     810           0 :   }
     811             : 
     812             :   
     813           0 :   if (ntrue(solveParOK())>0) {
     814           0 :     Float ang=arg(sum(solveCPar()(solveParOK()))/Float(ntrue(solveParOK())))*180.0/C::pi;
     815             :     
     816             :     
     817             :     logSink() << "Mean CROSS-HAND PHASE solution for " 
     818           0 :               << msmc().fieldName(currField())
     819           0 :               << " (spw = " << currSpw() << ") = "
     820             :               << ang
     821             :               << " deg."
     822           0 :               << LogIO::POST;
     823             :   }
     824             :   else
     825             :     logSink() << "CROSS-HAND PHASE solution for " 
     826           0 :               << msmc().fieldName(currField())
     827           0 :               << " (spw = " << currSpw() << ") "
     828             :               << " was not determined (insufficient data)."
     829           0 :               << LogIO::POST;
     830             :   
     831           0 : }
     832             : 
     833             : 
     834          14 : void XJones::solveOne(SDBList& sdbs) {
     835             : 
     836             :   // This just a simple average of the cross-hand
     837             :   //  visbilities...
     838             : 
     839          14 :   Int nSDB=sdbs.nSDB();
     840             : 
     841             :   //cout << "nSDB=" << nSDB << endl;
     842             : 
     843             :   // We are actually solving for all channels simultaneously
     844          14 :   solveCPar().reference(solveAllCPar());
     845          14 :   solveParOK().reference(solveAllParOK());
     846          14 :   solveParErr().reference(solveAllParErr());
     847          14 :   solveParSNR().reference(solveAllParSNR());
     848             :   
     849             :   // Fill solveCPar() with 1, nominally, and flagged
     850          14 :   solveCPar()=Complex(1.0);
     851          14 :   solveParOK()=false;
     852             : 
     853          14 :   Int nChan=sdbs.nChannels();
     854             : 
     855          14 :   Complex d /*,md*/;
     856             :   Float wt;
     857          14 :   Vector<DComplex> rl(nChan,0.0),lr(nChan,0.0);
     858          14 :   Double sumwt(0.0);
     859         158 :   for (Int isdb=0;isdb<nSDB;++isdb) {
     860         144 :     SolveDataBuffer& sdb(sdbs(isdb));
     861        6624 :   for (Int irow=0;irow<sdb.nRows();++irow) {
     862       12960 :     if (!sdb.flagRow()(irow) &&
     863        6480 :         sdb.antenna1()(irow)!=sdb.antenna2()(irow)) {
     864             : 
     865       58320 :       for (Int ich=0;ich<nChan;++ich) {
     866      103680 :         if (!sdb.flagCube()(1,ich,irow) &&
     867       51840 :             !sdb.flagCube()(2,ich,irow)) {
     868             :           
     869             :           // A common weight for both crosshands
     870             :           // TBD: we should probably consider this carefully...
     871             :           //  (also in D::guessPar...)
     872       51840 :           wt=Double(sdb.weightSpectrum()(1,ich,irow)+
     873       51840 :                     sdb.weightSpectrum()(2,ich,irow))/2.0;
     874             : 
     875             :           // correct weight for model normalization
     876             :           //      a=abs(vb.modelVisCube()(1,ich,irow));
     877             :           //      wt*=(a*a);
     878             :           
     879       51840 :           if (wt>0.0) {
     880             :             // Cross-hands only
     881      155520 :             for (Int icorr=1;icorr<3;++icorr) {
     882      103680 :               d=sdb.visCubeCorrected()(icorr,ich,irow);
     883             :               
     884      103680 :               if (abs(d)>0.0) {
     885             :                 
     886      103680 :                 if (icorr==1) 
     887       51840 :                   rl(ich)+=DComplex(Complex(wt)*d);
     888             :                 else
     889       51840 :                   lr(ich)+=DComplex(Complex(wt)*d);
     890             :                 
     891      103680 :                 sumwt+=Double(wt);
     892             :                 
     893             :               } // abs(d)>0
     894             :             } // icorr
     895             :           } // wt>0
     896             :         } // !flag
     897             :       } // ich
     898             :     } // !flagRow
     899             :   } // row
     900             :   } // isdb
     901             : 
     902             :   //  cout << "spw = " << currSpw() << endl;
     903             :   //  cout << " rl = " << rl << " " << phase(rl)*180.0/C::pi << endl;
     904             :   //  cout << " lr = " << lr << " " << phase(lr)*180.0/C::pi << endl;
     905             : 
     906             :   // Record results
     907          14 :   solveCPar()=Complex(1.0);
     908          14 :   solveParOK()=false;
     909         126 :   for (Int ich=0;ich<nChan;++ich) {
     910             :     // combine lr with rl
     911         112 :     rl(ich)+=conj(lr(ich));
     912             :   
     913             :     // Normalize to unit amplitude
     914             :     //  (note that the phase result is insensitive to sumwt)
     915         112 :     Double amp=abs(rl(ich));
     916             :     // For now, all antennas get the same solution
     917         112 :     IPosition blc(3,0,0,0);
     918         112 :     IPosition trc(3,0,0,nElem()-1);
     919         112 :     if (sumwt>0 && amp>0.0) {
     920         112 :       rl(ich)/=DComplex(amp);
     921         112 :       blc(1)=trc(1)=ich;
     922         112 :       solveCPar()(blc,trc)=Complex(rl(ich));
     923         112 :       solveParOK()(blc,trc)=true;
     924             :     }
     925         112 :   }
     926             : 
     927             :   
     928          14 :   if (ntrue(solveParOK())>0) {
     929          14 :     Float ang=arg(sum(solveCPar()(solveParOK()))/Float(ntrue(solveParOK())))*180.0/C::pi;
     930             :     
     931             :     
     932             :     logSink() << "Mean CROSS-HAND PHASE solution for " 
     933          28 :               << msmc().fieldName(currField())
     934          14 :               << " (spw = " << currSpw() << ") = "
     935             :               << ang
     936             :               << " deg."
     937          28 :               << LogIO::POST;
     938             :   }
     939             :   else
     940             :     logSink() << "CROSS-HAND PHASE solution for " 
     941           0 :               << msmc().fieldName(currField())
     942           0 :               << " (spw = " << currSpw() << ") "
     943             :               << " was not determined (insufficient data)."
     944           0 :               << LogIO::POST;
     945             :   
     946          14 : }
     947             : 
     948             : // **********************************************************
     949             : //  XfJones: CHANNELIZED position angle for circulars (antenna-based)
     950             : //
     951             : 
     952           0 : XfJones::XfJones(VisSet& vs) :
     953             :   VisCal(vs),             // virtual base
     954             :   VisMueller(vs),         // virtual base
     955           0 :   XJones(vs)              // immediate parent
     956             : {
     957           0 :   if (prtlev()>2) cout << "Xf::Xf(vs)" << endl;
     958           0 : }
     959             : 
     960           0 : XfJones::XfJones(String msname,Int MSnAnt,Int MSnSpw) :
     961             :   VisCal(msname,MSnAnt,MSnSpw),             // virtual base
     962             :   VisMueller(msname,MSnAnt,MSnSpw),         // virtual base
     963           0 :   XJones(msname,MSnAnt,MSnSpw)              // immediate parent
     964             : {
     965           0 :   if (prtlev()>2) cout << "Xf::Xf(msname,MSnAnt,MSnSpw)" << endl;
     966           0 : }
     967             : 
     968           5 : XfJones::XfJones(const MSMetaInfoForCal& msmc) :
     969             :   VisCal(msmc),             // virtual base
     970             :   VisMueller(msmc),         // virtual base
     971           5 :   XJones(msmc)              // immediate parent
     972             : {
     973           5 :   if (prtlev()>2) cout << "Xf::Xf(msmc)" << endl;
     974           5 : }
     975             : 
     976           0 : XfJones::XfJones(const Int& nAnt) :
     977             :   VisCal(nAnt), 
     978             :   VisMueller(nAnt),
     979           0 :   XJones(nAnt)
     980             : {
     981           0 :   if (prtlev()>2) cout << "Xf::Xf(nAnt)" << endl;
     982           0 : }
     983             : 
     984          10 : XfJones::~XfJones() {
     985           5 :   if (prtlev()>2) cout << "Xf::~Xf()" << endl;
     986          10 : }
     987             : 
     988           0 : void XfJones::initSolvePar() {
     989             : 
     990           0 :   SolvableVisJones::initSolvePar();
     991           0 :   return;
     992             : 
     993             : }
     994             : 
     995             : 
     996             : 
     997             : // **********************************************************
     998             : //  XparangJones Implementations
     999             : //
    1000             : 
    1001           0 : XparangJones::XparangJones(VisSet& vs) :
    1002             :   VisCal(vs),             // virtual base
    1003             :   VisMueller(vs),         // virtual base
    1004             :   XJones(vs),             // immediate parent
    1005           0 :   QU_(),
    1006           0 :   QURec_()
    1007             : {
    1008           0 :   if (prtlev()>2) cout << "Xparang::Xparang(vs)" << endl;
    1009           0 : }
    1010             : 
    1011           0 : XparangJones::XparangJones(String msname,Int MSnAnt,Int MSnSpw) :
    1012             :   VisCal(msname,MSnAnt,MSnSpw),             // virtual base
    1013             :   VisMueller(msname,MSnAnt,MSnSpw),         // virtual base
    1014             :   XJones(msname,MSnAnt,MSnSpw),             // immediate parent
    1015           0 :   QU_(),
    1016           0 :   QURec_()
    1017             : {
    1018           0 :   if (prtlev()>2) cout << "Xparang::Xparang(msname,MSnAnt,MSnSpw)" << endl;
    1019           0 : }
    1020             : 
    1021           7 : XparangJones::XparangJones(const MSMetaInfoForCal& msmc) :
    1022             :   VisCal(msmc),             // virtual base
    1023             :   VisMueller(msmc),         // virtual base
    1024             :   XJones(msmc),             // immediate parent
    1025           7 :   QU_(),
    1026           7 :   QURec_()
    1027             : {
    1028           7 :   if (prtlev()>2) cout << "Xparang::Xparang(msmc)" << endl;
    1029           7 : }
    1030             : 
    1031           0 : XparangJones::XparangJones(const Int& nAnt) :
    1032             :   VisCal(nAnt), 
    1033             :   VisMueller(nAnt),
    1034             :   XJones(nAnt),
    1035           0 :   QU_(),
    1036           0 :   QURec_()
    1037             : {
    1038           0 :   if (prtlev()>2) cout << "Xparang::Xparang(nAnt)" << endl;
    1039           0 : }
    1040             : 
    1041           7 : XparangJones::~XparangJones() {
    1042           7 :   if (prtlev()>2) cout << "Xparang::~Xparang()" << endl;
    1043           7 : }
    1044             : 
    1045           1 : void XparangJones::setApply(const Record& apply) {
    1046             : 
    1047           1 :   XJones::setApply(apply);
    1048             : 
    1049             :   // Force calwt to false 
    1050           1 :   calWt()=false;
    1051             : 
    1052           1 : }
    1053             : 
    1054             : 
    1055             : 
    1056           0 : void XparangJones::selfGatherAndSolve(VisSet& vs, VisEquation& ve) {
    1057             : 
    1058           0 :   if (prtlev()>4) cout << "   GlnXph::selfGatherAndSolve(ve)" << endl;
    1059             : 
    1060             :   // Inform logger/history
    1061           0 :   logSink() << "Solving for " << typeName()
    1062           0 :             << LogIO::POST;
    1063             : 
    1064             :   // Initialize the svc according to current VisSet
    1065             :   //  (this counts intervals, sizes CalSet)
    1066           0 :   Vector<Int> nChunkPerSol;
    1067           0 :   Int nSol = sizeUpSolve(vs,nChunkPerSol);
    1068             : 
    1069             :   // Create the Caltable
    1070           0 :   createMemCalTable();
    1071             : 
    1072             :   // The iterator, VisBuffer
    1073           0 :   VisIter& vi(vs.iter());
    1074           0 :   VisBuffer vb(vi);
    1075             : 
    1076             :   //  cout << "nSol = " << nSol << endl;
    1077             :   //  cout << "nChunkPerSol = " << nChunkPerSol << endl;
    1078             : 
    1079           0 :   Int nGood(0);
    1080           0 :   vi.originChunks();
    1081           0 :   for (Int isol=0;isol<nSol && vi.moreChunks();++isol) {
    1082             : 
    1083             :     // Arrange to accumulate
    1084           0 :     VisBuffAccumulator vba(nAnt(),preavg(),false);
    1085             :     
    1086           0 :     for (Int ichunk=0;ichunk<nChunkPerSol(isol);++ichunk) {
    1087             : 
    1088             :       // Current _chunk_'s spw
    1089           0 :       Int spw(vi.spectralWindow());
    1090             : 
    1091             :       // Abort if we encounter a spw for which a priori cal not available
    1092           0 :       if (!ve.spwOK(spw))
    1093           0 :         throw(AipsError("Pre-applied calibration not available for at least 1 spw. Check spw selection carefully."));
    1094             : 
    1095             : 
    1096             :       // Collapse each timestamp in this chunk according to VisEq
    1097             :       //  with calibration and averaging
    1098           0 :       for (vi.origin(); vi.more(); vi++) {
    1099             : 
    1100             :         // Force read of the field Id
    1101           0 :         vb.fieldId();
    1102             : 
    1103             :         // This forces the data/model/wt I/O, and applies
    1104             :         //   any prior calibrations
    1105           0 :         ve.collapse(vb);
    1106             : 
    1107             :         // If permitted/required by solvable component, normalize
    1108           0 :         if (normalizable())
    1109           0 :           vb.normalize();
    1110             : 
    1111             :         // If this solve not freqdep, and channels not averaged yet, do so
    1112           0 :         if (!freqDepMat() && vb.nChannel()>1)
    1113           0 :           vb.freqAveCubes();
    1114             : 
    1115             :         // Accumulate collapsed vb in a time average
    1116           0 :         vba.accumulate(vb);
    1117             :       }
    1118             :       // Advance the VisIter, if possible
    1119           0 :       if (vi.moreChunks()) vi.nextChunk();
    1120             : 
    1121             :     }
    1122             : 
    1123             :     // Finalize the averged VisBuffer
    1124           0 :     vba.finalizeAverage();
    1125             : 
    1126             :     // The VisBuffer to solve with
    1127           0 :     VisBuffer& svb(vba.aveVisBuff());
    1128             : 
    1129             :     // Establish meta-data for this interval
    1130             :     //  (some of this may be used _during_ solve)
    1131             :     //  (this sets currSpw() in the SVC)
    1132           0 :     Bool vbOk=syncSolveMeta(svb,-1);
    1133             : 
    1134           0 :     if (vbOk && svb.nRow()>0) {
    1135             : 
    1136             :       // solve for the X-Y phase term in the current VB
    1137           0 :       solveOneVB(svb);
    1138             : 
    1139           0 :       nGood++;
    1140             :     }
    1141             : 
    1142           0 :     keepNCT();
    1143             :     
    1144           0 :   }
    1145             :   
    1146             :   logSink() << "  Found good "
    1147           0 :             << typeName() << " solutions in "
    1148             :             << nGood << " intervals."
    1149           0 :             << LogIO::POST;
    1150             : 
    1151             :   // Store whole of result in a caltable
    1152           0 :   if (nGood==0)
    1153             :     logSink() << "No output calibration table written."
    1154           0 :               << LogIO::POST;
    1155             :   else {
    1156             : 
    1157             :     // Do global post-solve tinkering (e.g., phase-only, normalization, etc.)
    1158           0 :     globalPostSolveTinker();
    1159             : 
    1160             :     // write the table
    1161           0 :     storeNCT();
    1162             :   }
    1163             : 
    1164           0 : }
    1165             : 
    1166             : // Handle trivial vbga
    1167           0 : void XparangJones::selfSolveOne(VisBuffGroupAcc& vbga) {
    1168             : 
    1169             :   // Expecting only on VB in the vbga (with many times)
    1170           0 :   if (vbga.nBuf()!=1)
    1171           0 :     throw(AipsError("XparangJones can't process multi-vb vbga."));
    1172             : 
    1173             :   // Call single-VB specialized solver with the one vb
    1174           0 :   this->solveOneVB(vbga(0));
    1175             : 
    1176           0 : }
    1177             : 
    1178             : // SDBList (VI2) version
    1179          21 : void XparangJones::selfSolveOne(SDBList& sdbs) {
    1180             : 
    1181             :   // Expecting multiple SDBs (esp. in time)
    1182             :   // (2020Oct01: insufficient data caught later in solveOne(sdbs))
    1183             :   //  if (sdbs.nSDB()==1)
    1184             :   //    throw(AipsError("XparangJones needs multiple SDBs"));
    1185             : 
    1186             :   // Call single-VB specialized solver with the one vb
    1187          21 :   this->solveOne(sdbs);
    1188             : 
    1189          21 : }
    1190             : 
    1191             : // Solve for the X-Y phase from the cross-hand's slope in R/I
    1192           0 : void XparangJones::solveOneVB(const VisBuffer& vb) {
    1193             : 
    1194             :   // ensure
    1195           0 :   if (QU_.shape()!=IPosition(2,2,nSpw())) {
    1196           0 :     QU_.resize(2,nSpw());
    1197           0 :     QU_.set(0.0);
    1198             :   }
    1199             : 
    1200           0 :   Int thisSpw=spwMap()(vb.spectralWindow());
    1201             :   
    1202             :   // We are actually solving for all channels simultaneously
    1203           0 :   solveCPar().reference(solveAllCPar());
    1204           0 :   solveParOK().reference(solveAllParOK());
    1205           0 :   solveParErr().reference(solveAllParErr());
    1206           0 :   solveParSNR().reference(solveAllParSNR());
    1207             :   
    1208             :   // Fill solveCPar() with 1, nominally, and flagged
    1209           0 :   solveCPar()=Complex(1.0);
    1210           0 :   solveParOK()=false;
    1211             : 
    1212           0 :   Int nChan=vb.nChannel();
    1213             :   //  if (nChan>1)
    1214             :   //    throw(AipsError("X-Y phase solution NYI for channelized data"));
    1215             : 
    1216             :   // Find number of timestamps in the VB
    1217           0 :   Vector<uInt> ord;
    1218           0 :   Int nTime=genSort(ord,vb.time(),Sort::Ascending,Sort::NoDuplicates);
    1219             : 
    1220           0 :   Matrix<Double> x(nTime,nChan,0.0),y(nTime,nChan,0.0),wt(nTime,nChan,0.0),sig(nTime,nChan,0.0);
    1221           0 :   Matrix<Bool> mask(nTime,nChan,false);
    1222             : 
    1223           0 :   mask.set(false);
    1224           0 :   Complex v(0.0);
    1225           0 :   Float wt0(0.0);
    1226           0 :   Int iTime(-1);
    1227           0 :   Double currtime(-1.0);
    1228           0 :   for (Int irow=0;irow<vb.nRow();++irow) {
    1229           0 :     if (!vb.flagRow()(irow) &&
    1230           0 :         vb.antenna1()(irow)!=vb.antenna2()(irow)) {
    1231             : 
    1232             :       // Advance time index when we see a new time
    1233           0 :       if (vb.time()(irow)!=currtime) {
    1234           0 :         ++iTime;
    1235           0 :         currtime=vb.time()(irow); // remember the new current time
    1236             :       }
    1237             : 
    1238             :       // Weights not yet chan-dep
    1239           0 :       wt0=(vb.weightMat()(1,irow)+vb.weightMat()(2,irow));
    1240           0 :       if (wt0>0.0) {
    1241             : 
    1242           0 :         for (Int ich=0;ich<nChan;++ich) {
    1243           0 :           if (!vb.flag()(ich,irow)) {
    1244           0 :             v=vb.visCube()(1,ich,irow)+conj(vb.visCube()(2,ich,irow));
    1245           0 :             x(iTime,ich)+=Double(wt0*real(v));
    1246           0 :             y(iTime,ich)+=Double(wt0*imag(v));
    1247           0 :             wt(iTime,ich)+=Double(wt0);
    1248             :           }
    1249             :         }
    1250             :       }
    1251             :     }
    1252             :   }
    1253             : 
    1254             :   // Normalize data by accumulated weights
    1255           0 :   for (Int itime=0;itime<nTime;++itime) {
    1256           0 :     for (Int ich=0;ich<nChan;++ich) {
    1257           0 :       if (wt(itime,ich)>0.0) {
    1258           0 :         x(itime,ich)/=wt(itime,ich);
    1259           0 :         y(itime,ich)/=wt(itime,ich);
    1260           0 :         sig(itime,ich)=sqrt(1.0/wt(itime,ich));
    1261           0 :         mask(itime,ich)=true;
    1262             :       }
    1263             :       else
    1264           0 :         sig(itime,ich)=DBL_MAX;    // ~zero weight
    1265             :     }
    1266             :   }
    1267             : 
    1268             :   // Solve for each channel
    1269           0 :   Vector<Complex> Cph(nChan);
    1270           0 :   Cph.set(Complex(1.0,0.0));
    1271           0 :   Float currAmb(1.0);
    1272           0 :   Bool report(false);
    1273           0 :   for (Int ich=0;ich<nChan;++ich) {
    1274             : 
    1275           0 :     if (sum(wt.column(ich))>0.0) {
    1276           0 :       report=true;
    1277           0 :       LinearFit<Double> phfitter;
    1278           0 :       Polynomial<AutoDiff<Double> > line(1);
    1279           0 :       phfitter.setFunction(line);
    1280           0 :       Vector<Bool> m(mask.column(ich));
    1281             : 
    1282             :       // Fit shallow and steep, and always prefer shallow
    1283             :       
    1284             :       // Assumed shallow solve:
    1285           0 :       Vector<Double> solnA;
    1286           0 :       solnA.assign(phfitter.fit(x.column(ich),y.column(ich),sig.column(ich),&m));
    1287             : 
    1288             :       // Assumed steep solve:
    1289           0 :       Vector<Double> solnB;
    1290           0 :       solnB.assign(phfitter.fit(y.column(ich),x.column(ich),sig.column(ich),&m));
    1291             : 
    1292           0 :       Double Xph(0.0);
    1293           0 :       if (abs(solnA(1))<abs(solnB(1))) {
    1294           0 :         Xph=atan(solnA(1));
    1295             :       }
    1296             :       else {
    1297           0 :         Xph=atan(1.0/solnB(1));
    1298             :       }
    1299             : 
    1300           0 :       Cph(ich)=currAmb*Complex(DComplex(cos(Xph),sin(Xph)));
    1301             : 
    1302             :       // Watch for and remove ambiguity changes which can
    1303             :       //  occur near Xph~= +/-90 deg (the atan above can jump)
    1304             :       //  (NB: this does _not_ resolve the amb; it merely makes
    1305             :       //   it consistent within the spw)
    1306           0 :       if (ich>0) {
    1307             :         // If Xph changes by more than pi/2, probably a ambig jump...
    1308           0 :         Float dang=abs(arg(Cph(ich)/Cph(ich-1)));
    1309           0 :         if (dang > (C::pi/2.)) {
    1310           0 :           Cph(ich)*=-1.0;   // fix this one
    1311           0 :           currAmb*=-1.0;    // reverse currAmb, so curr amb is carried forward
    1312             :           //      cout << "  Found XY phase ambiguity jump at chan=" << ich << " in spw=" << currSpw();
    1313             :         }
    1314             :       }
    1315             : 
    1316             :       //      cout << " (" << currAmb << ")";
    1317             :       //      cout << endl;
    1318             : 
    1319             : 
    1320             :       // Set all antennas with this X-Y phase (as a complex number)
    1321           0 :       solveCPar()(Slice(0,1,1),Slice(ich,1,1),Slice())=Cph(ich);
    1322           0 :       solveParOK()(Slice(),Slice(ich,1,1),Slice())=true;
    1323           0 :     }
    1324             :     else {
    1325           0 :       solveCPar()(Slice(0,1,1),Slice(ich,1,1),Slice())=Complex(1.0);
    1326           0 :       solveParOK()(Slice(),Slice(ich,1,1),Slice())=false;
    1327             :     }
    1328             :   }
    1329             : 
    1330           0 :   if (report)
    1331           0 :     cout << endl 
    1332           0 :          << "Spw = " << thisSpw
    1333           0 :          << " (ich=" << nChan/2 << "/" << nChan << "): " << endl
    1334           0 :          << " X-Y phase = " << arg(Cph[nChan/2])*180.0/C::pi << " deg." << endl;
    1335             :       
    1336             : 
    1337             :   // Now fit for the source polarization
    1338             :   {
    1339             : 
    1340           0 :     Vector<Double> wtf(nTime,0.0),sigf(nTime,0.0),xf(nTime,0.0),yf(nTime,0.0);
    1341           0 :     Vector<Bool> maskf(nTime,false);
    1342             :     Float wt0;
    1343           0 :     Complex v;
    1344           0 :     Double currtime(-1.0);
    1345           0 :     Int iTime(-1);
    1346           0 :     for (Int irow=0;irow<vb.nRow();++irow) {
    1347           0 :       if (!vb.flagRow()(irow) &&
    1348           0 :           vb.antenna1()(irow)!=vb.antenna2()(irow)) {
    1349             :         
    1350           0 :         if (vb.time()(irow)!=currtime) {
    1351             :           // Advance time index when we see a new time
    1352           0 :           ++iTime;
    1353           0 :           currtime=vb.time()(irow); // remember the new current time
    1354             :         }
    1355             : 
    1356             :         // Weights not yet chan-dep
    1357           0 :         wt0=(vb.weightMat()(1,irow)+vb.weightMat()(2,irow));
    1358           0 :         if (wt0>0.0) {
    1359           0 :           for (Int ich=0;ich<nChan;++ich) {
    1360             :             
    1361           0 :             if (!vb.flag()(ich,irow)) {
    1362             :               // Correct x-hands for xy-phase and add together
    1363           0 :               v=vb.visCube()(1,ich,irow)/Cph(ich)+vb.visCube()(2,ich,irow)/conj(Cph(ich));
    1364           0 :               xf(iTime)+=Double(wt0*2.0*(vb.feed_pa(vb.time()(irow))(0)));
    1365           0 :               yf(iTime)+=Double(wt0*real(v)/2.0);
    1366           0 :               wtf(iTime)+=Double(wt0);
    1367             :             }
    1368             :           }
    1369             :         }
    1370             :       }
    1371             :     }
    1372             :     
    1373             :     // Normalize data by accumulated weights
    1374           0 :     for (Int itime=0;itime<nTime;++itime) {
    1375           0 :       if (wtf(itime)>0.0) {
    1376           0 :         xf(itime)/=wtf(itime);
    1377           0 :         yf(itime)/=wtf(itime);
    1378           0 :         sigf(itime)=sqrt(1.0/wtf(itime));
    1379           0 :         maskf(itime)=true;
    1380             :       }
    1381             :       else
    1382           0 :         sigf(itime)=DBL_MAX;    // ~zero weight
    1383             :     }
    1384             :     
    1385             :     // p0=Q, p1=U, p2 = real part of net instr pol offset
    1386             :     //  x is TWICE the parallactic angle
    1387           0 :     CompiledFunction<AutoDiff<Double> > fn;
    1388           0 :     fn.setFunction("-p0*sin(x) + p1*cos(x) + p2");
    1389             : 
    1390           0 :     LinearFit<Double> fitter;
    1391           0 :     fitter.setFunction(fn);
    1392             :     
    1393           0 :     Vector<Double> soln=fitter.fit(xf,yf,sigf,&maskf);
    1394             :     
    1395           0 :     QU_(0,thisSpw) = soln(0);
    1396           0 :     QU_(1,thisSpw) = soln(1);
    1397             : 
    1398             :     cout << " Fractional Poln: "
    1399           0 :          << "Q = " << QU_(0,thisSpw) << ", "
    1400           0 :          << "U = " << QU_(1,thisSpw) << "; "
    1401           0 :          << "P = " << sqrt(soln(0)*soln(0)+soln(1)*soln(1)) << ", "
    1402           0 :          << "X = " << atan2(soln(1),soln(0))*90.0/C::pi << "deg."
    1403           0 :          << endl;
    1404           0 :     cout << " Net (over baselines) instrumental polarization: " 
    1405           0 :          << soln(2) << endl;
    1406             : 
    1407           0 :   }     
    1408             : 
    1409           0 : }
    1410             : 
    1411             : // Solve for the cross-hand phase from the cross-hand's slope in R/I
    1412          21 : void XparangJones::solveOne(SDBList& sdbs) {
    1413             : 
    1414             :   //cout << "solvePol() = " << solvePol() << endl;
    1415             :   //  cout << boolalpha;
    1416             :   //  cout << "normalizable() = " << this->normalizable() << endl;
    1417             :   //  cout << "divideByStokesIModelForSolve() = " << this->divideByStokesIModelForSolve() << endl;
    1418             : 
    1419             :   // ensure
    1420          21 :   if (QU_.shape()!=IPosition(2,2,nSpw())) {
    1421           6 :     QU_.resize(2,nSpw());
    1422           6 :     QU_.set(0.0);
    1423             :   }
    1424             : 
    1425          21 :   Int thisSpw=sdbs.aggregateSpw();
    1426             :   
    1427             :   // We are actually solving for all channels simultaneously
    1428          21 :   solveCPar().reference(solveAllCPar());
    1429          21 :   solveParOK().reference(solveAllParOK());
    1430          21 :   solveParErr().reference(solveAllParErr());
    1431          21 :   solveParSNR().reference(solveAllParSNR());
    1432             :   
    1433             :   // Fill solveCPar() with 1, nominally, and flagged
    1434          21 :   solveCPar()=Complex(1.0);
    1435          21 :   solveParOK()=false;
    1436             : 
    1437          21 :   Int nChan=sdbs.nChannels();
    1438             : 
    1439             :   // Number of datapoints in fit is the number of SDBs
    1440          21 :   Int nSDB=sdbs.nSDB();
    1441             : 
    1442             : 
    1443             :   // This uniformizes the baseline-dep flags over all times (sdbs)
    1444             :   //  (~ensures minimally undistorted solution below, which uses average over baseline)
    1445          21 :   String extBLmessage;
    1446          21 :   Int nGoodSDB(0);
    1447          21 :   nGoodSDB=sdbs.extendCrossHandBaselineFlags(extBLmessage);
    1448             : 
    1449          21 :   if (sdbs.polBasis()==String("LIN")) {
    1450             : 
    1451           1 :   logSink() << "Solving for Cross-hand Phase and calibrator linear polarization in the LINEAR basis in spw=" << thisSpw<< LogIO::POST;
    1452             : 
    1453             :   // Report surviving data from extendCrossHandBaselineFlags
    1454           1 :   logSink() << " " << extBLmessage << LogIO::POST;
    1455             : 
    1456             :   // Trap insufficient data case (linear; Q,U + real offset in real part)
    1457           1 :   if (nGoodSDB<3) 
    1458           0 :     throw(AipsError("For a viable solution, the Xfparang+QU solve requires at least THREE distinct unflagged data segments in time/parallactic angle in the LINEAR basis.  Cannot proceed."));
    1459             : 
    1460           1 :   Matrix<Double> x(nSDB,nChan,0.0),y(nSDB,nChan,0.0),wt(nSDB,nChan,0.0),sig(nSDB,nChan,0.0);
    1461           1 :   Matrix<Bool> mask(nSDB,nChan,false);
    1462             : 
    1463           1 :   mask.set(false);
    1464           1 :   Complex v(0.0);
    1465           1 :   Float wt0(0.0);
    1466             :   
    1467          25 :   for (Int isdb=0;isdb<nSDB;++isdb) {
    1468          24 :     SolveDataBuffer& sdb(sdbs(isdb));
    1469             : 
    1470        1104 :     for (Int irow=0;irow<sdb.nRows();++irow) {
    1471        2160 :       if (!sdb.flagRow()(irow) &&
    1472        1080 :           sdb.antenna1()(irow)!=sdb.antenna2()(irow)) {
    1473             :         
    1474        9720 :         for (Int ich=0;ich<nChan;++ich) {
    1475        8640 :           if (!sdb.flagCube()(1,ich,irow)) {
    1476        8640 :             wt0=sdb.weightSpectrum()(1,ich,irow);
    1477        8640 :             v=sdb.visCubeCorrected()(1,ich,irow);
    1478        8640 :             x(isdb,ich)+=Double(wt0*real(v));
    1479        8640 :             y(isdb,ich)+=Double(wt0*imag(v));
    1480        8640 :             wt(isdb,ich)+=Double(wt0);
    1481             :           }
    1482        8640 :           if (!sdb.flagCube()(2,ich,irow)) {
    1483        8640 :             wt0=sdb.weightSpectrum()(2,ich,irow);
    1484        8640 :             v=conj(sdb.visCubeCorrected()(2,ich,irow));
    1485        8640 :             x(isdb,ich)+=Double(wt0*real(v));
    1486        8640 :             y(isdb,ich)+=Double(wt0*imag(v));
    1487        8640 :             wt(isdb,ich)+=Double(wt0);
    1488             :           }
    1489             :         }
    1490             :       }
    1491             :     }
    1492             :   }
    1493             : 
    1494             :   // Normalize data by accumulated weights
    1495          25 :   for (Int isdb=0;isdb<nSDB;++isdb) {
    1496         216 :     for (Int ich=0;ich<nChan;++ich) {
    1497         192 :       if (wt(isdb,ich)>0.0) {
    1498         192 :         x(isdb,ich)/=wt(isdb,ich);
    1499         192 :         y(isdb,ich)/=wt(isdb,ich);
    1500         192 :         sig(isdb,ich)=sqrt(1.0/wt(isdb,ich));
    1501         192 :         mask(isdb,ich)=true;
    1502             :       }
    1503             :       else
    1504           0 :         sig(isdb,ich)=DBL_MAX;    // ~zero weight
    1505             :     }
    1506             :   }
    1507             : 
    1508             :   // Solve for each channel
    1509           1 :   Vector<Complex> Cph(nChan);
    1510           1 :   Cph.set(Complex(1.0,0.0));
    1511           1 :   Float currAmb(1.0);
    1512           1 :   Bool report(false);
    1513           9 :   for (Int ich=0;ich<nChan;++ich) {
    1514             : 
    1515           8 :     if (sum(wt.column(ich))>0.0) {
    1516             :       //      report=true;
    1517           8 :       LinearFit<Double> phfitter;
    1518           8 :       Polynomial<AutoDiff<Double> > line(1);
    1519           8 :       phfitter.setFunction(line);
    1520           8 :       Vector<Bool> m(mask.column(ich));
    1521             : 
    1522             :       // Fit shallow and steep, and always prefer shallow
    1523             :       
    1524             :       // Assumed shallow solve:
    1525           8 :       Vector<Double> solnA;
    1526           8 :       solnA.assign(phfitter.fit(x.column(ich),y.column(ich),sig.column(ich),&m));
    1527             : 
    1528             :       // Assumed steep solve:
    1529           8 :       Vector<Double> solnB;
    1530           8 :       solnB.assign(phfitter.fit(y.column(ich),x.column(ich),sig.column(ich),&m));
    1531             : 
    1532           8 :       Double Xph(0.0);
    1533           8 :       if (abs(solnA(1))<abs(solnB(1))) {
    1534           8 :         Xph=atan(solnA(1));
    1535             :       }
    1536             :       else {
    1537           0 :         Xph=atan(1.0/solnB(1));
    1538             :       }
    1539             : 
    1540           8 :       Cph(ich)=currAmb*Complex(DComplex(cos(Xph),sin(Xph)));
    1541             : 
    1542             :       // Watch for and remove ambiguity changes which can
    1543             :       //  occur near Xph~= +/-90 deg (the atan above can jump)
    1544             :       //  (NB: this does _not_ resolve the absolute amb; it merely makes
    1545             :       //   it consistent within the spw over channels)
    1546           8 :       if (ich>0) {
    1547             :         // If Xph changes by more than pi/2, probably a ambig jump...
    1548           7 :         Float dang=abs(arg(Cph(ich)/Cph(ich-1)));
    1549           7 :         if (dang > (C::pi/2.)) {
    1550           0 :           Cph(ich)*=-1.0;   // fix this one
    1551           0 :           currAmb*=-1.0;    // reverse currAmb, so curr amb is carried forward
    1552             :           //cout << "  Found XY phase ambiguity jump at chan=" << ich << " in spw=" << currSpw() << endl;
    1553             :         }
    1554             :       }
    1555             : 
    1556             :       //      cout << " (" << currAmb << ")";
    1557             :       //      cout << endl;
    1558             : 
    1559             : 
    1560             :       // Set all antennas with this X-Y phase (as a complex number)
    1561           8 :       solveCPar()(Slice(0,1,1),Slice(ich,1,1),Slice())=Cph(ich);
    1562           8 :       solveParOK()(Slice(),Slice(ich,1,1),Slice())=true;
    1563           8 :     }
    1564             :     else {
    1565           0 :       solveCPar()(Slice(0,1,1),Slice(ich,1,1),Slice())=Complex(1.0);
    1566           0 :       solveParOK()(Slice(),Slice(ich,1,1),Slice())=false;
    1567             :     }
    1568             :   } // ichan 
    1569             : 
    1570             :   // Calculate correlation with model data (real part only!), insist it is positive
    1571             :   {
    1572             : 
    1573             : 
    1574             :     //logSink() << "Attempting 180 deg ambiguity resolution by comparison with specified linear polarization model." << LogIO::POST;
    1575             :       
    1576           1 :     Vector<Double> wtf(nSDB,0.0); //,sigf(nSDB,0.0),xf(nSDB,0.0),yf(nSDB,0.0);
    1577             :     //Vector<Bool> maskf(nSDB,false);
    1578             :     Float wt0;
    1579           1 :     Complex v,m;
    1580           1 :     Double Cp(0.0), Cn(0.0);
    1581           1 :     Double wtsum(0.0);
    1582             : 
    1583          25 :     for (Int isdb=0;isdb<nSDB;++isdb) {
    1584          24 :       SolveDataBuffer& sdb(sdbs(isdb));
    1585             : 
    1586        1104 :       for (Int irow=0;irow<sdb.nRows();++irow) {
    1587        2160 :         if (!sdb.flagRow()(irow) &&
    1588        1080 :             sdb.antenna1()(irow)!=sdb.antenna2()(irow)) {
    1589             :           
    1590        9720 :           for (Int ich=0;ich<nChan;++ich) {
    1591             :             
    1592        8640 :             if (!sdb.flagCube()(1,ich,irow)) {
    1593             :               // Correct x-hands for xy-phase and add together
    1594        8640 :               wt0=sdb.weightSpectrum()(1,ich,irow);
    1595        8640 :               v=sdb.visCubeCorrected()(1,ich,irow)/Cph(ich);
    1596        8640 :               m=sdb.visCubeModel()(1,ich,irow);
    1597        8640 :               Cp+=(wt0*real(v)*real(m));
    1598        8640 :               Cn+=(wt0*real(v)*real(m)*(-1.0));
    1599        8640 :               wtsum+=Double(wt0);
    1600             :             }
    1601        8640 :             if (!sdb.flagCube()(2,ich,irow)) {
    1602             :               // Correct x-hands for xy-phase and add together
    1603        8640 :               wt0=sdb.weightSpectrum()(2,ich,irow);
    1604        8640 :               v=sdb.visCubeCorrected()(2,ich,irow)/conj(Cph(ich));
    1605        8640 :               m=sdb.visCubeModel()(2,ich,irow);
    1606        8640 :               Cp+=(wt0*real(v)*real(m));
    1607        8640 :               Cn+=(wt0*real(v)*real(m)*(-1.0));
    1608        8640 :               wtsum+=Double(wt0);
    1609             :             }
    1610             :           }
    1611             :         }
    1612             :       }
    1613             :     }
    1614             : 
    1615             : 
    1616             :     //cout << "Cp,Cn = " << Cp << " " << Cn << endl;
    1617             : 
    1618           1 :     if ( Cn > Cp ) {
    1619           0 :       logSink() << " NB: 180 deg ambiguity detected and corrected!" << LogIO::POST;
    1620           0 :       Complex swap(-1.0,0.0);
    1621           0 :       Cph*=swap;
    1622             :       
    1623           0 :       MaskedArray<Complex> sCP(solveCPar()(solveParOK()));
    1624           0 :       sCP*=swap;
    1625             : 
    1626           0 :     }
    1627             : 
    1628             : 
    1629           1 :   }
    1630             : 
    1631             : 
    1632           1 :   if (ntrue(solveParOK())>0) {
    1633           1 :     Float ang=arg(sum(solveCPar()(solveParOK()))/Float(ntrue(solveParOK())))*180.0/C::pi;
    1634             : 
    1635           2 :     logSink() << " Fld = " << msmc().fieldName(currField())
    1636             :               << ", Spw = " << thisSpw
    1637             :               << " (ich=" << nChan/2 << "/" << nChan << "): " //<< endl
    1638           1 :               << " CROSS-HAND PHASE = " << arg(Cph[nChan/2])*180.0/C::pi << " deg."
    1639             :               << " (Mean = " << ang << ")"
    1640           3 :               << LogIO::POST;
    1641             :   }
    1642             :   else
    1643           0 :     logSink() << " Fld = " << msmc().fieldName(currField())
    1644             :               << ", Spw = " << thisSpw
    1645             :               << " (ich=" << nChan/2 << "/" << nChan << "): " << endl
    1646             :               << " CROSS-HAND PHASE was not determined (insufficient data)."
    1647           0 :               << LogIO::POST;
    1648             : 
    1649           1 :   if (report)
    1650           0 :     cout << endl 
    1651           0 :          << "Spw = " << thisSpw
    1652           0 :          << " (ich=" << nChan/2 << "/" << nChan << "): " // << endl
    1653           0 :          << " X-Y phase = " << arg(Cph[nChan/2])*180.0/C::pi << " deg." << endl;
    1654             :       
    1655             : 
    1656             : 
    1657             :   // Now fit for the source polarization
    1658             :   {
    1659             : 
    1660           1 :     Vector<Double> wtf(nSDB,0.0),sigf(nSDB,0.0),xf(nSDB,0.0),yf(nSDB,0.0);
    1661           1 :     Vector<Bool> maskf(nSDB,false);
    1662             :     Float wt0;
    1663           1 :     Complex v;
    1664          25 :     for (Int isdb=0;isdb<nSDB;++isdb) {
    1665          24 :       SolveDataBuffer& sdb(sdbs(isdb));
    1666             : 
    1667        1104 :       for (Int irow=0;irow<sdb.nRows();++irow) {
    1668        2160 :         if (!sdb.flagRow()(irow) &&
    1669        1080 :             sdb.antenna1()(irow)!=sdb.antenna2()(irow)) {
    1670             :           
    1671        1080 :           Float fpa(sdb.feedPa()(0));  // assumes same for all antennas!
    1672             :           
    1673        9720 :           for (Int ich=0;ich<nChan;++ich) {
    1674             :             
    1675        8640 :             if (!sdb.flagCube()(1,ich,irow)) {
    1676             :               // Correct x-hands for xy-phase and add together
    1677        8640 :               wt0=sdb.weightSpectrum()(1,ich,irow);
    1678        8640 :               v=sdb.visCubeCorrected()(1,ich,irow)/Cph(ich);
    1679        8640 :               xf(isdb)+=Double(wt0*2.0*(fpa));
    1680        8640 :               yf(isdb)+=Double(wt0*real(v));
    1681        8640 :               wtf(isdb)+=Double(wt0);
    1682             :             }
    1683        8640 :             if (!sdb.flagCube()(2,ich,irow)) {
    1684             :               // Correct x-hands for xy-phase and add together
    1685        8640 :               wt0=sdb.weightSpectrum()(2,ich,irow);
    1686        8640 :               v=sdb.visCubeCorrected()(2,ich,irow)/conj(Cph(ich));
    1687        8640 :               xf(isdb)+=Double(wt0*2.0*(fpa));
    1688        8640 :               yf(isdb)+=Double(wt0*real(v));
    1689        8640 :               wtf(isdb)+=Double(wt0);
    1690             :             }
    1691             :           }
    1692             :         }
    1693             :       }
    1694             :     }
    1695             :     
    1696             :     // Normalize data by accumulated weights
    1697          25 :     for (Int isdb=0;isdb<nSDB;++isdb) {
    1698          24 :       if (wtf(isdb)>0.0) {
    1699          24 :         xf(isdb)/=wtf(isdb);
    1700          24 :         yf(isdb)/=wtf(isdb);
    1701          24 :         sigf(isdb)=sqrt(1.0/wtf(isdb));
    1702          24 :         maskf(isdb)=true;
    1703             :       }
    1704             :       else
    1705           0 :         sigf(isdb)=DBL_MAX;    // ~zero weight
    1706             :     }
    1707             :     
    1708             :     // p0=Q, p1=U, p2 = real part of net instr pol offset
    1709             :     //  x is TWICE the parallactic angle
    1710           1 :     CompiledFunction<AutoDiff<Double> > fn;
    1711           1 :     fn.setFunction("-p0*sin(x) + p1*cos(x) + p2");
    1712             : 
    1713           1 :     LinearFit<Double> fitter;
    1714           1 :     fitter.setFunction(fn);
    1715             :     
    1716           1 :     Vector<Double> soln=fitter.fit(xf,yf,sigf,&maskf);
    1717             : 
    1718           1 :     srcPolPar().resize(2);
    1719           1 :     srcPolPar()(0)=soln(0);
    1720           1 :     srcPolPar()(1)=soln(1);
    1721             :         
    1722           1 :     QU_(0,thisSpw) = soln(0);
    1723           1 :     QU_(1,thisSpw) = soln(1);
    1724             : 
    1725           1 :     Float &Q(QU_(0,thisSpw)), &U(QU_(1,thisSpw));
    1726             : 
    1727             : 
    1728             :     logSink() << " Fractional Poln: "
    1729           1 :               << "Q = " << Q << ", "
    1730           1 :               << "U = " << U << "; "
    1731           1 :               << "P = " << sqrt(Q*Q+U*U) << ", "
    1732           2 :               << "X = " << atan2(U,Q)*90.0/C::pi << "deg."
    1733           1 :               << LogIO::POST;
    1734             :     logSink() << "  Net (over baselines) instrumental polarization (real part): " 
    1735           1 :               << soln(2) << LogIO::POST;
    1736             : 
    1737           1 :   } // poln solve scope 
    1738             : 
    1739           1 :   }
    1740          20 :   else if (sdbs.polBasis()==String("CIRC")) {
    1741             : 
    1742          20 :   logSink() << "Solving for Cross-hand Phase and calibrator linear polarization in the CIRCULAR basis in spw="<<thisSpw << LogIO::POST;
    1743             : 
    1744             :   // Report surviving data from extendCrossHandBaselineFlags
    1745          20 :   logSink() << " " << extBLmessage << LogIO::POST;
    1746             : 
    1747             :   // Trap insufficient data case (circular; |P| and a complex offset from complex data, cf law of cosines for isoceles triangle)
    1748          20 :   if (nGoodSDB<2) 
    1749           0 :     throw(AipsError("For a viable solution, the Xfparang+QU solve requires at least TWO distinct unflagged data segments in time/parallactic angle in the CIRCULAR basis.  Cannot proceed."));
    1750             : 
    1751          20 :   Matrix<Complex> V(nSDB,nChan,0.0),M(nSDB,nChan,0.0);
    1752          20 :   Matrix<Float> Wt(nSDB,nChan,0.0); // ,sig(nSDB,nChan,0.0);
    1753          20 :   Matrix<Bool> mask(nSDB,nChan,false);
    1754             : 
    1755          20 :   mask.set(false);
    1756          20 :   Complex v(0.0),m(0.0);
    1757          20 :   Float wt0(0.0);
    1758             :   
    1759         180 :   for (Int isdb=0;isdb<nSDB;++isdb) {
    1760         160 :     SolveDataBuffer& sdb(sdbs(isdb));
    1761             : 
    1762        7360 :     for (Int irow=0;irow<sdb.nRows();++irow) {
    1763       14400 :       if (!sdb.flagRow()(irow) &&
    1764        7200 :           sdb.antenna1()(irow)!=sdb.antenna2()(irow)) {
    1765             :         
    1766       64800 :         for (Int ich=0;ich<nChan;++ich) {
    1767       57600 :           if (!sdb.flagCube()(1,ich,irow)) {
    1768       57600 :             wt0=sdb.weightSpectrum()(1,ich,irow);
    1769       57600 :             v=sdb.visCubeCorrected()(1,ich,irow);
    1770       57600 :             m=sdb.visCubeModel()(1,ich,irow);
    1771       57600 :             V(isdb,ich)+=Complex(wt0*v);
    1772       57600 :             M(isdb,ich)+=Complex(wt0*m);
    1773       57600 :             Wt(isdb,ich)+=wt0;
    1774             :           }
    1775       57600 :           if (!sdb.flagCube()(2,ich,irow)) {
    1776       57600 :             wt0=sdb.weightSpectrum()(2,ich,irow);
    1777       57600 :             v=conj(sdb.visCubeCorrected()(2,ich,irow));
    1778       57600 :             m=conj(sdb.visCubeModel()(2,ich,irow));
    1779       57600 :             V(isdb,ich)+=Complex(wt0*v);
    1780       57600 :             M(isdb,ich)+=Complex(wt0*m);
    1781       57600 :             Wt(isdb,ich)+=wt0;
    1782             :           }
    1783             :         }
    1784             :       }
    1785             :     }
    1786             :   }
    1787             : 
    1788             :   // Normalize data by accumulated weights
    1789         180 :   for (Int isdb=0;isdb<nSDB;++isdb) {
    1790        1440 :     for (Int ich=0;ich<nChan;++ich) {
    1791        1280 :       if (Wt(isdb,ich)>0.0) {
    1792        1280 :         V(isdb,ich)/=Wt(isdb,ich);
    1793        1280 :         M(isdb,ich)/=Wt(isdb,ich);
    1794        1280 :         mask(isdb,ich)=true;
    1795             :       }
    1796             :     }
    1797             :   }
    1798             : 
    1799             :   // Now solve for cross-hand phase in each channel
    1800          20 :   Vector<Complex> Cph(nChan);
    1801          20 :   Cph.set(Complex(1.0,0.0));
    1802         180 :   for (Int ich=0;ich<nChan;++ich) {
    1803             : 
    1804         160 :     if (sum(Wt.column(ich))>0.0) {
    1805             : 
    1806         160 :       LSQFit fit(2,LSQComplex());
    1807         160 :       Vector<Complex> ce(2);
    1808         160 :       ce(0)=Complex(1.0);
    1809             :       
    1810        1440 :       for (int isdb=0;isdb<nSDB;++isdb) {
    1811        1280 :         ce(1)=M(isdb,ich);
    1812        1280 :         fit.makeNorm(ce.data(),Wt(isdb,ich),V(isdb,ich),LSQFit::COMPLEX);
    1813             :       }
    1814             : 
    1815             :       uInt rank;
    1816         160 :       Bool ok = fit.invert(rank);
    1817         160 :       DComplex sol[2];
    1818         160 :       if (ok)
    1819         160 :         fit.solve(sol);
    1820             :       else
    1821           0 :         throw(AipsError("Source polarization solution is singular; try solving for D-terms only."));
    1822             : 
    1823             :       //cout << "ich=" << ich << "  sol = " << sol[0] << "," << sol[1] << "   P=" << abs(sol[1]) << "   X=" << arg(sol[1])*180/C::pi << endl;
    1824             : 
    1825         160 :       Cph[ich]=sol[1]/abs(sol[1]);   // phase-only as complex number
    1826             : 
    1827             :       // Set all antennas with this X-Y phase (as a complex number)
    1828         160 :       solveCPar()(Slice(0,1,1),Slice(ich,1,1),Slice())=Cph(ich);
    1829         160 :       solveParOK()(Slice(),Slice(ich,1,1),Slice())=true;
    1830         160 :     }
    1831             :     else {
    1832             :       // In sufficient data, phase=0.0, flagged
    1833           0 :       solveCPar()(Slice(0,1,1),Slice(ich,1,1),Slice())=Complex(1.0);
    1834           0 :       solveParOK()(Slice(),Slice(ich,1,1),Slice())=false;
    1835             :     }
    1836             :   }  // ICH
    1837             : 
    1838          20 :   if (ntrue(solveParOK())>0) {
    1839          20 :     Float ang=arg(sum(solveCPar()(solveParOK()))/Float(ntrue(solveParOK())))*180.0/C::pi;
    1840             : 
    1841          40 :     logSink() << " Fld = " << msmc().fieldName(currField())
    1842             :               << ", Spw = " << thisSpw
    1843             :               << " (ich=" << nChan/2 << "/" << nChan << "): "  // << endl
    1844          20 :               << " CROSS-HAND PHASE = " << arg(Cph[nChan/2])*180.0/C::pi << " deg."
    1845             :               << " (Mean = " << ang << ")"
    1846          60 :               << LogIO::POST;
    1847             :   }
    1848             :   else
    1849           0 :     logSink() << " Fld = " << msmc().fieldName(currField())
    1850             :               << ", Spw = " << thisSpw
    1851             :               << " (ich=" << nChan/2 << "/" << nChan << "): " << endl
    1852             :               << " CROSS-HAND PHASE was not determined (insufficient data)."
    1853           0 :               << LogIO::POST;
    1854             : 
    1855             :   if (false)
    1856             :     cout << endl 
    1857             :          << "Spw = " << thisSpw
    1858             :          << " (ich=" << nChan/2 << "/" << nChan << "): " // << endl
    1859             :          << " X-Y phase = " << arg(Cph[nChan/2])*180.0/C::pi << " deg." << endl;
    1860             :       
    1861             : 
    1862             : 
    1863             :   // Now solve for QU (chan-aved)
    1864             :   {
    1865          20 :     LSQFit fit(2,LSQComplex());
    1866          20 :     Vector<Complex> ce(2);
    1867          20 :     ce(0)=Complex(1.0);
    1868             :       
    1869          20 :     Vector<Complex> V2(nSDB,Complex(0.0)), M2(nSDB,Complex(0.0));
    1870          20 :     Vector<Float> Wt2(nSDB,0.0f);
    1871         180 :     for (int isdb=0;isdb<nSDB;++isdb) {
    1872             :       // Accumulate along channel axis
    1873        1440 :       for (Int ich=0;ich<nChan;++ich) {
    1874        1280 :         if (Wt(isdb,ich)>0.0f) {
    1875        1280 :           V2[isdb]+=(Wt(isdb,ich)*V(isdb,ich)/Cph[ich]);   // include cross-hand phase correction
    1876        1280 :           M2[isdb]+=(Wt(isdb,ich)*M(isdb,ich)/abs(M(isdb,ich)));   // Divide by user-supplied |P|
    1877        1280 :           Wt2[isdb]+=Wt(isdb,ich);
    1878             :         }
    1879             :       }
    1880             :       // finalize averages
    1881         160 :       if (Wt2[isdb]>0.0) {
    1882         160 :         V2[isdb]/=Wt2[isdb];
    1883         160 :         M2[isdb]/=Wt2[isdb];
    1884             :       }
    1885             :       
    1886         160 :       ce(1)=M2(isdb);
    1887         160 :       fit.makeNorm(ce.data(),Wt2(isdb),V2(isdb),LSQFit::COMPLEX);
    1888             : 
    1889             :     }
    1890             : 
    1891             :     uInt rank;
    1892          20 :     Bool ok = fit.invert(rank);
    1893          20 :     DComplex sol[2];
    1894          20 :     if (ok)
    1895          20 :       fit.solve(sol);
    1896             :     else
    1897           0 :       throw(AipsError("Source polarization solution is singular; try solving for D-terms only."));
    1898             : 
    1899             :     //cout << "sol = " << sol[0] << "," << sol[1] << "   P=" << abs(sol[1]) << "   X=" << arg(sol[1])*90.0/C::pi << endl;
    1900             : 
    1901          20 :     srcPolPar().resize(2);
    1902          20 :     srcPolPar()(0)=real(sol[1]);
    1903          20 :     srcPolPar()(1)=imag(sol[1]);
    1904             :         
    1905          20 :     QU_(0,thisSpw) = real(sol[1]);
    1906          20 :     QU_(1,thisSpw) = imag(sol[1]);
    1907             : 
    1908          20 :     Float &Q(QU_(0,thisSpw)), &U(QU_(1,thisSpw));
    1909             : 
    1910             :     logSink() << " Fractional Poln: "
    1911          20 :               << "Q = " << Q << ", "
    1912          20 :               << "U = " << U << "; "
    1913          20 :               << "P = " << sqrt(Q*Q+U*U) << ", "
    1914          40 :               << "X = " << atan2(U,Q)*90.0/C::pi << "deg."
    1915          20 :               << LogIO::POST;
    1916             :     logSink() << " Net (over baselines) instrumental polarization: " 
    1917          20 :               << abs(sol[0]) << LogIO::POST;
    1918             : 
    1919             : 
    1920          20 :   } // Circ QU solve
    1921             : 
    1922          20 :   } // basis clauses
    1923             :   else {
    1924             : 
    1925           0 :     throw(AipsError("Cannot solve for cross-hand phase, don't know basis"));
    1926             :   }
    1927             : 
    1928             :   // Add this QU result to the QURec_
    1929             : 
    1930          21 :   Vector<Float> fStokes(4,0.0f);  // Fractional Stokes vector
    1931          21 :   fStokes(0)=1.0;
    1932          21 :   fStokes(1)=QU_(0,thisSpw);  // Q
    1933          21 :   fStokes(2)=QU_(1,thisSpw);  // U
    1934             : 
    1935          21 :   ostringstream os;
    1936          21 :   os << "Spw" << thisSpw;
    1937          21 :   String spwName(os);
    1938          21 :   String fldName(msmc().fieldName(currField()));
    1939          21 :   Record fld;
    1940          21 :   if (QURec_.isDefined(fldName))
    1941          15 :     fld=QURec_.asRecord(fldName);
    1942          21 :   fld.define(spwName,fStokes);
    1943             :   
    1944             :   // Add average
    1945          21 :   Int nspw=fld.nfields();  // the number of spws recorded in the record so far
    1946          21 :   Vector<Float> QUave(4,0.0f);
    1947          21 :   Int nave(0);
    1948          87 :   for (Int ispw=0;ispw<nspw;++ispw) {
    1949          66 :     String fieldname=fld.name(ispw);
    1950          66 :     if (fieldname!="SpwAve") {
    1951          51 :       QUave+=fld.asArrayFloat(ispw);
    1952          51 :       ++nave;
    1953             :     }
    1954          66 :   }
    1955          21 :   QUave/=Float(nave);
    1956          21 :   fld.define("SpwAve",QUave);
    1957             : 
    1958             :   // Replace this fld's record in QURec_
    1959          21 :   QURec_.defineRecord(fldName,fld);
    1960             : 
    1961          21 : }
    1962             : 
    1963           6 : void XparangJones::globalPostSolveTinker() {
    1964             : 
    1965             :     // Add QU info the caltable keywords
    1966           6 :     TableRecord& tr(ct_->rwKeywordSet());
    1967           6 :     Record qu;
    1968           6 :     qu.define("QU",QU_);
    1969           6 :     tr.defineRecord("QU",qu);
    1970             : 
    1971           6 : }
    1972             : 
    1973           6 : Record XparangJones::solveActionRec() {
    1974             : 
    1975             :   // Add QU info to QURec_, so Calibrater can extract and return
    1976           6 :   Record sAR;
    1977           6 :   sAR=SolvableVisCal::solveActionRec();
    1978           6 :   sAR.defineRecord("fStokes",QURec_);
    1979             : 
    1980           6 :   return sAR;
    1981             : 
    1982           0 : }
    1983             : 
    1984             : // **********************************************************
    1985             : //  XfparangJones Implementations
    1986             : //
    1987             : 
    1988             : // Constructor
    1989           0 : XfparangJones::XfparangJones(VisSet& vs)  :
    1990             :   VisCal(vs),             // virtual base
    1991             :   VisMueller(vs),         // virtual base
    1992           0 :   XparangJones(vs)        // immediate parent
    1993             : {
    1994           0 :   if (prtlev()>2) cout << "Xfparangf::Xfparang(vs)" << endl;
    1995           0 : }
    1996             : 
    1997           0 : XfparangJones::XfparangJones(String msname,Int MSnAnt,Int MSnSpw) :
    1998             :   VisCal(msname,MSnAnt,MSnSpw),             // virtual base
    1999             :   VisMueller(msname,MSnAnt,MSnSpw),         // virtual base
    2000           0 :   XparangJones(msname,MSnAnt,MSnSpw)        // immediate parent
    2001             : {
    2002           0 :   if (prtlev()>2) cout << "Xfparang::Xfparang(msname,MSnAnt,MSnSpw)" << endl;
    2003           0 : }
    2004             : 
    2005           7 : XfparangJones::XfparangJones(const MSMetaInfoForCal& msmc) :
    2006             :   VisCal(msmc),             // virtual base
    2007             :   VisMueller(msmc),         // virtual base
    2008           7 :   XparangJones(msmc)        // immediate parent
    2009             : {
    2010           7 :   if (prtlev()>2) cout << "Xfparang::Xfparang(msmc)" << endl;
    2011           7 : }
    2012             : 
    2013           0 : XfparangJones::XfparangJones(const Int& nAnt) :
    2014             :   VisCal(nAnt), 
    2015             :   VisMueller(nAnt),
    2016           0 :   XparangJones(nAnt)
    2017             : {
    2018           0 :   if (prtlev()>2) cout << "Xfparang::Xfparang(nAnt)" << endl;
    2019           0 : }
    2020             : 
    2021          14 : XfparangJones::~XfparangJones() {
    2022           7 :   if (prtlev()>2) cout << "Xfparang::~Xfparang()" << endl;
    2023          14 : }
    2024             : 
    2025             : 
    2026             : // **********************************************************
    2027             : //  PosAngJones Implementations
    2028             : //
    2029             : 
    2030           0 : PosAngJones::PosAngJones(VisSet& vs) :
    2031             :   VisCal(vs),             // virtual base
    2032             :   VisMueller(vs),         // virtual base
    2033             :   XJones(vs),             // immediate parent
    2034           0 :   jonestype_(Jones::Diagonal)
    2035             : {
    2036           0 :   if (prtlev()>2) cout << "PosAng::PosAng(vs)" << endl;
    2037           0 : }
    2038             : 
    2039           0 : PosAngJones::PosAngJones(String msname,Int MSnAnt,Int MSnSpw) :
    2040             :   VisCal(msname,MSnAnt,MSnSpw),             // virtual base
    2041             :   VisMueller(msname,MSnAnt,MSnSpw),         // virtual base
    2042             :   XJones(msname,MSnAnt,MSnSpw),             // immediate parent
    2043           0 :   jonestype_(Jones::Diagonal)
    2044             : {
    2045           0 :   if (prtlev()>2) cout << "PosAng::PosAng(msname,MSnAnt,MSnSpw)" << endl;
    2046           0 : }
    2047             : 
    2048           8 : PosAngJones::PosAngJones(const MSMetaInfoForCal& msmc) :
    2049             :   VisCal(msmc),             // virtual base
    2050             :   VisMueller(msmc),         // virtual base
    2051             :   XJones(msmc),             // immediate parent
    2052           8 :   jonestype_(Jones::Diagonal)
    2053             : {
    2054           8 :   if (prtlev()>2) cout << "PosAng::PosAng(msmc)" << endl;
    2055           8 : }
    2056             : 
    2057           0 : PosAngJones::PosAngJones(const Int& nAnt) :
    2058             :   VisCal(nAnt), 
    2059             :   VisMueller(nAnt),
    2060             :   XJones(nAnt),
    2061           0 :   jonestype_(Jones::Diagonal)
    2062             : {
    2063           0 :   if (prtlev()>2) cout << "PosAng::PosAng(nAnt)" << endl;
    2064           0 : }
    2065             : 
    2066          16 : PosAngJones::~PosAngJones() {
    2067           8 :   if (prtlev()>2) cout << "PosAng::~PosAng()" << endl;
    2068          16 : }
    2069             : 
    2070           1 : void PosAngJones::setApply(const Record& apply) {
    2071             : 
    2072           1 :   SolvableVisCal::setApply(apply);
    2073             : 
    2074             :   // Force calwt to false 
    2075           1 :   calWt()=false;
    2076             : 
    2077           1 : }
    2078             : 
    2079           7 : void PosAngJones::setSolve(const Record& solvepar) {
    2080             : 
    2081           7 :   SolvableVisCal::setSolve(solvepar);
    2082             : 
    2083             :   // Force calwt to false 
    2084           7 :   calWt()=false;
    2085             : 
    2086             :   // For X insist preavg is meaningful (5 minutes or user-supplied)
    2087           7 :   if (preavg()<0.0)
    2088           0 :     preavg()=30.0;
    2089             : 
    2090             :   // Force refant to none (==-1), because it is meaningless to
    2091             :   //  apply a refant to an X solution
    2092           7 :   if (refant()>-1) {
    2093             :     logSink() << ".   (Ignoring specified refant for " 
    2094           0 :               << typeName() << " solve.)"
    2095           0 :               << LogIO::POST;
    2096           0 :     refantlist().resize(1);
    2097           0 :     refantlist()(0)=-1;
    2098             :   }
    2099             : 
    2100           7 : }
    2101             : 
    2102             : // PJones needs to know pol basis and feed pa
    2103           0 : void PosAngJones::syncMeta(const VisBuffer& vb) {
    2104             : 
    2105             :   // Call parent (sets currTime())
    2106           0 :   VisJones::syncMeta(vb);
    2107             : 
    2108             :   // Basis
    2109           0 :   if (vb.corrType()(0)==5)         // Circulars
    2110           0 :     jonestype_=Jones::Diagonal;
    2111           0 :   else if (vb.corrType()(0)==9)    // Linears
    2112           0 :     jonestype_=Jones::General;
    2113             : 
    2114           0 : }
    2115             : 
    2116             : // PJones needs to know pol basis and feed pa
    2117         928 : void PosAngJones::syncMeta2(const vi::VisBuffer2& vb) {
    2118             : 
    2119             :   // Call parent (sets currTime())
    2120         928 :   VisJones::syncMeta2(vb);
    2121             : 
    2122             :   // Basis
    2123         928 :   if (vb.correlationTypes()(0)==5)         // Circulars
    2124         928 :     jonestype_=Jones::Diagonal;
    2125           0 :   else if (vb.correlationTypes()(0)==9)    // Linears
    2126           0 :     jonestype_=Jones::General;
    2127             : 
    2128         928 : }
    2129             : 
    2130           4 : void PosAngJones::calcAllJones() {
    2131             : 
    2132             :   //if (prtlev()>6) cout << "       PosAng::calcAllJones()" << endl;
    2133             : 
    2134             :   // Should handle OK flags in this method, and only
    2135             :   //  do Jones calc if OK
    2136             : 
    2137           4 :   Vector<Complex> oneJones;
    2138           4 :   Vector<Bool> oneJOK;
    2139           4 :   Vector<Float> onePar;
    2140           4 :   Vector<Bool> onePOK;
    2141             : 
    2142           4 :   ArrayIterator<Complex> Jiter(currJElem(),1);
    2143           4 :   ArrayIterator<Bool>    JOKiter(currJElemOK(),1);
    2144           4 :   ArrayIterator<Float>   Piter(currRPar(),1);
    2145           4 :   ArrayIterator<Bool>    POKiter(currParOK(),1);
    2146             : 
    2147          44 :   for (Int iant=0; iant<nAnt(); iant++) {
    2148             : 
    2149         360 :     for (Int ich=0; ich<nChanMat(); ich++) {
    2150             :       
    2151         320 :       oneJones.reference(Jiter.array());
    2152         320 :       oneJOK.reference(JOKiter.array());
    2153         320 :       onePar.reference(Piter.array());
    2154         320 :       onePOK.reference(POKiter.array());
    2155             : 
    2156             :       // Calculate the Jones matrix
    2157         320 :       calcOneJonesRPar(oneJones,oneJOK,onePar,onePOK);
    2158             : 
    2159             :       // Advance iterators
    2160         320 :       Jiter.next();
    2161         320 :       JOKiter.next();
    2162         320 :       if (freqDepPar()) {
    2163         320 :         Piter.next();
    2164         320 :         POKiter.next();
    2165             :       }
    2166             : 
    2167             :     }
    2168             :     // Step to next antenns's pars if we didn't in channel loop
    2169          40 :     if (!freqDepPar()) {
    2170           0 :       Piter.next();
    2171           0 :       POKiter.next();
    2172             :     }
    2173             :   }
    2174           4 : }
    2175             : 
    2176             : // Calculate a single Jones matrix by some means from parameters
    2177         320 : void PosAngJones::calcOneJonesRPar(Vector<Complex>& mat, Vector<Bool>& mOk,
    2178             :                                    const Vector<Float>& par, const Vector<Bool>& pOk ) {
    2179             : 
    2180         320 :   if (prtlev()>10) cout << "       PosAng::calcOneJones()" << endl;
    2181             : 
    2182         320 :   if (pOk(0)) {
    2183             : 
    2184         320 :     switch (jonesType()) {
    2185             :       // Circular version:
    2186         320 :     case Jones::Diagonal: {
    2187         320 :       mat(0)=Complex(cos(par(0)), sin(par(0)));
    2188         320 :       mat(1)=conj(mat(0));       
    2189         320 :       mOk=true;
    2190         320 :       break;
    2191             :     }
    2192             :       // Linear version:
    2193           0 :     case Jones::General: {
    2194           0 :       const Float a=-par(0);
    2195           0 :       mat(0)=mat(3)=cos(a);
    2196           0 :       mat(1)=sin(a);
    2197           0 :       mat(2)=-mat(1);
    2198           0 :       mOk=true;
    2199           0 :       break;
    2200             :     }
    2201           0 :     default:
    2202           0 :       throw(AipsError("PosAngJones doesn't know if it is Diag (Circ) or General (Lin)"));
    2203             :       break;
    2204             : 
    2205             :     }
    2206             : 
    2207             :   }
    2208         320 : }
    2209             : 
    2210             : 
    2211          22 : void PosAngJones::solveOne(SDBList& sdbs) {
    2212             : 
    2213             :   // This just a simple average of the cross-hand
    2214             :   //  visbilities...
    2215             : 
    2216          22 :   Int nSDB=sdbs.nSDB();
    2217             : 
    2218             :   // We are actually solving for all channels simultaneously
    2219          22 :   solveRPar().reference(solveAllRPar());
    2220          22 :   solveParOK().reference(solveAllParOK());
    2221          22 :   solveParErr().reference(solveAllParErr());
    2222          22 :   solveParSNR().reference(solveAllParSNR());
    2223             :   
    2224             :   // Fill solveCPar() with 1, nominally, and flagged
    2225          22 :   solveRPar()=0.0;
    2226          22 :   solveParOK()=false;
    2227             : 
    2228          22 :   Int nChan=sdbs.nChannels();
    2229             : 
    2230          22 :   String polBasis=sdbs.polBasis();
    2231             : 
    2232          22 :   Complex d,md;
    2233             :   Float wt;
    2234          22 :   Vector<DComplex> RL(nChan,0.0);
    2235          22 :   Vector<Double> sumwt(nChan,0.0);
    2236         230 :   for (Int isdb=0;isdb<nSDB;++isdb) {
    2237         208 :     SolveDataBuffer& sdb(sdbs(isdb));
    2238        9568 :     for (Int irow=0;irow<sdb.nRows();++irow) {
    2239       18720 :       if (!sdb.flagRow()(irow) &&
    2240        9360 :           sdb.antenna1()(irow)!=sdb.antenna2()(irow)) {
    2241             :         
    2242       84240 :         for (Int ich=0;ich<nChan;++ich) {
    2243             : 
    2244       74880 :           if (polBasis=="CIRC") {
    2245      115200 :             if (!sdb.flagCube()(1,ich,irow) &&
    2246       57600 :                 !sdb.flagCube()(2,ich,irow)) {
    2247             :             
    2248             :               // A common weight for both crosshands
    2249             :               // TBD: we should probably consider this carefully...
    2250             :               //  (also in D::guessPar...)
    2251       57600 :               wt=Double(sdb.weightSpectrum()(1,ich,irow)+
    2252       57600 :                         sdb.weightSpectrum()(2,ich,irow))/2.0;
    2253             :               
    2254       57600 :               if (wt>0.0) {
    2255             :                 // Cross-hands only
    2256      172800 :                 for (Int icorr=1;icorr<3;++icorr) {
    2257      115200 :                   d=sdb.visCubeCorrected()(icorr,ich,irow);
    2258      115200 :                   md=sdb.visCubeModel()(icorr,ich,irow);
    2259             :                   
    2260      115200 :                   if (abs(d)>0.0 && abs(md)>0.0) {
    2261             :                     
    2262      115200 :                     if (icorr==1) 
    2263       57600 :                       RL(ich)+=DComplex(Complex(wt)*d/md);
    2264             :                     else
    2265       57600 :                       RL(ich)+=conj(DComplex(Complex(wt)*d/md));
    2266             :                     
    2267      115200 :                     sumwt(ich)+=Double(wt);
    2268             :                     
    2269             :                   } // abs(d)>0
    2270             :                 } // icorr
    2271             :               } // wt>0
    2272             :             } // !flag
    2273             :           } // CIRC
    2274       17280 :           else if (polBasis=="LIN") {
    2275             :             // Need all 4 correlations
    2276       17280 :             if (!sdb.flagCube()(0,ich,irow) &&
    2277       17280 :                 !sdb.flagCube()(1,ich,irow) &&
    2278       51840 :                 !sdb.flagCube()(2,ich,irow) &&
    2279       17280 :                 !sdb.flagCube()(3,ich,irow)) {
    2280             :             
    2281             :               // A common weight  (correct?)
    2282       17280 :               wt=Double(sdb.weightSpectrum()(0,ich,irow)+
    2283       17280 :                         sdb.weightSpectrum()(1,ich,irow)+
    2284       17280 :                         sdb.weightSpectrum()(2,ich,irow)+
    2285       17280 :                         sdb.weightSpectrum()(3,ich,irow))/4.0;
    2286             :               
    2287       17280 :               if (wt>0.0) {
    2288             : 
    2289       17280 :                 Complex oneI(0.0,1.0);
    2290       17280 :                 const Cube<Complex>& vCC(sdb.visCubeCorrected());
    2291       17280 :                 Complex Q( (vCC(0,ich,irow)-vCC(3,ich,irow))/2.0 );
    2292       17280 :                 Complex U( (vCC(1,ich,irow)+vCC(2,ich,irow))/2.0 );
    2293       17280 :                 Complex d(Q+oneI*U);
    2294             :                 
    2295       17280 :                 const Cube<Complex> vCM(sdb.visCubeModel());
    2296       17280 :                 Complex Qm( (vCM(0,ich,irow)-vCM(3,ich,irow))/2.0 );
    2297       17280 :                 Complex Um( (vCM(1,ich,irow)+vCM(2,ich,irow))/2.0 );
    2298       17280 :                 Complex md(Qm+oneI*Um);
    2299             : 
    2300       17280 :                 if (abs(d)>0.0 && abs(md)>0.0) {
    2301       17280 :                   RL(ich)+=DComplex(Complex(wt)*d/md);
    2302       17280 :                   sumwt(ich)+=Double(wt);
    2303             :                 } // abs(d)>0
    2304             : 
    2305       17280 :               } // wt>0
    2306             :             } // !flag
    2307             :           } // LIN
    2308             : 
    2309             :         } // ich
    2310             :       } // !flagRow
    2311             :     } // row
    2312             :   } // isdb
    2313             : 
    2314             :   //  cout << "spw = " << currSpw() << endl;
    2315             : 
    2316             :   // Record results
    2317         198 :   for (Int ich=0;ich<nChan;++ich) {
    2318             :   
    2319             :     // Normalize to unit amplitude
    2320             :     //  (note that the phase result is insensitive to sumwt)
    2321         176 :     Double amp=abs(RL(ich));
    2322             :     // For now, all antennas get the same solution
    2323         176 :     IPosition blc(3,0,ich,0);
    2324         176 :     IPosition trc(3,0,ich,nElem()-1);
    2325         176 :     if (sumwt(ich)>0 && amp>0.0) {
    2326         176 :       RL(ich)/=amp;
    2327         176 :       solveRPar()(blc,trc)=arg(RL(ich))/2.0;
    2328         176 :       solveParOK()(blc,trc)=true;
    2329             :     }
    2330             :     else
    2331           0 :       RL(ich)=0.0;
    2332         176 :   }
    2333             : 
    2334          22 :   if (ntrue(solveParOK())>0) {
    2335          22 :     Float ang=arg(sum(RL))*90.0/C::pi;
    2336             :     logSink() << "Mean POSITION ANGLE OFFSET solution for " 
    2337          44 :               << msmc().fieldName(currField())
    2338          22 :               << " (spw = " << currSpw() << ") = "
    2339             :               << ang
    2340             :               << " deg."
    2341          44 :               << LogIO::POST;
    2342             :   }
    2343             :   else
    2344             :     logSink() << "POSITION ANGLE OFFSET solution for " 
    2345           0 :               << msmc().fieldName(currField())
    2346           0 :               << " (spw = " << currSpw() << ") "
    2347             :               << " was not determined (insufficient data)."
    2348           0 :               << LogIO::POST;
    2349             :   
    2350          22 : }
    2351             : 
    2352             : 
    2353             : 
    2354             : 
    2355             : // **********************************************************
    2356             : //  GlinXphJones Implementations
    2357             : //
    2358             : 
    2359           0 : GlinXphJones::GlinXphJones(VisSet& vs) :
    2360             :   VisCal(vs),             // virtual base
    2361             :   VisMueller(vs),         // virtual base
    2362             :   GJones(vs),             // immediate parent
    2363           0 :   QU_()
    2364             : {
    2365           0 :   if (prtlev()>2) cout << "GlinXph::GlinXph(vs)" << endl;
    2366           0 : }
    2367             : 
    2368           0 : GlinXphJones::GlinXphJones(String msname,Int MSnAnt,Int MSnSpw) :
    2369             :   VisCal(msname,MSnAnt,MSnSpw),             // virtual base
    2370             :   VisMueller(msname,MSnAnt,MSnSpw),         // virtual base
    2371             :   GJones(msname,MSnAnt,MSnSpw),             // immediate parent
    2372           0 :   QU_()
    2373             : {
    2374           0 :   if (prtlev()>2) cout << "GlinXph::GlinXph(msname,MSnAnt,MSnSpw)" << endl;
    2375           0 : }
    2376             : 
    2377           0 : GlinXphJones::GlinXphJones(const MSMetaInfoForCal& msmc) :
    2378             :   VisCal(msmc),             // virtual base
    2379             :   VisMueller(msmc),         // virtual base
    2380             :   GJones(msmc),             // immediate parent
    2381           0 :   QU_()
    2382             : {
    2383           0 :   if (prtlev()>2) cout << "GlinXph::GlinXph(msmc)" << endl;
    2384           0 : }
    2385             : 
    2386           0 : GlinXphJones::GlinXphJones(const Int& nAnt) :
    2387             :   VisCal(nAnt), 
    2388             :   VisMueller(nAnt),
    2389             :   GJones(nAnt),
    2390           0 :   QU_()
    2391             : {
    2392           0 :   if (prtlev()>2) cout << "GlinXph::GlinXph(nAnt)" << endl;
    2393           0 : }
    2394             : 
    2395           0 : GlinXphJones::~GlinXphJones() {
    2396           0 :   if (prtlev()>2) cout << "GlinXph::~GlinXph()" << endl;
    2397           0 : }
    2398             : 
    2399           0 : void GlinXphJones::setApply(const Record& apply) {
    2400             : 
    2401           0 :   GJones::setApply(apply);
    2402             : 
    2403             :   // Force calwt to false 
    2404           0 :   calWt()=false;
    2405             : 
    2406           0 : }
    2407             : 
    2408             : 
    2409             : 
    2410           0 : void GlinXphJones::selfGatherAndSolve(VisSet& vs, VisEquation& ve) {
    2411             : 
    2412           0 :   if (prtlev()>4) cout << "   GlnXph::selfGatherAndSolve(ve)" << endl;
    2413             : 
    2414             :   // Inform logger/history
    2415           0 :   logSink() << "Solving for " << typeName()
    2416           0 :             << LogIO::POST;
    2417             : 
    2418             :   // Initialize the svc according to current VisSet
    2419             :   //  (this counts intervals, sizes CalSet)
    2420           0 :   Vector<Int> nChunkPerSol;
    2421           0 :   Int nSol = sizeUpSolve(vs,nChunkPerSol);
    2422             : 
    2423             :   // Create the Caltable
    2424           0 :   createMemCalTable();
    2425             : 
    2426             :   // The iterator, VisBuffer
    2427           0 :   VisIter& vi(vs.iter());
    2428           0 :   VisBuffer vb(vi);
    2429             : 
    2430             :   //  cout << "nSol = " << nSol << endl;
    2431             :   //  cout << "nChunkPerSol = " << nChunkPerSol << endl;
    2432             : 
    2433           0 :   Int nGood(0);
    2434           0 :   vi.originChunks();
    2435           0 :   for (Int isol=0;isol<nSol && vi.moreChunks();++isol) {
    2436             : 
    2437             :     // Arrange to accumulate
    2438           0 :     VisBuffAccumulator vba(nAnt(),preavg(),false);
    2439             :     
    2440           0 :     for (Int ichunk=0;ichunk<nChunkPerSol(isol);++ichunk) {
    2441             : 
    2442             :       // Current _chunk_'s spw
    2443           0 :       Int spw(vi.spectralWindow());
    2444             : 
    2445             :       // Abort if we encounter a spw for which a priori cal not available
    2446           0 :       if (!ve.spwOK(spw))
    2447           0 :         throw(AipsError("Pre-applied calibration not available for at least 1 spw. Check spw selection carefully."));
    2448             : 
    2449             : 
    2450             :       // Collapse each timestamp in this chunk according to VisEq
    2451             :       //  with calibration and averaging
    2452           0 :       for (vi.origin(); vi.more(); vi++) {
    2453             : 
    2454             :         // Force read of the field Id
    2455           0 :         vb.fieldId();
    2456             : 
    2457             :         // This forces the data/model/wt I/O, and applies
    2458             :         //   any prior calibrations
    2459           0 :         ve.collapse(vb);
    2460             : 
    2461             :         // If permitted/required by solvable component, normalize
    2462           0 :         if (normalizable())
    2463           0 :           vb.normalize();
    2464             : 
    2465             :         // If this solve not freqdep, and channels not averaged yet, do so
    2466           0 :         if (!freqDepMat() && vb.nChannel()>1)
    2467           0 :           vb.freqAveCubes();
    2468             : 
    2469             :         // Accumulate collapsed vb in a time average
    2470           0 :         vba.accumulate(vb);
    2471             :       }
    2472             :       // Advance the VisIter, if possible
    2473           0 :       if (vi.moreChunks()) vi.nextChunk();
    2474             : 
    2475             :     }
    2476             : 
    2477             :     // Finalize the averged VisBuffer
    2478           0 :     vba.finalizeAverage();
    2479             : 
    2480             :     // The VisBuffer to solve with
    2481           0 :     VisBuffer& svb(vba.aveVisBuff());
    2482             : 
    2483             :     // Establish meta-data for this interval
    2484             :     //  (some of this may be used _during_ solve)
    2485             :     //  (this sets currSpw() in the SVC)
    2486           0 :     Bool vbOk=syncSolveMeta(svb,-1);
    2487             : 
    2488           0 :     if (vbOk && svb.nRow()>0) {
    2489             : 
    2490             :       // solve for the X-Y phase term in the current VB
    2491           0 :       solveOneVB(svb);
    2492             : 
    2493           0 :       nGood++;
    2494             :     }
    2495             : 
    2496           0 :     keepNCT();
    2497             :     
    2498           0 :   }
    2499             :   
    2500             :   logSink() << "  Found good "
    2501           0 :             << typeName() << " solutions in "
    2502             :             << nGood << " intervals."
    2503           0 :             << LogIO::POST;
    2504             : 
    2505             :   // Store whole of result in a caltable
    2506           0 :   if (nGood==0)
    2507             :     logSink() << "No output calibration table written."
    2508           0 :               << LogIO::POST;
    2509             :   else {
    2510             : 
    2511             :     // Do global post-solve tinkering (e.g., phase-only, normalization, etc.)
    2512           0 :     globalPostSolveTinker();
    2513             : 
    2514             :     // write the table
    2515           0 :     storeNCT();
    2516             :   }
    2517             : 
    2518           0 : }
    2519             : 
    2520             : // Handle trivial vbga
    2521           0 : void GlinXphJones::selfSolveOne(VisBuffGroupAcc& vbga) {
    2522             : 
    2523             :   // Expecting only on VB in the vbga (with many times)
    2524           0 :   if (vbga.nBuf()!=1)
    2525           0 :     throw(AipsError("GlinXphJones can't process multi-vb vbga."));
    2526             : 
    2527             :   // Call single-VB specialized solver with the one vb
    2528           0 :   this->solveOneVB(vbga(0));
    2529             : 
    2530           0 : }
    2531             : 
    2532             : // SDBList (VI2) version
    2533           0 : void GlinXphJones::selfSolveOne(SDBList& sdbs) {
    2534             : 
    2535             :   // Expecting multiple SDBs (esp. in time)
    2536           0 :   if (sdbs.nSDB()==1)
    2537           0 :     throw(AipsError("GlinXphJones needs multiple SDBs"));
    2538             : 
    2539             :   // Call single-VB specialized solver with the one vb
    2540           0 :   this->solveOne(sdbs);
    2541             : 
    2542           0 : }
    2543             : 
    2544             : // Solve for the X-Y phase from the cross-hand's slope in R/I
    2545           0 : void GlinXphJones::solveOneVB(const VisBuffer& vb) {
    2546             : 
    2547             :   // ensure
    2548           0 :   if (QU_.shape()!=IPosition(2,2,nSpw())) {
    2549           0 :     QU_.resize(2,nSpw());
    2550           0 :     QU_.set(0.0);
    2551             :   }
    2552             : 
    2553           0 :   Int thisSpw=spwMap()(vb.spectralWindow());
    2554             :   
    2555             :   // We are actually solving for all channels simultaneously
    2556           0 :   solveCPar().reference(solveAllCPar());
    2557           0 :   solveParOK().reference(solveAllParOK());
    2558           0 :   solveParErr().reference(solveAllParErr());
    2559           0 :   solveParSNR().reference(solveAllParSNR());
    2560             :   
    2561             :   // Fill solveCPar() with 1, nominally, and flagged
    2562           0 :   solveCPar()=Complex(1.0);
    2563           0 :   solveParOK()=false;
    2564             : 
    2565           0 :   Int nChan=vb.nChannel();
    2566             :   //  if (nChan>1)
    2567             :   //    throw(AipsError("X-Y phase solution NYI for channelized data"));
    2568             : 
    2569             :   // Find number of timestamps in the VB
    2570           0 :   Vector<uInt> ord;
    2571           0 :   Int nTime=genSort(ord,vb.time(),Sort::Ascending,Sort::NoDuplicates);
    2572             : 
    2573           0 :   Matrix<Double> x(nTime,nChan,0.0),y(nTime,nChan,0.0),wt(nTime,nChan,0.0),sig(nTime,nChan,0.0);
    2574           0 :   Matrix<Bool> mask(nTime,nChan,false);
    2575             : 
    2576           0 :   mask.set(false);
    2577           0 :   Complex v(0.0);
    2578           0 :   Float wt0(0.0);
    2579           0 :   Int iTime(-1);
    2580           0 :   Double currtime(-1.0);
    2581           0 :   for (Int irow=0;irow<vb.nRow();++irow) {
    2582           0 :     if (!vb.flagRow()(irow) &&
    2583           0 :         vb.antenna1()(irow)!=vb.antenna2()(irow)) {
    2584             : 
    2585             :       // Advance time index when we see a new time
    2586           0 :       if (vb.time()(irow)!=currtime) {
    2587           0 :         ++iTime;
    2588           0 :         currtime=vb.time()(irow); // remember the new current time
    2589             :       }
    2590             : 
    2591             :       // Weights not yet chan-dep
    2592           0 :       wt0=(vb.weightMat()(1,irow)+vb.weightMat()(2,irow));
    2593           0 :       if (wt0>0.0) {
    2594             : 
    2595           0 :         for (Int ich=0;ich<nChan;++ich) {
    2596           0 :           if (!vb.flag()(ich,irow)) {
    2597           0 :             v=vb.visCube()(1,ich,irow)+conj(vb.visCube()(2,ich,irow));
    2598           0 :             x(iTime,ich)+=Double(wt0*real(v));
    2599           0 :             y(iTime,ich)+=Double(wt0*imag(v));
    2600           0 :             wt(iTime,ich)+=Double(wt0);
    2601             :           }
    2602             :         }
    2603             :       }
    2604             :     }
    2605             :   }
    2606             : 
    2607             :   // Normalize data by accumulated weights
    2608           0 :   for (Int itime=0;itime<nTime;++itime) {
    2609           0 :     for (Int ich=0;ich<nChan;++ich) {
    2610           0 :       if (wt(itime,ich)>0.0) {
    2611           0 :         x(itime,ich)/=wt(itime,ich);
    2612           0 :         y(itime,ich)/=wt(itime,ich);
    2613           0 :         sig(itime,ich)=sqrt(1.0/wt(itime,ich));
    2614           0 :         mask(itime,ich)=true;
    2615             :       }
    2616             :       else
    2617           0 :         sig(itime,ich)=DBL_MAX;    // ~zero weight
    2618             :     }
    2619             :   }
    2620             : 
    2621             :   // Solve for each channel
    2622           0 :   Vector<Complex> Cph(nChan);
    2623           0 :   Cph.set(Complex(1.0,0.0));
    2624           0 :   Float currAmb(1.0);
    2625           0 :   Bool report(false);
    2626           0 :   for (Int ich=0;ich<nChan;++ich) {
    2627             : 
    2628           0 :     if (sum(wt.column(ich))>0.0) {
    2629           0 :       report=true;
    2630           0 :       LinearFit<Double> phfitter;
    2631           0 :       Polynomial<AutoDiff<Double> > line(1);
    2632           0 :       phfitter.setFunction(line);
    2633           0 :       Vector<Bool> m(mask.column(ich));
    2634             : 
    2635             :       // Fit shallow and steep, and always prefer shallow
    2636             :       
    2637             :       // Assumed shallow solve:
    2638           0 :       Vector<Double> solnA;
    2639           0 :       solnA.assign(phfitter.fit(x.column(ich),y.column(ich),sig.column(ich),&m));
    2640             : 
    2641             :       // Assumed steep solve:
    2642           0 :       Vector<Double> solnB;
    2643           0 :       solnB.assign(phfitter.fit(y.column(ich),x.column(ich),sig.column(ich),&m));
    2644             : 
    2645           0 :       Double Xph(0.0);
    2646           0 :       if (abs(solnA(1))<abs(solnB(1))) {
    2647           0 :         Xph=atan(solnA(1));
    2648             :       }
    2649             :       else {
    2650           0 :         Xph=atan(1.0/solnB(1));
    2651             :       }
    2652             : 
    2653           0 :       Cph(ich)=currAmb*Complex(DComplex(cos(Xph),sin(Xph)));
    2654             : 
    2655             :       // Watch for and remove ambiguity changes which can
    2656             :       //  occur near Xph~= +/-90 deg (the atan above can jump)
    2657             :       //  (NB: this does _not_ resolve the amb; it merely makes
    2658             :       //   it consistent within the spw)
    2659           0 :       if (ich>0) {
    2660             :         // If Xph changes by more than pi/2, probably a ambig jump...
    2661           0 :         Float dang=abs(arg(Cph(ich)/Cph(ich-1)));
    2662           0 :         if (dang > (C::pi/2.)) {
    2663           0 :           Cph(ich)*=-1.0;   // fix this one
    2664           0 :           currAmb*=-1.0;    // reverse currAmb, so curr amb is carried forward
    2665             :           //      cout << "  Found XY phase ambiguity jump at chan=" << ich << " in spw=" << currSpw();
    2666             :         }
    2667             :       }
    2668             : 
    2669             :       //      cout << " (" << currAmb << ")";
    2670             :       //      cout << endl;
    2671             : 
    2672             : 
    2673             :       // Set all antennas with this X-Y phase (as a complex number)
    2674           0 :       solveCPar()(Slice(0,1,1),Slice(ich,1,1),Slice())=Cph(ich);
    2675           0 :       solveParOK()(Slice(),Slice(ich,1,1),Slice())=true;
    2676           0 :     }
    2677             :     else {
    2678           0 :       solveCPar()(Slice(0,1,1),Slice(ich,1,1),Slice())=Complex(1.0);
    2679           0 :       solveParOK()(Slice(),Slice(ich,1,1),Slice())=false;
    2680             :     }
    2681             :   }
    2682             : 
    2683           0 :   if (report)
    2684           0 :     cout << endl 
    2685           0 :          << "Spw = " << thisSpw
    2686           0 :          << " (ich=" << nChan/2 << "/" << nChan << "): " << endl
    2687           0 :          << " X-Y phase = " << arg(Cph[nChan/2])*180.0/C::pi << " deg." << endl;
    2688             :       
    2689             : 
    2690             :   // Now fit for the source polarization
    2691             :   {
    2692             : 
    2693           0 :     Vector<Double> wtf(nTime,0.0),sigf(nTime,0.0),xf(nTime,0.0),yf(nTime,0.0);
    2694           0 :     Vector<Bool> maskf(nTime,false);
    2695             :     Float wt0;
    2696           0 :     Complex v;
    2697           0 :     Double currtime(-1.0);
    2698           0 :     Int iTime(-1);
    2699           0 :     for (Int irow=0;irow<vb.nRow();++irow) {
    2700           0 :       if (!vb.flagRow()(irow) &&
    2701           0 :           vb.antenna1()(irow)!=vb.antenna2()(irow)) {
    2702             :         
    2703           0 :         if (vb.time()(irow)!=currtime) {
    2704             :           // Advance time index when we see a new time
    2705           0 :           ++iTime;
    2706           0 :           currtime=vb.time()(irow); // remember the new current time
    2707             :         }
    2708             : 
    2709             :         // Weights not yet chan-dep
    2710           0 :         wt0=(vb.weightMat()(1,irow)+vb.weightMat()(2,irow));
    2711           0 :         if (wt0>0.0) {
    2712           0 :           for (Int ich=0;ich<nChan;++ich) {
    2713             :             
    2714           0 :             if (!vb.flag()(ich,irow)) {
    2715             :               // Correct x-hands for xy-phase and add together
    2716           0 :               v=vb.visCube()(1,ich,irow)/Cph(ich)+vb.visCube()(2,ich,irow)/conj(Cph(ich));
    2717           0 :               xf(iTime)+=Double(wt0*2.0*(vb.feed_pa(vb.time()(irow))(0)));
    2718           0 :               yf(iTime)+=Double(wt0*real(v)/2.0);
    2719           0 :               wtf(iTime)+=Double(wt0);
    2720             :             }
    2721             :           }
    2722             :         }
    2723             :       }
    2724             :     }
    2725             :     
    2726             :     // Normalize data by accumulated weights
    2727           0 :     for (Int itime=0;itime<nTime;++itime) {
    2728           0 :       if (wtf(itime)>0.0) {
    2729           0 :         xf(itime)/=wtf(itime);
    2730           0 :         yf(itime)/=wtf(itime);
    2731           0 :         sigf(itime)=sqrt(1.0/wtf(itime));
    2732           0 :         maskf(itime)=true;
    2733             :       }
    2734             :       else
    2735           0 :         sigf(itime)=DBL_MAX;    // ~zero weight
    2736             :     }
    2737             :     
    2738             :     // p0=Q, p1=U, p2 = real part of net instr pol offset
    2739             :     //  x is TWICE the parallactic angle
    2740           0 :     CompiledFunction<AutoDiff<Double> > fn;
    2741           0 :     fn.setFunction("-p0*sin(x) + p1*cos(x) + p2");
    2742             : 
    2743           0 :     LinearFit<Double> fitter;
    2744           0 :     fitter.setFunction(fn);
    2745             :     
    2746           0 :     Vector<Double> soln=fitter.fit(xf,yf,sigf,&maskf);
    2747             :     
    2748           0 :     QU_(0,thisSpw) = soln(0);
    2749           0 :     QU_(1,thisSpw) = soln(1);
    2750             : 
    2751             :     cout << " Fractional Poln: "
    2752           0 :          << "Q = " << QU_(0,thisSpw) << ", "
    2753           0 :          << "U = " << QU_(1,thisSpw) << "; "
    2754           0 :          << "P = " << sqrt(soln(0)*soln(0)+soln(1)*soln(1)) << ", "
    2755           0 :          << "X = " << atan2(soln(1),soln(0))*90.0/C::pi << "deg."
    2756           0 :          << endl;
    2757           0 :     cout << " Net (over baselines) instrumental polarization: " 
    2758           0 :          << soln(2) << endl;
    2759             : 
    2760           0 :   }     
    2761             : 
    2762           0 : }
    2763             : 
    2764             : // Solve for the X-Y phase from the cross-hand's slope in R/I
    2765           0 : void GlinXphJones::solveOne(SDBList& sdbs) {
    2766             : 
    2767             :   // ensure
    2768           0 :   if (QU_.shape()!=IPosition(2,2,nSpw())) {
    2769           0 :     QU_.resize(2,nSpw());
    2770           0 :     QU_.set(0.0);
    2771             :   }
    2772             : 
    2773           0 :   Int thisSpw=sdbs.aggregateSpw();
    2774             :   
    2775             :   // We are actually solving for all channels simultaneously
    2776           0 :   solveCPar().reference(solveAllCPar());
    2777           0 :   solveParOK().reference(solveAllParOK());
    2778           0 :   solveParErr().reference(solveAllParErr());
    2779           0 :   solveParSNR().reference(solveAllParSNR());
    2780             :   
    2781             :   // Fill solveCPar() with 1, nominally, and flagged
    2782           0 :   solveCPar()=Complex(1.0);
    2783           0 :   solveParOK()=false;
    2784             : 
    2785           0 :   Int nChan=sdbs.nChannels();
    2786             : 
    2787             :   // Number of datapoints in fit is the number of SDBs
    2788           0 :   Int nSDB=sdbs.nSDB();
    2789             : 
    2790           0 :   Matrix<Double> x(nSDB,nChan,0.0),y(nSDB,nChan,0.0),wt(nSDB,nChan,0.0),sig(nSDB,nChan,0.0);
    2791           0 :   Matrix<Bool> mask(nSDB,nChan,false);
    2792             : 
    2793           0 :   mask.set(false);
    2794           0 :   Complex v(0.0);
    2795           0 :   Float wt0(0.0);
    2796             :   
    2797           0 :   for (Int isdb=0;isdb<nSDB;++isdb) {
    2798           0 :     SolveDataBuffer& sdb(sdbs(isdb));
    2799             : 
    2800           0 :     for (Int irow=0;irow<sdb.nRows();++irow) {
    2801           0 :       if (!sdb.flagRow()(irow) &&
    2802           0 :           sdb.antenna1()(irow)!=sdb.antenna2()(irow)) {
    2803             :         
    2804           0 :         for (Int ich=0;ich<nChan;++ich) {
    2805           0 :           if (!sdb.flagCube()(1,ich,irow)) {
    2806           0 :             wt0=sdb.weightSpectrum()(1,ich,irow);
    2807           0 :             v=sdb.visCubeCorrected()(1,ich,irow);
    2808           0 :             x(isdb,ich)+=Double(wt0*real(v));
    2809           0 :             y(isdb,ich)+=Double(wt0*imag(v));
    2810           0 :             wt(isdb,ich)+=Double(wt0);
    2811             :           }
    2812           0 :           if (!sdb.flagCube()(2,ich,irow)) {
    2813           0 :             wt0=sdb.weightSpectrum()(2,ich,irow);
    2814           0 :             v=conj(sdb.visCubeCorrected()(2,ich,irow));
    2815           0 :             x(isdb,ich)+=Double(wt0*real(v));
    2816           0 :             y(isdb,ich)+=Double(wt0*imag(v));
    2817           0 :             wt(isdb,ich)+=Double(wt0);
    2818             :           }
    2819             :         }
    2820             :       }
    2821             :     }
    2822             :   }
    2823             : 
    2824             :   // Normalize data by accumulated weights
    2825           0 :   for (Int isdb=0;isdb<nSDB;++isdb) {
    2826           0 :     for (Int ich=0;ich<nChan;++ich) {
    2827           0 :       if (wt(isdb,ich)>0.0) {
    2828           0 :         x(isdb,ich)/=wt(isdb,ich);
    2829           0 :         y(isdb,ich)/=wt(isdb,ich);
    2830           0 :         sig(isdb,ich)=sqrt(1.0/wt(isdb,ich));
    2831           0 :         mask(isdb,ich)=true;
    2832             :       }
    2833             :       else
    2834           0 :         sig(isdb,ich)=DBL_MAX;    // ~zero weight
    2835             :     }
    2836             :   }
    2837             : 
    2838             :   // Solve for each channel
    2839           0 :   Vector<Complex> Cph(nChan);
    2840           0 :   Cph.set(Complex(1.0,0.0));
    2841           0 :   Float currAmb(1.0);
    2842           0 :   Bool report(false);
    2843           0 :   for (Int ich=0;ich<nChan;++ich) {
    2844             : 
    2845           0 :     if (sum(wt.column(ich))>0.0) {
    2846           0 :       report=true;
    2847           0 :       LinearFit<Double> phfitter;
    2848           0 :       Polynomial<AutoDiff<Double> > line(1);
    2849           0 :       phfitter.setFunction(line);
    2850           0 :       Vector<Bool> m(mask.column(ich));
    2851             : 
    2852             :       // Fit shallow and steep, and always prefer shallow
    2853             :       
    2854             :       // Assumed shallow solve:
    2855           0 :       Vector<Double> solnA;
    2856           0 :       solnA.assign(phfitter.fit(x.column(ich),y.column(ich),sig.column(ich),&m));
    2857             : 
    2858             :       // Assumed steep solve:
    2859           0 :       Vector<Double> solnB;
    2860           0 :       solnB.assign(phfitter.fit(y.column(ich),x.column(ich),sig.column(ich),&m));
    2861             : 
    2862           0 :       Double Xph(0.0);
    2863           0 :       if (abs(solnA(1))<abs(solnB(1))) {
    2864           0 :         Xph=atan(solnA(1));
    2865             :       }
    2866             :       else {
    2867           0 :         Xph=atan(1.0/solnB(1));
    2868             :       }
    2869             : 
    2870           0 :       Cph(ich)=currAmb*Complex(DComplex(cos(Xph),sin(Xph)));
    2871             : 
    2872             :       // Watch for and remove ambiguity changes which can
    2873             :       //  occur near Xph~= +/-90 deg (the atan above can jump)
    2874             :       //  (NB: this does _not_ resolve the amb; it merely makes
    2875             :       //   it consistent within the spw)
    2876           0 :       if (ich>0) {
    2877             :         // If Xph changes by more than pi/2, probably a ambig jump...
    2878           0 :         Float dang=abs(arg(Cph(ich)/Cph(ich-1)));
    2879           0 :         if (dang > (C::pi/2.)) {
    2880           0 :           Cph(ich)*=-1.0;   // fix this one
    2881           0 :           currAmb*=-1.0;    // reverse currAmb, so curr amb is carried forward
    2882             :           //      cout << "  Found XY phase ambiguity jump at chan=" << ich << " in spw=" << currSpw();
    2883             :         }
    2884             :       }
    2885             : 
    2886             :       //      cout << " (" << currAmb << ")";
    2887             :       //      cout << endl;
    2888             : 
    2889             : 
    2890             :       // Set all antennas with this X-Y phase (as a complex number)
    2891           0 :       solveCPar()(Slice(0,1,1),Slice(ich,1,1),Slice())=Cph(ich);
    2892           0 :       solveParOK()(Slice(),Slice(ich,1,1),Slice())=true;
    2893           0 :     }
    2894             :     else {
    2895           0 :       solveCPar()(Slice(0,1,1),Slice(ich,1,1),Slice())=Complex(1.0);
    2896           0 :       solveParOK()(Slice(),Slice(ich,1,1),Slice())=false;
    2897             :     }
    2898             :   }
    2899             : 
    2900           0 :   if (report)
    2901           0 :     cout << endl 
    2902           0 :          << "Spw = " << thisSpw
    2903           0 :          << " (ich=" << nChan/2 << "/" << nChan << "): " << endl
    2904           0 :          << " X-Y phase = " << arg(Cph[nChan/2])*180.0/C::pi << " deg." << endl;
    2905             :       
    2906             : 
    2907             :   // Now fit for the source polarization
    2908             :   {
    2909             : 
    2910           0 :     Vector<Double> wtf(nSDB,0.0),sigf(nSDB,0.0),xf(nSDB,0.0),yf(nSDB,0.0);
    2911           0 :     Vector<Bool> maskf(nSDB,false);
    2912             :     Float wt0;
    2913           0 :     Complex v;
    2914           0 :     for (Int isdb=0;isdb<nSDB;++isdb) {
    2915           0 :       SolveDataBuffer& sdb(sdbs(isdb));
    2916             : 
    2917           0 :       for (Int irow=0;irow<sdb.nRows();++irow) {
    2918           0 :         if (!sdb.flagRow()(irow) &&
    2919           0 :             sdb.antenna1()(irow)!=sdb.antenna2()(irow)) {
    2920             :           
    2921           0 :           Float fpa(sdb.feedPa()(0));  // assumes same for all antennas!
    2922             :           
    2923           0 :           for (Int ich=0;ich<nChan;++ich) {
    2924             :             
    2925           0 :             if (!sdb.flagCube()(1,ich,irow)) {
    2926             :               // Correct x-hands for xy-phase and add together
    2927           0 :               wt0=sdb.weightSpectrum()(1,ich,irow);
    2928           0 :               v=sdb.visCubeCorrected()(1,ich,irow)/Cph(ich);
    2929           0 :               xf(isdb)+=Double(wt0*2.0*(fpa));
    2930           0 :               yf(isdb)+=Double(wt0*real(v));
    2931           0 :               wtf(isdb)+=Double(wt0);
    2932             :             }
    2933           0 :             if (!sdb.flagCube()(2,ich,irow)) {
    2934             :               // Correct x-hands for xy-phase and add together
    2935           0 :               wt0=sdb.weightSpectrum()(2,ich,irow);
    2936           0 :               v=sdb.visCubeCorrected()(2,ich,irow)/conj(Cph(ich));
    2937           0 :               xf(isdb)+=Double(wt0*2.0*(fpa));
    2938           0 :               yf(isdb)+=Double(wt0*real(v));
    2939           0 :               wtf(isdb)+=Double(wt0);
    2940             :             }
    2941             :           }
    2942             :         }
    2943             :       }
    2944             :     }
    2945             :     
    2946             :     // Normalize data by accumulated weights
    2947           0 :     for (Int isdb=0;isdb<nSDB;++isdb) {
    2948           0 :       if (wtf(isdb)>0.0) {
    2949           0 :         xf(isdb)/=wtf(isdb);
    2950           0 :         yf(isdb)/=wtf(isdb);
    2951           0 :         sigf(isdb)=sqrt(1.0/wtf(isdb));
    2952           0 :         maskf(isdb)=true;
    2953             :       }
    2954             :       else
    2955           0 :         sigf(isdb)=DBL_MAX;    // ~zero weight
    2956             :     }
    2957             :     
    2958             :     // p0=Q, p1=U, p2 = real part of net instr pol offset
    2959             :     //  x is TWICE the parallactic angle
    2960           0 :     CompiledFunction<AutoDiff<Double> > fn;
    2961           0 :     fn.setFunction("-p0*sin(x) + p1*cos(x) + p2");
    2962             : 
    2963           0 :     LinearFit<Double> fitter;
    2964           0 :     fitter.setFunction(fn);
    2965             :     
    2966           0 :     Vector<Double> soln=fitter.fit(xf,yf,sigf,&maskf);
    2967             : 
    2968           0 :     srcPolPar().resize(2);
    2969           0 :     srcPolPar()(0)=soln(0);
    2970           0 :     srcPolPar()(1)=soln(1);
    2971             :         
    2972           0 :     QU_(0,thisSpw) = soln(0);
    2973           0 :     QU_(1,thisSpw) = soln(1);
    2974             : 
    2975             :     cout << " Fractional Poln: "
    2976           0 :          << "Q = " << QU_(0,thisSpw) << ", "
    2977           0 :          << "U = " << QU_(1,thisSpw) << "; "
    2978           0 :          << "P = " << sqrt(soln(0)*soln(0)+soln(1)*soln(1)) << ", "
    2979           0 :          << "X = " << atan2(soln(1),soln(0))*90.0/C::pi << "deg."
    2980           0 :          << endl;
    2981           0 :     cout << " Net (over baselines) instrumental polarization: " 
    2982           0 :          << soln(2) << endl;
    2983             : 
    2984           0 :   }     
    2985             : 
    2986           0 : }
    2987             : 
    2988           0 : void GlinXphJones::globalPostSolveTinker() {
    2989             : 
    2990             :     // Add QU info the the keywords
    2991           0 :     TableRecord& tr(ct_->rwKeywordSet());
    2992           0 :     Record qu;
    2993           0 :     qu.define("QU",QU_);
    2994           0 :     tr.defineRecord("QU",qu);
    2995             : 
    2996           0 : }
    2997             : 
    2998             : 
    2999             : // **********************************************************
    3000             : //  GlinXphfJones Implementations
    3001             : //
    3002             : 
    3003             : // Constructor
    3004           0 : GlinXphfJones::GlinXphfJones(VisSet& vs)  :
    3005             :   VisCal(vs),             // virtual base
    3006             :   VisMueller(vs),         // virtual base
    3007           0 :   GlinXphJones(vs)        // immediate parent
    3008             : {
    3009           0 :   if (prtlev()>2) cout << "GlinXphf::GlinXphf(vs)" << endl;
    3010           0 : }
    3011             : 
    3012           0 : GlinXphfJones::GlinXphfJones(String msname,Int MSnAnt,Int MSnSpw) :
    3013             :   VisCal(msname,MSnAnt,MSnSpw),             // virtual base
    3014             :   VisMueller(msname,MSnAnt,MSnSpw),         // virtual base
    3015           0 :   GlinXphJones(msname,MSnAnt,MSnSpw)        // immediate parent
    3016             : {
    3017           0 :   if (prtlev()>2) cout << "GlinXphf::GlinXphf(msname,MSnAnt,MSnSpw)" << endl;
    3018           0 : }
    3019             : 
    3020           0 : GlinXphfJones::GlinXphfJones(const MSMetaInfoForCal& msmc) :
    3021             :   VisCal(msmc),             // virtual base
    3022             :   VisMueller(msmc),         // virtual base
    3023           0 :   GlinXphJones(msmc)        // immediate parent
    3024             : {
    3025           0 :   if (prtlev()>2) cout << "GlinXphf::GlinXphf(msmc)" << endl;
    3026           0 : }
    3027             : 
    3028           0 : GlinXphfJones::GlinXphfJones(const Int& nAnt) :
    3029             :   VisCal(nAnt), 
    3030             :   VisMueller(nAnt),
    3031           0 :   GlinXphJones(nAnt)
    3032             : {
    3033           0 :   if (prtlev()>2) cout << "GlinXphf::GlinXphf(nAnt)" << endl;
    3034           0 : }
    3035             : 
    3036           0 : GlinXphfJones::~GlinXphfJones() {
    3037           0 :   if (prtlev()>2) cout << "GlinXphf::~GlinXphf()" << endl;
    3038           0 : }
    3039             : 
    3040             : 
    3041             : 
    3042             : 
    3043             : 
    3044             : 
    3045             : } //# NAMESPACE CASA - END

Generated by: LCOV version 1.16