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 265 : SingleDishBeamUtil::SingleDishBeamUtil(const MeasurementSet &ms, 25 : const String &referenceFrame, 26 : const String &movingSource, 27 : const String &pointingColumn, 28 265 : const String &antenna) 29 265 : : referenceFrame_(referenceFrame), movingSource_(movingSource), 30 530 : pointingColumn_(pointingColumn), antSel_(antenna) 31 : { 32 265 : ms_ = new MeasurementSet(ms); 33 265 : directionUnit_ = Unit("rad"); 34 265 : } 35 : 36 265 : Bool SingleDishBeamUtil::getMapPointings(Matrix<Double> &pointingList) { 37 : try { 38 265 : PointingDirectionCalculator calc(*ms_); 39 : 40 265 : calc.setDirectionColumn(pointingColumn_); 41 265 : calc.selectData(antSel_); 42 265 : calc.setFrame(referenceFrame_); 43 265 : MDirection::Types refType = MDirection::J2000; // any non planet value 44 265 : Bool status = False; 45 265 : status = MDirection::getType(refType, movingSource_); 46 533 : Bool doMovingSourceCorrection = (status == True && 47 268 : MDirection::N_Types < refType && 48 3 : refType < MDirection::N_Planets); 49 265 : if (doMovingSourceCorrection) { 50 3 : calc.setMovingSource(movingSource_); 51 : } 52 265 : calc.setDirectionListMatrixShape(PointingDirectionCalculator::COLUMN_MAJOR); 53 : 54 265 : pointingList = calc.getDirection(); 55 265 : directionUnit_ = Unit("rad"); 56 265 : Vector<Double> longitude = pointingList.column(0); 57 265 : Vector<Double> latitude = pointingList.column(1); 58 : 59 265 : 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 226 : Double longitudeMean = mean(longitude); 66 226 : Double longitudeStddev = stddev(longitude); 67 226 : 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 343 : } 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 0 : } 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 0 : } 98 226 : return True; 99 : } 100 : 101 265 : Bool SingleDishBeamUtil::getPointingSamplingRaster(Quantum<Vector<Double>> &sampling, Quantity &positionAngle) { 102 530 : LogIO os(_ORIGIN); 103 265 : os << "calculating sampling interval assuming raster scan." << LogIO::POST; 104 : try { 105 265 : Vector<Double> samplingVal(2, 0.0); 106 : // Get time sorted pointing (sort by ANTENNA, TIME) 107 265 : Matrix<Double> pointingList; 108 265 : ThrowIf (!getMapPointings(pointingList), "Failed to get map pointings"); 109 265 : os << "got " << pointingList.column(0).size() << " pointings of " << antSel_ << LogIO::POST; 110 : // Get timestamps 111 265 : Block<String> sortColumns(2); 112 265 : sortColumns[0] = "ANTENNA1"; 113 265 : sortColumns[1] = "TIME"; 114 265 : MSSelection thisSelection; 115 265 : thisSelection.setAntennaExpr(antSel_); 116 265 : TableExprNode exprNode = thisSelection.getTEN(&(*ms_)); 117 265 : if (exprNode.isNull()) { 118 0 : throw(AipsError("Invalid antenna selection")); 119 : } 120 265 : MeasurementSet tmp = (*ms_)(exprNode); 121 265 : CountedPtr<MeasurementSet> sortedMS = new MeasurementSet(tmp.sort(sortColumns)); 122 265 : AlwaysAssert(sortedMS->nrow() == pointingList.column(0).size(), AipsError); 123 265 : ScalarMeasColumn<MEpoch> timeColumn_; 124 265 : timeColumn_.attach(*sortedMS, "TIME"); 125 : // Get time and pointings of unique time stamp per antenna 126 265 : Vector<uInt> uniqueAntTimeIdx(sortedMS->nrow()); 127 265 : Vector<Double> uniqueAntTimes(sortedMS->nrow()); 128 265 : Double currentTime = timeColumn_.convert(0, MEpoch::UTC).get("s").getValue(); 129 265 : uInt itime = 0; 130 : // Initial time stamp 131 265 : uniqueAntTimeIdx(itime) = 0; 132 265 : uniqueAntTimes(itime) = currentTime; 133 265 : ++itime; 134 360033 : for (uInt i = 1; i < sortedMS->nrow(); ++i) { 135 359768 : Double nextTime = timeColumn_.convert(i, MEpoch::UTC).get("s").getValue(); 136 359768 : if (abs(nextTime-currentTime) > 1.e-14*currentTime) { 137 345436 : uniqueAntTimeIdx(itime) = i; 138 345436 : uniqueAntTimes(itime) = nextTime; 139 345436 : ++itime; 140 : } 141 359768 : currentTime = nextTime; 142 : } 143 265 : if (itime != uniqueAntTimeIdx.size()) { 144 34 : uniqueAntTimeIdx.resize(itime, True); 145 34 : uniqueAntTimes.resize(itime, True); 146 : } 147 265 : os << LogIO::DEBUG1 << uniqueAntTimeIdx.size() << " unique time stamps" << LogIO::POST; 148 265 : if (uniqueAntTimes.size() == 1) { 149 39 : samplingVal = 0.0; 150 39 : sampling = Quantum< Vector<Double> >(samplingVal, directionUnit_); 151 39 : positionAngle = Quantity(0.0, "rad"); 152 : os << LogIO::NORMAL 153 : << "Got only one pointing. exiting without calculating sampling interval." 154 39 : << LogIO::POST; 155 39 : return True; 156 : } 157 226 : Vector<Double> longitude(uniqueAntTimes.size()); 158 226 : Vector<Double> latitude(longitude.size()); 159 : { 160 226 : Vector<Double> all_longitude = pointingList.column(0); // time sorted pointing list 161 226 : Vector<Double> all_latitude = pointingList.column(1); 162 345888 : for (uint i = 0; i < uniqueAntTimeIdx.size(); ++i) { 163 345662 : longitude(i) = all_longitude[uniqueAntTimeIdx[i]]; 164 345662 : latitude(i) = all_latitude[uniqueAntTimeIdx[i]]; 165 : } 166 226 : } 167 : // calculate pointing interval assuming RASTER 168 226 : Vector<Double> delta_lon(longitude.size()-1); 169 226 : Vector<Double> delta_lat(delta_lon.size()); 170 : Double min_lat, max_lat; 171 226 : minMax(min_lat, max_lat, latitude); 172 226 : Double center_lat = 0.5*(min_lat+max_lat); 173 345662 : for (size_t i=0; i < delta_lon.size(); ++i) { 174 345436 : Double dlon = (longitude[i+1]-longitude[i])*cos(center_lat); 175 345436 : delta_lon[i] = abs(dlon) > 1.e-8 ? dlon : 1.e-8; 176 345436 : delta_lat[i] = latitude[i+1]-latitude[i]; 177 : } 178 : { // sampling interval along scan row 179 226 : Vector<Double> delta2(delta_lon.size()); 180 226 : delta2 = square(delta_lon) + square(delta_lat); 181 226 : samplingVal(0) = sqrt(median(delta2)); 182 452 : os << LogIO::DEBUG1 << "sampling interval along scan: " << samplingVal(0) 183 226 : << " " << directionUnit_.getName() << LogIO::POST; 184 226 : } 185 : { // position angle 186 226 : Vector<Double> delta_tan(delta_lon.size()); 187 226 : delta_tan = delta_lat/delta_lon; 188 226 : Double positionAngleVal = atan(median(delta_tan)); 189 226 : positionAngleVal = std::isfinite(positionAngleVal) ? positionAngleVal: 0.5*C::pi; 190 226 : positionAngle = Quantity(positionAngleVal, "rad"); 191 226 : os << LogIO::DEBUG1 << "position angle of scan direction: " << positionAngle << LogIO::POST; 192 226 : } 193 : { // sampling interval orthogonal to scan row 194 226 : vector<uInt> gap_idx(0); 195 226 : uInt numAntGap = 0; 196 226 : os << LogIO::DEBUG1 << "start analysing raster pattern by time gap " << LogIO::POST; 197 : {// detect raster gap by time interval 198 226 : Vector<Double> deltaTime(uniqueAntTimes.size()-1); 199 226 : Vector<Double> positiveTimeGap(deltaTime.size()); 200 226 : uInt itime = 0; 201 345662 : for (uInt i = 0; i < deltaTime.size(); ++i) { 202 345436 : deltaTime[i] = uniqueAntTimes[i+1] - uniqueAntTimes[i]; 203 345436 : if (deltaTime[i] > 0.0) { 204 345436 : positiveTimeGap[itime] = deltaTime[i]; 205 345436 : ++itime; 206 : } 207 : } 208 226 : positiveTimeGap.resize(itime, True); 209 226 : Double medianInterval5 = Double(5)*median(positiveTimeGap); 210 226 : os << LogIO::DEBUG1 << "Gap interval threshold = " << medianInterval5 << LogIO::POST; 211 345662 : for (uInt i = 0; i < deltaTime.size(); ++i) { 212 345436 : if (deltaTime[i] > medianInterval5) {// raster row gap 213 3934 : gap_idx.push_back(i); 214 341502 : } else if (deltaTime[i] < 0.0 || i == deltaTime.size()-1) { // antenna gap 215 226 : gap_idx.push_back(i); 216 226 : ++numAntGap; 217 : } 218 : } 219 226 : } 220 226 : 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 121 : 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 121 : Vector<Double> orth_vec(2); 231 121 : orth_vec(0) = cos(positionAngle.getValue("rad")+0.5*C::pi); 232 121 : orth_vec(1) = sin(positionAngle.getValue("rad")+0.5*C::pi); 233 121 : os << LogIO::DEBUG1 << "orthogonal vector = " << orth_vec << LogIO::POST; 234 : // median lon, lat in each raster row 235 121 : Vector<Double> typical_lon(gap_idx.size()); 236 121 : Vector<Double> typical_lat(gap_idx.size()); 237 4176 : for (uInt i = 0; i < gap_idx.size(); ++i) { 238 : uInt start_idx, num_dump; 239 4055 : if (i==0) start_idx = 0; 240 3934 : else start_idx = gap_idx[i-1] + 1; 241 4055 : num_dump = gap_idx[i]-start_idx + 1; 242 4055 : typical_lon(i) = median(longitude(Slice(start_idx, num_dump))); 243 4055 : 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 121 : << 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 121 : << typical_lat(Slice(0, min(typical_lat.size(), 10))) << LogIO::POST; 249 121 : Vector<Double> orth_dist(typical_lon.size()-1); 250 4055 : for (uInt i = 0; i < typical_lon.size()-1; ++i) { 251 : Double delta_row_lon, delta_row_lat; 252 3934 : delta_row_lon = typical_lon(i) - typical_lon(i+1); 253 3934 : delta_row_lat = typical_lat(i) - typical_lat(i+1); 254 : // product of orthogonal vector and row gap vector 255 7868 : orth_dist(i) = abs(delta_row_lon*orth_vec(0) + 256 3934 : delta_row_lat*orth_vec(1)); 257 : } 258 121 : samplingVal(1) = median(orth_dist); 259 242 : os << LogIO::DEBUG1 << "sampling interval between scan row: " << samplingVal(1) 260 121 : << " " << directionUnit_.getName() << LogIO::POST; 261 226 : } 262 121 : sampling = Quantum<Vector<Double>>(samplingVal, directionUnit_); 263 1981 : } 264 0 : catch (AipsError &e) { 265 0 : os << LogIO::SEVERE << "Failed due to the rror \"" << e.getMesg() << "\"" << LogIO::POST; 266 0 : return False; 267 0 : } 268 0 : catch (...) { 269 0 : os << LogIO::SEVERE << "Failed due to unknown error" << LogIO::POST; 270 0 : throw; 271 : return False; 272 0 : } 273 : os << LogIO::NORMAL << "sampling interval: " << sampling 274 121 : << ", pa: " << positionAngle << LogIO::POST; 275 121 : return True; 276 265 : } 277 : 278 : } //# NAMESPACE CASA - END