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
00157 s = self.getresult()
00158
00159
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
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
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
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
00221 pass
00222 elif nquad[0]>0 and nquad[3]>0:
00223
00224 rot[3] = True
00225 rot[2] = nquad[1]==0 and nquad[2]>0
00226
00227
00228 for i in xrange(len(a)):
00229 if rot[quadrant(a[i])]:
00230 a[i] -= 2*math.pi
00231 return a