casa
$Rev:20696$
|
00001 #******************************************************************************* 00002 # ALMA - Atacama Large Millimeter Array 00003 # Copyright (c) NAOJ - National Astronomical Observatory of Japan, 2011 00004 # (in the framework of the ALMA collaboration). 00005 # All rights reserved. 00006 # 00007 # This library is free software; you can redistribute it and/or 00008 # modify it under the terms of the GNU Lesser General Public 00009 # License as published by the Free Software Foundation; either 00010 # version 2.1 of the License, or (at your option) any later version. 00011 # 00012 # This library is distributed in the hope that it will be useful, 00013 # but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 # Lesser General Public License for more details. 00016 # 00017 # You should have received a copy of the GNU Lesser General Public 00018 # License along with this library; if not, write to the Free Software 00019 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00020 #******************************************************************************* 00021 from scipy.interpolate import interp1d 00022 from taskinit import gentools 00023 import numpy 00024 import string 00025 00026 _tb = gentools(['tb'])[0] 00027 00028 scaling={'GHz':1.0e-9, 00029 'MHz':1.0e-6, 00030 'kHz':1.0e-3, 00031 'Hz':1.0} 00032 00033 # 00034 # fillTsys( filename, specif, tsysif, mode ) 00035 # 00036 # high level function to fill Tsys on spectral data 00037 # 00038 # Tsys is processed along the following three steps: 00039 # 1. average within scan 00040 # 2. if possible, linearly interpolate in time. 00041 # 3. interpolate in frequency with specified mode if necessary 00042 # 00043 def fillTsys( filename, specif, tsysif=None, mode='linear',extrap=False ): 00044 """ 00045 high level function to fill Tsys on spectral data 00046 00047 Tsys is processed along the following three steps: 00048 1. average within scan 00049 2. if possible, linearly interpolate in time. 00050 3. interpolate in frequency with specified mode 00051 00052 filename -- input filename 00053 specif -- IFNO for spectral data 00054 tsysif -- IFNO for calibration (Tsys) data 00055 default: None (try to find automatically) 00056 options: any integer 00057 mode -- interpolation mode along frequency axis 00058 default: 'linear' 00059 options: 'linear',,'nearest','zero', 00060 'slinear','quadratic','cubic' 00061 any integer specifying an order of 00062 spline interpolation 00063 """ 00064 if tsysif is None or specif != tsysif: 00065 filler = TsysFiller( filename=filename, specif=specif, tsysif=tsysif, extrap=extrap ) 00066 polnos = filler.getPolarizations() 00067 for pol in polnos: 00068 filler.setPolarization( pol ) 00069 filler.fillScanAveragedTsys( mode=mode ) 00070 del filler 00071 else: 00072 filler = SimpleTsysFiller( filename=filename, ifno=specif ) 00073 polnos = filler.getPolarizations() 00074 for pol in polnos: 00075 filler.setPolarization( pol ) 00076 filler.fillScanAveragedTsys() 00077 del filler 00078 00079 # 00080 # Utility functions 00081 # 00082 def interpolateInFrequency( a, b, x, mode='linear' ): 00083 """ 00084 Interpolate 1d array 'b', which is based on axis 'a', 00085 to 'x'. 00086 00087 Inputs: 00088 a -- base axis (1-d array) 00089 b -- base array (1-d array) 00090 x -- transfer axis (1-d array) 00091 mode -- interpolation mode 00092 default: 'linear' 00093 options: 'linear','nearest','zero', 00094 'slinear','quadratic','cubic' 00095 00096 Returns: 00097 interpolated 1d array 00098 """ 00099 flipped = False 00100 if a[1] < a[0]: 00101 flipped = True 00102 a = a[::-1] 00103 b = b[::-1] 00104 x = x[::-1] 00105 f = interp1d( a, b, kind=mode ) 00106 y = f( x ) 00107 if flipped: 00108 y = y[::-1] 00109 return y 00110 00111 def interpolateInTime( t1, y1, t2, y2, t ): 00112 """ 00113 Linearly interpolate between y1 and y2, which are defined 00114 at t1 and t2 respectively, to t, where t1 <= t <= t2. 00115 00116 Inputs: 00117 t1, t2 -- base time stamp 00118 y1, y2 -- the value at t1 and t2 00119 (either scalar or conformed numpy array) 00120 t -- transfer time stamp 00121 00122 Returns: 00123 interpolated value 00124 """ 00125 if t == t1: 00126 return y1 00127 elif t == t2: 00128 return y2 00129 else: 00130 dt1 = t - t1 00131 dt2 = t2 - t 00132 y = ( y2 * dt1 + y1 * dt2 ) / ( dt1 + dt2 ) 00133 return y 00134 00135 # 00136 # base class for TsysFiller 00137 # 00138 class TsysFillerBase( object ): 00139 def __init__( self, filename ): 00140 self.filename = filename.rstrip('/') 00141 self.polno = None 00142 self.beamno = None 00143 self.table = gentools(['tb'])[0] 00144 self.table.open( self.filename, nomodify=False ) 00145 00146 def __del__( self ): 00147 if self.table is not None: 00148 self.table.close() 00149 00150 def _select( self, ifno, polno=None, beamno=None, scanno=None, srctype=None, exclude=False): 00151 """ 00152 Select data by IFNO, POLNO, BEAMNO, and SCANNO 00153 00154 ifno -- IFNO 00155 polno -- POLNO 00156 beamno -- BEAMNO 00157 scanno -- SCANNO 00158 srctype -- SRCTYPE 00159 exclude -- if True, srctype is list of excluded SRCTYPE 00160 """ 00161 taql = 'IFNO==%s'%(ifno) 00162 if polno is not None: 00163 taql += ' && POLNO==%s'%(polno) 00164 if beamno is not None: 00165 taql += ' && BEAMNO==%s'%(beamno) 00166 if scanno is not None: 00167 taql += ' && SCANNO==%s'%(scanno) 00168 if srctype is not None: 00169 try: 00170 st = list(srctype) 00171 except: 00172 st = [srctype] 00173 if exclude: 00174 logi = 'NOT IN' 00175 else: 00176 logi = 'IN' 00177 taql += ' && SRCTYPE %s %s'%(logi,st) 00178 return self.table.query( taql ) 00179 00180 def setPolarization( self, polno ): 00181 """ 00182 Set POLNO to be processed 00183 00184 polno -- POLNO 00185 """ 00186 self.polno = polno 00187 00188 def setBeam( self, beamno ): 00189 """ 00190 Set BEAMNO to be processed 00191 Since default BEAMNO is 0, this method is usually 00192 not necessary. 00193 00194 beamno -- BEAMNO 00195 """ 00196 self.beamno = beamno 00197 00198 def getScanAveragedTsys( self, ifno, scannos ): 00199 """ 00200 Get Tsys averaged within scan 00201 00202 ifno -- IFNO 00203 scannos -- list of SCANNO 00204 """ 00205 ret = [] 00206 for scan in scannos: 00207 tbsel = self._select( ifno=ifno, polno=self.polno, beamno=self.beamno, scanno=scan, srctype=[10,11], exclude=False ) 00208 tsys = tbsel.getcol('TSYS') 00209 tbsel.close() 00210 if tsys.size > 0: 00211 ret.append( tsys.mean( axis=1 ) ) 00212 else: 00213 ret.append( None ) 00214 return ret 00215 00216 def getScanAveragedTsysTime( self, ifno, scannos ): 00217 """ 00218 Get scan time (mid-point of the scan) 00219 00220 ifno -- IFNO 00221 scanno -- SCANNO 00222 """ 00223 ret = [] 00224 for scan in scannos: 00225 tbsel = self._select( ifno=ifno, polno=self.polno, beamno=self.beamno, scanno=scan, srctype=[10,11], exclude=False ) 00226 t = tbsel.getcol('TIME') 00227 tbsel.close() 00228 if t.size > 0: 00229 ret.append( t.mean() ) 00230 else: 00231 ret.append( None ) 00232 return ret 00233 00234 00235 # 00236 # class SimpleTsysFiller 00237 # 00238 # Working class to fill TSYS columns for spectral data and 00239 # its channel averaged data. It assumes that IFNO for target 00240 # and for ATM cal are the same so that no interpolation in 00241 # frequency is needed. 00242 # 00243 class SimpleTsysFiller( TsysFillerBase ): 00244 """ 00245 Simply Fill Tsys 00246 """ 00247 def __init__( self, filename, ifno ): 00248 """ 00249 Constructor 00250 00251 filename -- data in scantable format 00252 ifno -- IFNO to be processed 00253 """ 00254 super(SimpleTsysFiller,self).__init__( filename ) 00255 self.ifno = ifno 00256 print 'IFNO to be processed: %s'%(self.ifno) 00257 00258 def getPolarizations( self ): 00259 """ 00260 Get list of POLNO's 00261 """ 00262 tsel = self._select( ifno=self.ifno, srctype=[10,11], exclude=True ) 00263 pols = numpy.unique( tsel.getcol('POLNO') ) 00264 tsel.close() 00265 return pols 00266 00267 def fillScanAveragedTsys( self ): 00268 """ 00269 Fill Tsys 00270 00271 Tsys is averaged over scan first. Then, Tsys is 00272 interpolated in time. Finally, averaged and 00273 interpolated Tsys is used to fill TSYS field for 00274 spectral data. 00275 """ 00276 print 'POLNO=%s,BEAMNO=%s'%(self.polno,(self.beamno if self.beamno is not None else 'all')) 00277 srctype = [10,11] 00278 stab = self._select( ifno=self.ifno, polno=self.polno, beamno=self.beamno, srctype=srctype, exclude=True ) 00279 ttab = self._select( ifno=self.ifno, polno=self.polno, beamno=self.beamno, srctype=srctype, exclude=False ) 00280 # assume IFNO for channel averaged data is 00281 # (IFNO for sp data)+1 00282 tptab = self._select( ifno=self.ifno+1, polno=self.polno, beamno=self.beamno, srctype=srctype, exclude=True ) 00283 00284 # get scan numbers for calibration scan (Tsys) 00285 calscans = numpy.unique( ttab.getcol('SCANNO') ) 00286 calscans.sort() 00287 nscan = len(calscans) 00288 print 'nscan = ', nscan 00289 00290 # get scan averaged Tsys and time 00291 atsys = self.getScanAveragedTsys( self.ifno, calscans ) 00292 caltime = self.getScanAveragedTsysTime( self.ifno, calscans ) 00293 00294 # warning 00295 if len(caltime) == 1: 00296 print 'WARN: There is only one ATM cal session. No temporal interpolation will be done.' 00297 00298 # process all rows 00299 nrow = stab.nrows() 00300 cleat = 0 00301 for irow in xrange(nrow): 00302 #print 'process row %s'%(irow) 00303 t = stab.getcell( 'TIME', irow ) 00304 if t < caltime[0]: 00305 #print 'No Tsys available before this scan, use first Tsys' 00306 tsys = atsys[0] 00307 elif t > caltime[nscan-1]: 00308 #print 'No Tsys available after this scan, use last Tsys' 00309 tsys = atsys[nscan-1] 00310 else: 00311 idx = self._search( caltime, t, cleat ) 00312 cleat = max( 0, idx-1 ) 00313 if caltime[idx] == t: 00314 tsys = atsys[idx] 00315 else: 00316 t0 = caltime[idx-1] 00317 t1 = caltime[idx] 00318 tsys0 = atsys[idx-1] 00319 tsys1 = atsys[idx] 00320 tsys = interpolateInTime( t0, tsys0, t1, tsys1, t ) 00321 stab.putcell( 'TSYS', irow, tsys ) 00322 if tptab.nrows() > 0: 00323 tptab.putcell( 'TSYS', irow, numpy.median(tsys) ) 00324 stab.close() 00325 ttab.close() 00326 tptab.close() 00327 00328 00329 # 00330 # class TsysFiller 00331 # 00332 # Working class to fill TSYS columns for spectral data and 00333 # its channel averaged data. 00334 # 00335 # Basic Usage: 00336 # filler = TsysFiller( filename, specif, tsysif ) 00337 # polnos = filler.getPolarizations() 00338 # for pol in polnos: 00339 # filler.setPolarization( pol ) 00340 # filler.fillScanAveragedTsys( mode=mode ) 00341 # 00342 class TsysFiller( TsysFillerBase ): 00343 """ 00344 Fill Tsys 00345 """ 00346 def __init__( self, filename, specif, tsysif=None, extrap=False ): 00347 """ 00348 Constructor 00349 00350 filename -- input Scantable 00351 specif -- IFNO for spectral data 00352 tsysif -- IFNO for calibration scans (Tsys) 00353 """ 00354 super(TsysFiller,self).__init__( filename ) 00355 self.polno = None 00356 self.beamno = 0 00357 self.specif = specif 00358 self.abcsp = self._constructAbcissa( self.specif ) 00359 self.extend = extrap 00360 if tsysif is None: 00361 self.tsysif = None 00362 self.abctsys = None 00363 self._setupTsysConfig() 00364 else: 00365 self.tsysif = tsysif 00366 self.abctsys = self._constructAbcissa( self.tsysif ) 00367 if not self.extend and not self.__checkCoverage( self.abctsys, self.abcsp ): 00368 raise Exception( "Invalid specification of SPW for Tsys: it must cover SPW for target" ) 00369 if not self.extend: 00370 self.extend = self.__checkChannels( self.abctsys, self.abcsp ) 00371 print 'spectral IFNO %s: corresponding Tsys IFNO is %s'%(self.specif,self.tsysif) 00372 00373 def _setupTsysConfig( self ): 00374 """ 00375 Set up configuration for Tsys 00376 - determine IFNO for Tsys 00377 - set self.abctsys 00378 """ 00379 ftab=self.table.getkeyword('FREQUENCIES').split()[-1] 00380 ifnos = numpy.unique(self.table.getcol('IFNO')) 00381 nif = len(ifnos) 00382 for i in xrange(nif): 00383 tbsel=self.table.query('IFNO==%s'%(i)) 00384 if tbsel.nrows() == 0: 00385 continue 00386 nchan=len(tbsel.getcell('SPECTRA',0)) 00387 if nchan == 1: 00388 continue 00389 else: 00390 abc = self._constructAbcissa( i ) 00391 ## if abc.min() <= self.abcsp.min() \ 00392 ## and abc.max() >= self.abcsp.max(): 00393 if self.__checkCoverage( abc, self.abcsp ): 00394 self.tsysif = i 00395 self.abctsys = abc 00396 break 00397 00398 def __checkCoverage( self, a, b ): 00399 """ 00400 Check frequency coverage 00401 """ 00402 ea = self.__edge( a ) 00403 eb = self.__edge( b ) 00404 # 2012/05/09 TN 00405 # This is workaround for the issue that frequency coverage 00406 # doesn't match in LSRK frame although it exactly match in TOPO. 00407 ret = ( ea[0] < eb[0] and ea[1] > eb[1] ) \ 00408 or ( abs((ea[0]-eb[0])/ea[0]) < 1.0e-5 \ 00409 and abs((ea[1]-eb[1])/ea[1]) < 1.0e-5 ) 00410 return ret 00411 ## return (ea[0] <= eb[0] and ea[1] >= eb[1]) 00412 00413 def __checkChannels( self, a, b ): 00414 """ 00415 Check location of first and last channels 00416 """ 00417 return ( a.min() > b.min() or a.max() < b.max() ) 00418 00419 def __edge( self, abc ): 00420 """ 00421 return left and right edge values. 00422 """ 00423 incr = abc[1] - abc[0] 00424 ledge = abc[0] - 0.5 * incr 00425 redge = abc[-1] + 0.5 * incr 00426 if ledge > redge: 00427 return (redge,ledge) 00428 else: 00429 return (ledge,redge) 00430 00431 def _constructAbcissa( self, ifno ): 00432 """ 00433 Construct abcissa array from REFPIX, REFVAL, INCREMENT 00434 """ 00435 ftab=self.table.getkeyword('FREQUENCIES').split()[-1] 00436 tbsel = self.table.query('IFNO==%s'%(ifno)) 00437 nchan = len(tbsel.getcell('SPECTRA',0)) 00438 fid = tbsel.getcell('FREQ_ID',0) 00439 tbsel.close() 00440 del tbsel 00441 _tb.open(ftab) 00442 refpix = _tb.getcell('REFPIX',fid) 00443 refval = _tb.getcell('REFVAL',fid) 00444 increment = _tb.getcell('INCREMENT',fid) 00445 _tb.close() 00446 fstart = refval-refpix*increment 00447 fend = refval+(nchan-1-refpix+0.5)*increment 00448 return numpy.arange(fstart,fend,increment) 00449 00450 def getPolarizations( self ): 00451 """ 00452 Get list of POLNO's 00453 """ 00454 tsel = self._select( ifno=self.specif, srctype=[10,11], exclude=True ) 00455 pols = numpy.unique( tsel.getcol('POLNO') ) 00456 tsel.close() 00457 return pols 00458 00459 def getSpecAbcissa( self, unit='GHz' ): 00460 """ 00461 Get abcissa for spectral data 00462 00463 unit -- spectral unit 00464 default: 'GHz' 00465 options: 'channel', 'GHz', 'MHz', 'kHz', 'Hz' 00466 """ 00467 abc = self.abcsp 00468 if unit == 'channel': 00469 return numpy.arange(0,len(abc),1) 00470 else: 00471 return abc * scaling[unit] 00472 00473 def getTsysAbcissa( self, unit='GHz' ): 00474 """ 00475 Get abcissa for Tsys data 00476 00477 unit -- spectral unit 00478 default: 'GHz' 00479 options: 'channel', 'GHz', 'MHz', 'kHz', 'Hz' 00480 """ 00481 abc = self.abctsys 00482 if unit == 'channel': 00483 return numpy.arange(0,len(abc),1) 00484 else: 00485 return abc * scaling[unit] 00486 00487 def fillScanAveragedTsys( self, mode='linear' ): 00488 """ 00489 Fill Tsys 00490 00491 Tsys is averaged over scan first. Then, Tsys is 00492 interpolated in time and frequency. Finally, 00493 averaged and interpolated Tsys is used to fill 00494 TSYS field for spectral data. 00495 """ 00496 print 'POLNO=%s,BEAMNO=%s'%(self.polno,(self.beamno if self.beamno is not None else 'all')) 00497 stab = self._select( ifno=self.specif, polno=self.polno, beamno=self.beamno, srctype=[10,11], exclude=True ) 00498 ttab = self._select( ifno=self.tsysif, polno=self.polno, beamno=self.beamno, srctype=[10,11], exclude=False ) 00499 # assume IFNO for channel averaged data is 00500 # (IFNO for sp data)+1 00501 tptab = self._select( ifno=self.specif+1, polno=self.polno, beamno=self.beamno, srctype=[10,11], exclude=True ) 00502 00503 # get scan numbers for calibration scan (Tsys) 00504 calscans = numpy.unique( ttab.getcol('SCANNO') ) 00505 calscans.sort() 00506 nscan = len(calscans) 00507 print 'nscan = ', nscan 00508 00509 # get scan averaged Tsys and time 00510 atsys = self.getScanAveragedTsys( self.tsysif, calscans ) 00511 caltime = self.getScanAveragedTsysTime( self.tsysif, calscans ) 00512 00513 # warning 00514 if len(caltime) == 1: 00515 print 'WARN: There is only one ATM cal session. No temporal interpolation will be done.' 00516 00517 # extend tsys if necessary 00518 if self.extend: 00519 (abctsys,atsys)=self.__extend(self.abctsys,self.abcsp,atsys) 00520 else: 00521 abctsys = self.abctsys 00522 00523 # process all rows 00524 nrow = stab.nrows() 00525 cleat = 0 00526 for irow in xrange(nrow): 00527 #print 'process row %s'%(irow) 00528 t = stab.getcell( 'TIME', irow ) 00529 if t < caltime[0]: 00530 #print 'No Tsys available before this scan, use first Tsys' 00531 tsys = atsys[0] 00532 elif t > caltime[nscan-1]: 00533 #print 'No Tsys available after this scan, use last Tsys' 00534 tsys = atsys[nscan-1] 00535 else: 00536 idx = self._search( caltime, t, cleat ) 00537 cleat = max( 0, idx-1 ) 00538 if caltime[idx] == t: 00539 tsys = atsys[idx] 00540 else: 00541 t0 = caltime[idx-1] 00542 t1 = caltime[idx] 00543 tsys0 = atsys[idx-1] 00544 tsys1 = atsys[idx] 00545 tsys = interpolateInTime( t0, tsys0, t1, tsys1, t ) 00546 if len(tsys) == len(abctsys): 00547 newtsys = interpolateInFrequency( abctsys, 00548 tsys, 00549 self.abcsp, 00550 mode ) 00551 else: 00552 newtsys = tsys 00553 stab.putcell( 'TSYS', irow, newtsys ) 00554 #tptab.putcell( 'TSYS', irow, newtsys.mean() ) 00555 tptab.putcell( 'TSYS', irow, numpy.median(newtsys) ) 00556 00557 stab.close() 00558 ttab.close() 00559 tptab.close() 00560 del stab 00561 del ttab 00562 del tptab 00563 00564 def __extend( self, a, aref, b ): 00565 """ 00566 Extend spectra 00567 """ 00568 incr_ref = aref[1] - aref[0] 00569 incr = a[1] - a[0] 00570 ext0 = 0 00571 ext1 = 0 00572 l0 = aref[0] - 0.5 * incr_ref 00573 r0 = aref[-1] + 0.5 * incr_ref 00574 l = a[0] 00575 r = a[-1] 00576 #print 'l0=%s,l=%s'%(l0,l) 00577 #print 'r0=%s,r=%s'%(r0,r) 00578 if incr_ref > 0.0: 00579 while l > l0: 00580 ext0 += 1 00581 l -= incr 00582 while r < r0: 00583 ext1 += 1 00584 r += incr 00585 else: 00586 while l < l0: 00587 ext0 += 1 00588 l -= incr 00589 while r > r0: 00590 ext1 += 1 00591 r += incr 00592 if ext0 == 0 and ext1 == 0: 00593 return (a,b) 00594 #print 'ext0=%s,ext1=%s'%(ext0,ext1) 00595 abctsys = numpy.zeros(len(a)+ext0+ext1,dtype=a.dtype) 00596 for i in xrange(ext0): 00597 abctsys[i] = a[0] - incr * (ext0-i) 00598 for i in xrange(ext1): 00599 abctsys[i+len(a)+ext0] = a[-1] + incr * (1+i) 00600 abctsys[ext0:len(abctsys)-ext1] = a 00601 #print 'aref[0]=%s,abctsys[0]=%s'%(aref[0],abctsys[0]) 00602 #print 'aref[-1]=%s,abctsys[-1]=%s'%(aref[-1],abctsys[-1]) 00603 atsys = numpy.zeros( (len(b),len(abctsys)), dtype=type(b[0][0]) ) 00604 for i in xrange(len(b)): 00605 atsys[i][0:ext0] = b[i][0] 00606 atsys[i][len(abctsys)-ext1:] = b[i][-1] 00607 atsys[i][ext0:len(abctsys)-ext1] = b[i] 00608 return (abctsys,atsys) 00609 00610 def _search( self, tcol, t, startpos=0 ): 00611 """ 00612 Simple search 00613 00614 Return minimum index that satisfies tcol[index] > t. 00615 If such index couldn't be found, return -1. 00616 00617 tcol -- array 00618 t -- target value 00619 startpos -- optional start position (default 0) 00620 """ 00621 n = len(tcol) 00622 idx = min( n-1, max( 0, startpos ) ) 00623 if tcol[idx] > t: 00624 idx = 0 00625 while ( idx < n and tcol[idx] < t ): 00626 #print '%s: tcol[%s] = %s, t = %s'%(idx,idx,tcol[idx],t) 00627 idx += 1 00628 if idx == n: 00629 idx = -1 00630 #print 'Index not found, return -1' 00631 #else: 00632 #print 'found index %s: time[%s] = %s, target = %s'%(idx,idx,tcol[idx],t) 00633 00634 return idx 00635