LCOV - code coverage report
Current view: top level - msvis/MSVis - PointingDirectionCache.cc (source / functions) Hit Total Coverage
Test: casacpp_coverage.info Lines: 0 184 0.0 %
Date: 2024-10-10 15:00:01 Functions: 0 31 0.0 %

          Line data    Source code
       1             : /*
       2             :  * PointingDirectionCache.cc
       3             :  *
       4             :  *  Created on: Dec 1, 2016
       5             :  *      Author: jjacobs
       6             :  */
       7             : 
       8             : #include <msvis/MSVis/PointingDirectionCache.h>
       9             : 
      10             : #include <casacore/casa/Exceptions/Error.h>
      11             : #include <casacore/casa/BasicSL/String.h>
      12             : #include <casacore/measures/Measures/MDirection.h>
      13             : #include <casacore/ms/MeasurementSets/MSPointingColumns.h>
      14             : 
      15             : #include <memory>
      16             : 
      17             : using namespace casacore;
      18             : 
      19             : namespace casa {
      20             : namespace vi {
      21             : 
      22           0 : PointingColumns::PointingColumns (const MSPointingColumns & pointingColumns)
      23           0 : : pointingColumns_p (pointingColumns)
      24           0 : {}
      25             : 
      26             : Pointing
      27           0 : PointingColumns::getPointingRow (int row, double /*targetTime*/, bool asMeasure) const
      28             : {
      29           0 :     Pointing pointing;
      30             : 
      31             :     // See if the row's time is no earlier than 5 minutes before target
      32             :     // An optimization to avoid unnecessary direction measure conversions
      33             :     // for rows unlikely to be used (the conversions are fairly expensive)
      34             : 
      35           0 :     pointing.time = pointingColumns_p.time () (row);
      36           0 :     pointing.interval = pointingColumns_p.interval () (row);
      37           0 :     pointing.antennaId = pointingColumns_p.antennaId() (row);
      38           0 :     pointing.row = row;
      39           0 :     pointing.source = this;
      40             : 
      41           0 :     if (asMeasure){
      42             : 
      43             :         // Pointing is likely to be useful so get all the data.
      44             : 
      45           0 :         pointing.direction.reset (new MDirection (pointingColumns_p.directionMeas (row)));
      46             :     }
      47             : 
      48           0 :     return pointing;
      49           0 : }
      50             : 
      51             : int
      52           0 : PointingColumns::nRows () const
      53             : {
      54           0 :     return pointingColumns_p.nrow();
      55             : }
      56             : 
      57             : 
      58           0 : PointingDirectionCache::PointingDirectionCache (int nAntennas,
      59           0 :                                                 const PointingSource & pointingSource)
      60           0 : : antennaLevelCache_p (MinTimeEntries, MaxTimeEntries, nAntennas, this),
      61           0 :   antennaEarliestTime_p (nAntennas, -1.0),
      62           0 :   lastRowRead_p (-1),
      63           0 :   nFallbacks_p (0),
      64           0 :   nHits_p (0),
      65           0 :   pointingEofReached_p (false),
      66           0 :   pointingSource_p (pointingSource)
      67           0 : {}
      68             : 
      69           0 : PointingDirectionCache::~PointingDirectionCache ()
      70             : {
      71             : //    printf ("--> nHits=%d, nFallbacks_p=%d, %%fallback=%f, unused=0%3.2f\n", nHits_p, nFallbacks_p,
      72             : //            100.0 * nFallbacks_p / (nHits_p + nFallbacks_p), (nAdded_p - nHits_p) * 1.0f / nAdded_p);
      73           0 : }
      74             : 
      75             : void
      76           0 : PointingDirectionCache::fillCache (int antenna, double time, bool flushAndRewind) const
      77             : {
      78           0 :     if (flushAndRewind){
      79             : 
      80             :         // A time earlier than what was cached is needed: reload the cache from the
      81             :         // beginning.  If done with any frequency on large files it could produce a
      82             :         // performance problem.
      83             : 
      84           0 :         antennaLevelCache_p.flushTimes ();
      85           0 :         lastRowRead_p = -1;
      86             :     }
      87             : 
      88           0 :     int nRows = pointingSource_p.nRows();
      89             : 
      90             :     while (true) {
      91             : 
      92             :         // Begin reading rows from the pointing table.  Each row will be an entry for
      93             :         // an antenna at a particular time.  The implementation will fail if the
      94             :         // timestamps for each antenna are not monotonically increasing.
      95             : 
      96           0 :         lastRowRead_p ++;
      97             : 
      98           0 :         if (lastRowRead_p >= nRows){
      99           0 :             pointingEofReached_p = true;
     100           0 :             return; // can't fill anymore
     101             :         }
     102             : 
     103             :         // Pull out the information from the current row of the pointing table and
     104             :         // add it to the cache.
     105             : 
     106           0 :         Pointing pointing = pointingSource_p.getPointingRow (lastRowRead_p, time, false);
     107           0 :         antennaLevelCache_p.addEntry (pointing);
     108             : 
     109           0 :         if (antennaEarliestTime_p [pointing.antennaId] < 0) {
     110           0 :             antennaEarliestTime_p [pointing.antennaId] = pointing.time;
     111             :         }
     112             : 
     113             :         // See if the current row satisfies the underlying cache read request.
     114             :         // If so, return.
     115             : 
     116           0 :         if (pointing.antennaId == antenna){
     117             : 
     118           0 :             if (pd_cache::timeMatch (time, pointing.time, pointing.interval)){
     119           0 :                 return;
     120             :             }
     121             : 
     122           0 :             if (time < pointing.time){
     123             :                 // Target time before first pointing time, so give up.
     124           0 :                 return;
     125             :             }
     126             :         }
     127           0 :     }
     128             : }
     129             : 
     130             : std::pair <bool, casacore::MDirection>
     131           0 : PointingDirectionCache::getPointingDirection (int antenna, double time, const MDirection & phaseCenter) const
     132             : {
     133             :     using namespace pd_cache;
     134             : 
     135             :     // Try to fill the direction request from the cache.
     136             : 
     137           0 :     if (noDataForAntenna (antenna, time)){
     138           0 :         nFallbacks_p ++;
     139           0 :         return std::make_pair (true, phaseCenter); // antenna not in pointing subtable --> return default.
     140             :     }
     141             : 
     142             :     CacheAccessStatus status;
     143           0 :     const MDirection * direction = nullptr;
     144           0 :     MDirection result;
     145             : 
     146           0 :     std::tie (status, direction) = antennaLevelCache_p.getPointingDirection (antenna, time);
     147             : 
     148           0 :     if (status == CacheAccessStatus::Hit){
     149             : 
     150           0 :         result = * direction;
     151           0 :         nHits_p ++;
     152             : 
     153           0 :     } else if (status == CacheAccessStatus::MissInternal){
     154             : 
     155             :         // The cache contained values surrounding the requested value but not that
     156             :         // matched it.  Use the phaseCenter as a reasonable fallback.
     157             : 
     158           0 :         result = phaseCenter;
     159           0 :         nFallbacks_p ++;
     160             : 
     161             :     } else {
     162             : 
     163             :         // The request failed so add more data to the cache.
     164             : 
     165           0 :         bool flushAndRewind = status == CacheAccessStatus::MissPrior;
     166             : 
     167           0 :         fillCache (antenna, time, flushAndRewind);
     168             : 
     169             :         // Try to fill the direction request again.
     170             : 
     171           0 :         std::tie (status, direction) = antennaLevelCache_p.getPointingDirection (antenna, time);
     172             : 
     173           0 :         if (status != CacheAccessStatus::Hit){
     174             : 
     175             :             // Miss after cache fill so use the phaseCenter as a fallback.
     176             : 
     177           0 :             result = phaseCenter;
     178           0 :             nFallbacks_p ++;
     179             :         } else {
     180           0 :             result = * direction;
     181           0 :             nHits_p ++;
     182             :         }
     183             :     }
     184             : 
     185           0 : bool northPole = result.toString() == "00:00:00.000000 90.00.00.00000 J2000";
     186           0 : if (northPole){
     187           0 :     printf ("Returning north pole: ant=%d, t=%f\n", antenna, time);
     188             : }
     189             : 
     190           0 :     return std::make_pair (true, result);
     191           0 : }
     192             : 
     193             : const PointingSource *
     194           0 : PointingDirectionCache::getPointingSource () const
     195             : {
     196           0 :     return & pointingSource_p;
     197             : }
     198             : 
     199             : bool
     200           0 : PointingDirectionCache::noDataForAntenna (int antenna, double time) const
     201             : {
     202             :     // If the entire pointing subtable has been scanned once and the antenna
     203             :     // was not seen then return true.  This is intended to prevent excessive
     204             :     // reading of the pointing subtable when an antenna is missing.
     205             : 
     206           0 :     bool noData = false;
     207             : 
     208           0 :     if (antennaEarliestTime_p [antenna] >= 0){
     209             : 
     210             :         // If there is an earliest time for the antenna, then see if the requested
     211             :         // time is before it.
     212             : 
     213           0 :         noData = time < antennaEarliestTime_p [antenna];
     214             : 
     215             :     } else {
     216             : 
     217             :         // No data has been seen (yet?) for the current antenna.
     218             :         // If the entire subtable has been scanned once already, then
     219             :         // there is no data for this antenna.
     220             : 
     221           0 :         noData = pointingEofReached_p;
     222             :     }
     223             : 
     224           0 :     return noData;
     225             : }
     226             : 
     227             : namespace pd_cache {
     228             : 
     229             : bool
     230           0 : timeMatch (double time, double rowsTime, double rowsInterval)
     231             : {
     232             :     // The time matches if it falls within an interval around the pointing
     233             :     // table time.
     234             : 
     235           0 :     bool match = time >= rowsTime - rowsInterval &&
     236           0 :                  time <= rowsTime + rowsInterval;
     237             : 
     238           0 :     return match;
     239             : }
     240             : 
     241           0 : AntennaLevelCache::AntennaLevelCache (int minTimes, int maxTimes, int nAntennas,
     242           0 :                                       const PointingDirectionCache * pdCache)
     243           0 : : pointingDirectionCache_p (pdCache),
     244           0 :   timeLevelCache_p (nAntennas, TimeLevelCache (minTimes, maxTimes, this))
     245           0 : {}
     246             : 
     247             : 
     248             : void
     249           0 : AntennaLevelCache::addEntry (Pointing & pointing)
     250             : {
     251           0 :     timeLevelCache_p [pointing.antennaId].addEntry (pointing);
     252           0 : }
     253             : 
     254             : void
     255           0 : AntennaLevelCache::flushTimes ()
     256             : {
     257           0 :     for (auto & timeCache : timeLevelCache_p){
     258             : 
     259             :         // Flush the time cache for each antenna
     260             : 
     261           0 :         timeCache.flush ();
     262             :     }
     263           0 : }
     264             : 
     265             : std::pair<CacheAccessStatus,const casacore::MDirection *>
     266           0 : AntennaLevelCache::getPointingDirection (int antenna, double time)
     267             : {
     268           0 :     return timeLevelCache_p [antenna].getPointingDirection(time);
     269             : }
     270             : 
     271             : const PointingSource *
     272           0 : AntennaLevelCache::getPointingSource () const
     273             : {
     274           0 :     return pointingDirectionCache_p->getPointingSource ();
     275             : }
     276             : 
     277           0 : TimeLevelEntry::TimeLevelEntry (const Pointing & pointing,
     278           0 :                                 const TimeLevelCache * tlCache)
     279           0 : : direction_p (std::move (pointing.direction)),
     280           0 :   row_p (pointing.row),
     281           0 :   timeCenter_p (pointing.time),
     282           0 :   timeLevelCache_p (tlCache),
     283           0 :   interval_p (pointing.interval)
     284             : {
     285           0 : bool northPole = direction_p && direction_p->toString() == "00:00:00.000000 90.00.00.00000 J2000";
     286           0 : if (northPole){
     287           0 :     printf ("Returning north pole: ant=%d, t=%f\n", pointing.antennaId, pointing.time);
     288             : }
     289           0 : }
     290             : 
     291           0 : TimeLevelEntry::TimeLevelEntry (const TimeLevelEntry & other)
     292           0 :  : direction_p (std::move (other.direction_p)),
     293           0 :    row_p (other.row_p),
     294           0 :    timeCenter_p (other.timeCenter_p),
     295           0 :    timeLevelCache_p (other.timeLevelCache_p),
     296           0 :    interval_p (other.interval_p)
     297           0 : {}
     298             : 
     299           0 : TimeLevelEntry::~TimeLevelEntry ()
     300             : {
     301           0 : }
     302             : 
     303             : TimeLevelEntry &
     304           0 : TimeLevelEntry::operator= (const TimeLevelEntry & other)
     305             : {
     306           0 :     if (& other != this){
     307             : 
     308           0 :         direction_p = std::move (other.direction_p);
     309           0 :         other.direction_p = nullptr;
     310             : 
     311           0 :         row_p = other.row_p;
     312           0 :         timeCenter_p = other.timeCenter_p;
     313           0 :         interval_p = other.interval_p;
     314             :     }
     315             : 
     316           0 :     return * this;
     317             : }
     318             : 
     319             : const casacore::MDirection *
     320           0 : TimeLevelEntry::getDirection () const{
     321             : 
     322             :     // If the direction measure is actually cached, then simply return it.
     323             : 
     324           0 :     if (! direction_p){
     325             : 
     326             :         // Refetch the row and actually get the direction this time.  Put it in
     327             :         // the cache and return a pointer to it.
     328             : 
     329           0 :         Pointing p = timeLevelCache_p->getPointingSource()->getPointingRow (row_p, timeCenter_p, true);
     330           0 :         direction_p.reset (new MDirection (* p.direction.get()));
     331             : 
     332           0 :     }
     333             : 
     334           0 :     return direction_p.get();
     335             : }
     336             : 
     337             : double
     338           0 : TimeLevelEntry::getInterval () const{
     339           0 :     return interval_p;
     340             : }
     341             : 
     342             : double
     343           0 : TimeLevelEntry::getTime () const
     344             : {
     345           0 :     return timeCenter_p;
     346             : }
     347             : 
     348             : 
     349             : bool
     350           0 : operator== (double t, const TimeLevelEntry & tle)
     351             : {
     352           0 :     return timeMatch (t, tle.getTime(), tle.getInterval());
     353             : }
     354             : 
     355             : bool
     356           0 : operator== (const TimeLevelEntry & tle, double t)
     357             : {
     358           0 :     return t == tle;
     359             : }
     360             : 
     361             : bool
     362           0 : operator< (double t, const TimeLevelEntry & tle)
     363             : {
     364           0 :     return t < tle.getTime() - tle.getInterval();
     365             : }
     366             : 
     367             : bool
     368           0 : operator< (const TimeLevelEntry & tle, double t)
     369             : {
     370           0 :     return t > tle.getTime() + tle.getInterval();
     371             : }
     372             : 
     373             : 
     374           0 : TimeLevelCache::TimeLevelCache (int minTimes, int maxTimes, const AntennaLevelCache * alCache)
     375           0 : : antennaLevelCache_p (alCache),
     376           0 :   cache_p (),
     377           0 :   maxTimes_p (maxTimes),
     378           0 :   minTimes_p (minTimes)
     379           0 : {}
     380             : 
     381             : 
     382             : void
     383           0 : TimeLevelCache::addEntry (Pointing & pointing)
     384             : {
     385           0 :     if (cache_p.size() + 1 > (uint) maxTimes_p){
     386             : 
     387             :         // Compact the cache so that it contains minTimes_p elements.
     388             : 
     389           0 :         int nToErase = cache_p.size() - minTimes_p;
     390           0 :         cache_p.erase (cache_p.begin(), cache_p.begin() + nToErase);
     391             :     }
     392             : 
     393             :     // Add in the new entry on the end.
     394             : 
     395           0 :     if (! cache_p.empty() && pointing.time < cache_p.back().getTime()){
     396             : 
     397             :         // The pointing table is not increasing monotonically for this antenna.
     398             :         // As a kluge, flush the cache and continue.  This could behave badly
     399             :         // in some situations.
     400             : 
     401           0 :         cache_p.clear();
     402             :     }
     403             : 
     404           0 :     cache_p.push_back (TimeLevelEntry (pointing, this));
     405           0 : }
     406             : 
     407             : void
     408           0 : TimeLevelCache::flush ()
     409             : {
     410             :     // Get rid of everything.
     411             : 
     412           0 :     cache_p.clear();
     413           0 : }
     414             : 
     415             : std::pair<CacheAccessStatus, const casacore::MDirection *>
     416           0 : TimeLevelCache::getPointingDirection (double time)
     417             : {
     418           0 :     if (cache_p.empty ()){
     419           0 :         return std::make_pair (CacheAccessStatus::MissPost, nullptr);
     420             :     }
     421             : 
     422             :     // Find the first element that is >= the requested time.
     423             : 
     424           0 :     Cache::iterator lowerBound = std::lower_bound (cache_p.begin(), cache_p.end(), time);
     425             : 
     426           0 :     if (lowerBound == cache_p.end()){
     427             : 
     428             :         // Handle the case where there is no lower bound
     429             : 
     430           0 :         TimeLevelEntry & tle = cache_p.back();
     431           0 :         double upperTime = tle.getTime() + tle.getInterval();
     432             : 
     433           0 :         if (time <= upperTime){
     434             : 
     435             :             // Last element in cache's interval contains the target
     436             :             // time: we found it
     437             : 
     438           0 :             return std::make_pair (CacheAccessStatus::Hit, tle.getDirection());
     439             :         }
     440             : 
     441             :         // Off the top end of existing data in the cache
     442             : 
     443           0 :         return std::make_pair (CacheAccessStatus::MissPost, nullptr);
     444             :     }
     445             : 
     446           0 :     if (time == * lowerBound){
     447             : 
     448             :         // We've found it
     449             : 
     450           0 :         return std::make_pair (CacheAccessStatus::Hit, lowerBound->getDirection());
     451             : 
     452             :     }
     453             : 
     454           0 :     if (lowerBound == cache_p.begin()){
     455             : 
     456             :         // Nothing below it so it's a miss
     457             : 
     458           0 :         return std::make_pair (CacheAccessStatus::MissPrior, nullptr);
     459             :     }
     460             : 
     461           0 :     if (time == * (lowerBound - 1)){
     462             : 
     463           0 :         return std::make_pair (CacheAccessStatus::Hit,
     464           0 :                                (lowerBound - 1)->getDirection());
     465             :     }
     466             : 
     467             :     // Cache miss: must be between two cache entries (this could be handled
     468             :     // by interpolating between the two entries, but might indicate a flaw in
     469             :     // the algorithm or the data; for now just let the caller sort it out).
     470             : 
     471           0 :     return std::make_pair (CacheAccessStatus::MissInternal, nullptr);
     472             : }
     473             : 
     474             : const PointingSource *
     475           0 : TimeLevelCache::getPointingSource () const
     476             : {
     477           0 :     return antennaLevelCache_p->getPointingSource ();
     478             : }
     479             : 
     480             : 
     481             : } // end namespace pd_cache
     482             : 
     483             : } // end namespace vi
     484             : } // end namepsace casa
     485             : 

Generated by: LCOV version 1.16