LCOV - code coverage report
Current view: top level - synthesis/Utilities - SingleDishBeamUtil.cc (source / functions) Hit Total Coverage
Test: ctest_coverage.info Lines: 151 174 86.8 %
Date: 2023-11-06 10:06:49 Functions: 3 3 100.0 %

          Line data    Source code
       1             : #include <cassert>
       2             : #include <memory>
       3             : #include <iostream>
       4             : 
       5             : #include <casacore/casa/Arrays/ArrayMath.h>
       6             : #include <casacore/casa/Arrays/Vector.h>
       7             : #include <casacore/casa/Utilities/Assert.h>
       8             : #include <casacore/casa/Logging.h>
       9             : #include <casacore/measures/Measures/MDirection.h>
      10             : #include <casacore/measures/TableMeasures/ScalarMeasColumn.h>
      11             : #include <casacore/ms/MSSel/MSSelection.h>
      12             : #include <casacore/tables/TaQL/ExprNode.h>
      13             : 
      14             : #include <synthesis/Utilities/PointingDirectionCalculator.h>
      15             : #include <synthesis/Utilities/SingleDishBeamUtil.h>
      16             : 
      17             : using namespace std;
      18             : using namespace casacore;
      19             : 
      20             : #define _ORIGIN LogOrigin("SingleDishBeamUtil", __func__, WHERE)
      21             : 
      22             : namespace casa { //# NAMESPACE CASA - BEGIN
      23             : 
      24         242 : SingleDishBeamUtil::SingleDishBeamUtil(const MeasurementSet &ms,
      25             :                                        const String &referenceFrame,
      26             :                                        const String &movingSource,
      27             :                                        const String &pointingColumn,
      28         242 :                                        const String &antenna)
      29             :        : referenceFrame_(referenceFrame), movingSource_(movingSource),
      30         242 :          pointingColumn_(pointingColumn), antSel_(antenna)
      31             : {
      32         242 :   ms_ = new MeasurementSet(ms);
      33         242 :   directionUnit_ = Unit("rad");
      34         242 : }
      35             : 
      36         242 : Bool SingleDishBeamUtil::getMapPointings(Matrix<Double> &pointingList) {
      37             :     try {
      38         242 :         PointingDirectionCalculator calc(*ms_);
      39             : 
      40         242 :         calc.setDirectionColumn(pointingColumn_);
      41         242 :         calc.selectData(antSel_);
      42         242 :         calc.setFrame(referenceFrame_);
      43         242 :         MDirection::Types refType = MDirection::J2000; // any non planet value
      44         242 :         Bool status = False;
      45         242 :         status = MDirection::getType(refType, movingSource_);
      46         245 :         Bool doMovingSourceCorrection = (status == True &&
      47         245 :                 MDirection::N_Types < refType &&
      48           3 :                 refType < MDirection::N_Planets);
      49         242 :         if (doMovingSourceCorrection) {
      50           3 :             calc.setMovingSource(movingSource_);
      51             :         }
      52         242 :         calc.setDirectionListMatrixShape(PointingDirectionCalculator::COLUMN_MAJOR);
      53             : 
      54         242 :         pointingList = calc.getDirection();
      55         242 :         directionUnit_ = Unit("rad");
      56         242 :         Vector<Double> longitude = pointingList.column(0);
      57         242 :         Vector<Double> latitude = pointingList.column(1);
      58             : 
      59         242 :         if (longitude.size() < 2) return True; // no need for boundary check.
      60             : 
      61             :         // Diagnose if longitude values are divided by periodic boundary surface
      62             :         // (+-pi or 0, 2pi)
      63             :         // In this case, mean of longitude should be around 0 (+-pi) or pi (0,2pi)
      64             :         // and stddev of longitude array be around pi.
      65         222 :         Double longitudeMean = mean(longitude);
      66         222 :         Double longitudeStddev = stddev(longitude);
      67         222 :         if (longitudeStddev > 2.0 / 3.0 * C::pi) {
      68             :             // likely to be the case
      69           0 :             if (abs(longitudeMean) < 0.5 * C::pi) {
      70             :                 // periodic boundary surface would be +-pi
      71           0 :                 for (size_t i = 0; i < longitude.nelements(); ++i) {
      72           0 :                     if (longitude[i] < 0.0) {
      73           0 :                         longitude[i] += C::_2pi;
      74             :                     }
      75             :                 }
      76             :             }
      77           0 :             else if (abs(longitudeMean - C::pi) < 0.5 * C::pi ) {
      78             :                 // periodic boundary surface would be 0,2pi
      79           0 :                 for (size_t i = 0; i < longitude.nelements(); ++i) {
      80           0 :                     if (longitude[i] < C::pi) {
      81           0 :                         longitude[i] += C::_2pi;
      82             :                     }
      83             :                 }
      84             :             }
      85             :         }
      86             :     }
      87           0 :     catch (AipsError &e) {
      88           0 :         LogIO os(LogOrigin("Imager", "getMapPointings", WHERE));
      89           0 :         os << LogIO::SEVERE << "Failed due to the rror \"" << e.getMesg() << "\"" << LogIO::POST;
      90           0 :         return False;
      91             :     }
      92           0 :     catch (...) {
      93           0 :         LogIO os(LogOrigin("Imager", "getMapPointings", WHERE));
      94           0 :         os << LogIO::SEVERE << "Failed due to unknown error" << LogIO::POST;
      95           0 :         throw;
      96             :         return False;
      97             :     }
      98         222 :     return True;
      99             : }
     100             : 
     101         242 :   Bool SingleDishBeamUtil::getPointingSamplingRaster(Quantum<Vector<Double>> &sampling, Quantity &positionAngle) {
     102         726 :     LogIO os(_ORIGIN);
     103         242 :     os << "calculating sampling interval assuming raster scan." << LogIO::POST;
     104             :     try {
     105         242 :       Vector<Double> samplingVal(2, 0.0);
     106             :       // Get time sorted pointing (sort by ANTENNA, TIME)
     107         242 :       Matrix<Double> pointingList;
     108         242 :       ThrowIf (!getMapPointings(pointingList), "Failed to get map pointings");
     109         242 :       os << "got " << pointingList.column(0).size() << " pointings of " << antSel_  << LogIO::POST;
     110             :       // Get timestamps
     111         242 :       Block<String> sortColumns(2);
     112         242 :       sortColumns[0] = "ANTENNA1";
     113         242 :       sortColumns[1] = "TIME";
     114         242 :       MSSelection thisSelection;
     115         242 :       thisSelection.setAntennaExpr(antSel_);
     116         242 :       TableExprNode exprNode = thisSelection.getTEN(&(*ms_));
     117         242 :       if (exprNode.isNull()) {
     118           0 :         throw(AipsError("Invalid antenna selection"));
     119             :       }
     120         242 :       MeasurementSet tmp = (*ms_)(exprNode);
     121         242 :       CountedPtr<MeasurementSet> sortedMS = new MeasurementSet(tmp.sort(sortColumns));
     122         242 :       AlwaysAssert(sortedMS->nrow() == pointingList.column(0).size(), AipsError);
     123         242 :       ScalarMeasColumn<MEpoch> timeColumn_;
     124         242 :       timeColumn_.attach(*sortedMS, "TIME");
     125             :       // Get time and pointings of unique time stamp per antenna
     126         242 :       Vector<uInt> uniqueAntTimeIdx(sortedMS->nrow());
     127         242 :       Vector<Double> uniqueAntTimes(sortedMS->nrow());
     128         242 :       Double currentTime = timeColumn_.convert(0, MEpoch::UTC).get("s").getValue();
     129         242 :       uInt itime = 0;
     130             :       // Initial time stamp
     131         242 :       uniqueAntTimeIdx(itime) = 0;
     132         242 :       uniqueAntTimes(itime) = currentTime;
     133         242 :       ++itime;
     134      344642 :       for (uInt i = 1; i < sortedMS->nrow(); ++i) {
     135      344400 :         Double nextTime = timeColumn_.convert(i, MEpoch::UTC).get("s").getValue();
     136      344400 :         if (abs(nextTime-currentTime) > 1.e-14*currentTime) {
     137      330068 :           uniqueAntTimeIdx(itime) = i;
     138      330068 :           uniqueAntTimes(itime) = nextTime;
     139      330068 :           ++itime;
     140             :         }
     141      344400 :         currentTime = nextTime;
     142             :       }
     143         242 :       if (itime != uniqueAntTimeIdx.size()) {
     144          34 :         uniqueAntTimeIdx.resize(itime, True);
     145          34 :         uniqueAntTimes.resize(itime, True); 
     146             :       }
     147         242 :       os << LogIO::DEBUG1 << uniqueAntTimeIdx.size() << " unique time stamps"  << LogIO::POST;
     148         242 :       if (uniqueAntTimes.size() == 1) {
     149          20 :         samplingVal = 0.0;
     150          20 :         sampling = Quantum< Vector<Double> >(samplingVal, directionUnit_);
     151          20 :         positionAngle = Quantity(0.0, "rad");
     152             :         os << LogIO::NORMAL
     153             :            << "Got only one pointing. exiting without calculating sampling interval."
     154          20 :            << LogIO::POST;
     155          20 :         return True;
     156             :       }
     157         222 :       Vector<Double> longitude(uniqueAntTimes.size());
     158         222 :       Vector<Double> latitude(longitude.size());
     159             :       {
     160         444 :         Vector<Double> all_longitude = pointingList.column(0); // time sorted pointing list
     161         444 :         Vector<Double> all_latitude = pointingList.column(1);
     162      330512 :         for (uint i = 0; i < uniqueAntTimeIdx.size(); ++i) {
     163      330290 :           longitude(i) = all_longitude[uniqueAntTimeIdx[i]];
     164      330290 :           latitude(i) = all_latitude[uniqueAntTimeIdx[i]];
     165             :         }
     166             :       }
     167             :       // calculate pointing interval assuming RASTER
     168         222 :       Vector<Double> delta_lon(longitude.size()-1);
     169         222 :       Vector<Double> delta_lat(delta_lon.size());
     170             :       Double min_lat, max_lat;
     171         222 :       minMax(min_lat, max_lat, latitude);
     172         222 :       Double center_lat = 0.5*(min_lat+max_lat);
     173      330290 :       for (size_t i=0; i < delta_lon.size(); ++i) {
     174      330068 :         Double dlon = (longitude[i+1]-longitude[i])*cos(center_lat);
     175      330068 :         delta_lon[i] = abs(dlon) > 1.e-8 ? dlon : 1.e-8;
     176      330068 :         delta_lat[i] = latitude[i+1]-latitude[i];
     177             :       }
     178             :       { // sampling interval along scan row
     179         444 :         Vector<Double> delta2(delta_lon.size());
     180         222 :         delta2 = square(delta_lon) + square(delta_lat);
     181         222 :         samplingVal(0) = sqrt(median(delta2));
     182         444 :         os << LogIO::DEBUG1 << "sampling interval along scan: " << samplingVal(0)
     183         222 :            << " " << directionUnit_.getName() << LogIO::POST;
     184             :       }
     185             :       { // position angle
     186         444 :         Vector<Double> delta_tan(delta_lon.size());
     187         222 :         delta_tan = delta_lat/delta_lon;
     188         222 :         Double positionAngleVal = atan(median(delta_tan));
     189         222 :         positionAngleVal = std::isfinite(positionAngleVal) ? positionAngleVal: 0.5*C::pi;
     190         222 :         positionAngle = Quantity(positionAngleVal, "rad");
     191         222 :         os << LogIO::DEBUG1 << "position angle of scan direction: " << positionAngle << LogIO::POST;
     192             :       }
     193             :       { // sampling interval orthogonal to scan row
     194         222 :         vector<uInt> gap_idx(0);
     195         222 :         uInt numAntGap = 0;
     196         222 :         os << LogIO::DEBUG1 << "start analysing raster pattern by time gap " << LogIO::POST;
     197             :         {// detect raster gap by time interval
     198         444 :           Vector<Double> deltaTime(uniqueAntTimes.size()-1);
     199         444 :           Vector<Double> positiveTimeGap(deltaTime.size());
     200         222 :           uInt itime = 0;
     201      330290 :           for (uInt i = 0; i < deltaTime.size(); ++i) {
     202      330068 :             deltaTime[i] = uniqueAntTimes[i+1] - uniqueAntTimes[i];
     203      330068 :             if (deltaTime[i] > 0.0) {
     204      330068 :               positiveTimeGap[itime] = deltaTime[i];
     205      330068 :               ++itime;
     206             :             }
     207             :           }
     208         222 :           positiveTimeGap.resize(itime, True);
     209         222 :           Double medianInterval5 = Double(5)*median(positiveTimeGap);
     210         222 :           os << LogIO::DEBUG1 << "Gap interval threshold = " << medianInterval5  << LogIO::POST;
     211      330290 :           for (uInt i = 0; i < deltaTime.size(); ++i) {
     212      330068 :             if (deltaTime[i] > medianInterval5) {// raster row gap
     213        3730 :               gap_idx.push_back(i);
     214      326338 :             } else if (deltaTime[i] < 0.0 || i == deltaTime.size()-1) { // antenna gap
     215         222 :               gap_idx.push_back(i);
     216         222 :               ++numAntGap;
     217             :             }
     218             :           }
     219             :         }
     220         222 :         if (gap_idx.size()==numAntGap) {// no gap detected.
     221         105 :           os << LogIO::NORMAL << "No time gap found in scans. The scan pattern may not be RASTER. Median sampling interval will be returned." << LogIO::POST;
     222         105 :           samplingVal(1) = 0.0;
     223         105 :           sampling = Quantum< Vector<Double> >(samplingVal, directionUnit_);
     224         105 :           os << "sampling interval: " << sampling << ", pa: " << positionAngle  << LogIO::POST;
     225         105 :           return True;
     226             :         }
     227         117 :         os << LogIO::DEBUG1 << gap_idx.size() << " raster rows detected" << LogIO::POST;
     228             :         //os << LogIO::DEBUGGING << "gap idx = " << Vector<uInt>(gap_idx) << LogIO::POST;
     229             :         // a unit vector of orthogonal direction
     230         234 :         Vector<Double> orth_vec(2);
     231         117 :         orth_vec(0) = cos(positionAngle.getValue("rad")+0.5*C::pi);
     232         117 :         orth_vec(1) = sin(positionAngle.getValue("rad")+0.5*C::pi);
     233         117 :         os << LogIO::DEBUG1 << "orthogonal vector = " << orth_vec << LogIO::POST;
     234             :         // median lon, lat in each raster row
     235         234 :         Vector<Double> typical_lon(gap_idx.size());
     236         234 :         Vector<Double> typical_lat(gap_idx.size());
     237        3964 :         for (uInt i = 0; i < gap_idx.size(); ++i) {
     238             :           uInt start_idx, num_dump;
     239        3847 :           if (i==0) start_idx = 0;
     240        3730 :           else start_idx = gap_idx[i-1] + 1;
     241        3847 :           num_dump = gap_idx[i]-start_idx + 1;
     242        3847 :           typical_lon(i) = median(longitude(Slice(start_idx, num_dump)));
     243        3847 :           typical_lat(i) = median(latitude(Slice(start_idx, num_dump)));
     244             :         }
     245             :         os << LogIO::DEBUG1 << "Typical longitude of scan row (first 10 at max) = "
     246         117 :            << typical_lon(Slice(0, min(typical_lon.size(), 10))) << LogIO::POST;
     247             :         os << LogIO::DEBUG1 << "Typical latitude of scan row (first 10 at max) = "
     248         117 :            << typical_lat(Slice(0, min(typical_lat.size(), 10))) << LogIO::POST;
     249         234 :         Vector<Double> orth_dist(typical_lon.size()-1);
     250        3847 :         for (uInt i = 0; i < typical_lon.size()-1; ++i) {
     251             :           Double delta_row_lon, delta_row_lat;
     252        3730 :           delta_row_lon = typical_lon(i) - typical_lon(i+1);
     253        3730 :           delta_row_lat = typical_lat(i) - typical_lat(i+1);
     254             :           // product of orthogonal vector and row gap vector
     255        7460 :           orth_dist(i) = abs(delta_row_lon*orth_vec(0) + 
     256        3730 :                              delta_row_lat*orth_vec(1));
     257             :         }
     258         117 :         samplingVal(1) = median(orth_dist);
     259         234 :         os << LogIO::DEBUG1 << "sampling interval between scan row: " << samplingVal(1)
     260         117 :            << " " << directionUnit_.getName() << LogIO::POST;
     261             :       }
     262         117 :       sampling = Quantum<Vector<Double>>(samplingVal, directionUnit_);
     263             :     }
     264           0 :     catch (AipsError &e) {
     265           0 :         os << LogIO::SEVERE << "Failed due to the rror \"" << e.getMesg() << "\"" << LogIO::POST;
     266           0 :         return False;
     267             :     }
     268           0 :     catch (...) {
     269           0 :         os << LogIO::SEVERE << "Failed due to unknown error" << LogIO::POST;
     270           0 :         throw;
     271             :         return False;
     272             :     }
     273             :     os << LogIO::NORMAL << "sampling interval: " << sampling
     274         117 :        << ", pa: " << positionAngle  << LogIO::POST;
     275         117 :     return True;
     276             : }
     277             : 
     278             : } //# NAMESPACE CASA - END

Generated by: LCOV version 1.16