| [2613] | 1 | from asap.scantable import scantable | 
|---|
|  | 2 | from asap._asap import _edgemarker | 
|---|
| [2635] | 3 | import numpy | 
|---|
|  | 4 | import math | 
|---|
|  | 5 |  | 
|---|
| [2613] | 6 | class edgemarker: | 
|---|
|  | 7 | """ | 
|---|
|  | 8 | The edgemarker is a helper tool to calibrate OTF observation | 
|---|
|  | 9 | without explicit OFF scans. According to a few user-specified | 
|---|
|  | 10 | options, the class automatically detects an edge region of the | 
|---|
|  | 11 | map and mark integrations within this region as OFF. | 
|---|
|  | 12 |  | 
|---|
|  | 13 | The edgemarker supports raster pattern as well as other generic | 
|---|
|  | 14 | ones (e.g. lissajous, double circle). The constructor takes | 
|---|
|  | 15 | one boolean parameter to specify whether scan pattern is raster | 
|---|
|  | 16 | or not. This is because that edge detection algorithms for raster | 
|---|
|  | 17 | and others are different. | 
|---|
|  | 18 |  | 
|---|
|  | 19 | Current limitation of this class is that it cannot handle some | 
|---|
|  | 20 | complicated observed area. Typical case is that the area has | 
|---|
|  | 21 | clear 'dent' (e.g. a composite area consisting of two diamond- | 
|---|
| [2616] | 22 | shaped areas that slightly overlap). In such case, the class | 
|---|
| [2613] | 23 | will fail to detect such feature. | 
|---|
|  | 24 |  | 
|---|
|  | 25 | Note that the class takes a copy of input data so that input | 
|---|
|  | 26 | data will not be overwritten. Result will be provided as a | 
|---|
|  | 27 | separate data whose contents are essentially the same as input | 
|---|
|  | 28 | except for that some integrations are marked as OFF. | 
|---|
|  | 29 |  | 
|---|
|  | 30 | Here is typical usage: | 
|---|
|  | 31 |  | 
|---|
|  | 32 | s = scantable( 'otf.asap', average=False ) | 
|---|
|  | 33 | marker = edgemarker( israster=False ) | 
|---|
|  | 34 | marker.setdata( s ) | 
|---|
|  | 35 | marker.setoption( fraction='15%', width=0.5 ) | 
|---|
|  | 36 | marker.mark() | 
|---|
|  | 37 |  | 
|---|
|  | 38 | # get result as scantable instance | 
|---|
|  | 39 | s2 = marker.getresult() | 
|---|
|  | 40 |  | 
|---|
|  | 41 | # save result on disk | 
|---|
|  | 42 | marker.save( 'otfwithoff.asap', overwrite=True ) | 
|---|
|  | 43 | """ | 
|---|
|  | 44 | def __init__( self, israster=False ): | 
|---|
|  | 45 | """ | 
|---|
|  | 46 | Constructor. | 
|---|
|  | 47 |  | 
|---|
|  | 48 | israster -- Whether scan pattern is raster or not. Set True | 
|---|
|  | 49 | if scan pattern is raster. Default is False. | 
|---|
|  | 50 | """ | 
|---|
|  | 51 | self.israster = israster | 
|---|
|  | 52 | self.marker = _edgemarker( self.israster ) | 
|---|
|  | 53 | self.st = None | 
|---|
|  | 54 |  | 
|---|
|  | 55 | def setdata( self, st ): | 
|---|
|  | 56 | """ | 
|---|
|  | 57 | Set data to be processed. | 
|---|
|  | 58 |  | 
|---|
|  | 59 | st -- Data as scantable instance. | 
|---|
|  | 60 | """ | 
|---|
|  | 61 | self.st = st | 
|---|
|  | 62 | self.marker._setdata( self.st, False ) | 
|---|
|  | 63 | self.marker._examine() | 
|---|
|  | 64 |  | 
|---|
|  | 65 | def setoption( self, *args, **kwargs ): | 
|---|
|  | 66 | """ | 
|---|
|  | 67 | Set options for edge detection. Valid options depend on | 
|---|
| [2616] | 68 | whether scan pattern is raster or not (i.e. constructor | 
|---|
|  | 69 | is called with israster=True or False). | 
|---|
| [2613] | 70 |  | 
|---|
| [2616] | 71 | === for raster (israster=True) === | 
|---|
| [2613] | 72 | fraction -- Fraction of OFF integration in each raster | 
|---|
|  | 73 | row. Either numerical value (<1.0) or string | 
|---|
|  | 74 | is accepted. For string, its value should be | 
|---|
|  | 75 | 'auto' or format 'xx%'. For example, '10%' | 
|---|
|  | 76 | is same as 0.1. The 'auto' option estimates | 
|---|
|  | 77 | number of OFFs based on t_OFF = sqrt(N) t_ON. | 
|---|
|  | 78 | Default is 0.1. | 
|---|
|  | 79 | npts -- Number of OFF integration in each raster row. | 
|---|
|  | 80 | Default is -1 (use fraction). | 
|---|
|  | 81 |  | 
|---|
|  | 82 | Note that number of integrations specified by the above | 
|---|
|  | 83 | parameters will be marked as OFF from both ends. So, twice | 
|---|
|  | 84 | of specified number/fraction will be marked as OFF. For | 
|---|
|  | 85 | example, if you specify fraction='10%', resultant fraction | 
|---|
|  | 86 | of OFF integrations will be 20%. | 
|---|
|  | 87 |  | 
|---|
|  | 88 | Note also that, if both fraction and npts are specified, | 
|---|
|  | 89 | specification by npts will come before. | 
|---|
|  | 90 |  | 
|---|
| [2616] | 91 | === for non-raster (israster=False) === | 
|---|
| [2613] | 92 | fraction -- Fraction of edge area with respect to whole | 
|---|
|  | 93 | observed area. Either numerical value (<1.0) | 
|---|
|  | 94 | or string is accepted. For string, its value | 
|---|
|  | 95 | should be in 'xx%' format. For example, '10%' | 
|---|
|  | 96 | is same as 0.1. Default is 0.1. | 
|---|
|  | 97 | width -- Pixel width for edge detection. It should be given | 
|---|
| [2616] | 98 | as a fraction of the median spatial separation | 
|---|
|  | 99 | between neighboring integrations in time. Default | 
|---|
|  | 100 | is 0.5. In the most case, default value will be fine. | 
|---|
|  | 101 | Larger value will cause worse result. Smaller value | 
|---|
|  | 102 | may improve result. However, if too small value is | 
|---|
|  | 103 | set (e.g. 1.0e-5), the algorithm may not work. | 
|---|
| [2613] | 104 | elongated -- Set True only if observed area is extremely | 
|---|
|  | 105 | elongated in one direction. Default is False. | 
|---|
|  | 106 | In most cases, default value will be fine. | 
|---|
|  | 107 | """ | 
|---|
|  | 108 | option = {} | 
|---|
|  | 109 | if self.israster: | 
|---|
|  | 110 | keys = [ 'fraction', 'npts' ] | 
|---|
|  | 111 | else: | 
|---|
|  | 112 | keys = [ 'fraction', 'width', 'elongated' ] | 
|---|
|  | 113 | for key in keys: | 
|---|
|  | 114 | if kwargs.has_key( key ): | 
|---|
|  | 115 | option[key] = kwargs[key] | 
|---|
|  | 116 |  | 
|---|
|  | 117 | if len(option) > 0: | 
|---|
|  | 118 | self.marker._setoption( option ) | 
|---|
|  | 119 |  | 
|---|
|  | 120 | def mark( self ): | 
|---|
|  | 121 | """ | 
|---|
|  | 122 | Process data. Edge region is detected according to detection | 
|---|
|  | 123 | parameters given by setoption(). Then, integrations within | 
|---|
|  | 124 | edge region will be marked as OFF. | 
|---|
|  | 125 | """ | 
|---|
|  | 126 | self.marker._detect() | 
|---|
|  | 127 | self.marker._mark() | 
|---|
|  | 128 |  | 
|---|
|  | 129 | def getresult( self ): | 
|---|
|  | 130 | """ | 
|---|
|  | 131 | Get result as scantable instance. Returned scantable is | 
|---|
|  | 132 | copy of input scantable except for that some data are | 
|---|
|  | 133 | marked as OFF as a result of edge detection and marking. | 
|---|
|  | 134 | """ | 
|---|
|  | 135 | return scantable( self.marker._get() ) | 
|---|
|  | 136 |  | 
|---|
|  | 137 | def save( self, name, overwrite=False ): | 
|---|
|  | 138 | """ | 
|---|
|  | 139 | Save result as scantable. | 
|---|
|  | 140 |  | 
|---|
|  | 141 | name -- Name of the scantable. | 
|---|
|  | 142 | overwrite -- Overwrite existing data if True. Default is | 
|---|
|  | 143 | False. | 
|---|
|  | 144 | """ | 
|---|
|  | 145 | s = self.getresult() | 
|---|
|  | 146 | s.save( name, overwrite=overwrite ) | 
|---|
| [2635] | 147 |  | 
|---|
|  | 148 | def plot( self ): | 
|---|
|  | 149 | """ | 
|---|
|  | 150 | """ | 
|---|
|  | 151 | from matplotlib import pylab as pl | 
|---|
|  | 152 | from asap import selector | 
|---|
|  | 153 | from asap._asap import srctype as st | 
|---|
|  | 154 | pl.clf() | 
|---|
|  | 155 |  | 
|---|
|  | 156 | # result as a scantable | 
|---|
|  | 157 | s = self.getresult() | 
|---|
|  | 158 |  | 
|---|
|  | 159 | # ON scan | 
|---|
|  | 160 | sel = selector() | 
|---|
|  | 161 | sel.set_types( int(st.pson) ) | 
|---|
|  | 162 | s.set_selection( sel ) | 
|---|
|  | 163 | diron = numpy.array( s.get_directionval() ).transpose() | 
|---|
|  | 164 | diron[0] = rotate( diron[0] ) | 
|---|
|  | 165 | s.set_selection() | 
|---|
|  | 166 | sel.reset() | 
|---|
|  | 167 |  | 
|---|
|  | 168 | # OFF scan | 
|---|
|  | 169 | sel.set_types( int(st.psoff) ) | 
|---|
|  | 170 | s.set_selection( sel ) | 
|---|
|  | 171 | diroff = numpy.array( s.get_directionval() ).transpose() | 
|---|
|  | 172 | diroff[0] = rotate( diroff[0] ) | 
|---|
|  | 173 | s.set_selection() | 
|---|
|  | 174 | sel.reset() | 
|---|
|  | 175 | del s | 
|---|
|  | 176 | del sel | 
|---|
|  | 177 |  | 
|---|
|  | 178 | # plot | 
|---|
|  | 179 | pl.ioff() | 
|---|
|  | 180 | ax=pl.axes() | 
|---|
|  | 181 | ax.set_aspect(1.0) | 
|---|
|  | 182 | pl.plot( diron[0], diron[1], '.', color='blue', label='ON' ) | 
|---|
|  | 183 | pl.plot( diroff[0], diroff[1], '.', color='green', label='OFF' ) | 
|---|
|  | 184 | [xmin,xmax,ymin,ymax] = pl.axis() | 
|---|
|  | 185 | pl.axis([xmax,xmin,ymin,ymax]) | 
|---|
|  | 186 | pl.legend(loc='best',prop={'size':'small'},numpoints=1) | 
|---|
|  | 187 | pl.xlabel( 'R.A. [rad]' ) | 
|---|
|  | 188 | pl.ylabel( 'Declination [rad]' ) | 
|---|
|  | 189 | pl.title( 'edgemarker result' ) | 
|---|
|  | 190 | pl.ion() | 
|---|
|  | 191 | pl.draw() | 
|---|
|  | 192 |  | 
|---|
|  | 193 | def _0to2pi( v ): | 
|---|
|  | 194 | return v % (2.0*math.pi) | 
|---|
|  | 195 |  | 
|---|
|  | 196 | def quadrant( v ): | 
|---|
|  | 197 | vl = _0to2pi( v ) | 
|---|
|  | 198 | base = 0.5 * math.pi | 
|---|
|  | 199 | return int( vl / base ) | 
|---|
|  | 200 |  | 
|---|
|  | 201 | def quadrantList( a ): | 
|---|
|  | 202 | n = len(a) | 
|---|
|  | 203 | nquad = numpy.zeros( 4, dtype=int ) | 
|---|
|  | 204 | for i in xrange(n): | 
|---|
|  | 205 | v = quadrant( a[i] ) | 
|---|
|  | 206 | nquad[v] += 1 | 
|---|
|  | 207 | #print nquad | 
|---|
|  | 208 | return nquad | 
|---|
|  | 209 |  | 
|---|
|  | 210 | def rotate( v ): | 
|---|
|  | 211 | a = numpy.zeros( len(v), dtype=float ) | 
|---|
|  | 212 | for i in xrange(len(v)): | 
|---|
|  | 213 | a[i] = _0to2pi( v[i] ) | 
|---|
|  | 214 | nquad = quadrantList( a ) | 
|---|
|  | 215 | quadList = [[],[],[],[]] | 
|---|
|  | 216 | rot = numpy.zeros( 4, dtype=bool ) | 
|---|
|  | 217 | if all( nquad==0 ): | 
|---|
|  | 218 | print 'no data' | 
|---|
|  | 219 | elif all( nquad>0 ): | 
|---|
|  | 220 | #print 'extends in all quadrants' | 
|---|
|  | 221 | pass | 
|---|
|  | 222 | elif nquad[0]>0 and nquad[3]>0: | 
|---|
|  | 223 | #print 'need rotation' | 
|---|
|  | 224 | rot[3] = True | 
|---|
|  | 225 | rot[2] = nquad[1]==0 and nquad[2]>0 | 
|---|
|  | 226 | #print rot | 
|---|
|  | 227 |  | 
|---|
|  | 228 | for i in xrange(len(a)): | 
|---|
|  | 229 | if rot[quadrant(a[i])]: | 
|---|
|  | 230 | a[i] -= 2*math.pi | 
|---|
|  | 231 | return a | 
|---|