casa  $Rev:20696$
 All Classes Namespaces Files Functions Variables
filltsys.py
Go to the documentation of this file.
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