casa  $Rev:20696$
 All Classes Namespaces Files Functions Variables
edgemarker.py
Go to the documentation of this file.
00001 from asap.scantable import scantable
00002 from asap._asap import _edgemarker
00003 import numpy
00004 import math
00005         
00006 class edgemarker:
00007     """
00008     The edgemarker is a helper tool to calibrate OTF observation
00009     without explicit OFF scans. According to a few user-specified
00010     options, the class automatically detects an edge region of the
00011     map and mark integrations within this region as OFF.
00012 
00013     The edgemarker supports raster pattern as well as other generic 
00014     ones (e.g. lissajous, double circle). The constructor takes
00015     one boolean parameter to specify whether scan pattern is raster
00016     or not. This is because that edge detection algorithms for raster
00017     and others are different.
00018 
00019     Current limitation of this class is that it cannot handle some
00020     complicated observed area. Typical case is that the area has
00021     clear 'dent' (e.g. a composite area consisting of two diamond-
00022     shaped areas that slightly overlap). In such case, the class
00023     will fail to detect such feature.
00024 
00025     Note that the class takes a copy of input data so that input
00026     data will not be overwritten. Result will be provided as a
00027     separate data whose contents are essentially the same as input
00028     except for that some integrations are marked as OFF.
00029 
00030     Here is typical usage:
00031 
00032        s = scantable( 'otf.asap', average=False )
00033        marker = edgemarker( israster=False )
00034        marker.setdata( s )
00035        marker.setoption( fraction='15%', width=0.5 )
00036        marker.mark()
00037 
00038        # get result as scantable instance
00039        s2 = marker.getresult()
00040 
00041        # save result on disk
00042        marker.save( 'otfwithoff.asap', overwrite=True )
00043     """
00044     def __init__( self, israster=False ):
00045         """
00046         Constructor.
00047 
00048         israster -- Whether scan pattern is raster or not. Set True
00049                     if scan pattern is raster. Default is False.
00050         """
00051         self.israster = israster
00052         self.marker = _edgemarker( self.israster )
00053         self.st = None
00054 
00055     def setdata( self, st ):
00056         """
00057         Set data to be processed.
00058 
00059         st -- Data as scantable instance.
00060         """
00061         self.st = st
00062         self.marker._setdata( self.st, False )
00063         self.marker._examine()
00064 
00065     def setoption( self, *args, **kwargs ):
00066         """
00067         Set options for edge detection. Valid options depend on
00068         whether scan pattern is raster or not (i.e. constructor
00069         is called with israster=True or False).
00070 
00071         === for raster (israster=True) ===
00072         fraction -- Fraction of OFF integration in each raster
00073                     row. Either numerical value (<1.0) or string
00074                     is accepted. For string, its value should be
00075                     'auto' or format 'xx%'. For example, '10%'
00076                     is same as 0.1. The 'auto' option estimates
00077                     number of OFFs based on t_OFF = sqrt(N) t_ON.
00078                     Default is 0.1.
00079         npts -- Number of OFF integration in each raster row.
00080                 Default is -1 (use fraction).
00081 
00082         Note that number of integrations specified by the above
00083         parameters will be marked as OFF from both ends. So, twice
00084         of specified number/fraction will be marked as OFF. For
00085         example, if you specify fraction='10%', resultant fraction
00086         of OFF integrations will be 20%.
00087         
00088         Note also that, if both fraction and npts are specified,
00089         specification by npts will come before. 
00090 
00091         === for non-raster (israster=False) ===
00092         fraction -- Fraction of edge area with respect to whole
00093                     observed area. Either numerical value (<1.0)
00094                     or string is accepted. For string, its value
00095                     should be in 'xx%' format. For example, '10%'
00096                     is same as 0.1. Default is 0.1.
00097         width -- Pixel width for edge detection. It should be given
00098                  as a fraction of the median spatial separation
00099                  between neighboring integrations in time. Default
00100                  is 0.5. In the most case, default value will be fine.
00101                  Larger value will cause worse result. Smaller value
00102                  may improve result. However, if too small value is
00103                  set (e.g. 1.0e-5), the algorithm may not work.
00104         elongated -- Set True only if observed area is extremely
00105                      elongated in one direction. Default is False.
00106                      In most cases, default value will be fine.
00107         """
00108         option = {}
00109         if self.israster:
00110             keys = [ 'fraction', 'npts' ]
00111         else:
00112             keys = [ 'fraction', 'width', 'elongated' ]
00113         for key in keys:
00114             if kwargs.has_key( key ):
00115                 option[key] = kwargs[key]
00116 
00117         if len(option) > 0:
00118             self.marker._setoption( option )
00119 
00120     def mark( self ):
00121         """
00122         Process data. Edge region is detected according to detection
00123         parameters given by setoption(). Then, integrations within
00124         edge region will be marked as OFF.
00125         """
00126         self.marker._detect()
00127         self.marker._mark()
00128 
00129     def getresult( self ):
00130         """
00131         Get result as scantable instance. Returned scantable is
00132         copy of input scantable except for that some data are
00133         marked as OFF as a result of edge detection and marking.
00134         """
00135         return scantable( self.marker._get() )
00136 
00137     def save( self, name, overwrite=False ):
00138         """
00139         Save result as scantable.
00140 
00141         name -- Name of the scantable.
00142         overwrite -- Overwrite existing data if True. Default is
00143                      False.
00144         """
00145         s = self.getresult()
00146         s.save( name, overwrite=overwrite )
00147 
00148     def plot( self ):
00149         """
00150         """
00151         from matplotlib import pylab as pl
00152         from asap import selector
00153         from asap._asap import srctype as st
00154         pl.clf()
00155 
00156         # result as a scantable
00157         s = self.getresult()
00158 
00159         # ON scan
00160         sel = selector()
00161         sel.set_types( int(st.pson) )
00162         s.set_selection( sel )
00163         diron = numpy.array( s.get_directionval() ).transpose()
00164         diron[0] = rotate( diron[0] )
00165         s.set_selection()
00166         sel.reset()
00167 
00168         # OFF scan
00169         sel.set_types( int(st.psoff) )
00170         s.set_selection( sel )
00171         diroff = numpy.array( s.get_directionval() ).transpose()
00172         diroff[0] = rotate( diroff[0] )
00173         s.set_selection()
00174         sel.reset()
00175         del s
00176         del sel
00177 
00178         # plot
00179         pl.ioff()
00180         ax=pl.axes()
00181         ax.set_aspect(1.0)
00182         pl.plot( diron[0], diron[1], '.', color='blue', label='ON' )
00183         pl.plot( diroff[0], diroff[1], '.', color='green', label='OFF' )
00184         [xmin,xmax,ymin,ymax] = pl.axis()
00185         pl.axis([xmax,xmin,ymin,ymax])
00186         pl.legend(loc='best',prop={'size':'small'},numpoints=1)
00187         pl.xlabel( 'R.A. [rad]' )
00188         pl.ylabel( 'Declination [rad]' )
00189         pl.title( 'edgemarker result' )
00190         pl.ion()
00191         pl.draw()
00192 
00193 def _0to2pi( v ):
00194     return v % (2.0*math.pi)
00195 
00196 def quadrant( v ):
00197     vl = _0to2pi( v )
00198     base = 0.5 * math.pi
00199     return int( vl / base )
00200 
00201 def quadrantList( a ):
00202     n = len(a)
00203     nquad = numpy.zeros( 4, dtype=int )
00204     for i in xrange(n):
00205         v = quadrant( a[i] )
00206         nquad[v] += 1
00207     #print nquad
00208     return nquad
00209 
00210 def rotate( v ):
00211     a = numpy.zeros( len(v), dtype=float )
00212     for i in xrange(len(v)):
00213         a[i] = _0to2pi( v[i] )
00214     nquad = quadrantList( a )
00215     quadList = [[],[],[],[]]
00216     rot = numpy.zeros( 4, dtype=bool )
00217     if all( nquad==0 ):
00218         print 'no data'
00219     elif all( nquad>0 ):
00220         #print 'extends in all quadrants'
00221         pass
00222     elif nquad[0]>0 and nquad[3]>0:
00223         #print 'need rotation'
00224         rot[3] = True
00225         rot[2] = nquad[1]==0 and nquad[2]>0
00226     #print rot
00227 
00228     for i in xrange(len(a)):
00229         if rot[quadrant(a[i])]:
00230             a[i] -= 2*math.pi
00231     return a