// ----------------------------------------------------------------------------- /* CalStatsDerived.cc Description: ------------ This file contains the member functions of the classes derived from CalStats. Classes: -------- CalStatsReal - This class feeds real data to the CalStats base class. CalStatsAmp - This class converts complex data to amplitudes and initializes the CalStats base class. CalStatsPhase - This class converts complex data to phases and initializes the CalStats base class. Inhertited classes: ------------------- CalStats - This class calculates statistics on CASA caltables. Modification history: --------------------- 2011 Nov 15 - Nick Elias, NRAO Initial version created with classes CalStatsAmp and CalStatsPhase. 2011 Dec 11 - Nick Elias, NRAO Class CalStatsReal added. 2012 Jan 25 - Nick Elias, NRAO Logging capability added. Error checking added. */ // ----------------------------------------------------------------------------- // Includes // ----------------------------------------------------------------------------- #include <calanalysis/CalAnalysis/CalStatsDerived.h> #include <casacore/casa/Utilities/GenSort.h> // ----------------------------------------------------------------------------- // Start of casa namespace // ----------------------------------------------------------------------------- using namespace casacore; namespace casa { const uInt CalStatsPhase::NUM_ITER_UNWRAP = 50; const Double CalStatsPhase::NEW_RANGE_FACTOR = 5.0; // ----------------------------------------------------------------------------- // Start of CalStatsReal class // ----------------------------------------------------------------------------- /* CalStatsReal Description: ------------ This class feeds real data to the CalStats base class. Inhertited classes: ------------------- CalStats - This class calculates statistics of new CASA caltables. Class public member functions: ------------------------------ CalStatsReal - This constructor feeds real data to the CalStats base class. ~CalStatsReal - This destructor deallocates the internal memory of an instance. 2011 Dec 11 - Nick Elias, NRAO Initial version created with public member functions CalStatsReal() and ~CalStatsReal(). */ // ----------------------------------------------------------------------------- // Start of CalStatsReal public member functions // ----------------------------------------------------------------------------- /* CalStatsReal::CalStatsReal Description: ------------ This class feeds real data to the CalStats base class.. NB: The FEED axis is always included as an iteration axis by default because one cannot perform a fit along it. The other iteration axis is defined by the user. NB: The default CalStats constructor is called first as the default, then the standard one is called at the end. Inputs: ------- oValue - This reference to a Cube<Double> instance contains the values. oValueErr - This reference to a Cube<Double> instance contains the value errors. oFlag - This reference to a Cube<Bool> instance contains the flags. oFeed - This reference to a Vector<String> instance is the feed abscissae. oFrequency - This reference to a Vector<Double> instance is the frequency abscissae. oTime - This reference to a Vector<Double> instance is the time abscissae. eAxisIterUserID - This reference to a CalStats::AXIS enum contains either the FREQUENCY or TIME iteration axes (user defined). Outputs: -------- None. Modification history: --------------------- 2011 Dec 11 - Nick Elias, NRAO Initial version. 2012 Jan 25 - Nick Elias, NRAO Error checking added. */ // ----------------------------------------------------------------------------- CalStatsReal::CalStatsReal( const Cube<Double>& oValue, const Cube<Double>& oValueErr, const Cube<Bool>& oFlag, const Vector<String>& oFeed, const Vector<Double>& oFrequency, const Vector<Double>& oTime, const CalStats::AXIS& eAxisIterUserID ) : CalStats() { // Create an instance of the CalStats base class constructor and copy its // state to this CalStatsReal instance CalStats* poCS; try { poCS = new CalStats( oValue, oValueErr, oFlag, oFeed, oFrequency, oTime, eAxisIterUserID ); } catch ( AipsError oAE ) { throw( oAE ); } oAxisIterID = IPosition( poCS->axisIterID() ); eAxisNonIterID = poCS->axisNonIterID(); oAxisIterFeed = Vector<String>( poCS->axisIterFeed() ); oAxisIterUser = Vector<Double>( poCS->axisIterUser() ); oAxisNonIter = Vector<Double>( poCS->axisNonIter() ); oStatsShape = IPosition( poCS->statsShape() ); poValue = new Cube<Double>( poCS->value() ); poValueErr = new Cube<Double>( poCS->valueErr() ); poFlag = new Cube<Bool>( poCS->flag() ); poValueIter = new ArrayIterator<Double>( *poValue, oAxisIterID, false ); poValueIter->reset(); poValueErrIter = new ArrayIterator<Double>( *poValueErr, oAxisIterID, false ); poValueErrIter->reset(); poFlagIter = new ArrayIterator<Bool>( *poFlag, oAxisIterID, false ); poFlagIter->reset(); delete poCS; // Return return; } // ----------------------------------------------------------------------------- /* CalStatsReal::~CalStatsReal Description: ------------ This destructor deallocates the internal memory of an instance. Inputs: ------- None. Outputs: -------- None. Modification history: --------------------- 2011 Dec 11 - Nick Elias, NRAO Initial version. */ // ----------------------------------------------------------------------------- CalStatsReal::~CalStatsReal( void ) {} // ----------------------------------------------------------------------------- // End of CalStatsReal public member functions // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // End of CalStatsReal class // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // Start of CalStatsAmp class // ----------------------------------------------------------------------------- /* CalStatsAmp Description: ------------ This class converts complex data to amplitudes and initializes the CalStats base class. Inhertited classes: ------------------- CalStats - This class calculates statistics of new CASA caltables. CalStatsAmp public member functions: ------------------------------------ CalStatsAmp - This generic constructor converts complex data to amplitudes and initializes the CalStats base class. ~CalStatsAmp - This destructor deallocates the internal memory of an instance. CalStatsAmp public static member functions: ------------------------------------------- norm - This member function normalizes the amplitudes their errors. Modification history: --------------------- 2011 Nov 15 - Nick Elias, NRAO Initial version created with public member functions are CalStatsAmp() and ~CalStatsAmp(); and public static member function is norm(). */ // ----------------------------------------------------------------------------- // Start of CalStatsAmp public member functions // ----------------------------------------------------------------------------- /* CalStatsAmp::CalStatsAmp Description: ------------ This class converts complex data and their errors to amplitudes and their errors and initializes the CalStats base class. NB: The FEED axis is always included as an iteration axis by default because one cannot perform a fit along it. The other iteration axis is defined by the user. NB: The default CalStats constructor is called first as the default, then the standard one is called at the end. NB: The are no input data real-imaginary cross correlations available, so the amplitude errors do not include them. Inputs: ------- oValue - This reference to a Cube<DComplex> instance contains the values. oValueErr - This reference to a Cube<Double> instance contains the value errors (real and imaginary parts). oFlag - This reference to a Cube<Bool> instance contains the flags. oFeed - This reference to a Vector<String> instance is the feed abscissae. oFrequency - This reference to a Vector<Double> instance is the frequency abscissae. oTime - This reference to a Vector<Double> instance is the time abscissae. eAxisIterUserID - This reference to a CalStats::AXIS enum contains either the FREQUENCY or TIME iteration axes (user defined). bNorm - This reference to a Bool variable contains the normalization flag (true = normalize, false = don't normalize). Outputs: -------- None. Modification history: --------------------- 2011 Nov 15 - Nick Elias, NRAO Initial version. 2012 Jan 25 - Nick Elias, NRAO Error checking added. 2012 Feb 15 - Nick Elias, NRAO Value error input parameter changed from DComplex to Double. 2012 Mar 28 - Nick Elias, NRAO Changed the normalization code so that vectors are iteratively fed to CalStatsAmp::norm(). 2012 Mar 29 - Nick Elias, NRAO Member function can now normalize along the frequency and time axes. */ // ----------------------------------------------------------------------------- CalStatsAmp::CalStatsAmp( const Cube<DComplex>& oValue, const Cube<Double>& oValueErr, const Cube<Bool>& oFlag, const Vector<String>& oFeed, const Vector<Double>& oFrequency, const Vector<Double>& oTime, const CalStats::AXIS& eAxisIterUserID, const Bool& bNorm ) : CalStats() { // Calculate the amplitudes and their errors Cube<Double> oAmp( amplitude( oValue ) ); Cube<Double> oAmpErr = oValueErr.copy(); // Create the iterators. The input flag cube is copied since it cannot be // modified. IPosition oIterShape( 2, (ssize_t) CalStats::FEED, eAxisIterUserID ); Cube<Bool> oFlagCopy( oFlag.copy() ); ArrayIterator<Bool> oFlagIter( oFlagCopy, oIterShape, false ); ArrayIterator<Double> oAmpIter( oAmp, oIterShape, false ); ArrayIterator<Double> oAmpErrIter( oAmpErr, oIterShape, false ); // If selected, normalize the amplitudes and their errors while ( bNorm && !oAmpIter.pastEnd() ) { uInt uiNumAbs = 0; if ( eAxisIterUserID == CalStats::TIME ) { uiNumAbs = oFrequency.nelements(); } else { uiNumAbs = oTime.nelements(); } if ( uiNumAbs <= 1 ) { oFlagIter.next(); oAmpIter.next(); oAmpErrIter.next(); continue; } IPosition oShape( 1, uiNumAbs ); Vector<Bool> oFlagV( oFlagIter.array().copy().reform(oShape) ); Vector<Double> oAmpV( oAmpIter.array().copy().reform(oShape) ); Vector<Double> oAmpErrV( oAmpErrIter.array().copy().reform(oShape) ); norm( oAmpV, oAmpErrV, oFlagV ); oFlagIter.array() = oFlagV; oAmpIter.array() = oAmpV; oAmpErrIter.array() = oAmpErrV; oFlagIter.next(); oAmpIter.next(); oAmpErrIter.next(); } // Create an instance of the CalStats base class constructor and copy its // state to this CalStatsAmp instance CalStats* poCS; try { poCS = new CalStats( oAmp, oAmpErr, oFlagCopy, oFeed, oFrequency, oTime, eAxisIterUserID ); } catch ( AipsError oAE ) { throw( oAE ); } oAxisIterID = IPosition( poCS->axisIterID() ); eAxisNonIterID = poCS->axisNonIterID(); oAxisIterFeed = Vector<String>( poCS->axisIterFeed() ); oAxisIterUser = Vector<Double>( poCS->axisIterUser() ); oAxisNonIter = Vector<Double>( poCS->axisNonIter() ); oStatsShape = IPosition( poCS->statsShape() ); poValue = new Cube<Double>( poCS->value() ); poValueErr = new Cube<Double>( poCS->valueErr() ); poFlag = new Cube<Bool>( poCS->flag() ); poValueIter = new ArrayIterator<Double>( *poValue, oAxisIterID, false ); poValueIter->reset(); poValueErrIter = new ArrayIterator<Double>( *poValueErr, oAxisIterID, false ); poValueErrIter->reset(); poFlagIter = new ArrayIterator<Bool>( *poFlag, oAxisIterID, false ); poFlagIter->reset(); delete poCS; // Return return; } // ----------------------------------------------------------------------------- /* CalStatsAmp::~CalStatsAmp Description: ------------ This destructor deallocates the internal memory of an instance. Inputs: ------- None. Outputs: -------- None. Modification history: --------------------- 2011 Nov 15 - Nick Elias, NRAO Initial version. */ // ----------------------------------------------------------------------------- CalStatsAmp::~CalStatsAmp( void ) {} // ----------------------------------------------------------------------------- // End of CalStatsAmp public member functions // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // Start of CalStatsAmp public static member functions // ----------------------------------------------------------------------------- /* CalStatsAmp::norm Description: ------------ This member function normalizes the amplitudes and their errors. NB: The normalization is applied only along the frequency axis. NB: The normalization is applied only when the number of unflagged frequencies is greater than 1. NB: All flags corresponding to amplitudes less then 1.0E-08 times the peak amplitude (along the FREQUENCY axis) are updated to true. Inputs: ------- oAmp - This reference to a Vector<Double> instance contains the unnormalized amplitudes. oAmpErr - This reference to a Vector<Double> instance contains the unnormalized amplitude errors. oFlag - This reference to a Vector<Bool> instance contains the flags. Outputs: -------- oAmp - This reference to a Vector<Double> instance contains the normalized amplitudes. oAmpErr - This reference to a Vector<Double> instance contains the normalized amplitude errors. oFlag - This reference to a Vector<Bool> instance contains the flags. Modification history: --------------------- 2011 Dec 11 - Nick Elias, NRAO Initial version. 2012 Jan 25 - Nick Elias, NRAO Logging capability added. 2012 Mar 28 - Nick Elias, NRAO Eliminated iteration and made input amplitude, amplitude error, and flag cubes into vectors. Flags are now actually used. */ // ----------------------------------------------------------------------------- void CalStatsAmp::norm( Vector<Double>& oAmp, Vector<Double>& oAmpErr, Vector<Bool>& oFlag ) { // Eliminate the flagged amplitudes and their errors MaskedArray<Double> oAmpM( oAmp, !oFlag ); Vector<Double> oAmpC( oAmpM.getCompressedArray() ); MaskedArray<Double> oAmpErrM( oAmpErr, !oFlag ); Vector<Double> oAmpErrC( oAmpErrM.getCompressedArray() ); // Return if the length of the abscissa is unity uInt uiNumAbsC = oAmpC.nelements(); if ( uiNumAbsC <= 1 ) { LogIO log( LogOrigin( "CalStatsAmp", "norm", WHERE ) ); // An entire scan for a basline being flagged can cause this // This is not entirly uncommon so this was switched from WARN -> NORMAL log << LogIO::NORMAL << "Abscissa has a dimension <= 1, no normalization" << LogIO::POST; return; } // Normalize the amplitudes and their errors along the abscissa. Flag all low // amplitudes. Double dAmpMax = max( oAmpC ); Vector<Bool> oFlagLow( oAmp <= (Double) 1.0E-08*dAmpMax ); oFlag = oFlag || oFlagLow; uInt uiNumAbs = oAmp.nelements(); for ( uInt a=0; a<uiNumAbs; a++ ) { if ( !oFlag[a] ) { oAmp[a] /= dAmpMax; oAmpErr[a] /= dAmpMax; } } // Return return; } // ----------------------------------------------------------------------------- // End of CalStatsAmp public static member functions // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // End of CalStatsAmp class // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // Start of CalStatsPhase class // ----------------------------------------------------------------------------- /* CalStatsPhase Description: ------------ This class converts complex data to phases and initializes the CalStats base class. Inhertited classes: ------------------- CalStats - This class calculates statistics of new CASA caltables. Class public member functions: ------------------------------ CalStatsPhase - This generic constructor converts complex data to amplitudes and initializes the CalStats base class. ~CalStatsPhase - This destructor deallocates the internal memory of an instance. CalStatsPhase public static member functions: --------------------------------------------- unwrapGD - This member function unwraps the phases along the frequency axis with respect to the group delay. unwrapSimple - This member function performs a simple unwrapping procedure for both frequency and temporal abscissae. CalStatsPhase private static member functions: ---------------------------------------------- fringePacket2 - This member function forms the squared-amplitude fringe packet. CalStatsPhase templated private static member functions: -------------------------------------------------------- maxLocation - This member function finds the abscissa corresponding to the peak value of an ordinate vector. Modification history: --------------------- 2011 Nov 15 - Nick Elias, NRAO Initial version created with public member functions are CalStatsPhase() and ~CalStatsPhase(); and public static member function is unwrap(). 2012 Mar 27 - Nick Elias, NRAO Private static member functions fringePacket2() and maxLocation() added. Private static member variables NUM_ITER_UNWRAP and NEW_RANGE_FACTOR added. 2012 Mar 30 - Nick Elias, NRAO Public static member function unwrap() renamed to unwrapGD(). Public static member function unwrapSimple() added. */ // ----------------------------------------------------------------------------- // Start of CalStatsPhase public member functions // ----------------------------------------------------------------------------- /* CalStatsPhase::CalStatsPhase Description: ------------ This class converts complex data and their errors to phases and their errors and initializes the CalStats base class. NB: The FEED axis is always included as an iteration axis by default because one cannot perform a fit along it. The other iteration axis is defined by the user. NB: All flags corresponding to amplitudes less then 1.0E-08 times the peak amplitude are updated to true. NB: The default CalStats constructor is called first as the default, then the standard one is called at the end. NB: The are no input data real-imaginary cross correlations available, so the phase errors do not include them. NB: For unwrapping along the time axis (iteration axis CalStats::FREQUENCY), the unwrapSimple() function is used. For unwrapping along the frequency axis (iteration axis CalStats::TIME), dJumpMax==0.0 leads to unwrapGD() while dJumpMax!=0.0 leads to unwrapSimple(). Inputs: ------- oValue - This reference to a Cube<DComplex> instance contains the values. oValueErr - This reference to a Cube<Double> instance contains the value errors (real and imaginary parts). oFlag - This reference to a Cube<Bool> instance contains the flags. oFeed - This reference to a Vector<String> instance is the feed abscissae. oFrequency - This reference to a Vector<Double> instance is the frequency abscissae. oTime - This reference to a Vector<Double> instance is the time abscissae. eAxisIterUserID - This reference to a CalStats::AXIS enum contains either the CalStats::FREQUENCY or CalStats::TIME iteration axes (user defined). bUnwrap - This reference to a Bool variable contains the unwrapping flag (true = unwrap, false = don't unwrap). dJumpMax - This reference to a Double variable contains the maximum deviation from +/- M_PI for adjacent points to be unwrapped by +/- 2.0*M_PI (in radians). This parameter is always used when the specified iteration axis is CalStats::FREQUENCY (unwrapping along the CalStats::TIME axis). If the specified iteration axis is CalStats::TIME (unwrapping along the CalStats::FREQUENCY axis), this parameter selects the type of unwrapping (dJumpMax==0.0 --> group-delay unwrapping, dJumpMax != 0.0 --> simple unwrapping). Outputs: -------- None. Modification history: --------------------- 2011 Nov 15 - Nick Elias, NRAO Initial version. 2011 Dec 11 - Nick Elias, NRAO Flag updates according to low amplitude was added. 2012 Jan 25 - Nick Elias, NRAO Error checking added. 2012 Feb 15 - Nick Elias, NRAO Value error input parameter changed from DComplex to Double. 2012 Mar 28 - Nick Elias, NRAO Changed the unwrapping code so that vectors are iteratively fed to CalStatsPhase::unwrapSimple() or CalStatsPhase::unwrapGD(). 2012 Apr 01 - Nick Elias, NRAO Input parameter dJumpMax added. */ // ----------------------------------------------------------------------------- CalStatsPhase::CalStatsPhase( const Cube<DComplex>& oValue, const Cube<Double>& oValueErr, const Cube<Bool>& oFlag, const Vector<String>& oFeed, const Vector<Double>& oFrequency, const Vector<Double>& oTime, const CalStats::AXIS& eAxisIterUserID, const Bool& bUnwrap, const Double& dJumpMax ) : CalStats() { // Calculate the phases and the initial phase error cube (set to 0.0) Cube<Double> oPhase( phase(oValue) ); Cube<Double> oPhaseErr( oPhase.shape() ); // Create the iterators. The input flag cube is copied since it cannot be // modified. IPosition oIterShape( 2, (ssize_t) CalStats::FEED, eAxisIterUserID ); ReadOnlyArrayIterator<DComplex> oValueIter( oValue, oIterShape, false ); ReadOnlyArrayIterator<Double> oValueErrIter( oValueErr, oIterShape, false ); Cube<Bool> oFlagCopy( oFlag.copy() ); ArrayIterator<Bool> oFlagIter( oFlagCopy, oIterShape, false ); ArrayIterator<Double> oPhaseIter( oPhase, oIterShape, false ); ArrayIterator<Double> oPhaseErrIter( oPhaseErr, oIterShape, false ); // If selected, unwrap the phases while ( bUnwrap && !oPhaseIter.pastEnd() ) { uInt uiNumAbs = 0; if ( eAxisIterUserID == CalStats::TIME ) { uiNumAbs = oFrequency.nelements(); } else { uiNumAbs = oTime.nelements(); } if ( uiNumAbs <= 1 ) { oFlagIter.next(); oPhaseIter.next(); continue; } IPosition oShape( 1, uiNumAbs ); Vector<Bool> oFlagV( oFlagIter.array().copy().reform(oShape) ); Vector<Double> oPhaseV( oPhaseIter.array().copy().reform(oShape) ); if ( eAxisIterUserID == CalStats::TIME ) { if ( dJumpMax == 0.0 ) { unwrapGD( oPhaseV, oFrequency, oFlagV ); } else { unwrapSimple( oPhaseV, dJumpMax, oFlagV ); } } else { unwrapSimple( oPhaseV, dJumpMax, oFlagV ); } oPhaseIter.array() = oPhaseV; oFlagIter.next(); oPhaseIter.next(); } // Reset the iterators oFlagIter.reset(); oPhaseIter.next(); // Calculate the phase errors. They require dividing by amplitude. Set the // flags according to low amplitudes (corresponding to phase errors greater // than M_PI). All flagged phase errors are set to M_PI. The iteration axes // are FEED and TIME. A FREQUENCY for loop is located inside the FEED/TIME // iteration loop to set the errors. while ( !oValueIter.pastEnd() ) { uInt uiNumAbs = oValueIter.array().nelements(); IPosition oShape( 1, uiNumAbs ); Vector<Double> oPhaseErrV( oShape ); Vector<DComplex> oValueV( oValueIter.array().copy().reform(oShape) ); Vector<Double> oValueErrV( oValueErrIter.array().copy().reform(oShape) ); Vector<Double> oAmpV( amplitude(oValueV) ); Vector<Bool> oFlagV( oFlagIter.array().copy().reform(oShape) ); Vector<Bool> oFlagLow( oAmpV <= oValueErrV/((Double) M_PI) ); oFlagV = oFlagV || oFlagLow; oFlagIter.array() = oFlagV; for ( uInt a=0; a<uiNumAbs; a++ ) { if ( !oFlagV[a] ) { oPhaseErrV[a] = oValueErrV[a] / oAmpV[a]; } else { oPhaseErrV[a] = (Double) M_PI; } } oPhaseErrIter.array() = oPhaseErrV; oValueIter.next(); oValueErrIter.next(); oFlagIter.next(); oPhaseErrIter.next(); } // Create an instance of the CalStats base class constructor and copy its // state to this CalStatsPhase instance CalStats* poCS; try { poCS = new CalStats( oPhase, oPhaseErr, oFlagCopy, oFeed, oFrequency, oTime, eAxisIterUserID ); } catch ( AipsError oAE ) { throw( oAE ); } oAxisIterID = IPosition( poCS->axisIterID() ); eAxisNonIterID = poCS->axisNonIterID(); oAxisIterFeed = Vector<String>( poCS->axisIterFeed() ); oAxisIterUser = Vector<Double>( poCS->axisIterUser() ); oAxisNonIter = Vector<Double>( poCS->axisNonIter() ); oStatsShape = IPosition( poCS->statsShape() ); poValue = new Cube<Double>( poCS->value() ); poValueErr = new Cube<Double>( poCS->valueErr() ); poFlag = new Cube<Bool>( poCS->flag() ); poValueIter = new ArrayIterator<Double>( *poValue, oAxisIterID, false ); poValueIter->reset(); poValueErrIter = new ArrayIterator<Double>( *poValueErr, oAxisIterID, false ); poValueErrIter->reset(); poFlagIter = new ArrayIterator<Bool>( *poFlag, oAxisIterID, false ); poFlagIter->reset(); delete poCS; // Return return; } // ----------------------------------------------------------------------------- /* CalStatsPhase::~CalStatsPhase Description: ------------ This destructor deallocates the internal memory of an instance. Inputs: ------- None. Outputs: -------- None. Modification history: --------------------- 2011 Nov 15 - Nick Elias, NRAO Initial version. */ // ----------------------------------------------------------------------------- CalStatsPhase::~CalStatsPhase( void ) {} // ----------------------------------------------------------------------------- // End of CalStatsPhase public member functions // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // Start of CalStatsPhase public static member functions // ----------------------------------------------------------------------------- /* CalStatsPhase::unwrapSimple Description: ------------ This member function performs a simple unwrapping procedure for both frequency and temporal abscissae. Algorithm: ---------- * If the first point is a little less than +M_PI and the second point is a little more than -M_PI, add 2.0*M_PI to the second point. * If the first point is a little more than -M_PI and the second point is a ittle less than +M_PI, subtract 2.0*M_PI from the second point. * "A little more" means within dJumpMax of +/- M_PI. * Flagged data are ignored, which could be a problem if a lot of them occur sequentially. Inputs: ------- oPhase - This reference to a Vector<Double> instance contains the wrapped phases. dJumpMax - This reference to a Double variable contains the maximum deviation from +/- M_PI for adjacent points to be unwrapped by +/- 2.0*M_PI (in radians). oFlag - This reference to a Vector<Bool> instance contains the flags. Outputs: -------- oPhase - This reference to a Vector<Double> instance contains the unwrapped phases. Modification history: --------------------- 2012 Mar 30 - Nick Elias, NRAO Initial version. */ // ----------------------------------------------------------------------------- void CalStatsPhase::unwrapSimple( Vector<Double>& oPhase, const Double& dJumpMax, const Vector<Bool>& oFlag ) { // Initialize the number of elements and make an immutable copy of the input // phase vector uInt uiNumAbs = oPhase.nelements(); Vector<Double> oPhaseIn( oPhase.copy() ); // Perform the simple unwrapping. The unwrapping occurs if subsequent points // are both within dJumpMax of M_PI or -M_PI. Flagged data are ignored. for ( uInt a=1; a<uiNumAbs; a++ ) { if ( oFlag[a-1] ) continue; uInt a2 = 0; for ( a2=a; a2<uiNumAbs; a2++ ) { if ( !oFlag[a2] ) break; } if ( a2 >= uiNumAbs ) return; // The first point is a little less than +M_PI and the second point is a // little more than -M_PI, add 2.0*M_PI to the second point if ( M_PI-oPhaseIn[a-1] <= dJumpMax && M_PI+oPhaseIn[a2] <= dJumpMax ) { for ( uInt a3=a2; a3<uiNumAbs; a3++ ) oPhase[a3] += 2.0 * M_PI; } // The first point is a little more than -M_PI and the second point is a // little less than +M_PI, subtract 2.0*M_PI from the second point if ( M_PI+oPhaseIn[a-1] <= dJumpMax && M_PI-oPhaseIn[a2] <= dJumpMax ) { for ( uInt a3=a2; a3<uiNumAbs; a3++ ) oPhase[a3] -= 2.0 * M_PI; } } // Return return; } // ----------------------------------------------------------------------------- /* CalStatsPhase::unwrapGD Description: ------------ This member function unwraps the phases along the frequency axis with respect to the group delay. NB: The unwrapping is applied only along the frequency axis. The unwrapping is applied only when the number of unflagged frequencies is greater than 1 (setting the bar very low). If the number of unflagged frequencies is small, you may get crappy results. NB: This algorithm should work better with low-S/N data compared to simple unwrapping, but it will not work if the S/N is abysmally low. To understand why this statement is true, consider an analogy with BPOLY. BPOLY is used when the S/N per channel is low. The fit uses all bandpass data at once to maximize the available S/N. This phase unwrapping algorithm creates a fringe packet, using all of the bandpass data at once, to estimate the delay before unwrapping. NB: If the true delay is outside of the unaliased search window, the unwrapping aliases into the search window. This scenario is OK if only continuous phase plotting is required. NB: If the dispersion or other wiggles are large, this technique may not work well. Algorithm: ---------- * Sort the input frequencies and form the differences. Sort the frequency differences. The desired frequency increment is the minimum value (except for zero, which arises datasets from spectral windows with the same frequencies; choose the next largest value). * Determine the maximum frequency. * For the first iteration: - Calculate the initial time increment using the maximum frequency (Nyquist criterion). - Calculate the initial time range using the frequency increment. - Form the initial time vector. The edges of the unaliased search window are +/- 0.5 times the initial time range (centered around zero group delay). - Form the initial squared-amplitude fringe packet. - Find the peak of the initial squared-amplitude fringe packet. The time corresponding to this peak is the initial group delay estimate. * For subsequent iterations: - Set the new time range to NEW_RANGE_FACTOR times the previous time increment. - Set the new time increment to the new time range divided by the number of times (this number is the same as the initial iteration and doesn't change). - Form the new time vector. The edges of the search window are +/- times the new time range (centered around the previous group delay estimate). - Form the new squared-amplitude fringe packet. - Find the peak of the new squared-amplitude fringe packet. The time corresponding to this peak is the new group delay estimate. - Has the group delay converged? If not repeat for another iteration, otherwise stop iterating. The maximum number of iterations is NUM_ITER_UNWRAP. * Form the phase versus frequency using the group delay. * Subtract the group delay phases from the input phases to form the differential delay. * Subtract the first differential delay from all of the differential delays. * Starting at the first frequency, search for phase jumps. For each one found, add/subtract 2.0*M_PI*N (N=the number of phase jumps; it can be positive or negative) for all subsequenct frequencies. Flagged data are not unwrapped. Inputs: ------- oPhase - This reference to a Vector<Double> instance contains the wrapped phases. oFrequency - This reference to a Vector<Double> instance is the frequency abscissa. oFlag - This reference to a Vector<Bool> instance contains the flags. Outputs: -------- oPhase - This reference to a Vector<Double> instance contains the unwrapped phases. Modification history: --------------------- 2011 Nov 15 - Nick Elias, NRAO Initial stub version. 2012 Jan 25 - Nick Elias, NRAO Logging capability added. 2012 Mar 27 - Nick Elias, NRAO Initial working version. 2012 Mar 28 - Nick Elias, NRAO Eliminated iteration and made input phase and flag cubes into vectors. Flags are now actually used. */ // ----------------------------------------------------------------------------- void CalStatsPhase::unwrapGD( Vector<Double>& oPhase, const Vector<Double>& oFrequency, const Vector<Bool>& oFlag ) { // Eliminate the flagged phases and frequencies MaskedArray<Double> oPhaseM( oPhase, !oFlag ); Vector<Double> oPhaseC( oPhaseM.getCompressedArray() ); MaskedArray<Double> oFrequencyM( oFrequency, !oFlag ); Vector<Double> oFrequencyC( oFrequencyM.getCompressedArray() ); // Return if the length of the frequency axis is unity uInt uiNumFrequencyC = oFrequencyC.nelements(); if ( uiNumFrequencyC <= 1 ) { LogIO log( LogOrigin( "CalStatsPhase", "unwrap", WHERE ) ); log << LogIO::WARN << "Frequency axis has a dimension <= 1, no unwrapping performed" << LogIO::POST; return; } // Calculate the minimum frequency increment. If there are overlapping // spectral windows, some of the frequency increments may be zero if the // duplicate channels are not flagged. Make sure to ignore all zeros. Vector<Double> oFreqDeltaC( uiNumFrequencyC-1 ); for ( uInt f=1; f<uiNumFrequencyC; f++ ) { oFreqDeltaC[f-1] = oFrequencyC[f] - oFrequencyC[f-1]; } Sort::Order eOrder = Sort::Ascending; Int iOptions = Sort::QuickSort; GenSort<Double>::sort( oFreqDeltaC, eOrder, (int) iOptions ); uInt fD = 0; for ( fD=0; fD<uiNumFrequencyC-1; fD++ ) { if ( oFreqDeltaC[fD] != 0.0 ) break; } if ( fD >= uiNumFrequencyC-1 ) { LogIO log( LogOrigin( "CalStatsPhase", "unwrap", WHERE ) ); log << "Something is very wrong with the frequencies" << LogIO::EXCEPTION; } Double dFreqDeltaC = oFreqDeltaC[fD]; // Calculate the maximum frequency Double dFreqMaxC = max( oFrequencyC ); // Calculate the initial time increment and range Double dTimeDelta = 0.5 / dFreqMaxC; Double dTimeRange = 1.0 / dFreqDeltaC; // Initialize the time vector. The center of the time range is zero group // delay. uInt uiNumTime = (uInt) fabs( dTimeRange/dTimeDelta + 1.0 ); Vector<Double> oTime( uiNumTime ); indgen<Double>( oTime, 0.0-0.5*dTimeRange, dTimeDelta ); // Form the squared magnitude of the initial fringe packet and find the group // delay Vector<Double> oFringePacket( fringePacket2( oPhaseC, oFrequencyC, oTime ) ); Double dGroupDelay = maxLocation<Double>( oTime, oFringePacket ); // Initialize the number of iterations uInt uiNumIter = 0; // Iteratively improve the group delay estimate while ( uiNumIter < NUM_ITER_UNWRAP ) { dTimeRange = NEW_RANGE_FACTOR * dTimeDelta; Double dTimeDeltaNew = dTimeRange / (uiNumTime+1); indgen<Double>( oTime, dGroupDelay-0.5*dTimeDelta, dTimeDeltaNew ); oFringePacket = fringePacket2( oPhaseC, oFrequencyC, oTime ); Double dGroupDelayNew = maxLocation<Double>( oTime, oFringePacket ); if ( fabs(dGroupDelayNew-dGroupDelay)/dTimeDelta < 1.0E-08 ) break; dTimeDelta = dTimeDeltaNew; dGroupDelay = dGroupDelayNew; uiNumIter++; } // If the number of iterations has been exceeded, print a warning and set // the group delay to the middle of the time range if ( uiNumIter > NUM_ITER_UNWRAP ) { dGroupDelay = mean( oTime ); LogIO log( LogOrigin( "CalStatsPhase", "unwrap", WHERE ) ); log << LogIO::WARN << "Number of iterations exceeded for group delay calculation\n" << "Using the mean time from the last iteration" << LogIO::POST; } // Calculate the differential phases, which are the input phases minus the // group delay. Make sure to subtract the initial value from all values. uInt uiNumFrequency = oFrequency.nelements(); Vector<Double> oDiffPhase( oPhase - 2.0*M_PI*oFrequency*dGroupDelay ); oDiffPhase -= oDiffPhase[0]; // If phase wraps are found, unwrap them. Flagged data are not unwrapped. for ( uInt f=1; f<uiNumFrequency; f++ ) { if ( oFlag[f-1] ) continue; uInt f2 = 0; for ( f2=f; f2<uiNumFrequency; f2++ ) if ( !oFlag[f2] ) break; if ( f2 >= uiNumFrequency ) break; Double dDiffPhase = oDiffPhase[f2] - oDiffPhase[f-1]; Int N = 0; while ( dDiffPhase + 2.0*M_PI*N <= -M_PI ) N++; while ( dDiffPhase + 2.0*M_PI*N > M_PI ) N--; if ( N == 0 ) continue; for ( uInt f3=f2; f3<uiNumFrequency; f3++ ) oPhase[f3] += 2.0 * M_PI * N; } // Return return; } // ----------------------------------------------------------------------------- // End of CalStatsPhase public static member functions // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // Start of CalStatsPhase private static member functions // ----------------------------------------------------------------------------- /* Description: ------------ This member function forms the squared-amplitude fringe packet. Inputs: ------- oPhase - This Vector<Double>() instance contains the wrapped phases. oFrequency - This Vector<Double>() instance contains the frequencies. oTime - This Vector<Double>() instance contains the times. Outputs: -------- The reference to the Vector<Double>() instance containing the squared-amplitude fringe packet, returned via the function value. Modification history: --------------------- 2012 Mar 27 - Nick Elias, NRAO Initial version. */ // ----------------------------------------------------------------------------- Vector<Double>& CalStatsPhase::fringePacket2( const Vector<Double>& oPhase, const Vector<Double>& oFrequency, const Vector<Double>& oTime ) { // Initialize the pointer to the squared-amplitude fringe packet uInt uiNumTime = oTime.nelements(); Vector<Double>* poFringePacket; poFringePacket = new Vector<Double>( uiNumTime, 0.0 ); // Calculate the complex fringe packet for all times using all frequencies and // phases uInt uiNumFrequency = oFrequency.nelements(); Vector<DComplex> oFringePacketC( uiNumTime, DComplex( 0.0, 0.0 ) ); for ( uInt f=0; f<uiNumFrequency; f++ ) { Vector<DComplex> oFringePacketCF( uiNumTime, DComplex( 0.0, 0.0 ) ); setReal( oFringePacketCF, cos( 2.0*M_PI*oFrequency[f]*oTime - oPhase[f] ) ); setImag( oFringePacketCF, sin( 2.0*M_PI*oFrequency[f]*oTime - oPhase[f] ) ); operator+=( oFringePacketC, oFringePacketCF ); } // Calculate and return the reference to the squared-amplitude fringe packet *poFringePacket = square( amplitude( oFringePacketC ) ); *poFringePacket /= (Double) uiNumFrequency * uiNumFrequency; return( *poFringePacket ); } // ----------------------------------------------------------------------------- // End of CalStatsPhase private static member functions // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // End of CalStatsPhase class // ----------------------------------------------------------------------------- }; // ----------------------------------------------------------------------------- // End of casa namespace // -----------------------------------------------------------------------------