source: trunk/python/scantable.py @ 407

Last change on this file since 407 was 407, checked in by mar637, 19 years ago

Minor fixes around asapreader/sdreader

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