//# XJones.cc: Implementation of cross-hand phase calibration //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003,2011 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library is distributed in the hope that it will be useful, but WITHOUT //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or //# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include <synthesis/MeasurementComponents/XJones.h> #include <synthesis/MeasurementComponents/CalCorruptor.h> #include <synthesis/MeasurementComponents/MSMetaInfoForCal.h> #include <synthesis/MeasurementComponents/SolveDataBuffer.h> #include <msvis/MSVis/VisBuffer.h> #include <msvis/MSVis/VisBuffAccumulator.h> #include <synthesis/CalTables/CTIter.h> #include <synthesis/MeasurementEquations/VisEquation.h> #include <scimath/Fitting/LSQFit.h> #include <scimath/Fitting/LinearFit.h> #include <scimath/Functionals/CompiledFunction.h> #include <scimath/Functionals/Polynomial.h> #include <scimath/Mathematics/AutoDiff.h> #include <casa/BasicMath/Math.h> #include <tables/TaQL/ExprNode.h> #include <casa/Arrays/ArrayMath.h> #include <casa/Arrays/MaskArrMath.h> #include <casa/Arrays/MatrixMath.h> #include <casa/BasicSL/String.h> #include <casa/Utilities/Assert.h> #include <casa/Utilities/GenSort.h> #include <casa/Exceptions/Error.h> #include <casa/OS/Memory.h> #include <casa/System/Aipsrc.h> #include <casa/sstream.h> #include <measures/Measures/MCBaseline.h> #include <measures/Measures/MDirection.h> #include <measures/Measures/MEpoch.h> #include <measures/Measures/MeasTable.h> #include <casa/Logging/LogMessage.h> #include <casa/Logging/LogSink.h> // math.h ? using namespace casacore; namespace casa { //# NAMESPACE CASA - BEGIN // ********************************************************** // XMueller: positiona angle for circulars // XMueller::XMueller(VisSet& vs) : VisCal(vs), // virtual base VisMueller(vs), // virtual base SolvableVisMueller(vs) // immediate parent { if (prtlev()>2) cout << "X::X(vs)" << endl; } XMueller::XMueller(String msname,Int MSnAnt,Int MSnSpw) : VisCal(msname,MSnAnt,MSnSpw), // virtual base VisMueller(msname,MSnAnt,MSnSpw), // virtual base SolvableVisMueller(msname,MSnAnt,MSnSpw) // immediate parent { if (prtlev()>2) cout << "X::X(msname,MSnAnt,MSnSpw)" << endl; } XMueller::XMueller(const MSMetaInfoForCal& msmc) : VisCal(msmc), // virtual base VisMueller(msmc), // virtual base SolvableVisMueller(msmc) // immediate parent { if (prtlev()>2) cout << "X::X(msmc)" << endl; } XMueller::XMueller(const Int& nAnt) : VisCal(nAnt), VisMueller(nAnt), SolvableVisMueller(nAnt) { if (prtlev()>2) cout << "X::X(nAnt)" << endl; } XMueller::~XMueller() { if (prtlev()>2) cout << "X::~X()" << endl; } void XMueller::setApply(const Record& apply) { SolvableVisCal::setApply(apply); // Force calwt to false calWt()=false; } void XMueller::setSolve(const Record& solvepar) { cout << "XMueller: parType() = " << this->parType() << endl; SolvableVisCal::setSolve(solvepar); // Force calwt to false calWt()=false; // For X insist preavg is meaningful (5 minutes or user-supplied) if (preavg()<0.0) preavg()=300.0; cout << "ct_ = " << ct_ << endl; } void XMueller::newselfSolve(VisSet& vs, VisEquation& ve) { if (prtlev()>4) cout << " M::selfSolve(ve)" << endl; // Inform logger/history logSink() << "Solving for " << typeName() << LogIO::POST; // Initialize the svc according to current VisSet // (this counts intervals, sizes CalSet) Vector<Int> nChunkPerSol; Int nSol = sizeUpSolve(vs,nChunkPerSol); // Create the Caltable createMemCalTable(); // The iterator, VisBuffer VisIter& vi(vs.iter()); VisBuffer vb(vi); // cout << "nSol = " << nSol << endl; // cout << "nChunkPerSol = " << nChunkPerSol << endl; Vector<Int> slotidx(vs.numberSpw(),-1); Int nGood(0); vi.originChunks(); for (Int isol=0;isol<nSol && vi.moreChunks();++isol) { // Arrange to accumulate VisBuffAccumulator vba(nAnt(),preavg(),false); for (Int ichunk=0;ichunk<nChunkPerSol(isol);++ichunk) { // Current _chunk_'s spw Int spw(vi.spectralWindow()); // Abort if we encounter a spw for which a priori cal not available if (!ve.spwOK(spw)) throw(AipsError("Pre-applied calibration not available for at least 1 spw. Check spw selection carefully.")); // Collapse each timestamp in this chunk according to VisEq // with calibration and averaging for (vi.origin(); vi.more(); vi++) { // Force read of the field Id vb.fieldId(); // This forces the data/model/wt I/O, and applies // any prior calibrations ve.collapse(vb); // If permitted/required by solvable component, normalize //if (normalizable()) // vb.normalize(); // If this solve not freqdep, and channels not averaged yet, do so if (!freqDepMat() && vb.nChannel()>1) vb.freqAveCubes(); // Accumulate collapsed vb in a time average vba.accumulate(vb); } // Advance the VisIter, if possible if (vi.moreChunks()) vi.nextChunk(); } // Finalize the averged VisBuffer vba.finalizeAverage(); // The VisBuffer to solve with VisBuffer& svb(vba.aveVisBuff()); // Establish meta-data for this interval // (some of this may be used _during_ solve) // (this sets currSpw() in the SVC) Bool vbOk=syncSolveMeta(svb,-1); Int thisSpw=spwMap()(svb.spectralWindow()); slotidx(thisSpw)++; // We are actually solving for all channels simultaneously solveCPar().reference(solveAllCPar()); solveParOK().reference(solveAllParOK()); solveParErr().reference(solveAllParErr()); solveParSNR().reference(solveAllParSNR()); // Fill solveCPar() with 1, nominally, and flagged solveCPar()=Complex(1.0); solveParOK()=false; if (vbOk && svb.nRow()>0) { // solve for the R-L phase term in the current VB solveOneVB(svb); if (solveParOK()(0,0,0)) logSink() << "Position angle offset solution for " << msmc().fieldName(currField()) << " (spw = " << currSpw() << ") = " << arg(solveCPar()(0,0,0))*180.0/C::pi/2.0 << " deg." << LogIO::POST; else logSink() << "Position angle offset solution for " << msmc().fieldName(currField()) << " (spw = " << currSpw() << ") " << " was not determined (insufficient data)." << LogIO::POST; nGood++; } keepNCT(); } logSink() << " Found good " << typeName() << " solutions in " << nGood << " intervals." << LogIO::POST; // Store whole of result in a caltable if (nGood==0) logSink() << "No output calibration table written." << LogIO::POST; else { // Do global post-solve tinkering (e.g., phase-only, normalization, etc.) // TBD // globalPostSolveTinker(); // write the table storeNCT(); } } void XMueller::calcAllMueller() { // cout << "currMElem().shape() = " << currMElem().shape() << endl; // Put the phase factor into the cross-hand diagonals // (1,0) for the para-hands IPosition blc(3,0,0,0); IPosition trc(3,0,nChanMat()-1,nElem()-1); currMElem()(blc,trc)=Complex(1.0); blc(0)=trc(0)=1; currMElem()(blc,trc)=currCPar()(0,0,0); blc(0)=trc(0)=2; currMElem()(blc,trc)=conj(currCPar()(0,0,0)); blc(0)=trc(0)=3; currMElem()(blc,trc)=Complex(1.0); currMElemOK()=true; } void XMueller::solveOneVB(const VisBuffer& vb) { // This just a simple average of the cross-hand // visbilities... Complex d,md; Float wt,a; DComplex rl(0.0),lr(0.0); Double sumwt(0.0); for (Int irow=0;irow<vb.nRow();++irow) { if (!vb.flagRow()(irow) && vb.antenna1()(irow)!=vb.antenna2()(irow)) { for (Int ich=0;ich<vb.nChannel();++ich) { if (!vb.flag()(ich,irow)) { // A common weight for both crosshands // TBD: we should probably consider this carefully... // (also in D::guessPar...) wt=Double(vb.weightMat()(1,irow)+ vb.weightMat()(2,irow))/2.0; // correct weight for model normalization a=abs(vb.modelVisCube()(1,ich,irow)); wt*=(a*a); if (wt>0.0) { // Cross-hands only for (Int icorr=1;icorr<3;++icorr) { md=vb.modelVisCube()(icorr,ich,irow); d=vb.visCube()(icorr,ich,irow); if (abs(d)>0.0) { if (icorr==1) rl+=DComplex(Complex(wt)*d/md); else lr+=DComplex(Complex(wt)*d/md); sumwt+=Double(wt); } // abs(d)>0 } // icorr } // wt>0 } // !flag } // ich } // !flagRow } // row /* cout << "spw = " << currSpw() << endl; cout << " rl = " << rl << " " << arg(rl)*180.0/C::pi << endl; cout << " lr = " << lr << " " << arg(lr)*180.0/C::pi << endl; */ // combine lr with rl rl+=conj(lr); // Normalize to unit amplitude // (note that the phase result is insensitive to sumwt) Double amp=abs(rl); if (sumwt>0 && amp>0.0) { rl/=DComplex(amp); solveCPar()=Complex(rl); solveParOK()=true; } } // ********************************************************** // XJones: position angle for circulars (antenna-based // XJones::XJones(VisSet& vs) : VisCal(vs), // virtual base VisMueller(vs), // virtual base SolvableVisJones(vs) // immediate parent { if (prtlev()>2) cout << "X::X(vs)" << endl; } XJones::XJones(String msname,Int MSnAnt,Int MSnSpw) : VisCal(msname,MSnAnt,MSnSpw), // virtual base VisMueller(msname,MSnAnt,MSnSpw), // virtual base SolvableVisJones(msname,MSnAnt,MSnSpw) // immediate parent { if (prtlev()>2) cout << "X::X(msname,MSnAnt,MSnSpw)" << endl; } XJones::XJones(const MSMetaInfoForCal& msmc) : VisCal(msmc), // virtual base VisMueller(msmc), // virtual base SolvableVisJones(msmc) // immediate parent { if (prtlev()>2) cout << "X::X(msmc)" << endl; } XJones::XJones(const Int& nAnt) : VisCal(nAnt), VisMueller(nAnt), SolvableVisJones(nAnt) { if (prtlev()>2) cout << "X::X(nAnt)" << endl; } XJones::~XJones() { if (prtlev()>2) cout << "X::~X()" << endl; } void XJones::setApply(const Record& apply) { SolvableVisCal::setApply(apply); // Force calwt to false calWt()=false; } void XJones::setSolve(const Record& solvepar) { SolvableVisCal::setSolve(solvepar); // Force calwt to false calWt()=false; // For X insist preavg is meaningful (5 minutes or user-supplied) if (preavg()<0.0) preavg()=300.0; // Force refant to none (==-1), because it is meaningless to // apply a refant to an X solution if (refant()>-1) { logSink() << ". (Ignoring specified refant for " << typeName() << " solve.)" << LogIO::POST; refantlist().resize(1); refantlist()(0)=-1; } } void XJones::newselfSolve(VisSet& vs, VisEquation& ve) { if (prtlev()>4) cout << " Xj::newselfSolve(ve)" << endl; // Inform logger/history logSink() << "Solving for " << typeName() << LogIO::POST; // Initialize the svc according to current VisSet // (this counts intervals, sizes CalSet) Vector<Int> nChunkPerSol; Int nSol = sizeUpSolve(vs,nChunkPerSol); // Create the Caltable createMemCalTable(); // The iterator, VisBuffer VisIter& vi(vs.iter()); VisBuffer vb(vi); // cout << "nSol = " << nSol << endl; // cout << "nChunkPerSol = " << nChunkPerSol << endl; Vector<Int> slotidx(vs.numberSpw(),-1); Int nGood(0); vi.originChunks(); for (Int isol=0;isol<nSol && vi.moreChunks();++isol) { // Arrange to accumulate VisBuffAccumulator vba(nAnt(),preavg(),false); for (Int ichunk=0;ichunk<nChunkPerSol(isol);++ichunk) { // Current _chunk_'s spw Int spw(vi.spectralWindow()); // Abort if we encounter a spw for which a priori cal not available if (!ve.spwOK(spw)) throw(AipsError("Pre-applied calibration not available for at least 1 spw. Check spw selection carefully.")); // Collapse each timestamp in this chunk according to VisEq // with calibration and averaging for (vi.origin(); vi.more(); vi++) { // Force read of the field Id vb.fieldId(); // This forces the data/model/wt I/O, and applies // any prior calibrations ve.collapse(vb); // If permitted/required by solvable component, normalize if (normalizable()) vb.normalize(); // If this solve not freqdep, and channels not averaged yet, do so if (!freqDepMat() && vb.nChannel()>1) vb.freqAveCubes(); // Accumulate collapsed vb in a time average vba.accumulate(vb); } // Advance the VisIter, if possible if (vi.moreChunks()) vi.nextChunk(); } // Finalize the averged VisBuffer vba.finalizeAverage(); // The VisBuffer to solve with VisBuffer& svb(vba.aveVisBuff()); // Establish meta-data for this interval // (some of this may be used _during_ solve) // (this sets currSpw() in the SVC) Bool vbOk=syncSolveMeta(svb,-1); Int thisSpw=spwMap()(svb.spectralWindow()); slotidx(thisSpw)++; // We are actually solving for all channels simultaneously solveCPar().reference(solveAllCPar()); solveParOK().reference(solveAllParOK()); solveParErr().reference(solveAllParErr()); solveParSNR().reference(solveAllParSNR()); // Fill solveCPar() with 1, nominally, and flagged solveCPar()=Complex(1.0); solveParOK()=false; if (vbOk && svb.nRow()>0) { // solve for the R-L phase term in the current VB solveOneVB(svb); if (ntrue(solveParOK())>0) { Float ang=arg(sum(solveCPar()(solveParOK()))/Float(ntrue(solveParOK())))*180.0/C::pi; logSink() << "Mean CROSS-HAND PHASE solution for " << msmc().fieldName(currField()) << " (spw = " << currSpw() << ") = " << ang << " deg." << LogIO::POST; } else logSink() << "CROSS-HAND PHASE solution for " << msmc().fieldName(currField()) << " (spw = " << currSpw() << ") " << " was not determined (insufficient data)." << LogIO::POST; nGood++; } keepNCT(); } logSink() << " Found good " << typeName() << " solutions in " << nGood << " intervals." << LogIO::POST; // Store whole of result in a caltable if (nGood==0) logSink() << "No output calibration table written." << LogIO::POST; else { // Do global post-solve tinkering (e.g., phase-only, normalization, etc.) // TBD // globalPostSolveTinker(); // write the table storeNCT(); } } void XJones::calcAllJones() { // cout << "currJElem().shape() = " << currJElem().shape() << endl; // put the par in the first position on the diagonal // [p 0] // [0 1] // Set first element to the parameter IPosition blc(3,0,0,0); IPosition trc(3,0,nChanMat()-1,nElem()-1); currJElem()(blc,trc)=currCPar(); currJElemOK()(blc,trc)=currParOK(); // Set second diag element to one blc(0)=trc(0)=1; currJElem()(blc,trc)=Complex(1.0); currJElemOK()(blc,trc)=currParOK(); } void XJones::solveOneVB(const VisBuffer& vb) { // This just a simple average of the cross-hand // visbilities... // We are actually solving for all channels simultaneously solveCPar().reference(solveAllCPar()); solveParOK().reference(solveAllParOK()); solveParErr().reference(solveAllParErr()); solveParSNR().reference(solveAllParSNR()); // Fill solveCPar() with 1, nominally, and flagged solveCPar()=Complex(1.0); solveParOK()=false; Int nChan=vb.nChannel(); Complex d /*,md*/; Float wt; Vector<DComplex> rl(nChan,0.0),lr(nChan,0.0); Double sumwt(0.0); for (Int irow=0;irow<vb.nRow();++irow) { if (!vb.flagRow()(irow) && vb.antenna1()(irow)!=vb.antenna2()(irow)) { for (Int ich=0;ich<nChan;++ich) { if (!vb.flag()(ich,irow)) { // A common weight for both crosshands // TBD: we should probably consider this carefully... // (also in D::guessPar...) wt=Double(vb.weightMat()(1,irow)+ vb.weightMat()(2,irow))/2.0; // correct weight for model normalization // a=abs(vb.modelVisCube()(1,ich,irow)); // wt*=(a*a); if (wt>0.0) { // Cross-hands only for (Int icorr=1;icorr<3;++icorr) { // md=vb.modelVisCube()(icorr,ich,irow); d=vb.visCube()(icorr,ich,irow); if (abs(d)>0.0) { if (icorr==1) rl(ich)+=DComplex(Complex(wt)*d); // rl(ich)+=DComplex(Complex(wt)*d/md); else lr(ich)+=DComplex(Complex(wt)*d); // lr(ich)+=DComplex(Complex(wt)*d/md); sumwt+=Double(wt); } // abs(d)>0 } // icorr } // wt>0 } // !flag } // ich } // !flagRow } // row // cout << "spw = " << currSpw() << endl; // cout << " rl = " << rl << " " << phase(rl)*180.0/C::pi << endl; // cout << " lr = " << lr << " " << phase(lr)*180.0/C::pi << endl; // Record results solveCPar()=Complex(1.0); solveParOK()=false; for (Int ich=0;ich<nChan;++ich) { // combine lr with rl rl(ich)+=conj(lr(ich)); // Normalize to unit amplitude // (note that the phase result is insensitive to sumwt) Double amp=abs(rl(ich)); // For now, all antennas get the same solution IPosition blc(3,0,0,0); IPosition trc(3,0,0,nElem()-1); if (sumwt>0 && amp>0.0) { rl(ich)/=DComplex(amp); blc(1)=trc(1)=ich; solveCPar()(blc,trc)=Complex(rl(ich)); solveParOK()(blc,trc)=true; } } if (ntrue(solveParOK())>0) { Float ang=arg(sum(solveCPar()(solveParOK()))/Float(ntrue(solveParOK())))*180.0/C::pi; logSink() << "Mean CROSS-HAND PHASE solution for " << msmc().fieldName(currField()) << " (spw = " << currSpw() << ") = " << ang << " deg." << LogIO::POST; } else logSink() << "CROSS-HAND PHASE solution for " << msmc().fieldName(currField()) << " (spw = " << currSpw() << ") " << " was not determined (insufficient data)." << LogIO::POST; } void XJones::solveOneSDB(SolveDataBuffer& sdb) { // This just a simple average of the cross-hand // visbilities... // We are actually solving for all channels simultaneously solveCPar().reference(solveAllCPar()); solveParOK().reference(solveAllParOK()); solveParErr().reference(solveAllParErr()); solveParSNR().reference(solveAllParSNR()); // Fill solveCPar() with 1, nominally, and flagged solveCPar()=Complex(1.0); solveParOK()=false; Int nChan=sdb.nChannels(); Complex d /*,md*/; Float wt; Vector<DComplex> rl(nChan,0.0),lr(nChan,0.0); Double sumwt(0.0); for (Int irow=0;irow<sdb.nRows();++irow) { if (!sdb.flagRow()(irow) && sdb.antenna1()(irow)!=sdb.antenna2()(irow)) { for (Int ich=0;ich<nChan;++ich) { if (!sdb.flagCube()(1,ich,irow) && !sdb.flagCube()(2,ich,irow)) { // A common weight for both crosshands // TBD: we should probably consider this carefully... // (also in D::guessPar...) wt=Double(sdb.weightSpectrum()(1,ich,irow)+ sdb.weightSpectrum()(2,ich,irow))/2.0; // correct weight for model normalization // a=abs(vb.modelVisCube()(1,ich,irow)); // wt*=(a*a); if (wt>0.0) { // Cross-hands only for (Int icorr=1;icorr<3;++icorr) { d=sdb.visCubeCorrected()(icorr,ich,irow); if (abs(d)>0.0) { if (icorr==1) rl(ich)+=DComplex(Complex(wt)*d); else lr(ich)+=DComplex(Complex(wt)*d); sumwt+=Double(wt); } // abs(d)>0 } // icorr } // wt>0 } // !flag } // ich } // !flagRow } // row // cout << "spw = " << currSpw() << endl; // cout << " rl = " << rl << " " << phase(rl)*180.0/C::pi << endl; // cout << " lr = " << lr << " " << phase(lr)*180.0/C::pi << endl; // Record results solveCPar()=Complex(1.0); solveParOK()=false; for (Int ich=0;ich<nChan;++ich) { // combine lr with rl rl(ich)+=conj(lr(ich)); // Normalize to unit amplitude // (note that the phase result is insensitive to sumwt) Double amp=abs(rl(ich)); // For now, all antennas get the same solution IPosition blc(3,0,0,0); IPosition trc(3,0,0,nElem()-1); if (sumwt>0 && amp>0.0) { rl(ich)/=DComplex(amp); blc(1)=trc(1)=ich; solveCPar()(blc,trc)=Complex(rl(ich)); solveParOK()(blc,trc)=true; } } if (ntrue(solveParOK())>0) { Float ang=arg(sum(solveCPar()(solveParOK()))/Float(ntrue(solveParOK())))*180.0/C::pi; logSink() << "Mean CROSS-HAND PHASE solution for " << msmc().fieldName(currField()) << " (spw = " << currSpw() << ") = " << ang << " deg." << LogIO::POST; } else logSink() << "CROSS-HAND PHASE solution for " << msmc().fieldName(currField()) << " (spw = " << currSpw() << ") " << " was not determined (insufficient data)." << LogIO::POST; } void XJones::solveOne(SDBList& sdbs) { // This just a simple average of the cross-hand // visbilities... Int nSDB=sdbs.nSDB(); //cout << "nSDB=" << nSDB << endl; // We are actually solving for all channels simultaneously solveCPar().reference(solveAllCPar()); solveParOK().reference(solveAllParOK()); solveParErr().reference(solveAllParErr()); solveParSNR().reference(solveAllParSNR()); // Fill solveCPar() with 1, nominally, and flagged solveCPar()=Complex(1.0); solveParOK()=false; Int nChan=sdbs.nChannels(); Complex d /*,md*/; Float wt; Vector<DComplex> rl(nChan,0.0),lr(nChan,0.0); Double sumwt(0.0); for (Int isdb=0;isdb<nSDB;++isdb) { SolveDataBuffer& sdb(sdbs(isdb)); for (Int irow=0;irow<sdb.nRows();++irow) { if (!sdb.flagRow()(irow) && sdb.antenna1()(irow)!=sdb.antenna2()(irow)) { for (Int ich=0;ich<nChan;++ich) { if (!sdb.flagCube()(1,ich,irow) && !sdb.flagCube()(2,ich,irow)) { // A common weight for both crosshands // TBD: we should probably consider this carefully... // (also in D::guessPar...) wt=Double(sdb.weightSpectrum()(1,ich,irow)+ sdb.weightSpectrum()(2,ich,irow))/2.0; // correct weight for model normalization // a=abs(vb.modelVisCube()(1,ich,irow)); // wt*=(a*a); if (wt>0.0) { // Cross-hands only for (Int icorr=1;icorr<3;++icorr) { d=sdb.visCubeCorrected()(icorr,ich,irow); if (abs(d)>0.0) { if (icorr==1) rl(ich)+=DComplex(Complex(wt)*d); else lr(ich)+=DComplex(Complex(wt)*d); sumwt+=Double(wt); } // abs(d)>0 } // icorr } // wt>0 } // !flag } // ich } // !flagRow } // row } // isdb // cout << "spw = " << currSpw() << endl; // cout << " rl = " << rl << " " << phase(rl)*180.0/C::pi << endl; // cout << " lr = " << lr << " " << phase(lr)*180.0/C::pi << endl; // Record results solveCPar()=Complex(1.0); solveParOK()=false; for (Int ich=0;ich<nChan;++ich) { // combine lr with rl rl(ich)+=conj(lr(ich)); // Normalize to unit amplitude // (note that the phase result is insensitive to sumwt) Double amp=abs(rl(ich)); // For now, all antennas get the same solution IPosition blc(3,0,0,0); IPosition trc(3,0,0,nElem()-1); if (sumwt>0 && amp>0.0) { rl(ich)/=DComplex(amp); blc(1)=trc(1)=ich; solveCPar()(blc,trc)=Complex(rl(ich)); solveParOK()(blc,trc)=true; } } if (ntrue(solveParOK())>0) { Float ang=arg(sum(solveCPar()(solveParOK()))/Float(ntrue(solveParOK())))*180.0/C::pi; logSink() << "Mean CROSS-HAND PHASE solution for " << msmc().fieldName(currField()) << " (spw = " << currSpw() << ") = " << ang << " deg." << LogIO::POST; } else logSink() << "CROSS-HAND PHASE solution for " << msmc().fieldName(currField()) << " (spw = " << currSpw() << ") " << " was not determined (insufficient data)." << LogIO::POST; } // ********************************************************** // XfJones: CHANNELIZED position angle for circulars (antenna-based) // XfJones::XfJones(VisSet& vs) : VisCal(vs), // virtual base VisMueller(vs), // virtual base XJones(vs) // immediate parent { if (prtlev()>2) cout << "Xf::Xf(vs)" << endl; } XfJones::XfJones(String msname,Int MSnAnt,Int MSnSpw) : VisCal(msname,MSnAnt,MSnSpw), // virtual base VisMueller(msname,MSnAnt,MSnSpw), // virtual base XJones(msname,MSnAnt,MSnSpw) // immediate parent { if (prtlev()>2) cout << "Xf::Xf(msname,MSnAnt,MSnSpw)" << endl; } XfJones::XfJones(const MSMetaInfoForCal& msmc) : VisCal(msmc), // virtual base VisMueller(msmc), // virtual base XJones(msmc) // immediate parent { if (prtlev()>2) cout << "Xf::Xf(msmc)" << endl; } XfJones::XfJones(const Int& nAnt) : VisCal(nAnt), VisMueller(nAnt), XJones(nAnt) { if (prtlev()>2) cout << "Xf::Xf(nAnt)" << endl; } XfJones::~XfJones() { if (prtlev()>2) cout << "Xf::~Xf()" << endl; } void XfJones::initSolvePar() { SolvableVisJones::initSolvePar(); return; } // ********************************************************** // XparangJones Implementations // XparangJones::XparangJones(VisSet& vs) : VisCal(vs), // virtual base VisMueller(vs), // virtual base XJones(vs), // immediate parent QU_(), QURec_() { if (prtlev()>2) cout << "Xparang::Xparang(vs)" << endl; } XparangJones::XparangJones(String msname,Int MSnAnt,Int MSnSpw) : VisCal(msname,MSnAnt,MSnSpw), // virtual base VisMueller(msname,MSnAnt,MSnSpw), // virtual base XJones(msname,MSnAnt,MSnSpw), // immediate parent QU_(), QURec_() { if (prtlev()>2) cout << "Xparang::Xparang(msname,MSnAnt,MSnSpw)" << endl; } XparangJones::XparangJones(const MSMetaInfoForCal& msmc) : VisCal(msmc), // virtual base VisMueller(msmc), // virtual base XJones(msmc), // immediate parent QU_(), QURec_() { if (prtlev()>2) cout << "Xparang::Xparang(msmc)" << endl; } XparangJones::XparangJones(const Int& nAnt) : VisCal(nAnt), VisMueller(nAnt), XJones(nAnt), QU_(), QURec_() { if (prtlev()>2) cout << "Xparang::Xparang(nAnt)" << endl; } XparangJones::~XparangJones() { if (prtlev()>2) cout << "Xparang::~Xparang()" << endl; } void XparangJones::setApply(const Record& apply) { XJones::setApply(apply); // Force calwt to false calWt()=false; } void XparangJones::selfGatherAndSolve(VisSet& vs, VisEquation& ve) { if (prtlev()>4) cout << " GlnXph::selfGatherAndSolve(ve)" << endl; // Inform logger/history logSink() << "Solving for " << typeName() << LogIO::POST; // Initialize the svc according to current VisSet // (this counts intervals, sizes CalSet) Vector<Int> nChunkPerSol; Int nSol = sizeUpSolve(vs,nChunkPerSol); // Create the Caltable createMemCalTable(); // The iterator, VisBuffer VisIter& vi(vs.iter()); VisBuffer vb(vi); // cout << "nSol = " << nSol << endl; // cout << "nChunkPerSol = " << nChunkPerSol << endl; Int nGood(0); vi.originChunks(); for (Int isol=0;isol<nSol && vi.moreChunks();++isol) { // Arrange to accumulate VisBuffAccumulator vba(nAnt(),preavg(),false); for (Int ichunk=0;ichunk<nChunkPerSol(isol);++ichunk) { // Current _chunk_'s spw Int spw(vi.spectralWindow()); // Abort if we encounter a spw for which a priori cal not available if (!ve.spwOK(spw)) throw(AipsError("Pre-applied calibration not available for at least 1 spw. Check spw selection carefully.")); // Collapse each timestamp in this chunk according to VisEq // with calibration and averaging for (vi.origin(); vi.more(); vi++) { // Force read of the field Id vb.fieldId(); // This forces the data/model/wt I/O, and applies // any prior calibrations ve.collapse(vb); // If permitted/required by solvable component, normalize if (normalizable()) vb.normalize(); // If this solve not freqdep, and channels not averaged yet, do so if (!freqDepMat() && vb.nChannel()>1) vb.freqAveCubes(); // Accumulate collapsed vb in a time average vba.accumulate(vb); } // Advance the VisIter, if possible if (vi.moreChunks()) vi.nextChunk(); } // Finalize the averged VisBuffer vba.finalizeAverage(); // The VisBuffer to solve with VisBuffer& svb(vba.aveVisBuff()); // Establish meta-data for this interval // (some of this may be used _during_ solve) // (this sets currSpw() in the SVC) Bool vbOk=syncSolveMeta(svb,-1); if (vbOk && svb.nRow()>0) { // solve for the X-Y phase term in the current VB solveOneVB(svb); nGood++; } keepNCT(); } logSink() << " Found good " << typeName() << " solutions in " << nGood << " intervals." << LogIO::POST; // Store whole of result in a caltable if (nGood==0) logSink() << "No output calibration table written." << LogIO::POST; else { // Do global post-solve tinkering (e.g., phase-only, normalization, etc.) globalPostSolveTinker(); // write the table storeNCT(); } } // Handle trivial vbga void XparangJones::selfSolveOne(VisBuffGroupAcc& vbga) { // Expecting only on VB in the vbga (with many times) if (vbga.nBuf()!=1) throw(AipsError("XparangJones can't process multi-vb vbga.")); // Call single-VB specialized solver with the one vb this->solveOneVB(vbga(0)); } // SDBList (VI2) version void XparangJones::selfSolveOne(SDBList& sdbs) { // Expecting multiple SDBs (esp. in time) // (2020Oct01: insufficient data caught later in solveOne(sdbs)) // if (sdbs.nSDB()==1) // throw(AipsError("XparangJones needs multiple SDBs")); // Call single-VB specialized solver with the one vb this->solveOne(sdbs); } // Solve for the X-Y phase from the cross-hand's slope in R/I void XparangJones::solveOneVB(const VisBuffer& vb) { // ensure if (QU_.shape()!=IPosition(2,2,nSpw())) { QU_.resize(2,nSpw()); QU_.set(0.0); } Int thisSpw=spwMap()(vb.spectralWindow()); // We are actually solving for all channels simultaneously solveCPar().reference(solveAllCPar()); solveParOK().reference(solveAllParOK()); solveParErr().reference(solveAllParErr()); solveParSNR().reference(solveAllParSNR()); // Fill solveCPar() with 1, nominally, and flagged solveCPar()=Complex(1.0); solveParOK()=false; Int nChan=vb.nChannel(); // if (nChan>1) // throw(AipsError("X-Y phase solution NYI for channelized data")); // Find number of timestamps in the VB Vector<uInt> ord; Int nTime=genSort(ord,vb.time(),Sort::Ascending,Sort::NoDuplicates); Matrix<Double> x(nTime,nChan,0.0),y(nTime,nChan,0.0),wt(nTime,nChan,0.0),sig(nTime,nChan,0.0); Matrix<Bool> mask(nTime,nChan,false); mask.set(false); Complex v(0.0); Float wt0(0.0); Int iTime(-1); Double currtime(-1.0); for (Int irow=0;irow<vb.nRow();++irow) { if (!vb.flagRow()(irow) && vb.antenna1()(irow)!=vb.antenna2()(irow)) { // Advance time index when we see a new time if (vb.time()(irow)!=currtime) { ++iTime; currtime=vb.time()(irow); // remember the new current time } // Weights not yet chan-dep wt0=(vb.weightMat()(1,irow)+vb.weightMat()(2,irow)); if (wt0>0.0) { for (Int ich=0;ich<nChan;++ich) { if (!vb.flag()(ich,irow)) { v=vb.visCube()(1,ich,irow)+conj(vb.visCube()(2,ich,irow)); x(iTime,ich)+=Double(wt0*real(v)); y(iTime,ich)+=Double(wt0*imag(v)); wt(iTime,ich)+=Double(wt0); } } } } } // Normalize data by accumulated weights for (Int itime=0;itime<nTime;++itime) { for (Int ich=0;ich<nChan;++ich) { if (wt(itime,ich)>0.0) { x(itime,ich)/=wt(itime,ich); y(itime,ich)/=wt(itime,ich); sig(itime,ich)=sqrt(1.0/wt(itime,ich)); mask(itime,ich)=true; } else sig(itime,ich)=DBL_MAX; // ~zero weight } } // Solve for each channel Vector<Complex> Cph(nChan); Cph.set(Complex(1.0,0.0)); Float currAmb(1.0); Bool report(false); for (Int ich=0;ich<nChan;++ich) { if (sum(wt.column(ich))>0.0) { report=true; LinearFit<Double> phfitter; Polynomial<AutoDiff<Double> > line(1); phfitter.setFunction(line); Vector<Bool> m(mask.column(ich)); // Fit shallow and steep, and always prefer shallow // Assumed shallow solve: Vector<Double> solnA; solnA.assign(phfitter.fit(x.column(ich),y.column(ich),sig.column(ich),&m)); // Assumed steep solve: Vector<Double> solnB; solnB.assign(phfitter.fit(y.column(ich),x.column(ich),sig.column(ich),&m)); Double Xph(0.0); if (abs(solnA(1))<abs(solnB(1))) { Xph=atan(solnA(1)); } else { Xph=atan(1.0/solnB(1)); } Cph(ich)=currAmb*Complex(DComplex(cos(Xph),sin(Xph))); // Watch for and remove ambiguity changes which can // occur near Xph~= +/-90 deg (the atan above can jump) // (NB: this does _not_ resolve the amb; it merely makes // it consistent within the spw) if (ich>0) { // If Xph changes by more than pi/2, probably a ambig jump... Float dang=abs(arg(Cph(ich)/Cph(ich-1))); if (dang > (C::pi/2.)) { Cph(ich)*=-1.0; // fix this one currAmb*=-1.0; // reverse currAmb, so curr amb is carried forward // cout << " Found XY phase ambiguity jump at chan=" << ich << " in spw=" << currSpw(); } } // cout << " (" << currAmb << ")"; // cout << endl; // Set all antennas with this X-Y phase (as a complex number) solveCPar()(Slice(0,1,1),Slice(ich,1,1),Slice())=Cph(ich); solveParOK()(Slice(),Slice(ich,1,1),Slice())=true; } else { solveCPar()(Slice(0,1,1),Slice(ich,1,1),Slice())=Complex(1.0); solveParOK()(Slice(),Slice(ich,1,1),Slice())=false; } } if (report) cout << endl << "Spw = " << thisSpw << " (ich=" << nChan/2 << "/" << nChan << "): " << endl << " X-Y phase = " << arg(Cph[nChan/2])*180.0/C::pi << " deg." << endl; // Now fit for the source polarization { Vector<Double> wtf(nTime,0.0),sigf(nTime,0.0),xf(nTime,0.0),yf(nTime,0.0); Vector<Bool> maskf(nTime,false); Float wt0; Complex v; Double currtime(-1.0); Int iTime(-1); for (Int irow=0;irow<vb.nRow();++irow) { if (!vb.flagRow()(irow) && vb.antenna1()(irow)!=vb.antenna2()(irow)) { if (vb.time()(irow)!=currtime) { // Advance time index when we see a new time ++iTime; currtime=vb.time()(irow); // remember the new current time } // Weights not yet chan-dep wt0=(vb.weightMat()(1,irow)+vb.weightMat()(2,irow)); if (wt0>0.0) { for (Int ich=0;ich<nChan;++ich) { if (!vb.flag()(ich,irow)) { // Correct x-hands for xy-phase and add together v=vb.visCube()(1,ich,irow)/Cph(ich)+vb.visCube()(2,ich,irow)/conj(Cph(ich)); xf(iTime)+=Double(wt0*2.0*(vb.feed_pa(vb.time()(irow))(0))); yf(iTime)+=Double(wt0*real(v)/2.0); wtf(iTime)+=Double(wt0); } } } } } // Normalize data by accumulated weights for (Int itime=0;itime<nTime;++itime) { if (wtf(itime)>0.0) { xf(itime)/=wtf(itime); yf(itime)/=wtf(itime); sigf(itime)=sqrt(1.0/wtf(itime)); maskf(itime)=true; } else sigf(itime)=DBL_MAX; // ~zero weight } // p0=Q, p1=U, p2 = real part of net instr pol offset // x is TWICE the parallactic angle CompiledFunction<AutoDiff<Double> > fn; fn.setFunction("-p0*sin(x) + p1*cos(x) + p2"); LinearFit<Double> fitter; fitter.setFunction(fn); Vector<Double> soln=fitter.fit(xf,yf,sigf,&maskf); QU_(0,thisSpw) = soln(0); QU_(1,thisSpw) = soln(1); cout << " Fractional Poln: " << "Q = " << QU_(0,thisSpw) << ", " << "U = " << QU_(1,thisSpw) << "; " << "P = " << sqrt(soln(0)*soln(0)+soln(1)*soln(1)) << ", " << "X = " << atan2(soln(1),soln(0))*90.0/C::pi << "deg." << endl; cout << " Net (over baselines) instrumental polarization: " << soln(2) << endl; } } // Solve for the cross-hand phase from the cross-hand's slope in R/I void XparangJones::solveOne(SDBList& sdbs) { //cout << "solvePol() = " << solvePol() << endl; // cout << boolalpha; // cout << "normalizable() = " << this->normalizable() << endl; // cout << "divideByStokesIModelForSolve() = " << this->divideByStokesIModelForSolve() << endl; // ensure if (QU_.shape()!=IPosition(2,2,nSpw())) { QU_.resize(2,nSpw()); QU_.set(0.0); } Int thisSpw=sdbs.aggregateSpw(); // We are actually solving for all channels simultaneously solveCPar().reference(solveAllCPar()); solveParOK().reference(solveAllParOK()); solveParErr().reference(solveAllParErr()); solveParSNR().reference(solveAllParSNR()); // Fill solveCPar() with 1, nominally, and flagged solveCPar()=Complex(1.0); solveParOK()=false; Int nChan=sdbs.nChannels(); // Number of datapoints in fit is the number of SDBs Int nSDB=sdbs.nSDB(); // This uniformizes the baseline-dep flags over all times (sdbs) // (~ensures minimally undistorted solution below, which uses average over baseline) String extBLmessage; Int nGoodSDB(0); nGoodSDB=sdbs.extendCrossHandBaselineFlags(extBLmessage); if (sdbs.polBasis()==String("LIN")) { logSink() << "Solving for Cross-hand Phase and calibrator linear polarization in the LINEAR basis in spw=" << thisSpw<< LogIO::POST; // Report surviving data from extendCrossHandBaselineFlags logSink() << " " << extBLmessage << LogIO::POST; // Trap insufficient data case (linear; Q,U + real offset in real part) if (nGoodSDB<3) 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.")); Matrix<Double> x(nSDB,nChan,0.0),y(nSDB,nChan,0.0),wt(nSDB,nChan,0.0),sig(nSDB,nChan,0.0); Matrix<Bool> mask(nSDB,nChan,false); mask.set(false); Complex v(0.0); Float wt0(0.0); for (Int isdb=0;isdb<nSDB;++isdb) { SolveDataBuffer& sdb(sdbs(isdb)); for (Int irow=0;irow<sdb.nRows();++irow) { if (!sdb.flagRow()(irow) && sdb.antenna1()(irow)!=sdb.antenna2()(irow)) { for (Int ich=0;ich<nChan;++ich) { if (!sdb.flagCube()(1,ich,irow)) { wt0=sdb.weightSpectrum()(1,ich,irow); v=sdb.visCubeCorrected()(1,ich,irow); x(isdb,ich)+=Double(wt0*real(v)); y(isdb,ich)+=Double(wt0*imag(v)); wt(isdb,ich)+=Double(wt0); } if (!sdb.flagCube()(2,ich,irow)) { wt0=sdb.weightSpectrum()(2,ich,irow); v=conj(sdb.visCubeCorrected()(2,ich,irow)); x(isdb,ich)+=Double(wt0*real(v)); y(isdb,ich)+=Double(wt0*imag(v)); wt(isdb,ich)+=Double(wt0); } } } } } // Normalize data by accumulated weights for (Int isdb=0;isdb<nSDB;++isdb) { for (Int ich=0;ich<nChan;++ich) { if (wt(isdb,ich)>0.0) { x(isdb,ich)/=wt(isdb,ich); y(isdb,ich)/=wt(isdb,ich); sig(isdb,ich)=sqrt(1.0/wt(isdb,ich)); mask(isdb,ich)=true; } else sig(isdb,ich)=DBL_MAX; // ~zero weight } } // Solve for each channel Vector<Complex> Cph(nChan); Cph.set(Complex(1.0,0.0)); Float currAmb(1.0); Bool report(false); for (Int ich=0;ich<nChan;++ich) { if (sum(wt.column(ich))>0.0) { // report=true; LinearFit<Double> phfitter; Polynomial<AutoDiff<Double> > line(1); phfitter.setFunction(line); Vector<Bool> m(mask.column(ich)); // Fit shallow and steep, and always prefer shallow // Assumed shallow solve: Vector<Double> solnA; solnA.assign(phfitter.fit(x.column(ich),y.column(ich),sig.column(ich),&m)); // Assumed steep solve: Vector<Double> solnB; solnB.assign(phfitter.fit(y.column(ich),x.column(ich),sig.column(ich),&m)); Double Xph(0.0); if (abs(solnA(1))<abs(solnB(1))) { Xph=atan(solnA(1)); } else { Xph=atan(1.0/solnB(1)); } Cph(ich)=currAmb*Complex(DComplex(cos(Xph),sin(Xph))); // Watch for and remove ambiguity changes which can // occur near Xph~= +/-90 deg (the atan above can jump) // (NB: this does _not_ resolve the absolute amb; it merely makes // it consistent within the spw over channels) if (ich>0) { // If Xph changes by more than pi/2, probably a ambig jump... Float dang=abs(arg(Cph(ich)/Cph(ich-1))); if (dang > (C::pi/2.)) { Cph(ich)*=-1.0; // fix this one currAmb*=-1.0; // reverse currAmb, so curr amb is carried forward //cout << " Found XY phase ambiguity jump at chan=" << ich << " in spw=" << currSpw() << endl; } } // cout << " (" << currAmb << ")"; // cout << endl; // Set all antennas with this X-Y phase (as a complex number) solveCPar()(Slice(0,1,1),Slice(ich,1,1),Slice())=Cph(ich); solveParOK()(Slice(),Slice(ich,1,1),Slice())=true; } else { solveCPar()(Slice(0,1,1),Slice(ich,1,1),Slice())=Complex(1.0); solveParOK()(Slice(),Slice(ich,1,1),Slice())=false; } } // ichan // Calculate correlation with model data (real part only!), insist it is positive { //logSink() << "Attempting 180 deg ambiguity resolution by comparison with specified linear polarization model." << LogIO::POST; Vector<Double> wtf(nSDB,0.0); //,sigf(nSDB,0.0),xf(nSDB,0.0),yf(nSDB,0.0); //Vector<Bool> maskf(nSDB,false); Float wt0; Complex v,m; Double Cp(0.0), Cn(0.0); Double wtsum(0.0); for (Int isdb=0;isdb<nSDB;++isdb) { SolveDataBuffer& sdb(sdbs(isdb)); for (Int irow=0;irow<sdb.nRows();++irow) { if (!sdb.flagRow()(irow) && sdb.antenna1()(irow)!=sdb.antenna2()(irow)) { for (Int ich=0;ich<nChan;++ich) { if (!sdb.flagCube()(1,ich,irow)) { // Correct x-hands for xy-phase and add together wt0=sdb.weightSpectrum()(1,ich,irow); v=sdb.visCubeCorrected()(1,ich,irow)/Cph(ich); m=sdb.visCubeModel()(1,ich,irow); Cp+=(wt0*real(v)*real(m)); Cn+=(wt0*real(v)*real(m)*(-1.0)); wtsum+=Double(wt0); } if (!sdb.flagCube()(2,ich,irow)) { // Correct x-hands for xy-phase and add together wt0=sdb.weightSpectrum()(2,ich,irow); v=sdb.visCubeCorrected()(2,ich,irow)/conj(Cph(ich)); m=sdb.visCubeModel()(2,ich,irow); Cp+=(wt0*real(v)*real(m)); Cn+=(wt0*real(v)*real(m)*(-1.0)); wtsum+=Double(wt0); } } } } } //cout << "Cp,Cn = " << Cp << " " << Cn << endl; if ( Cn > Cp ) { logSink() << " NB: 180 deg ambiguity detected and corrected!" << LogIO::POST; Complex swap(-1.0,0.0); Cph*=swap; MaskedArray<Complex> sCP(solveCPar()(solveParOK())); sCP*=swap; } } if (ntrue(solveParOK())>0) { Float ang=arg(sum(solveCPar()(solveParOK()))/Float(ntrue(solveParOK())))*180.0/C::pi; logSink() << " Fld = " << msmc().fieldName(currField()) << ", Spw = " << thisSpw << " (ich=" << nChan/2 << "/" << nChan << "): " //<< endl << " CROSS-HAND PHASE = " << arg(Cph[nChan/2])*180.0/C::pi << " deg." << " (Mean = " << ang << ")" << LogIO::POST; } else logSink() << " Fld = " << msmc().fieldName(currField()) << ", Spw = " << thisSpw << " (ich=" << nChan/2 << "/" << nChan << "): " << endl << " CROSS-HAND PHASE was not determined (insufficient data)." << LogIO::POST; if (report) cout << endl << "Spw = " << thisSpw << " (ich=" << nChan/2 << "/" << nChan << "): " // << endl << " X-Y phase = " << arg(Cph[nChan/2])*180.0/C::pi << " deg." << endl; // Now fit for the source polarization { Vector<Double> wtf(nSDB,0.0),sigf(nSDB,0.0),xf(nSDB,0.0),yf(nSDB,0.0); Vector<Bool> maskf(nSDB,false); Float wt0; Complex v; for (Int isdb=0;isdb<nSDB;++isdb) { SolveDataBuffer& sdb(sdbs(isdb)); for (Int irow=0;irow<sdb.nRows();++irow) { if (!sdb.flagRow()(irow) && sdb.antenna1()(irow)!=sdb.antenna2()(irow)) { Float fpa(sdb.feedPa()(0)); // assumes same for all antennas! for (Int ich=0;ich<nChan;++ich) { if (!sdb.flagCube()(1,ich,irow)) { // Correct x-hands for xy-phase and add together wt0=sdb.weightSpectrum()(1,ich,irow); v=sdb.visCubeCorrected()(1,ich,irow)/Cph(ich); xf(isdb)+=Double(wt0*2.0*(fpa)); yf(isdb)+=Double(wt0*real(v)); wtf(isdb)+=Double(wt0); } if (!sdb.flagCube()(2,ich,irow)) { // Correct x-hands for xy-phase and add together wt0=sdb.weightSpectrum()(2,ich,irow); v=sdb.visCubeCorrected()(2,ich,irow)/conj(Cph(ich)); xf(isdb)+=Double(wt0*2.0*(fpa)); yf(isdb)+=Double(wt0*real(v)); wtf(isdb)+=Double(wt0); } } } } } // Normalize data by accumulated weights for (Int isdb=0;isdb<nSDB;++isdb) { if (wtf(isdb)>0.0) { xf(isdb)/=wtf(isdb); yf(isdb)/=wtf(isdb); sigf(isdb)=sqrt(1.0/wtf(isdb)); maskf(isdb)=true; } else sigf(isdb)=DBL_MAX; // ~zero weight } // p0=Q, p1=U, p2 = real part of net instr pol offset // x is TWICE the parallactic angle CompiledFunction<AutoDiff<Double> > fn; fn.setFunction("-p0*sin(x) + p1*cos(x) + p2"); LinearFit<Double> fitter; fitter.setFunction(fn); Vector<Double> soln=fitter.fit(xf,yf,sigf,&maskf); srcPolPar().resize(2); srcPolPar()(0)=soln(0); srcPolPar()(1)=soln(1); QU_(0,thisSpw) = soln(0); QU_(1,thisSpw) = soln(1); Float &Q(QU_(0,thisSpw)), &U(QU_(1,thisSpw)); logSink() << " Fractional Poln: " << "Q = " << Q << ", " << "U = " << U << "; " << "P = " << sqrt(Q*Q+U*U) << ", " << "X = " << atan2(U,Q)*90.0/C::pi << "deg." << LogIO::POST; logSink() << " Net (over baselines) instrumental polarization (real part): " << soln(2) << LogIO::POST; } // poln solve scope } else if (sdbs.polBasis()==String("CIRC")) { logSink() << "Solving for Cross-hand Phase and calibrator linear polarization in the CIRCULAR basis in spw="<<thisSpw << LogIO::POST; // Report surviving data from extendCrossHandBaselineFlags logSink() << " " << extBLmessage << LogIO::POST; // Trap insufficient data case (circular; |P| and a complex offset from complex data, cf law of cosines for isoceles triangle) if (nGoodSDB<2) 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.")); Matrix<Complex> V(nSDB,nChan,0.0),M(nSDB,nChan,0.0); Matrix<Float> Wt(nSDB,nChan,0.0); // ,sig(nSDB,nChan,0.0); Matrix<Bool> mask(nSDB,nChan,false); mask.set(false); Complex v(0.0),m(0.0); Float wt0(0.0); for (Int isdb=0;isdb<nSDB;++isdb) { SolveDataBuffer& sdb(sdbs(isdb)); for (Int irow=0;irow<sdb.nRows();++irow) { if (!sdb.flagRow()(irow) && sdb.antenna1()(irow)!=sdb.antenna2()(irow)) { for (Int ich=0;ich<nChan;++ich) { if (!sdb.flagCube()(1,ich,irow)) { wt0=sdb.weightSpectrum()(1,ich,irow); v=sdb.visCubeCorrected()(1,ich,irow); m=sdb.visCubeModel()(1,ich,irow); V(isdb,ich)+=Complex(wt0*v); M(isdb,ich)+=Complex(wt0*m); Wt(isdb,ich)+=wt0; } if (!sdb.flagCube()(2,ich,irow)) { wt0=sdb.weightSpectrum()(2,ich,irow); v=conj(sdb.visCubeCorrected()(2,ich,irow)); m=conj(sdb.visCubeModel()(2,ich,irow)); V(isdb,ich)+=Complex(wt0*v); M(isdb,ich)+=Complex(wt0*m); Wt(isdb,ich)+=wt0; } } } } } // Normalize data by accumulated weights for (Int isdb=0;isdb<nSDB;++isdb) { for (Int ich=0;ich<nChan;++ich) { if (Wt(isdb,ich)>0.0) { V(isdb,ich)/=Wt(isdb,ich); M(isdb,ich)/=Wt(isdb,ich); mask(isdb,ich)=true; } } } // Now solve for cross-hand phase in each channel Vector<Complex> Cph(nChan); Cph.set(Complex(1.0,0.0)); for (Int ich=0;ich<nChan;++ich) { if (sum(Wt.column(ich))>0.0) { LSQFit fit(2,LSQComplex()); Vector<Complex> ce(2); ce(0)=Complex(1.0); for (int isdb=0;isdb<nSDB;++isdb) { ce(1)=M(isdb,ich); fit.makeNorm(ce.data(),Wt(isdb,ich),V(isdb,ich),LSQFit::COMPLEX); } uInt rank; Bool ok = fit.invert(rank); DComplex sol[2]; if (ok) fit.solve(sol); else throw(AipsError("Source polarization solution is singular; try solving for D-terms only.")); //cout << "ich=" << ich << " sol = " << sol[0] << "," << sol[1] << " P=" << abs(sol[1]) << " X=" << arg(sol[1])*180/C::pi << endl; Cph[ich]=sol[1]/abs(sol[1]); // phase-only as complex number // Set all antennas with this X-Y phase (as a complex number) solveCPar()(Slice(0,1,1),Slice(ich,1,1),Slice())=Cph(ich); solveParOK()(Slice(),Slice(ich,1,1),Slice())=true; } else { // In sufficient data, phase=0.0, flagged solveCPar()(Slice(0,1,1),Slice(ich,1,1),Slice())=Complex(1.0); solveParOK()(Slice(),Slice(ich,1,1),Slice())=false; } } // ICH if (ntrue(solveParOK())>0) { Float ang=arg(sum(solveCPar()(solveParOK()))/Float(ntrue(solveParOK())))*180.0/C::pi; logSink() << " Fld = " << msmc().fieldName(currField()) << ", Spw = " << thisSpw << " (ich=" << nChan/2 << "/" << nChan << "): " // << endl << " CROSS-HAND PHASE = " << arg(Cph[nChan/2])*180.0/C::pi << " deg." << " (Mean = " << ang << ")" << LogIO::POST; } else logSink() << " Fld = " << msmc().fieldName(currField()) << ", Spw = " << thisSpw << " (ich=" << nChan/2 << "/" << nChan << "): " << endl << " CROSS-HAND PHASE was not determined (insufficient data)." << LogIO::POST; if (false) cout << endl << "Spw = " << thisSpw << " (ich=" << nChan/2 << "/" << nChan << "): " // << endl << " X-Y phase = " << arg(Cph[nChan/2])*180.0/C::pi << " deg." << endl; // Now solve for QU (chan-aved) { LSQFit fit(2,LSQComplex()); Vector<Complex> ce(2); ce(0)=Complex(1.0); Vector<Complex> V2(nSDB,Complex(0.0)), M2(nSDB,Complex(0.0)); Vector<Float> Wt2(nSDB,0.0f); for (int isdb=0;isdb<nSDB;++isdb) { // Accumulate along channel axis for (Int ich=0;ich<nChan;++ich) { if (Wt(isdb,ich)>0.0f) { V2[isdb]+=(Wt(isdb,ich)*V(isdb,ich)/Cph[ich]); // include cross-hand phase correction M2[isdb]+=(Wt(isdb,ich)*M(isdb,ich)/abs(M(isdb,ich))); // Divide by user-supplied |P| Wt2[isdb]+=Wt(isdb,ich); } } // finalize averages if (Wt2[isdb]>0.0) { V2[isdb]/=Wt2[isdb]; M2[isdb]/=Wt2[isdb]; } ce(1)=M2(isdb); fit.makeNorm(ce.data(),Wt2(isdb),V2(isdb),LSQFit::COMPLEX); } uInt rank; Bool ok = fit.invert(rank); DComplex sol[2]; if (ok) fit.solve(sol); else throw(AipsError("Source polarization solution is singular; try solving for D-terms only.")); //cout << "sol = " << sol[0] << "," << sol[1] << " P=" << abs(sol[1]) << " X=" << arg(sol[1])*90.0/C::pi << endl; srcPolPar().resize(2); srcPolPar()(0)=real(sol[1]); srcPolPar()(1)=imag(sol[1]); QU_(0,thisSpw) = real(sol[1]); QU_(1,thisSpw) = imag(sol[1]); Float &Q(QU_(0,thisSpw)), &U(QU_(1,thisSpw)); logSink() << " Fractional Poln: " << "Q = " << Q << ", " << "U = " << U << "; " << "P = " << sqrt(Q*Q+U*U) << ", " << "X = " << atan2(U,Q)*90.0/C::pi << "deg." << LogIO::POST; logSink() << " Net (over baselines) instrumental polarization: " << abs(sol[0]) << LogIO::POST; } // Circ QU solve } // basis clauses else { throw(AipsError("Cannot solve for cross-hand phase, don't know basis")); } // Add this QU result to the QURec_ Vector<Float> fStokes(4,0.0f); // Fractional Stokes vector fStokes(0)=1.0; fStokes(1)=QU_(0,thisSpw); // Q fStokes(2)=QU_(1,thisSpw); // U ostringstream os; os << "Spw" << thisSpw; String spwName(os); String fldName(msmc().fieldName(currField())); Record fld; if (QURec_.isDefined(fldName)) fld=QURec_.asRecord(fldName); fld.define(spwName,fStokes); // Add average Int nspw=fld.nfields(); // the number of spws recorded in the record so far Vector<Float> QUave(4,0.0f); Int nave(0); for (Int ispw=0;ispw<nspw;++ispw) { String fieldname=fld.name(ispw); if (fieldname!="SpwAve") { QUave+=fld.asArrayFloat(ispw); ++nave; } } QUave/=Float(nave); fld.define("SpwAve",QUave); // Replace this fld's record in QURec_ QURec_.defineRecord(fldName,fld); } void XparangJones::globalPostSolveTinker() { // Add QU info the caltable keywords TableRecord& tr(ct_->rwKeywordSet()); Record qu; qu.define("QU",QU_); tr.defineRecord("QU",qu); } Record XparangJones::solveActionRec() { // Add QU info to QURec_, so Calibrater can extract and return Record sAR; sAR=SolvableVisCal::solveActionRec(); sAR.defineRecord("fStokes",QURec_); return sAR; } // ********************************************************** // XfparangJones Implementations // // Constructor XfparangJones::XfparangJones(VisSet& vs) : VisCal(vs), // virtual base VisMueller(vs), // virtual base XparangJones(vs) // immediate parent { if (prtlev()>2) cout << "Xfparangf::Xfparang(vs)" << endl; } XfparangJones::XfparangJones(String msname,Int MSnAnt,Int MSnSpw) : VisCal(msname,MSnAnt,MSnSpw), // virtual base VisMueller(msname,MSnAnt,MSnSpw), // virtual base XparangJones(msname,MSnAnt,MSnSpw) // immediate parent { if (prtlev()>2) cout << "Xfparang::Xfparang(msname,MSnAnt,MSnSpw)" << endl; } XfparangJones::XfparangJones(const MSMetaInfoForCal& msmc) : VisCal(msmc), // virtual base VisMueller(msmc), // virtual base XparangJones(msmc) // immediate parent { if (prtlev()>2) cout << "Xfparang::Xfparang(msmc)" << endl; } XfparangJones::XfparangJones(const Int& nAnt) : VisCal(nAnt), VisMueller(nAnt), XparangJones(nAnt) { if (prtlev()>2) cout << "Xfparang::Xfparang(nAnt)" << endl; } XfparangJones::~XfparangJones() { if (prtlev()>2) cout << "Xfparang::~Xfparang()" << endl; } // ********************************************************** // PosAngJones Implementations // PosAngJones::PosAngJones(VisSet& vs) : VisCal(vs), // virtual base VisMueller(vs), // virtual base XJones(vs), // immediate parent jonestype_(Jones::Diagonal) { if (prtlev()>2) cout << "PosAng::PosAng(vs)" << endl; } PosAngJones::PosAngJones(String msname,Int MSnAnt,Int MSnSpw) : VisCal(msname,MSnAnt,MSnSpw), // virtual base VisMueller(msname,MSnAnt,MSnSpw), // virtual base XJones(msname,MSnAnt,MSnSpw), // immediate parent jonestype_(Jones::Diagonal) { if (prtlev()>2) cout << "PosAng::PosAng(msname,MSnAnt,MSnSpw)" << endl; } PosAngJones::PosAngJones(const MSMetaInfoForCal& msmc) : VisCal(msmc), // virtual base VisMueller(msmc), // virtual base XJones(msmc), // immediate parent jonestype_(Jones::Diagonal) { if (prtlev()>2) cout << "PosAng::PosAng(msmc)" << endl; } PosAngJones::PosAngJones(const Int& nAnt) : VisCal(nAnt), VisMueller(nAnt), XJones(nAnt), jonestype_(Jones::Diagonal) { if (prtlev()>2) cout << "PosAng::PosAng(nAnt)" << endl; } PosAngJones::~PosAngJones() { if (prtlev()>2) cout << "PosAng::~PosAng()" << endl; } void PosAngJones::setApply(const Record& apply) { SolvableVisCal::setApply(apply); // Force calwt to false calWt()=false; } void PosAngJones::setSolve(const Record& solvepar) { SolvableVisCal::setSolve(solvepar); // Force calwt to false calWt()=false; // For X insist preavg is meaningful (5 minutes or user-supplied) if (preavg()<0.0) preavg()=30.0; // Force refant to none (==-1), because it is meaningless to // apply a refant to an X solution if (refant()>-1) { logSink() << ". (Ignoring specified refant for " << typeName() << " solve.)" << LogIO::POST; refantlist().resize(1); refantlist()(0)=-1; } } // PJones needs to know pol basis and feed pa void PosAngJones::syncMeta(const VisBuffer& vb) { // Call parent (sets currTime()) VisJones::syncMeta(vb); // Basis if (vb.corrType()(0)==5) // Circulars jonestype_=Jones::Diagonal; else if (vb.corrType()(0)==9) // Linears jonestype_=Jones::General; } // PJones needs to know pol basis and feed pa void PosAngJones::syncMeta2(const vi::VisBuffer2& vb) { // Call parent (sets currTime()) VisJones::syncMeta2(vb); // Basis if (vb.correlationTypes()(0)==5) // Circulars jonestype_=Jones::Diagonal; else if (vb.correlationTypes()(0)==9) // Linears jonestype_=Jones::General; } void PosAngJones::calcAllJones() { //if (prtlev()>6) cout << " PosAng::calcAllJones()" << endl; // Should handle OK flags in this method, and only // do Jones calc if OK Vector<Complex> oneJones; Vector<Bool> oneJOK; Vector<Float> onePar; Vector<Bool> onePOK; ArrayIterator<Complex> Jiter(currJElem(),1); ArrayIterator<Bool> JOKiter(currJElemOK(),1); ArrayIterator<Float> Piter(currRPar(),1); ArrayIterator<Bool> POKiter(currParOK(),1); for (Int iant=0; iant<nAnt(); iant++) { for (Int ich=0; ich<nChanMat(); ich++) { oneJones.reference(Jiter.array()); oneJOK.reference(JOKiter.array()); onePar.reference(Piter.array()); onePOK.reference(POKiter.array()); // Calculate the Jones matrix calcOneJonesRPar(oneJones,oneJOK,onePar,onePOK); // Advance iterators Jiter.next(); JOKiter.next(); if (freqDepPar()) { Piter.next(); POKiter.next(); } } // Step to next antenns's pars if we didn't in channel loop if (!freqDepPar()) { Piter.next(); POKiter.next(); } } } // Calculate a single Jones matrix by some means from parameters void PosAngJones::calcOneJonesRPar(Vector<Complex>& mat, Vector<Bool>& mOk, const Vector<Float>& par, const Vector<Bool>& pOk ) { if (prtlev()>10) cout << " PosAng::calcOneJones()" << endl; if (pOk(0)) { switch (jonesType()) { // Circular version: case Jones::Diagonal: { mat(0)=Complex(cos(par(0)), sin(par(0))); mat(1)=conj(mat(0)); mOk=true; break; } // Linear version: case Jones::General: { const Float a=-par(0); mat(0)=mat(3)=cos(a); mat(1)=sin(a); mat(2)=-mat(1); mOk=true; break; } default: throw(AipsError("PosAngJones doesn't know if it is Diag (Circ) or General (Lin)")); break; } } } void PosAngJones::solveOne(SDBList& sdbs) { // This just a simple average of the cross-hand // visbilities... Int nSDB=sdbs.nSDB(); // We are actually solving for all channels simultaneously solveRPar().reference(solveAllRPar()); solveParOK().reference(solveAllParOK()); solveParErr().reference(solveAllParErr()); solveParSNR().reference(solveAllParSNR()); // Fill solveCPar() with 1, nominally, and flagged solveRPar()=0.0; solveParOK()=false; Int nChan=sdbs.nChannels(); String polBasis=sdbs.polBasis(); Complex d,md; Float wt; Vector<DComplex> RL(nChan,0.0); Vector<Double> sumwt(nChan,0.0); for (Int isdb=0;isdb<nSDB;++isdb) { SolveDataBuffer& sdb(sdbs(isdb)); for (Int irow=0;irow<sdb.nRows();++irow) { if (!sdb.flagRow()(irow) && sdb.antenna1()(irow)!=sdb.antenna2()(irow)) { for (Int ich=0;ich<nChan;++ich) { if (polBasis=="CIRC") { if (!sdb.flagCube()(1,ich,irow) && !sdb.flagCube()(2,ich,irow)) { // A common weight for both crosshands // TBD: we should probably consider this carefully... // (also in D::guessPar...) wt=Double(sdb.weightSpectrum()(1,ich,irow)+ sdb.weightSpectrum()(2,ich,irow))/2.0; if (wt>0.0) { // Cross-hands only for (Int icorr=1;icorr<3;++icorr) { d=sdb.visCubeCorrected()(icorr,ich,irow); md=sdb.visCubeModel()(icorr,ich,irow); if (abs(d)>0.0 && abs(md)>0.0) { if (icorr==1) RL(ich)+=DComplex(Complex(wt)*d/md); else RL(ich)+=conj(DComplex(Complex(wt)*d/md)); sumwt(ich)+=Double(wt); } // abs(d)>0 } // icorr } // wt>0 } // !flag } // CIRC else if (polBasis=="LIN") { // Need all 4 correlations if (!sdb.flagCube()(0,ich,irow) && !sdb.flagCube()(1,ich,irow) && !sdb.flagCube()(2,ich,irow) && !sdb.flagCube()(3,ich,irow)) { // A common weight (correct?) wt=Double(sdb.weightSpectrum()(0,ich,irow)+ sdb.weightSpectrum()(1,ich,irow)+ sdb.weightSpectrum()(2,ich,irow)+ sdb.weightSpectrum()(3,ich,irow))/4.0; if (wt>0.0) { Complex oneI(0.0,1.0); const Cube<Complex>& vCC(sdb.visCubeCorrected()); Complex Q( (vCC(0,ich,irow)-vCC(3,ich,irow))/2.0 ); Complex U( (vCC(1,ich,irow)+vCC(2,ich,irow))/2.0 ); Complex d(Q+oneI*U); const Cube<Complex> vCM(sdb.visCubeModel()); Complex Qm( (vCM(0,ich,irow)-vCM(3,ich,irow))/2.0 ); Complex Um( (vCM(1,ich,irow)+vCM(2,ich,irow))/2.0 ); Complex md(Qm+oneI*Um); if (abs(d)>0.0 && abs(md)>0.0) { RL(ich)+=DComplex(Complex(wt)*d/md); sumwt(ich)+=Double(wt); } // abs(d)>0 } // wt>0 } // !flag } // LIN } // ich } // !flagRow } // row } // isdb // cout << "spw = " << currSpw() << endl; // Record results for (Int ich=0;ich<nChan;++ich) { // Normalize to unit amplitude // (note that the phase result is insensitive to sumwt) Double amp=abs(RL(ich)); // For now, all antennas get the same solution IPosition blc(3,0,ich,0); IPosition trc(3,0,ich,nElem()-1); if (sumwt(ich)>0 && amp>0.0) { RL(ich)/=amp; solveRPar()(blc,trc)=arg(RL(ich))/2.0; solveParOK()(blc,trc)=true; } else RL(ich)=0.0; } if (ntrue(solveParOK())>0) { Float ang=arg(sum(RL))*90.0/C::pi; logSink() << "Mean POSITION ANGLE OFFSET solution for " << msmc().fieldName(currField()) << " (spw = " << currSpw() << ") = " << ang << " deg." << LogIO::POST; } else logSink() << "POSITION ANGLE OFFSET solution for " << msmc().fieldName(currField()) << " (spw = " << currSpw() << ") " << " was not determined (insufficient data)." << LogIO::POST; } // ********************************************************** // GlinXphJones Implementations // GlinXphJones::GlinXphJones(VisSet& vs) : VisCal(vs), // virtual base VisMueller(vs), // virtual base GJones(vs), // immediate parent QU_() { if (prtlev()>2) cout << "GlinXph::GlinXph(vs)" << endl; } GlinXphJones::GlinXphJones(String msname,Int MSnAnt,Int MSnSpw) : VisCal(msname,MSnAnt,MSnSpw), // virtual base VisMueller(msname,MSnAnt,MSnSpw), // virtual base GJones(msname,MSnAnt,MSnSpw), // immediate parent QU_() { if (prtlev()>2) cout << "GlinXph::GlinXph(msname,MSnAnt,MSnSpw)" << endl; } GlinXphJones::GlinXphJones(const MSMetaInfoForCal& msmc) : VisCal(msmc), // virtual base VisMueller(msmc), // virtual base GJones(msmc), // immediate parent QU_() { if (prtlev()>2) cout << "GlinXph::GlinXph(msmc)" << endl; } GlinXphJones::GlinXphJones(const Int& nAnt) : VisCal(nAnt), VisMueller(nAnt), GJones(nAnt), QU_() { if (prtlev()>2) cout << "GlinXph::GlinXph(nAnt)" << endl; } GlinXphJones::~GlinXphJones() { if (prtlev()>2) cout << "GlinXph::~GlinXph()" << endl; } void GlinXphJones::setApply(const Record& apply) { GJones::setApply(apply); // Force calwt to false calWt()=false; } void GlinXphJones::selfGatherAndSolve(VisSet& vs, VisEquation& ve) { if (prtlev()>4) cout << " GlnXph::selfGatherAndSolve(ve)" << endl; // Inform logger/history logSink() << "Solving for " << typeName() << LogIO::POST; // Initialize the svc according to current VisSet // (this counts intervals, sizes CalSet) Vector<Int> nChunkPerSol; Int nSol = sizeUpSolve(vs,nChunkPerSol); // Create the Caltable createMemCalTable(); // The iterator, VisBuffer VisIter& vi(vs.iter()); VisBuffer vb(vi); // cout << "nSol = " << nSol << endl; // cout << "nChunkPerSol = " << nChunkPerSol << endl; Int nGood(0); vi.originChunks(); for (Int isol=0;isol<nSol && vi.moreChunks();++isol) { // Arrange to accumulate VisBuffAccumulator vba(nAnt(),preavg(),false); for (Int ichunk=0;ichunk<nChunkPerSol(isol);++ichunk) { // Current _chunk_'s spw Int spw(vi.spectralWindow()); // Abort if we encounter a spw for which a priori cal not available if (!ve.spwOK(spw)) throw(AipsError("Pre-applied calibration not available for at least 1 spw. Check spw selection carefully.")); // Collapse each timestamp in this chunk according to VisEq // with calibration and averaging for (vi.origin(); vi.more(); vi++) { // Force read of the field Id vb.fieldId(); // This forces the data/model/wt I/O, and applies // any prior calibrations ve.collapse(vb); // If permitted/required by solvable component, normalize if (normalizable()) vb.normalize(); // If this solve not freqdep, and channels not averaged yet, do so if (!freqDepMat() && vb.nChannel()>1) vb.freqAveCubes(); // Accumulate collapsed vb in a time average vba.accumulate(vb); } // Advance the VisIter, if possible if (vi.moreChunks()) vi.nextChunk(); } // Finalize the averged VisBuffer vba.finalizeAverage(); // The VisBuffer to solve with VisBuffer& svb(vba.aveVisBuff()); // Establish meta-data for this interval // (some of this may be used _during_ solve) // (this sets currSpw() in the SVC) Bool vbOk=syncSolveMeta(svb,-1); if (vbOk && svb.nRow()>0) { // solve for the X-Y phase term in the current VB solveOneVB(svb); nGood++; } keepNCT(); } logSink() << " Found good " << typeName() << " solutions in " << nGood << " intervals." << LogIO::POST; // Store whole of result in a caltable if (nGood==0) logSink() << "No output calibration table written." << LogIO::POST; else { // Do global post-solve tinkering (e.g., phase-only, normalization, etc.) globalPostSolveTinker(); // write the table storeNCT(); } } // Handle trivial vbga void GlinXphJones::selfSolveOne(VisBuffGroupAcc& vbga) { // Expecting only on VB in the vbga (with many times) if (vbga.nBuf()!=1) throw(AipsError("GlinXphJones can't process multi-vb vbga.")); // Call single-VB specialized solver with the one vb this->solveOneVB(vbga(0)); } // SDBList (VI2) version void GlinXphJones::selfSolveOne(SDBList& sdbs) { // Expecting multiple SDBs (esp. in time) if (sdbs.nSDB()==1) throw(AipsError("GlinXphJones needs multiple SDBs")); // Call single-VB specialized solver with the one vb this->solveOne(sdbs); } // Solve for the X-Y phase from the cross-hand's slope in R/I void GlinXphJones::solveOneVB(const VisBuffer& vb) { // ensure if (QU_.shape()!=IPosition(2,2,nSpw())) { QU_.resize(2,nSpw()); QU_.set(0.0); } Int thisSpw=spwMap()(vb.spectralWindow()); // We are actually solving for all channels simultaneously solveCPar().reference(solveAllCPar()); solveParOK().reference(solveAllParOK()); solveParErr().reference(solveAllParErr()); solveParSNR().reference(solveAllParSNR()); // Fill solveCPar() with 1, nominally, and flagged solveCPar()=Complex(1.0); solveParOK()=false; Int nChan=vb.nChannel(); // if (nChan>1) // throw(AipsError("X-Y phase solution NYI for channelized data")); // Find number of timestamps in the VB Vector<uInt> ord; Int nTime=genSort(ord,vb.time(),Sort::Ascending,Sort::NoDuplicates); Matrix<Double> x(nTime,nChan,0.0),y(nTime,nChan,0.0),wt(nTime,nChan,0.0),sig(nTime,nChan,0.0); Matrix<Bool> mask(nTime,nChan,false); mask.set(false); Complex v(0.0); Float wt0(0.0); Int iTime(-1); Double currtime(-1.0); for (Int irow=0;irow<vb.nRow();++irow) { if (!vb.flagRow()(irow) && vb.antenna1()(irow)!=vb.antenna2()(irow)) { // Advance time index when we see a new time if (vb.time()(irow)!=currtime) { ++iTime; currtime=vb.time()(irow); // remember the new current time } // Weights not yet chan-dep wt0=(vb.weightMat()(1,irow)+vb.weightMat()(2,irow)); if (wt0>0.0) { for (Int ich=0;ich<nChan;++ich) { if (!vb.flag()(ich,irow)) { v=vb.visCube()(1,ich,irow)+conj(vb.visCube()(2,ich,irow)); x(iTime,ich)+=Double(wt0*real(v)); y(iTime,ich)+=Double(wt0*imag(v)); wt(iTime,ich)+=Double(wt0); } } } } } // Normalize data by accumulated weights for (Int itime=0;itime<nTime;++itime) { for (Int ich=0;ich<nChan;++ich) { if (wt(itime,ich)>0.0) { x(itime,ich)/=wt(itime,ich); y(itime,ich)/=wt(itime,ich); sig(itime,ich)=sqrt(1.0/wt(itime,ich)); mask(itime,ich)=true; } else sig(itime,ich)=DBL_MAX; // ~zero weight } } // Solve for each channel Vector<Complex> Cph(nChan); Cph.set(Complex(1.0,0.0)); Float currAmb(1.0); Bool report(false); for (Int ich=0;ich<nChan;++ich) { if (sum(wt.column(ich))>0.0) { report=true; LinearFit<Double> phfitter; Polynomial<AutoDiff<Double> > line(1); phfitter.setFunction(line); Vector<Bool> m(mask.column(ich)); // Fit shallow and steep, and always prefer shallow // Assumed shallow solve: Vector<Double> solnA; solnA.assign(phfitter.fit(x.column(ich),y.column(ich),sig.column(ich),&m)); // Assumed steep solve: Vector<Double> solnB; solnB.assign(phfitter.fit(y.column(ich),x.column(ich),sig.column(ich),&m)); Double Xph(0.0); if (abs(solnA(1))<abs(solnB(1))) { Xph=atan(solnA(1)); } else { Xph=atan(1.0/solnB(1)); } Cph(ich)=currAmb*Complex(DComplex(cos(Xph),sin(Xph))); // Watch for and remove ambiguity changes which can // occur near Xph~= +/-90 deg (the atan above can jump) // (NB: this does _not_ resolve the amb; it merely makes // it consistent within the spw) if (ich>0) { // If Xph changes by more than pi/2, probably a ambig jump... Float dang=abs(arg(Cph(ich)/Cph(ich-1))); if (dang > (C::pi/2.)) { Cph(ich)*=-1.0; // fix this one currAmb*=-1.0; // reverse currAmb, so curr amb is carried forward // cout << " Found XY phase ambiguity jump at chan=" << ich << " in spw=" << currSpw(); } } // cout << " (" << currAmb << ")"; // cout << endl; // Set all antennas with this X-Y phase (as a complex number) solveCPar()(Slice(0,1,1),Slice(ich,1,1),Slice())=Cph(ich); solveParOK()(Slice(),Slice(ich,1,1),Slice())=true; } else { solveCPar()(Slice(0,1,1),Slice(ich,1,1),Slice())=Complex(1.0); solveParOK()(Slice(),Slice(ich,1,1),Slice())=false; } } if (report) cout << endl << "Spw = " << thisSpw << " (ich=" << nChan/2 << "/" << nChan << "): " << endl << " X-Y phase = " << arg(Cph[nChan/2])*180.0/C::pi << " deg." << endl; // Now fit for the source polarization { Vector<Double> wtf(nTime,0.0),sigf(nTime,0.0),xf(nTime,0.0),yf(nTime,0.0); Vector<Bool> maskf(nTime,false); Float wt0; Complex v; Double currtime(-1.0); Int iTime(-1); for (Int irow=0;irow<vb.nRow();++irow) { if (!vb.flagRow()(irow) && vb.antenna1()(irow)!=vb.antenna2()(irow)) { if (vb.time()(irow)!=currtime) { // Advance time index when we see a new time ++iTime; currtime=vb.time()(irow); // remember the new current time } // Weights not yet chan-dep wt0=(vb.weightMat()(1,irow)+vb.weightMat()(2,irow)); if (wt0>0.0) { for (Int ich=0;ich<nChan;++ich) { if (!vb.flag()(ich,irow)) { // Correct x-hands for xy-phase and add together v=vb.visCube()(1,ich,irow)/Cph(ich)+vb.visCube()(2,ich,irow)/conj(Cph(ich)); xf(iTime)+=Double(wt0*2.0*(vb.feed_pa(vb.time()(irow))(0))); yf(iTime)+=Double(wt0*real(v)/2.0); wtf(iTime)+=Double(wt0); } } } } } // Normalize data by accumulated weights for (Int itime=0;itime<nTime;++itime) { if (wtf(itime)>0.0) { xf(itime)/=wtf(itime); yf(itime)/=wtf(itime); sigf(itime)=sqrt(1.0/wtf(itime)); maskf(itime)=true; } else sigf(itime)=DBL_MAX; // ~zero weight } // p0=Q, p1=U, p2 = real part of net instr pol offset // x is TWICE the parallactic angle CompiledFunction<AutoDiff<Double> > fn; fn.setFunction("-p0*sin(x) + p1*cos(x) + p2"); LinearFit<Double> fitter; fitter.setFunction(fn); Vector<Double> soln=fitter.fit(xf,yf,sigf,&maskf); QU_(0,thisSpw) = soln(0); QU_(1,thisSpw) = soln(1); cout << " Fractional Poln: " << "Q = " << QU_(0,thisSpw) << ", " << "U = " << QU_(1,thisSpw) << "; " << "P = " << sqrt(soln(0)*soln(0)+soln(1)*soln(1)) << ", " << "X = " << atan2(soln(1),soln(0))*90.0/C::pi << "deg." << endl; cout << " Net (over baselines) instrumental polarization: " << soln(2) << endl; } } // Solve for the X-Y phase from the cross-hand's slope in R/I void GlinXphJones::solveOne(SDBList& sdbs) { // ensure if (QU_.shape()!=IPosition(2,2,nSpw())) { QU_.resize(2,nSpw()); QU_.set(0.0); } Int thisSpw=sdbs.aggregateSpw(); // We are actually solving for all channels simultaneously solveCPar().reference(solveAllCPar()); solveParOK().reference(solveAllParOK()); solveParErr().reference(solveAllParErr()); solveParSNR().reference(solveAllParSNR()); // Fill solveCPar() with 1, nominally, and flagged solveCPar()=Complex(1.0); solveParOK()=false; Int nChan=sdbs.nChannels(); // Number of datapoints in fit is the number of SDBs Int nSDB=sdbs.nSDB(); Matrix<Double> x(nSDB,nChan,0.0),y(nSDB,nChan,0.0),wt(nSDB,nChan,0.0),sig(nSDB,nChan,0.0); Matrix<Bool> mask(nSDB,nChan,false); mask.set(false); Complex v(0.0); Float wt0(0.0); for (Int isdb=0;isdb<nSDB;++isdb) { SolveDataBuffer& sdb(sdbs(isdb)); for (Int irow=0;irow<sdb.nRows();++irow) { if (!sdb.flagRow()(irow) && sdb.antenna1()(irow)!=sdb.antenna2()(irow)) { for (Int ich=0;ich<nChan;++ich) { if (!sdb.flagCube()(1,ich,irow)) { wt0=sdb.weightSpectrum()(1,ich,irow); v=sdb.visCubeCorrected()(1,ich,irow); x(isdb,ich)+=Double(wt0*real(v)); y(isdb,ich)+=Double(wt0*imag(v)); wt(isdb,ich)+=Double(wt0); } if (!sdb.flagCube()(2,ich,irow)) { wt0=sdb.weightSpectrum()(2,ich,irow); v=conj(sdb.visCubeCorrected()(2,ich,irow)); x(isdb,ich)+=Double(wt0*real(v)); y(isdb,ich)+=Double(wt0*imag(v)); wt(isdb,ich)+=Double(wt0); } } } } } // Normalize data by accumulated weights for (Int isdb=0;isdb<nSDB;++isdb) { for (Int ich=0;ich<nChan;++ich) { if (wt(isdb,ich)>0.0) { x(isdb,ich)/=wt(isdb,ich); y(isdb,ich)/=wt(isdb,ich); sig(isdb,ich)=sqrt(1.0/wt(isdb,ich)); mask(isdb,ich)=true; } else sig(isdb,ich)=DBL_MAX; // ~zero weight } } // Solve for each channel Vector<Complex> Cph(nChan); Cph.set(Complex(1.0,0.0)); Float currAmb(1.0); Bool report(false); for (Int ich=0;ich<nChan;++ich) { if (sum(wt.column(ich))>0.0) { report=true; LinearFit<Double> phfitter; Polynomial<AutoDiff<Double> > line(1); phfitter.setFunction(line); Vector<Bool> m(mask.column(ich)); // Fit shallow and steep, and always prefer shallow // Assumed shallow solve: Vector<Double> solnA; solnA.assign(phfitter.fit(x.column(ich),y.column(ich),sig.column(ich),&m)); // Assumed steep solve: Vector<Double> solnB; solnB.assign(phfitter.fit(y.column(ich),x.column(ich),sig.column(ich),&m)); Double Xph(0.0); if (abs(solnA(1))<abs(solnB(1))) { Xph=atan(solnA(1)); } else { Xph=atan(1.0/solnB(1)); } Cph(ich)=currAmb*Complex(DComplex(cos(Xph),sin(Xph))); // Watch for and remove ambiguity changes which can // occur near Xph~= +/-90 deg (the atan above can jump) // (NB: this does _not_ resolve the amb; it merely makes // it consistent within the spw) if (ich>0) { // If Xph changes by more than pi/2, probably a ambig jump... Float dang=abs(arg(Cph(ich)/Cph(ich-1))); if (dang > (C::pi/2.)) { Cph(ich)*=-1.0; // fix this one currAmb*=-1.0; // reverse currAmb, so curr amb is carried forward // cout << " Found XY phase ambiguity jump at chan=" << ich << " in spw=" << currSpw(); } } // cout << " (" << currAmb << ")"; // cout << endl; // Set all antennas with this X-Y phase (as a complex number) solveCPar()(Slice(0,1,1),Slice(ich,1,1),Slice())=Cph(ich); solveParOK()(Slice(),Slice(ich,1,1),Slice())=true; } else { solveCPar()(Slice(0,1,1),Slice(ich,1,1),Slice())=Complex(1.0); solveParOK()(Slice(),Slice(ich,1,1),Slice())=false; } } if (report) cout << endl << "Spw = " << thisSpw << " (ich=" << nChan/2 << "/" << nChan << "): " << endl << " X-Y phase = " << arg(Cph[nChan/2])*180.0/C::pi << " deg." << endl; // Now fit for the source polarization { Vector<Double> wtf(nSDB,0.0),sigf(nSDB,0.0),xf(nSDB,0.0),yf(nSDB,0.0); Vector<Bool> maskf(nSDB,false); Float wt0; Complex v; for (Int isdb=0;isdb<nSDB;++isdb) { SolveDataBuffer& sdb(sdbs(isdb)); for (Int irow=0;irow<sdb.nRows();++irow) { if (!sdb.flagRow()(irow) && sdb.antenna1()(irow)!=sdb.antenna2()(irow)) { Float fpa(sdb.feedPa()(0)); // assumes same for all antennas! for (Int ich=0;ich<nChan;++ich) { if (!sdb.flagCube()(1,ich,irow)) { // Correct x-hands for xy-phase and add together wt0=sdb.weightSpectrum()(1,ich,irow); v=sdb.visCubeCorrected()(1,ich,irow)/Cph(ich); xf(isdb)+=Double(wt0*2.0*(fpa)); yf(isdb)+=Double(wt0*real(v)); wtf(isdb)+=Double(wt0); } if (!sdb.flagCube()(2,ich,irow)) { // Correct x-hands for xy-phase and add together wt0=sdb.weightSpectrum()(2,ich,irow); v=sdb.visCubeCorrected()(2,ich,irow)/conj(Cph(ich)); xf(isdb)+=Double(wt0*2.0*(fpa)); yf(isdb)+=Double(wt0*real(v)); wtf(isdb)+=Double(wt0); } } } } } // Normalize data by accumulated weights for (Int isdb=0;isdb<nSDB;++isdb) { if (wtf(isdb)>0.0) { xf(isdb)/=wtf(isdb); yf(isdb)/=wtf(isdb); sigf(isdb)=sqrt(1.0/wtf(isdb)); maskf(isdb)=true; } else sigf(isdb)=DBL_MAX; // ~zero weight } // p0=Q, p1=U, p2 = real part of net instr pol offset // x is TWICE the parallactic angle CompiledFunction<AutoDiff<Double> > fn; fn.setFunction("-p0*sin(x) + p1*cos(x) + p2"); LinearFit<Double> fitter; fitter.setFunction(fn); Vector<Double> soln=fitter.fit(xf,yf,sigf,&maskf); srcPolPar().resize(2); srcPolPar()(0)=soln(0); srcPolPar()(1)=soln(1); QU_(0,thisSpw) = soln(0); QU_(1,thisSpw) = soln(1); cout << " Fractional Poln: " << "Q = " << QU_(0,thisSpw) << ", " << "U = " << QU_(1,thisSpw) << "; " << "P = " << sqrt(soln(0)*soln(0)+soln(1)*soln(1)) << ", " << "X = " << atan2(soln(1),soln(0))*90.0/C::pi << "deg." << endl; cout << " Net (over baselines) instrumental polarization: " << soln(2) << endl; } } void GlinXphJones::globalPostSolveTinker() { // Add QU info the the keywords TableRecord& tr(ct_->rwKeywordSet()); Record qu; qu.define("QU",QU_); tr.defineRecord("QU",qu); } // ********************************************************** // GlinXphfJones Implementations // // Constructor GlinXphfJones::GlinXphfJones(VisSet& vs) : VisCal(vs), // virtual base VisMueller(vs), // virtual base GlinXphJones(vs) // immediate parent { if (prtlev()>2) cout << "GlinXphf::GlinXphf(vs)" << endl; } GlinXphfJones::GlinXphfJones(String msname,Int MSnAnt,Int MSnSpw) : VisCal(msname,MSnAnt,MSnSpw), // virtual base VisMueller(msname,MSnAnt,MSnSpw), // virtual base GlinXphJones(msname,MSnAnt,MSnSpw) // immediate parent { if (prtlev()>2) cout << "GlinXphf::GlinXphf(msname,MSnAnt,MSnSpw)" << endl; } GlinXphfJones::GlinXphfJones(const MSMetaInfoForCal& msmc) : VisCal(msmc), // virtual base VisMueller(msmc), // virtual base GlinXphJones(msmc) // immediate parent { if (prtlev()>2) cout << "GlinXphf::GlinXphf(msmc)" << endl; } GlinXphfJones::GlinXphfJones(const Int& nAnt) : VisCal(nAnt), VisMueller(nAnt), GlinXphJones(nAnt) { if (prtlev()>2) cout << "GlinXphf::GlinXphf(nAnt)" << endl; } GlinXphfJones::~GlinXphfJones() { if (prtlev()>2) cout << "GlinXphf::~GlinXphf()" << endl; } } //# NAMESPACE CASA - END