source: trunk/python/scantable.py @ 436

Last change on this file since 436 was 436, checked in by kil064, 19 years ago

arg. 'all' -> 'allaxes' to be consistent with asapmath.py
use same doc. fragment from asapmath.py as well

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.1 KB
Line 
1from asap._asap import sdtable
2from asap import rcParams
3from numarray import ones,zeros
4import sys
5
6class scantable(sdtable):
7    """
8        The ASAP container for scans
9    """
10   
11    def __init__(self, filename, unit=None):
12        """
13        Create a scantable from a saved one or make a reference
14        Parameters:
15            filename:    the name of an asap table on disk
16                         or
17                         the name of a rpfits/sdfits/ms file
18                         (integrations within scans are auto averaged
19                         and the whole file is read)
20                         or
21                         [advanced] a reference to an existing
22                         scantable
23           unit:         brightness unit; must be consistent with K or Jy.
24                         Over-rides the default selected by the reader
25                         (input rpfits/sdfits/ms) or replaces the value
26                         in existing scantables
27        """
28        self._vb = rcParams['verbose']
29        self._p = None
30        from os import stat as st
31        import stat
32        if isinstance(filename,sdtable):
33            sdtable.__init__(self, filename)           
34            if unit is not None:
35                self.set_fluxunit(unit)                       
36        else:
37            import os.path
38            if not os.path.exists(filename):
39                print "File '%s' not found." % (filename)
40                return
41            filename = os.path.expandvars(filename)
42            if os.path.isdir(filename):
43                # crude check if asap table
44                if os.path.exists(filename+'/table.info'):
45                    sdtable.__init__(self, filename)
46                    if unit is not None:
47                        self.set_fluxunit(unit)                       
48                else:
49                    print "The given file '%s'is not a valid asap table." % (filename)
50                    return
51            else:
52                autoav = rcParams['scantable.autoaverage']
53
54                from asap._asap import sdreader
55                ifSel = -1
56                beamSel = -1
57                r = sdreader(filename,ifSel,beamSel)
58                print 'Importing data...'
59                r._read([-1])
60                tbl = r._getdata()
61                if unit is not None:
62                    tbl.set_fluxunit(unit)
63                if autoav:
64                    from asap._asap import average
65                    tmp = tuple([tbl])
66                    print 'Auto averaging integrations...'
67                    tbl2 = average(tmp,(),True,'none')
68                    sdtable.__init__(self,tbl2)
69                    del r,tbl
70                else:
71                    sdtable.__init__(self,tbl)
72
73    def save(self, name=None, format=None, overwrite=False):
74        """
75        Store the scantable on disk. This can be an asap (aips++) Table, SDFITS,
76        Image FITS or MS2 format.
77        Parameters:
78            name:        the name of the outputfile. For format="FITS" this
79                         is the directory file name into which all the files will
80                         be written (default is 'asap_FITS')
81            format:      an optional file format. Default is ASAP.
82                         Allowed are - 'ASAP' (save as ASAP [aips++] Table),
83                                       'SDFITS' (save as SDFITS file)
84                                       'FITS' (saves each row as a FITS Image)
85                                       'ASCII' (saves as ascii text file)
86                                       'MS2' (saves as an aips++
87                                              MeasurementSet V2)
88            overwrite:   If the file should be overwritten if it exists.
89                         The default False is to return with warning
90                         without writing the output. USE WITH CARE.
91        Example:
92            scan.save('myscan.asap')
93            scan.save('myscan.sdfits','SDFITS')
94        """
95        from os import path
96        if format is None: format = rcParams['scantable.save']
97        suffix = '.'+format.lower()
98        if name is None or name =="":
99            name = 'scantable'+suffix
100            print "No filename given. Using default name %s..." % name
101        name = path.expandvars(name)
102        if path.isfile(name) or path.isdir(name):
103            if not overwrite:
104                print "File %s already exists." % name
105                return
106        if format == 'ASAP':
107            self._save(name)
108        else:
109            from asap._asap import sdwriter as _sw
110            w = _sw(format)
111            w.write(self, name)
112        return
113
114    def copy(self):
115        """
116        Return a copy of this scantable.
117        Parameters:
118            none
119        Example:
120            copiedscan = scan.copy()
121        """
122        sd = scantable(sdtable._copy(self))
123        return sd
124
125    def get_scan(self, scanid=None):
126        """
127        Return a specific scan (by scanno) or collection of scans (by
128        source name) in a new scantable.
129        Parameters:
130            scanid:    a scanno or a source name
131        Example:
132            scan.get_scan('323p459')
133            # gets all scans containing the source '323p459'
134        """
135        if scanid is None:
136            print "Please specify a scan no or name to retrieve from the scantable"
137        try:
138            if type(scanid) is str:
139                s = sdtable._getsource(self,scanid)
140                return scantable(s)
141            elif type(scanid) is int:
142                s = sdtable._getscan(self,[scanid])
143                return scantable(s)
144            elif type(scanid) is list:
145                s = sdtable._getscan(self,scanid)
146                return scantable(s)
147            else:
148                print "Illegal scanid type, use 'int' or 'list' if ints."
149        except RuntimeError:
150            print "Couldn't find any match."
151
152    def __str__(self):
153        return sdtable._summary(self,True)
154
155    def summary(self,filename=None, verbose=None):
156        """
157        Print a summary of the contents of this scantable.
158        Parameters:
159            filename:    the name of a file to write the putput to
160                         Default - no file output
161            verbose:     print extra info such as the frequency table
162                         The default (False) is taken from .asaprc
163        """
164        info = sdtable._summary(self, verbose)
165        if verbose is None: verbose = rcParams['scantable.verbosesummary']
166        if filename is not None:
167            if filename is "":
168                filename = 'scantable_summary.txt'
169            from os.path import expandvars, isdir
170            filename = expandvars(filename)
171            if not isdir(filename):
172                data = open(filename, 'w')
173                data.write(info)
174                data.close()
175            else:
176                print "Illegal file name '%s'." % (filename)
177        print info
178
179    def set_cursor(self, thebeam=0,theif=0,thepol=0):
180        """
181        Set the spectrum for individual operations.
182        Parameters:
183            thebeam,theif,thepol:    a number
184        Example:
185            scan.set_cursor(0,0,1)
186            pol1sig = scan.stats(all=False) # returns std dev for beam=0
187                                            # if=0, pol=1
188        """
189        self.setbeam(thebeam)
190        self.setpol(thepol)
191        self.setif(theif)
192        return
193
194    def get_cursor(self):
195        """
196        Return/print a the current 'cursor' into the Beam/IF/Pol cube.
197        Parameters:
198            none
199        Returns:
200            a list of values (currentBeam,currentIF,currentPol)
201        Example:
202            none
203        """
204        i = self.getbeam()
205        j = self.getif()
206        k = self.getpol()
207        if self._vb:
208            print "--------------------------------------------------"
209            print " Cursor position"
210            print "--------------------------------------------------"
211            out = 'Beam=%d IF=%d Pol=%d ' % (i,j,k)
212            print out
213        return i,j,k
214
215    def stats(self, stat='stddev', mask=None, allaxes=None):
216        """
217        Determine the specified statistic of the current beam/if/pol
218        Takes a 'mask' as an optional parameter to specify which
219        channels should be excluded.
220        Parameters:
221            stat:    'min', 'max', 'sumsq', 'sum', 'mean'
222                     'var', 'stddev', 'avdev', 'rms', 'median'
223            mask:    an optional mask specifying where the statistic
224                     should be determined.
225            allaxes: if True apply to all spectra. Otherwise
226                     apply only to the selected (beam/pol/if)spectra only.
227                     The default is taken from .asaprc (True if none)
228        Example:
229            scan.set_unit('channel')
230            msk = scan.create_mask([100,200],[500,600])
231            scan.stats(stat='mean', mask=m)
232        """
233        if allaxes is None: allaxes = rcParams['scantable.allaxes']
234        from asap._asap import stats as _stats
235        from numarray import array,zeros,Float
236        if mask == None:
237            mask = ones(self.nchan())
238        axes = ['Beam','IF','Pol','Time']
239
240        beamSel,IFSel,polSel = (self.getbeam(),self.getif(),self.getpol())
241        if allaxes:
242            n = self.nbeam()*self.nif()*self.npol()*self.nrow()
243            shp = [self.nbeam(),self.nif(),self.npol(),self.nrow()]
244            arr = array(zeros(n),shape=shp,type=Float)
245
246            for i in range(self.nbeam()):
247                self.setbeam(i)
248                for j in range(self.nif()):
249                    self.setif(j)
250                    for k in range(self.npol()):
251                        self.setpol(k)
252                        arr[i,j,k,:] = _stats(self,mask,stat,-1)
253            retval = {'axes': axes, 'data': arr, 'cursor':None}
254            tm = [self._gettime(val) for val in range(self.nrow())]
255            if self._vb:
256                self._print_values(retval,stat,tm)
257            self.setbeam(beamSel)
258            self.setif(IFSel)
259            self.setpol(polSel)
260            return retval
261
262        else:
263            statval = _stats(self,mask,stat,-1)
264            out = ''
265            for l in range(self.nrow()):
266                tm = self._gettime(l)
267                out += 'Time[%s]:\n' % (tm)
268                if self.nbeam() > 1: out +=  ' Beam[%d] ' % (beamSel)
269                if self.nif() > 1: out +=  ' IF[%d] ' % (IFSel)
270                if self.npol() > 1: out +=  ' Pol[%d] ' % (polSel)
271                out += '= %3.3f\n' % (statval[l])
272                out +=  "--------------------------------------------------\n"
273
274            if self._vb:
275                print "--------------------------------------------------"
276                print " ",stat
277                print "--------------------------------------------------"
278                print out
279            retval = {'axes': axes, 'data': array(statval), 'cursor':(i,j,k)}
280            return retval
281
282    def stddev(self,mask=None, allaxes=None):
283        """
284        Determine the standard deviation of the current beam/if/pol
285        Takes a 'mask' as an optional parameter to specify which
286        channels should be excluded.
287        Parameters:
288            mask:    an optional mask specifying where the standard
289                     deviation should be determined.
290            allaxes: optional flag to show all or a cursor selected
291                     spectrum of Beam/IF/Pol. Default is all or taken
292                     from .asaprc
293
294        Example:
295            scan.set_unit('channel')
296            msk = scan.create_mask([100,200],[500,600])
297            scan.stddev(mask=m)
298        """
299        if allaxes is None: allaxes = rcParams['scantable.allaxes']
300        return self.stats(stat='stddev',mask=mask, allaxes=allaxes);
301
302    def get_tsys(self, allaxes=None):
303        """
304        Return the System temperatures.
305        Parameters:
306           allaxes:     if True apply to all spectra. Otherwise
307                        apply only to the selected (beam/pol/if)spectra only.
308                        The default is taken from .asaprc (True if none)
309        Returns:
310            a list of Tsys values.
311        """
312        if allaxes is None: allaxes = rcParams['scantable.allaxes']
313        from numarray import array,zeros,Float
314        axes = ['Beam','IF','Pol','Time']
315
316        if allaxes:
317            n = self.nbeam()*self.nif()*self.npol()*self.nrow()
318            shp = [self.nbeam(),self.nif(),self.npol(),self.nrow()]
319            arr = array(zeros(n),shape=shp,type=Float)
320
321            for i in range(self.nbeam()):
322                self.setbeam(i)
323                for j in range(self.nif()):
324                    self.setif(j)
325                    for k in range(self.npol()):
326                        self.setpol(k)
327                        arr[i,j,k,:] = self._gettsys()
328            retval = {'axes': axes, 'data': arr, 'cursor':None}
329            tm = [self._gettime(val) for val in range(self.nrow())]
330            if self._vb:
331                self._print_values(retval,'Tsys',tm)
332            return retval
333
334        else:
335            i,j,k = (self.getbeam(),self.getif(),self.getpol())
336            statval = self._gettsys()
337            out = ''
338            for l in range(self.nrow()):
339                tm = self._gettime(l)
340                out += 'Time[%s]:\n' % (tm)
341                if self.nbeam() > 1: out +=  ' Beam[%d] ' % (i)
342                if self.nif() > 1: out +=  ' IF[%d] ' % (j)
343                if self.npol() > 1: out +=  ' Pol[%d] ' % (k)
344                out += '= %3.3f\n' % (statval[l])
345                out +=  "--------------------------------------------------\n"
346
347            if self._vb:
348                print "--------------------------------------------------"
349                print " TSys"
350                print "--------------------------------------------------"
351                print out
352            retval = {'axes': axes, 'data': array(statval), 'cursor':(i,j,k)}
353            return retval
354       
355    def get_time(self, row=-1):
356        """
357        Get a list of time stamps for the observations.
358        Return a string for each integration in the scantable.
359        Parameters:
360            row:    row no of integration. Default -1 return all rows
361        Example:
362            none
363        """
364        out = []
365        if row == -1:
366            for i in range(self.nrow()):
367                out.append(self._gettime(i))
368            return out
369        else:
370            if row < self.nrow():
371                return self._gettime(row)
372
373    def set_unit(self, unit='channel'):
374        """
375        Set the unit for all following operations on this scantable
376        Parameters:
377            unit:    optional unit, default is 'channel'
378                     one of '*Hz','km/s','channel', ''
379        """
380
381        if unit in ['','pixel', 'channel']:
382            unit = ''
383        inf = list(self._getcoordinfo())
384        inf[0] = unit
385        self._setcoordinfo(inf)
386        if self._p: self.plot()
387
388    def set_instrument (self, instr):
389        """
390        Set the instrument for subsequent processing
391        Parameters:
392            instr:    Select from 'ATPKSMB', 'ATPKSHOH', 'ATMOPRA',
393                      'DSS-43' (Tid), 'CEDUNA', and 'HOBART'
394        """
395        self._setInstrument(instr)
396
397    def set_doppler(self, doppler='RADIO'):
398        """
399        Set the doppler for all following operations on this scantable.
400        Parameters:
401            doppler:    One of 'RADIO', 'OPTICAL', 'Z', 'BETA', 'GAMMA'
402        """
403
404        inf = list(self._getcoordinfo())
405        inf[2] = doppler
406        self._setcoordinfo(inf)
407        if self._p: self.plot()
408 
409    def set_freqframe(self, frame=None):
410        """
411        Set the frame type of the Spectral Axis.
412        Parameters:
413            frame:   an optional frame type, default 'LSRK'.
414        Examples:
415            scan.set_freqframe('BARY')
416        """
417        if not frame: frame = rcParams['scantable.freqframe']
418        valid = ['REST','TOPO','LSRD','LSRK','BARY', \
419                   'GEO','GALACTO','LGROUP','CMB']
420        if frame in valid:
421            inf = list(self._getcoordinfo())
422            inf[1] = frame
423            self._setcoordinfo(inf)
424        else:
425            print "Please specify a valid freq type. Valid types are:\n",valid
426           
427    def get_unit(self):
428        """
429        Get the default unit set in this scantable
430        Parameters:
431        Returns:
432            A unit string
433        """
434        inf = self._getcoordinfo()
435        unit = inf[0]
436        if unit == '': unit = 'channel'
437        return unit
438
439    def get_abcissa(self, rowno=0):
440        """
441        Get the abcissa in the current coordinate setup for the currently
442        selected Beam/IF/Pol
443        Parameters:
444            rowno:    an optional row number in the scantable. Default is the
445                      first row, i.e. rowno=0
446        Returns:
447            The abcissa values and it's format string (as a dictionary)
448        """
449        abc = self._getabcissa(rowno)
450        lbl = self._getabcissalabel(rowno)       
451        return abc, lbl
452        #return {'abcissa':abc,'label':lbl}
453
454    def create_mask(self, *args, **kwargs):
455        """
456        Compute and return a mask based on [min,max] windows.
457        The specified windows are to be INCLUDED, when the mask is
458        applied.
459        Parameters:
460            [min,max],[min2,max2],...
461                Pairs of start/end points specifying the regions
462                to be masked
463            invert:     optional argument. If specified as True,
464                        return an inverted mask, i.e. the regions
465                        specified are EXCLUDED
466        Example:
467            scan.set_unit('channel')
468
469            a)
470            msk = scan.set_mask([400,500],[800,900])
471            # masks everything outside 400 and 500
472            # and 800 and 900 in the unit 'channel'
473
474            b)
475            msk = scan.set_mask([400,500],[800,900], invert=True)
476            # masks the regions between 400 and 500
477            # and 800 and 900 in the unit 'channel'
478           
479        """
480        u = self._getcoordinfo()[0]
481        if self._vb:
482            if u == "": u = "channel"
483            print "The current mask window unit is", u
484        n = self.nchan()
485        data = self._getabcissa()
486        msk = zeros(n)
487        for  window in args:
488            if (len(window) != 2 or window[0] > window[1] ):
489                print "A window needs to be defined as [min,max]"
490                return
491            for i in range(n):
492                if data[i] >= window[0] and data[i] < window[1]:
493                    msk[i] = 1
494        if kwargs.has_key('invert'):
495            if kwargs.get('invert'):
496                from numarray import logical_not
497                msk = logical_not(msk)
498        return msk
499   
500    def get_restfreqs(self):
501        """
502        Get the restfrequency(s) stored in this scantable.
503        The return value(s) are always of unit 'Hz'
504        Parameters:
505            none
506        Returns:
507            a list of doubles
508        """
509        return list(self._getrestfreqs())
510
511    def lines(self):
512        """
513        Print the list of known spectral lines
514        """
515        sdtable._lines(self)
516
517    def set_restfreqs(self, freqs=None, unit='Hz', lines=None, source=None, theif=None):
518        """
519        Select the restfrequency for the specified source and IF OR
520        replace for all IFs.  If the 'freqs' argument holds a scalar,
521        then that rest frequency will be applied to the selected
522        data (and added to the list of available rest frequencies).
523        In this way, you can set a rest frequency for each
524        source and IF combination.   If the 'freqs' argument holds
525        a vector, then it MUST be of length the number of IFs
526        (and the available restfrequencies will be replaced by
527        this vector).  In this case, *all* data ('source' and
528        'theif' are ignored) have the restfrequency set per IF according
529        to the corresponding value you give in the 'freqs' vector. 
530        E.g. 'freqs=[1e9,2e9]'  would mean IF 0 gets restfreq 1e9 and
531        IF 1 gets restfreq 2e9.
532
533        You can also specify the frequencies via known line names
534        in the argument 'lines'.  Use 'freqs' or 'lines'.  'freqs'
535        takes precedence. See the list of known names via function
536        scantable.lines()
537        Parameters:
538            freqs:   list of rest frequencies
539            unit:    unit for rest frequency (default 'Hz')
540            lines:   list of known spectral lines names (alternative to freqs).
541                     See possible list via scantable.lines()
542            source:  Source name (blank means all)
543            theif:   IF (-1 means all)
544        Example:
545            scan.set_restfreqs(freqs=1.4e9, source='NGC253', theif=2)
546            scan.set_restfreqs(freqs=[1.4e9,1.67e9])
547        """
548        if source is None:
549            source = ""
550        if theif is None:
551            theif = -1
552        t = type(freqs)
553        if t is int or t is float:
554           freqs = [freqs]
555        if freqs is None:
556           freqs = []
557        t = type(lines)
558        if t is str:
559           lines = [lines]
560        if lines is None:
561           lines = []
562        sdtable._setrestfreqs(self, freqs, unit, lines, source, theif)
563        return
564
565
566    def flag_spectrum(self, thebeam, theif, thepol):
567        """
568        This flags a selected spectrum in the scan 'for good'.
569        USE WITH CARE - not reversible.
570        Use masks for non-permanent exclusion of channels.
571        Parameters:
572            thebeam,theif,thepol:    all have to be explicitly
573                                     specified
574        Example:
575            scan.flag_spectrum(0,0,1)
576            flags the spectrum for Beam=0, IF=0, Pol=1
577        """
578        if (thebeam < self.nbeam() and
579            theif < self.nif() and
580            thepol < self.npol()):
581            sdtable.setbeam(self, thebeam)
582            sdtable.setif(self, theif)
583            sdtable.setpol(self, thepol)
584            sdtable._flag(self)
585        else:
586            print "Please specify a valid (Beam/IF/Pol)"
587        return
588
589    def rotate_xyphase (self, angle, allaxes=None):
590        """
591        Rotate the phase of the XY correlation.  This is done in situ
592        in the data.  So if you call this function more than once
593        then each call rotates the phase further.       
594        Parameters:
595            angle:   The angle (degrees) to rotate (add) by.
596            allaxes: If True apply to all spectra. Otherwise
597                     apply only to the selected (beam/pol/if)spectra only.
598                     The default is taken from .asaprc (True if none)
599        Examples:
600            scan.rotate_xyphase(2.3)
601        """
602        if allaxes is None: allaxes = rcParams['scantable.allaxes']
603        sdtable._rotate_xyphase(self, angle, allaxes)
604           
605    def plot(self, what='spectrum',col='Pol', panel=None):
606        """
607        Plot the spectra contained in the scan. Alternatively you can also
608        Plot Tsys vs Time
609        Parameters:
610            what:     a choice of 'spectrum' (default) or 'tsys'
611            col:      which out of Beams/IFs/Pols should be colour stacked
612            panel:    set up multiple panels, currently not working.
613        """
614        print "Warning! Not fully functional. Use plotter.plot() instead"
615       
616        validcol = {'Beam':self.nbeam(),'IF':self.nif(),'Pol':self.npol()}
617
618        validyax = ['spectrum','tsys']
619        from asap.asaplot import ASAPlot
620        if not self._p:
621            self._p = ASAPlot()
622            #print "Plotting not enabled"
623            #return
624        if self._p.is_dead:
625            del self._p
626            self._p = ASAPlot()
627        npan = 1
628        x = None
629        if what == 'tsys':
630            n = self.nrow()
631            if n < 2:
632                print "Only one integration. Can't plot."
633                return
634        self._p.hold()
635        self._p.clear()
636        if panel == 'Time':
637            npan = self.nrow()
638            self._p.set_panels(rows=npan)
639        xlab,ylab,tlab = None,None,None
640        self._vb = False
641        sel = self.get_cursor()       
642        for i in range(npan):
643            if npan > 1:
644                self._p.subplot(i)
645            for j in range(validcol[col]):
646                x = None
647                y = None
648                m = None
649                tlab = self._getsourcename(i)
650                import re
651                tlab = re.sub('_S','',tlab)
652                if col == 'Beam':
653                    self.setbeam(j)
654                elif col == 'IF':
655                    self.setif(j)
656                elif col == 'Pol':
657                    self.setpol(j)
658                if what == 'tsys':
659                    x = range(self.nrow())
660                    xlab = 'Time [pixel]'
661                    m = list(ones(len(x)))
662                    y = []
663                    ylab = r'$T_{sys}$'
664                    for k in range(len(x)):
665                        y.append(self._gettsys(k))
666                else:
667                    x,xlab = self.get_abcissa(i)
668                    y = self._getspectrum(i)
669                    ylab = r'Flux'
670                    m = self._getmask(i)
671                llab = col+' '+str(j)
672                self._p.set_line(label=llab)
673                self._p.plot(x,y,m)
674            self._p.set_axes('xlabel',xlab)
675            self._p.set_axes('ylabel',ylab)
676            self._p.set_axes('title',tlab)
677        self._p.release()
678        self.set_cursor(sel[0],sel[1],sel[2])
679        self._vb = rcParams['verbose']
680        return
681
682        print out
683
684    def _print_values(self, dat, label='', timestamps=[]):
685        d = dat['data']
686        a = dat['axes']
687        shp = d.getshape()
688        out = ''
689        for i in range(shp[3]):
690            out += '%s [%s]:\n' % (a[3],timestamps[i])
691            t = d[:,:,:,i]
692            for j in range(shp[0]):
693                if shp[0] > 1: out +=  ' %s[%d] ' % (a[0],j)
694                for k in range(shp[1]):
695                    if shp[1] > 1: out +=  ' %s[%d] ' % (a[1],k)
696                    for l in range(shp[2]):
697                        if shp[2] > 1: out +=  ' %s[%d] ' % (a[2],l)
698                        out += '= %3.3f\n' % (t[j,k,l])
699            out += "--------------------------------------------------\n"
700        print "--------------------------------------------------"
701        print " ", label
702        print "--------------------------------------------------"
703        print out
Note: See TracBrowser for help on using the repository browser.