source: branches/mergetest/python/scantable.py @ 1779

Last change on this file since 1779 was 1779, checked in by Kana Sugimoto, 14 years ago

New Development: Yes

JIRA Issue: No (test merging alma branch)

Ready for Test: Yes

Interface Changes: Yes

What Interface Changed:

Test Programs:

Put in Release Notes: No

Module(s):

Description:


  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 90.4 KB
Line 
1import os
2try:
3    from functools import wraps as wraps_dec
4except ImportError:
5    from asap.compatibility import wraps as wraps_dec
6
7from asap._asap import Scantable
8from asap import rcParams
9from asap import print_log, print_log_dec
10from asap import asaplog
11from asap import selector
12from asap import linecatalog
13from asap.coordinate import coordinate
14from asap import _n_bools, mask_not, mask_and, mask_or
15
16
17def preserve_selection(func):
18    @wraps_dec(func)
19    def wrap(obj, *args, **kw):
20        basesel = obj.get_selection()
21        val = func(obj, *args, **kw)
22        obj.set_selection(basesel)
23        return val
24    return wrap
25
26
27def is_scantable(filename):
28    return (os.path.isdir(filename)
29            and not os.path.exists(filename+'/table.f1')
30            and os.path.exists(filename+'/table.info'))
31
32
33class scantable(Scantable):
34    """
35        The ASAP container for scans
36    """
37
38    #@print_log_dec
39    def __init__(self, filename, average=None, unit=None, getpt=None, antenna=None, parallactify=None):
40        """
41        Create a scantable from a saved one or make a reference
42        Parameters:
43            filename:    the name of an asap table on disk
44                         or
45                         the name of a rpfits/sdfits/ms file
46                         (integrations within scans are auto averaged
47                         and the whole file is read)
48                         or
49                         [advanced] a reference to an existing
50                         scantable
51            average:     average all integrations withinb a scan on read.
52                         The default (True) is taken from .asaprc.
53            unit:         brightness unit; must be consistent with K or Jy.
54                         Over-rides the default selected by the reader
55                         (input rpfits/sdfits/ms) or replaces the value
56                         in existing scantables
57            getpt:       for MeasurementSet input data only:
58                         If True, all pointing data are filled.
59                         The deafult is False, which makes time to load
60                         the MS data faster in some cases.
61            antenna:     Antenna selection. integer (id) or string (name
62                         or id).
63            parallactify: Indcicate that the data had been parallatified.
64                          Default is taken form rc file.
65        """
66        if average is None:
67            average = rcParams['scantable.autoaverage']
68        if getpt is None:
69            getpt = True
70        if antenna is None:
71            antenna = ''
72        elif type(antenna) == int:
73            antenna = '%s'%antenna
74        elif type(antenna) == list:
75            tmpstr = ''
76            for i in range( len(antenna) ):
77                if type(antenna[i]) == int:
78                    tmpstr = tmpstr + ('%s,'%(antenna[i]))
79                elif type(antenna[i]) == str:
80                    tmpstr=tmpstr+antenna[i]+','
81                else:
82                    asaplog.push('Bad antenna selection.')
83                    print_log('ERROR')
84                    return
85            antenna = tmpstr.rstrip(',')
86        parallactify = parallactify or rcParams['scantable.parallactify']
87        varlist = vars()
88        from asap._asap import stmath
89        self._math = stmath( rcParams['insitu'] )
90        if isinstance(filename, Scantable):
91            Scantable.__init__(self, filename)
92        else:
93            if isinstance(filename, str):
94                filename = os.path.expandvars(filename)
95                filename = os.path.expanduser(filename)
96                if not os.path.exists(filename):
97                    s = "File '%s' not found." % (filename)
98                    if rcParams['verbose']:
99                        asaplog.push(s)
100                        print_log('ERROR')
101                        return
102                    raise IOError(s)
103                if is_scantable(filename):
104                    ondisk = rcParams['scantable.storage'] == 'disk'
105                    Scantable.__init__(self, filename, ondisk)
106                    if unit is not None:
107                        self.set_fluxunit(unit)
108                    # do not reset to the default freqframe
109                    #self.set_freqframe(rcParams['scantable.freqframe'])
110                elif os.path.isdir(filename) \
111                         and not os.path.exists(filename+'/table.f1'):
112                    msg = "The given file '%s'is not a valid " \
113                          "asap table." % (filename)
114                    if rcParams['verbose']:
115                        #print msg
116                        asaplog.push( msg )
117                        print_log( 'ERROR' )
118                        return
119                    else:
120                        raise IOError(msg)
121                else:
122                    self._fill([filename], unit, average, getpt, antenna)
123            elif (isinstance(filename, list) or isinstance(filename, tuple)) \
124                  and isinstance(filename[-1], str):
125                self._fill(filename, unit, average, getpt, antenna)
126        self.parallactify(parallactify)
127        self._add_history("scantable", varlist)
128        print_log()
129
130    #@print_log_dec
131    def save(self, name=None, format=None, overwrite=False):
132        """
133        Store the scantable on disk. This can be an asap (aips++) Table,
134        SDFITS or MS2 format.
135        Parameters:
136            name:        the name of the outputfile. For format "ASCII"
137                         this is the root file name (data in 'name'.txt
138                         and header in 'name'_header.txt)
139            format:      an optional file format. Default is ASAP.
140                         Allowed are - 'ASAP' (save as ASAP [aips++] Table),
141                                       'SDFITS' (save as SDFITS file)
142                                       'ASCII' (saves as ascii text file)
143                                       'MS2' (saves as an aips++
144                                              MeasurementSet V2)
145                                       'FITS' (save as image FITS - not
146                                               readable by class)
147                                       'CLASS' (save as FITS readable by CLASS)
148            overwrite:   If the file should be overwritten if it exists.
149                         The default False is to return with warning
150                         without writing the output. USE WITH CARE.
151        Example:
152            scan.save('myscan.asap')
153            scan.save('myscan.sdfits', 'SDFITS')
154        """
155        from os import path
156        format = format or rcParams['scantable.save']
157        suffix = '.'+format.lower()
158        if name is None or name == "":
159            name = 'scantable'+suffix
160            msg = "No filename given. Using default name %s..." % name
161            asaplog.push(msg)
162        name = path.expandvars(name)
163        if path.isfile(name) or path.isdir(name):
164            if not overwrite:
165                msg = "File %s exists." % name
166                if rcParams['verbose']:
167                    #print msg
168                    asaplog.push( msg )
169                    print_log( 'ERROR' )
170                    return
171                else:
172                    raise IOError(msg)
173        format2 = format.upper()
174        if format2 == 'ASAP':
175            self._save(name)
176        else:
177            from asap._asap import stwriter as stw
178            writer = stw(format2)
179            writer.write(self, name)
180        print_log()
181        return
182
183    def copy(self):
184        """
185        Return a copy of this scantable.
186        Note:
187            This makes a full (deep) copy. scan2 = scan1 makes a reference.
188        Parameters:
189            none
190        Example:
191            copiedscan = scan.copy()
192        """
193        sd = scantable(Scantable._copy(self))
194        return sd
195
196    def drop_scan(self, scanid=None):
197        """
198        Return a new scantable where the specified scan number(s) has(have)
199        been dropped.
200        Parameters:
201            scanid:    a (list of) scan number(s)
202        """
203        from asap import _is_sequence_or_number as _is_valid
204        from asap import _to_list
205        from asap import unique
206        if not _is_valid(scanid):
207            if rcParams['verbose']:
208                #print "Please specify a scanno to drop from the scantable"
209                asaplog.push( 'Please specify a scanno to drop from the scantable' )
210                print_log( 'ERROR' )
211                return
212            else:
213                raise RuntimeError("No scan given")
214        try:
215            scanid = _to_list(scanid)
216            allscans = unique([ self.getscan(i) for i in range(self.nrow())])
217            for sid in scanid: allscans.remove(sid)
218            if len(allscans) == 0:
219                raise ValueError("Can't remove all scans")
220        except ValueError:
221            if rcParams['verbose']:
222                #print "Couldn't find any match."
223                print_log()
224                asaplog.push( "Couldn't find any match." )
225                print_log( 'ERROR' )
226                return
227            else: raise
228        try:
229            sel = selector(scans=allscans)
230            return self._select_copy(sel)
231        except RuntimeError:
232            if rcParams['verbose']:
233                #print "Couldn't find any match."
234                print_log()
235                asaplog.push( "Couldn't find any match." )
236                print_log( 'ERROR' )
237            else:
238                raise
239
240    def _select_copy(self, selection):
241        orig = self.get_selection()
242        self.set_selection(orig+selection)
243        cp = self.copy()
244        self.set_selection(orig)
245        return cp
246
247    def get_scan(self, scanid=None):
248        """
249        Return a specific scan (by scanno) or collection of scans (by
250        source name) in a new scantable.
251        Note:
252            See scantable.drop_scan() for the inverse operation.
253        Parameters:
254            scanid:    a (list of) scanno or a source name, unix-style
255                       patterns are accepted for source name matching, e.g.
256                       '*_R' gets all 'ref scans
257        Example:
258            # get all scans containing the source '323p459'
259            newscan = scan.get_scan('323p459')
260            # get all 'off' scans
261            refscans = scan.get_scan('*_R')
262            # get a susbset of scans by scanno (as listed in scan.summary())
263            newscan = scan.get_scan([0, 2, 7, 10])
264        """
265        if scanid is None:
266            if rcParams['verbose']:
267                #print "Please specify a scan no or name to " \
268                #      "retrieve from the scantable"
269                asaplog.push( 'Please specify a scan no or name to retrieve from the scantable' )
270                print_log( 'ERROR' )
271                return
272            else:
273                raise RuntimeError("No scan given")
274
275        try:
276            bsel = self.get_selection()
277            sel = selector()
278            if type(scanid) is str:
279                sel.set_name(scanid)
280                return self._select_copy(sel)
281            elif type(scanid) is int:
282                sel.set_scans([scanid])
283                return self._select_copy(sel)
284            elif type(scanid) is list:
285                sel.set_scans(scanid)
286                return self._select_copy(sel)
287            else:
288                msg = "Illegal scanid type, use 'int' or 'list' if ints."
289                if rcParams['verbose']:
290                    #print msg
291                    asaplog.push( msg )
292                    print_log( 'ERROR' )
293                else:
294                    raise TypeError(msg)
295        except RuntimeError:
296            if rcParams['verbose']:
297                #print "Couldn't find any match."
298                print_log()
299                asaplog.push( "Couldn't find any match." )
300                print_log( 'ERROR' )
301            else: raise
302
303    def __str__(self):
304        return Scantable._summary(self, True)
305
306    def summary(self, filename=None):
307        """
308        Print a summary of the contents of this scantable.
309        Parameters:
310            filename:    the name of a file to write the putput to
311                         Default - no file output
312        """
313        info = Scantable._summary(self, True)
314        if filename is not None:
315            if filename is "":
316                filename = 'scantable_summary.txt'
317            from os.path import expandvars, isdir
318            filename = expandvars(filename)
319            if not isdir(filename):
320                data = open(filename, 'w')
321                data.write(info)
322                data.close()
323            else:
324                msg = "Illegal file name '%s'." % (filename)
325                if rcParams['verbose']:
326                    #print msg
327                    asaplog.push( msg )
328                    print_log( 'ERROR' )
329                else:
330                    raise IOError(msg)
331        if rcParams['verbose']:
332            try:
333                from IPython.genutils import page as pager
334            except ImportError:
335                from pydoc import pager
336            pager(info)
337        else:
338            return info
339
340    def get_spectrum(self, rowno):
341        """Return the spectrum for the current row in the scantable as a list.
342        Parameters:
343             rowno:   the row number to retrieve the spectrum from
344        """
345        return self._getspectrum(rowno)
346
347    def get_mask(self, rowno):
348        """Return the mask for the current row in the scantable as a list.
349        Parameters:
350             rowno:   the row number to retrieve the mask from
351        """
352        return self._getmask(rowno)
353
354    def set_spectrum(self, spec, rowno):
355        """Return the spectrum for the current row in the scantable as a list.
356        Parameters:
357             spec:   the spectrum
358             rowno:    the row number to set the spectrum for
359        """
360        assert(len(spec) == self.nchan())
361        return self._setspectrum(spec, rowno)
362
363    def get_coordinate(self, rowno):
364        """Return the (spectral) coordinate for a a given 'rowno'.
365        NOTE:
366            * This coordinate is only valid until a scantable method modifies
367              the frequency axis.
368            * This coordinate does contain the original frequency set-up
369              NOT the new frame. The conversions however are done using the user
370              specified frame (e.g. LSRK/TOPO). To get the 'real' coordinate,
371              use scantable.freq_align first. Without it there is no closure,
372              i.e.
373              c = myscan.get_coordinate(0)
374              c.to_frequency(c.get_reference_pixel()) != c.get_reference_value()
375
376        Parameters:
377             rowno:    the row number for the spectral coordinate
378
379        """
380        return coordinate(Scantable.get_coordinate(self, rowno))
381
382    def get_selection(self):
383        """
384        Get the selection object currently set on this scantable.
385        Parameters:
386            none
387        Example:
388            sel = scan.get_selection()
389            sel.set_ifs(0)              # select IF 0
390            scan.set_selection(sel)     # apply modified selection
391        """
392        return selector(self._getselection())
393
394    def set_selection(self, selection=None, **kw):
395        """
396        Select a subset of the data. All following operations on this scantable
397        are only applied to thi selection.
398        Parameters:
399            selection:    a selector object (default unset the selection),
400
401            or
402
403            any combination of
404            "pols", "ifs", "beams", "scans", "cycles", "name", "query"
405
406        Examples:
407            sel = selector()         # create a selection object
408            self.set_scans([0, 3])    # select SCANNO 0 and 3
409            scan.set_selection(sel)  # set the selection
410            scan.summary()           # will only print summary of scanno 0 an 3
411            scan.set_selection()     # unset the selection
412            # or the equivalent
413            scan.set_selection(scans=[0,3])
414            scan.summary()           # will only print summary of scanno 0 an 3
415            scan.set_selection()     # unset the selection
416        """
417        if selection is None:
418            # reset
419            if len(kw) == 0:
420                selection = selector()
421            else:
422                # try keywords
423                for k in kw:
424                    if k not in selector.fields:
425                        raise KeyError("Invalid selection key '%s', valid keys are %s" % (k, selector.fields))
426                selection = selector(**kw)
427        self._setselection(selection)
428
429    def get_row(self, row=0, insitu=None):
430        """
431        Select a row in the scantable.
432        Return a scantable with single row.
433        Parameters:
434            row: row no of integration, default is 0.
435            insitu: if False a new scantable is returned.
436                    Otherwise, the scaling is done in-situ
437                    The default is taken from .asaprc (False)
438        """
439        if insitu is None: insitu = rcParams['insitu']
440        if not insitu:
441            workscan = self.copy()
442        else:
443            workscan = self
444        # Select a row
445        sel=selector()
446        sel.set_scans([workscan.getscan(row)])
447        sel.set_cycles([workscan.getcycle(row)])
448        sel.set_beams([workscan.getbeam(row)])
449        sel.set_ifs([workscan.getif(row)])
450        sel.set_polarisations([workscan.getpol(row)])
451        sel.set_name(workscan._getsourcename(row))
452        workscan.set_selection(sel)
453        if not workscan.nrow() == 1:
454            msg = "Cloud not identify single row. %d rows selected."%(workscan.nrow())
455            raise RuntimeError(msg)
456        del sel
457        if insitu:
458            self._assign(workscan)
459        else:
460            return workscan
461
462    #def stats(self, stat='stddev', mask=None):
463    def stats(self, stat='stddev', mask=None, form='3.3f'):
464        """
465        Determine the specified statistic of the current beam/if/pol
466        Takes a 'mask' as an optional parameter to specify which
467        channels should be excluded.
468        Parameters:
469            stat:    'min', 'max', 'min_abc', 'max_abc', 'sumsq', 'sum',
470                     'mean', 'var', 'stddev', 'avdev', 'rms', 'median'
471            mask:    an optional mask specifying where the statistic
472                     should be determined.
473            form:    format string to print statistic values
474        Example:
475            scan.set_unit('channel')
476            msk = scan.create_mask([100, 200], [500, 600])
477            scan.stats(stat='mean', mask=m)
478        """
479        mask = mask or []
480        if not self._check_ifs():
481            raise ValueError("Cannot apply mask as the IFs have different "
482                             "number of channels. Please use setselection() "
483                             "to select individual IFs")
484        rtnabc = False
485        if stat.lower().endswith('_abc'): rtnabc = True
486        getchan = False
487        if stat.lower().startswith('min') or stat.lower().startswith('max'):
488            chan = self._math._minmaxchan(self, mask, stat)
489            getchan = True
490            statvals = []
491        if not rtnabc: statvals = self._math._stats(self, mask, stat)
492
493        #def cb(i):
494        #    return statvals[i]
495
496        #return self._row_callback(cb, stat)
497
498        label=stat
499        #callback=cb
500        out = ""
501        #outvec = []
502        sep = '-'*50
503        for i in range(self.nrow()):
504            refstr = ''
505            statunit= ''
506            if getchan:
507                qx, qy = self.chan2data(rowno=i, chan=chan[i])
508                if rtnabc:
509                    statvals.append(qx['value'])
510                    refstr = ('(value: %'+form) % (qy['value'])+' ['+qy['unit']+'])'
511                    statunit= '['+qx['unit']+']'
512                else:
513                    refstr = ('(@ %'+form) % (qx['value'])+' ['+qx['unit']+'])'
514
515            tm = self._gettime(i)
516            src = self._getsourcename(i)
517            out += 'Scan[%d] (%s) ' % (self.getscan(i), src)
518            out += 'Time[%s]:\n' % (tm)
519            if self.nbeam(-1) > 1:
520                out +=  ' Beam[%d] ' % (self.getbeam(i))
521            if self.nif(-1) > 1: out +=  ' IF[%d] ' % (self.getif(i))
522            if self.npol(-1) > 1: out +=  ' Pol[%d] ' % (self.getpol(i))
523            #outvec.append(callback(i))
524            #out += ('= %'+form) % (outvec[i]) +'   '+refstr+'\n'
525            out += ('= %'+form) % (statvals[i]) +'   '+refstr+'\n'
526            out +=  sep+"\n"
527
528        if rcParams['verbose']:
529            import os
530            if os.environ.has_key( 'USER' ):
531                usr=os.environ['USER']
532            else:
533                import commands
534                usr=commands.getoutput( 'whoami' )
535            tmpfile='/tmp/tmp_'+usr+'_casapy_asap_scantable_stats'
536            f=open(tmpfile,'w')
537            print >> f, sep
538            print >> f, ' %s %s' % (label, statunit)
539            print >> f, sep
540            print >> f, out
541            f.close()
542            f=open(tmpfile,'r')
543            x=f.readlines()
544            f.close()
545            blanc=''
546            asaplog.push(blanc.join(x), False)
547            #for xx in x:
548            #    asaplog.push( xx, False )
549            print_log()
550        return statvals
551
552    def chan2data(self, rowno=0, chan=0):
553        """
554        Returns channel/frequency/velocity and spectral value
555        at an arbitrary row and channel in the scantable.
556        Parameters:
557            rowno:   a row number in the scantable. Default is the
558                     first row, i.e. rowno=0
559            chan:    a channel in the scantable. Default is the first
560                     channel, i.e. pos=0
561        """
562        if isinstance(rowno, int) and isinstance(chan, int):
563            qx = {'unit': self.get_unit(),
564                  'value': self._getabcissa(rowno)[chan]}
565            qy = {'unit': self.get_fluxunit(),
566                  'value': self._getspectrum(rowno)[chan]}
567            return qx, qy
568
569    def stddev(self, mask=None):
570        """
571        Determine the standard deviation of the current beam/if/pol
572        Takes a 'mask' as an optional parameter to specify which
573        channels should be excluded.
574        Parameters:
575            mask:    an optional mask specifying where the standard
576                     deviation should be determined.
577
578        Example:
579            scan.set_unit('channel')
580            msk = scan.create_mask([100, 200], [500, 600])
581            scan.stddev(mask=m)
582        """
583        return self.stats(stat='stddev', mask=mask);
584
585
586    def get_column_names(self):
587        """
588        Return a  list of column names, which can be used for selection.
589        """
590        return list(Scantable.get_column_names(self))
591
592    def get_tsys(self, row=-1):
593        """
594        Return the System temperatures.
595        Returns:
596            a list of Tsys values for the current selection
597        """
598        if row > -1:
599            return self._get_column(self._gettsys, row)
600        return self._row_callback(self._gettsys, "Tsys")
601
602
603    def get_weather(self, row=-1):
604        values = self._get_column(self._get_weather, row)
605        if row > -1:
606            return {'temperature': values[0],
607                    'pressure': values[1], 'humidity' : values[2],
608                    'windspeed' : values[3], 'windaz' : values[4]
609                    }
610        else:
611            out = []
612            for r in values:
613
614                out.append({'temperature': r[0],
615                            'pressure': r[1], 'humidity' : r[2],
616                            'windspeed' : r[3], 'windaz' : r[4]
617                    })
618            return out
619
620    def _row_callback(self, callback, label):
621        out = ""
622        outvec = []
623        sep = '-'*50
624        for i in range(self.nrow()):
625            tm = self._gettime(i)
626            src = self._getsourcename(i)
627            out += 'Scan[%d] (%s) ' % (self.getscan(i), src)
628            out += 'Time[%s]:\n' % (tm)
629            if self.nbeam(-1) > 1:
630                out +=  ' Beam[%d] ' % (self.getbeam(i))
631            if self.nif(-1) > 1: out +=  ' IF[%d] ' % (self.getif(i))
632            if self.npol(-1) > 1: out +=  ' Pol[%d] ' % (self.getpol(i))
633            outvec.append(callback(i))
634            out += '= %3.3f\n' % (outvec[i])
635            out +=  sep+'\n'
636        if rcParams['verbose']:
637            asaplog.push(sep)
638            asaplog.push(" %s" % (label))
639            asaplog.push(sep)
640            asaplog.push(out)
641            print_log()
642        return outvec
643
644    def _get_column(self, callback, row=-1):
645        """
646        """
647        if row == -1:
648            return [callback(i) for i in range(self.nrow())]
649        else:
650            if  0 <= row < self.nrow():
651                return callback(row)
652
653
654    def get_time(self, row=-1, asdatetime=False):
655        """
656        Get a list of time stamps for the observations.
657        Return a datetime object for each integration time stamp in the scantable.
658        Parameters:
659            row:          row no of integration. Default -1 return all rows
660            asdatetime:   return values as datetime objects rather than strings
661        Example:
662            none
663        """
664        from time import strptime
665        from datetime import datetime
666        times = self._get_column(self._gettime, row)
667        if not asdatetime:
668            return times
669        format = "%Y/%m/%d/%H:%M:%S"
670        if isinstance(times, list):
671            return [datetime(*strptime(i, format)[:6]) for i in times]
672        else:
673            return datetime(*strptime(times, format)[:6])
674
675
676    def get_inttime(self, row=-1):
677        """
678        Get a list of integration times for the observations.
679        Return a time in seconds for each integration in the scantable.
680        Parameters:
681            row:    row no of integration. Default -1 return all rows.
682        Example:
683            none
684        """
685        return self._get_column(self._getinttime, row)
686
687
688    def get_sourcename(self, row=-1):
689        """
690        Get a list source names for the observations.
691        Return a string for each integration in the scantable.
692        Parameters:
693            row:    row no of integration. Default -1 return all rows.
694        Example:
695            none
696        """
697        return self._get_column(self._getsourcename, row)
698
699    def get_elevation(self, row=-1):
700        """
701        Get a list of elevations for the observations.
702        Return a float for each integration in the scantable.
703        Parameters:
704            row:    row no of integration. Default -1 return all rows.
705        Example:
706            none
707        """
708        return self._get_column(self._getelevation, row)
709
710    def get_azimuth(self, row=-1):
711        """
712        Get a list of azimuths for the observations.
713        Return a float for each integration in the scantable.
714        Parameters:
715            row:    row no of integration. Default -1 return all rows.
716        Example:
717            none
718        """
719        return self._get_column(self._getazimuth, row)
720
721    def get_parangle(self, row=-1):
722        """
723        Get a list of parallactic angles for the observations.
724        Return a float for each integration in the scantable.
725        Parameters:
726            row:    row no of integration. Default -1 return all rows.
727        Example:
728            none
729        """
730        return self._get_column(self._getparangle, row)
731
732    def get_direction(self, row=-1):
733        """
734        Get a list of Positions on the sky (direction) for the observations.
735        Return a string for each integration in the scantable.
736        Parameters:
737            row:    row no of integration. Default -1 return all rows
738        Example:
739            none
740        """
741        return self._get_column(self._getdirection, row)
742
743    def get_directionval(self, row=-1):
744        """
745        Get a list of Positions on the sky (direction) for the observations.
746        Return a float for each integration in the scantable.
747        Parameters:
748            row:    row no of integration. Default -1 return all rows
749        Example:
750            none
751        """
752        return self._get_column(self._getdirectionvec, row)
753
754    #@print_log_dec
755    def set_unit(self, unit='channel'):
756        """
757        Set the unit for all following operations on this scantable
758        Parameters:
759            unit:    optional unit, default is 'channel'
760                     one of '*Hz', 'km/s', 'channel', ''
761        """
762        varlist = vars()
763        if unit in ['', 'pixel', 'channel']:
764            unit = ''
765        inf = list(self._getcoordinfo())
766        inf[0] = unit
767        self._setcoordinfo(inf)
768        self._add_history("set_unit", varlist)
769
770    #@print_log_dec
771    def set_instrument(self, instr):
772        """
773        Set the instrument for subsequent processing.
774        Parameters:
775            instr:    Select from 'ATPKSMB', 'ATPKSHOH', 'ATMOPRA',
776                      'DSS-43' (Tid), 'CEDUNA', and 'HOBART'
777        """
778        self._setInstrument(instr)
779        self._add_history("set_instument", vars())
780        print_log()
781
782    #@print_log_dec
783    def set_feedtype(self, feedtype):
784        """
785        Overwrite the feed type, which might not be set correctly.
786        Parameters:
787            feedtype:     'linear' or 'circular'
788        """
789        self._setfeedtype(feedtype)
790        self._add_history("set_feedtype", vars())
791        print_log()
792
793    #@print_log_dec
794    def set_doppler(self, doppler='RADIO'):
795        """
796        Set the doppler for all following operations on this scantable.
797        Parameters:
798            doppler:    One of 'RADIO', 'OPTICAL', 'Z', 'BETA', 'GAMMA'
799        """
800        varlist = vars()
801        inf = list(self._getcoordinfo())
802        inf[2] = doppler
803        self._setcoordinfo(inf)
804        self._add_history("set_doppler", vars())
805        print_log()
806
807    #@print_log_dec
808    def set_freqframe(self, frame=None):
809        """
810        Set the frame type of the Spectral Axis.
811        Parameters:
812            frame:   an optional frame type, default 'LSRK'. Valid frames are:
813                     'TOPO', 'LSRD', 'LSRK', 'BARY',
814                     'GEO', 'GALACTO', 'LGROUP', 'CMB'
815        Examples:
816            scan.set_freqframe('BARY')
817        """
818        frame = frame or rcParams['scantable.freqframe']
819        varlist = vars()
820        # "REST" is not implemented in casacore
821        #valid = ['REST', 'TOPO', 'LSRD', 'LSRK', 'BARY', \
822        #           'GEO', 'GALACTO', 'LGROUP', 'CMB']
823        valid = ['TOPO', 'LSRD', 'LSRK', 'BARY', \
824                   'GEO', 'GALACTO', 'LGROUP', 'CMB']
825
826        if frame in valid:
827            inf = list(self._getcoordinfo())
828            inf[1] = frame
829            self._setcoordinfo(inf)
830            self._add_history("set_freqframe", varlist)
831        else:
832            msg  = "Please specify a valid freq type. Valid types are:\n", valid
833            if rcParams['verbose']:
834                #print msg
835                asaplog.push( msg )
836                print_log( 'ERROR' )
837            else:
838                raise TypeError(msg)
839        print_log()
840
841    def set_dirframe(self, frame=""):
842        """
843        Set the frame type of the Direction on the sky.
844        Parameters:
845            frame:   an optional frame type, default ''. Valid frames are:
846                     'J2000', 'B1950', 'GALACTIC'
847        Examples:
848            scan.set_dirframe('GALACTIC')
849        """
850        varlist = vars()
851        try:
852            Scantable.set_dirframe(self, frame)
853        except RuntimeError, msg:
854            if rcParams['verbose']:
855                #print msg
856                print_log()
857                asaplog.push( str(msg) )
858                print_log( 'ERROR' )
859            else:
860                raise
861        self._add_history("set_dirframe", varlist)
862
863    def get_unit(self):
864        """
865        Get the default unit set in this scantable
866        Returns:
867            A unit string
868        """
869        inf = self._getcoordinfo()
870        unit = inf[0]
871        if unit == '': unit = 'channel'
872        return unit
873
874    def get_abcissa(self, rowno=0):
875        """
876        Get the abcissa in the current coordinate setup for the currently
877        selected Beam/IF/Pol
878        Parameters:
879            rowno:    an optional row number in the scantable. Default is the
880                      first row, i.e. rowno=0
881        Returns:
882            The abcissa values and the format string (as a dictionary)
883        """
884        abc = self._getabcissa(rowno)
885        lbl = self._getabcissalabel(rowno)
886        print_log()
887        return abc, lbl
888
889    def flag(self, mask=None, unflag=False):
890        """
891        Flag the selected data using an optional channel mask.
892        Parameters:
893            mask:   an optional channel mask, created with create_mask. Default
894                    (no mask) is all channels.
895            unflag:    if True, unflag the data
896        """
897        varlist = vars()
898        mask = mask or []
899        try:
900            self._flag(mask, unflag)
901        except RuntimeError, msg:
902            if rcParams['verbose']:
903                #print msg
904                print_log()
905                asaplog.push( str(msg) )
906                print_log( 'ERROR' )
907                return
908            else: raise
909        self._add_history("flag", varlist)
910
911    def flag_row(self, rows=[], unflag=False):
912        """
913        Flag the selected data in row-based manner.
914        Parameters:
915            rows:   list of row numbers to be flagged. Default is no row (must be explicitly specified to execute row-based flagging).
916            unflag: if True, unflag the data.
917        """
918        varlist = vars()
919        try:
920            self._flag_row(rows, unflag)
921        except RuntimeError, msg:
922            if rcParams['verbose']:
923                print_log()
924                asaplog.push( str(msg) )
925                print_log('ERROR')
926                return
927            else: raise
928        self._add_history("flag_row", varlist)
929       
930    def clip(self, uthres=None, dthres=None, clipoutside=True, unflag=False):
931        """
932        Flag the selected data outside a specified range (in channel-base)
933        Parameters:
934            uthres:      upper threshold.
935            dthres:      lower threshold
936            clipoutside: True for flagging data outside the range [dthres:uthres].
937                         False for glagging data inside the range.
938            unflag     : if True, unflag the data.
939        """
940        varlist = vars()
941        try:
942            self._clip(uthres, dthres, clipoutside, unflag)
943        except RuntimeError, msg:
944            if rcParams['verbose']:
945                print_log()
946                asaplog.push(str(msg))
947                print_log('ERROR')
948                return
949            else: raise
950        self._add_history("clip", varlist)
951
952    #@print_log_dec
953    def lag_flag(self, start, end, unit="MHz", insitu=None):
954    #def lag_flag(self, frequency, width=0.0, unit="GHz", insitu=None):
955        """
956        Flag the data in 'lag' space by providing a frequency to remove.
957        Flagged data in the scantable gets interpolated over the region.
958        No taper is applied.
959        Parameters:
960            start:    the start frequency (really a period within the
961                      bandwidth)  or period to remove
962            end:      the end frequency or period to remove
963            unit:     the frequency unit (default "MHz") or "" for
964                      explicit lag channels
965        Notes:
966            It is recommended to flag edges of the band or strong
967            signals beforehand.
968        """
969        if insitu is None: insitu = rcParams['insitu']
970        self._math._setinsitu(insitu)
971        varlist = vars()
972        base = { "GHz": 1000000000., "MHz": 1000000., "kHz": 1000., "Hz": 1.}
973        if not (unit == "" or base.has_key(unit)):
974            raise ValueError("%s is not a valid unit." % unit)
975        try:
976            if unit == "":
977                s = scantable(self._math._lag_flag(self, start, end, "lags"))
978            else:
979                s = scantable(self._math._lag_flag(self, start*base[unit],
980                                                   end*base[unit], "frequency"))
981        except RuntimeError, msg:
982            if rcParams['verbose']:
983                #print msg
984                print_log()
985                asaplog.push( str(msg) )
986                print_log( 'ERROR' )
987                return
988            else: raise
989        s._add_history("lag_flag", varlist)
990        print_log()
991        if insitu:
992            self._assign(s)
993        else:
994            return s
995
996    #@print_log_dec
997    def create_mask(self, *args, **kwargs):
998        """
999        Compute and return a mask based on [min, max] windows.
1000        The specified windows are to be INCLUDED, when the mask is
1001        applied.
1002        Parameters:
1003            [min, max], [min2, max2], ...
1004                Pairs of start/end points (inclusive)specifying the regions
1005                to be masked
1006            invert:     optional argument. If specified as True,
1007                        return an inverted mask, i.e. the regions
1008                        specified are EXCLUDED
1009            row:        create the mask using the specified row for
1010                        unit conversions, default is row=0
1011                        only necessary if frequency varies over rows.
1012        Example:
1013            scan.set_unit('channel')
1014            a)
1015            msk = scan.create_mask([400, 500], [800, 900])
1016            # masks everything outside 400 and 500
1017            # and 800 and 900 in the unit 'channel'
1018
1019            b)
1020            msk = scan.create_mask([400, 500], [800, 900], invert=True)
1021            # masks the regions between 400 and 500
1022            # and 800 and 900 in the unit 'channel'
1023            c)
1024            mask only channel 400
1025            msk =  scan.create_mask([400])
1026        """
1027        row = kwargs.get("row", 0)
1028        data = self._getabcissa(row)
1029        u = self._getcoordinfo()[0]
1030        if rcParams['verbose']:
1031            if u == "": u = "channel"
1032            msg = "The current mask window unit is %s" % u
1033            i = self._check_ifs()
1034            if not i:
1035                msg += "\nThis mask is only valid for IF=%d" % (self.getif(i))
1036            asaplog.push(msg)
1037        n = self.nchan()
1038        msk = _n_bools(n, False)
1039        # test if args is a 'list' or a 'normal *args - UGLY!!!
1040
1041        ws = (isinstance(args[-1][-1], int) or isinstance(args[-1][-1], float)) \
1042             and args or args[0]
1043        for window in ws:
1044            if len(window) == 1:
1045                window = [window[0], window[0]]
1046            if len(window) == 0 or  len(window) > 2:
1047                raise ValueError("A window needs to be defined as [start(, end)]")
1048            if window[0] > window[1]:
1049                tmp = window[0]
1050                window[0] = window[1]
1051                window[1] = tmp
1052            for i in range(n):
1053                if data[i] >= window[0] and data[i] <= window[1]:
1054                    msk[i] = True
1055        if kwargs.has_key('invert'):
1056            if kwargs.get('invert'):
1057                msk = mask_not(msk)
1058        print_log()
1059        return msk
1060
1061    def get_masklist(self, mask=None, row=0):
1062        """
1063        Compute and return a list of mask windows, [min, max].
1064        Parameters:
1065            mask:       channel mask, created with create_mask.
1066            row:        calcutate the masklist using the specified row
1067                        for unit conversions, default is row=0
1068                        only necessary if frequency varies over rows.
1069        Returns:
1070            [min, max], [min2, max2], ...
1071                Pairs of start/end points (inclusive)specifying
1072                the masked regions
1073        """
1074        if not (isinstance(mask,list) or isinstance(mask, tuple)):
1075            raise TypeError("The mask should be list or tuple.")
1076        if len(mask) < 2:
1077            raise TypeError("The mask elements should be > 1")
1078        if self.nchan() != len(mask):
1079            msg = "Number of channels in scantable != number of mask elements"
1080            raise TypeError(msg)
1081        data = self._getabcissa(row)
1082        u = self._getcoordinfo()[0]
1083        if rcParams['verbose']:
1084            if u == "": u = "channel"
1085            msg = "The current mask window unit is %s" % u
1086            i = self._check_ifs()
1087            if not i:
1088                msg += "\nThis mask is only valid for IF=%d" % (self.getif(i))
1089            asaplog.push(msg)
1090        masklist=[]
1091        ist, ien = None, None
1092        ist, ien=self.get_mask_indices(mask)
1093        if ist is not None and ien is not None:
1094            for i in xrange(len(ist)):
1095                range=[data[ist[i]],data[ien[i]]]
1096                range.sort()
1097                masklist.append([range[0],range[1]])
1098        return masklist
1099
1100    def get_mask_indices(self, mask=None):
1101        """
1102        Compute and Return lists of mask start indices and mask end indices.
1103         Parameters:
1104            mask:       channel mask, created with create_mask.
1105        Returns:
1106            List of mask start indices and that of mask end indices,
1107            i.e., [istart1,istart2,....], [iend1,iend2,....].
1108        """
1109        if not (isinstance(mask,list) or isinstance(mask, tuple)):
1110            raise TypeError("The mask should be list or tuple.")
1111        if len(mask) < 2:
1112            raise TypeError("The mask elements should be > 1")
1113        istart=[]
1114        iend=[]
1115        if mask[0]: istart.append(0)
1116        for i in range(len(mask)-1):
1117            if not mask[i] and mask[i+1]:
1118                istart.append(i+1)
1119            elif mask[i] and not mask[i+1]:
1120                iend.append(i)
1121        if mask[len(mask)-1]: iend.append(len(mask)-1)
1122        if len(istart) != len(iend):
1123            raise RuntimeError("Numbers of mask start != mask end.")
1124        for i in range(len(istart)):
1125            if istart[i] > iend[i]:
1126                raise RuntimeError("Mask start index > mask end index")
1127                break
1128        return istart,iend
1129
1130#    def get_restfreqs(self):
1131#        """
1132#        Get the restfrequency(s) stored in this scantable.
1133#        The return value(s) are always of unit 'Hz'
1134#        Parameters:
1135#            none
1136#        Returns:
1137#            a list of doubles
1138#        """
1139#        return list(self._getrestfreqs())
1140
1141    def get_restfreqs(self, ids=None):
1142        """
1143        Get the restfrequency(s) stored in this scantable.
1144        The return value(s) are always of unit 'Hz'
1145        Parameters:
1146            ids: (optional) a list of MOLECULE_ID for that restfrequency(s) to
1147                 be retrieved
1148        Returns:
1149            dictionary containing ids and a list of doubles for each id
1150        """
1151        if ids is None:
1152            rfreqs={}
1153            idlist = self.getmolnos()
1154            for i in idlist:
1155                rfreqs[i]=list(self._getrestfreqs(i))
1156            return rfreqs
1157        else:
1158            if type(ids)==list or type(ids)==tuple:
1159                rfreqs={}
1160                for i in ids:
1161                    rfreqs[i]=list(self._getrestfreqs(i))
1162                return rfreqs
1163            else:
1164                return list(self._getrestfreqs(ids))
1165            #return list(self._getrestfreqs(ids))
1166
1167    def set_restfreqs(self, freqs=None, unit='Hz'):
1168        """
1169        ********NEED TO BE UPDATED begin************
1170        Set or replace the restfrequency specified and
1171        If the 'freqs' argument holds a scalar,
1172        then that rest frequency will be applied to all the selected
1173        data.  If the 'freqs' argument holds
1174        a vector, then it MUST be of equal or smaller length than
1175        the number of IFs (and the available restfrequencies will be
1176        replaced by this vector).  In this case, *all* data have
1177        the restfrequency set per IF according
1178        to the corresponding value you give in the 'freqs' vector.
1179        E.g. 'freqs=[1e9, 2e9]'  would mean IF 0 gets restfreq 1e9 and
1180        IF 1 gets restfreq 2e9.
1181        ********NEED TO BE UPDATED end************
1182        You can also specify the frequencies via a linecatalog.
1183
1184        Parameters:
1185            freqs:   list of rest frequency values or string idenitfiers
1186            unit:    unit for rest frequency (default 'Hz')
1187
1188        Example:
1189            # set the given restfrequency for the all currently selected IFs
1190            scan.set_restfreqs(freqs=1.4e9)
1191            # set multiple restfrequencies to all the selected data
1192            scan.set_restfreqs(freqs=[1.4e9, 1.41e9, 1.42e9])
1193            # If the number of IFs in the data is >= 2 the IF0 gets the first
1194            # value IF1 the second... NOTE that freqs needs to be
1195            # specified in list of list (e.g. [[],[],...] ).
1196            scan.set_restfreqs(freqs=[[1.4e9],[1.67e9]])
1197            #set the given restfrequency for the whole table (by name)
1198            scan.set_restfreqs(freqs="OH1667")
1199
1200        Note:
1201            To do more sophisticate Restfrequency setting, e.g. on a
1202            source and IF basis, use scantable.set_selection() before using
1203            this function.
1204            # provide your scantable is called scan
1205            selection = selector()
1206            selection.set_name("ORION*")
1207            selection.set_ifs([1])
1208            scan.set_selection(selection)
1209            scan.set_restfreqs(freqs=86.6e9)
1210
1211        """
1212        varlist = vars()
1213        from asap import linecatalog
1214        # simple  value
1215        if isinstance(freqs, int) or isinstance(freqs, float):
1216            # TT mod
1217            #self._setrestfreqs(freqs, "",unit)
1218            self._setrestfreqs([freqs], [""],unit)
1219        # list of values
1220        elif isinstance(freqs, list) or isinstance(freqs, tuple):
1221            # list values are scalars
1222            if isinstance(freqs[-1], int) or isinstance(freqs[-1], float):
1223                self._setrestfreqs(freqs, [""],unit)
1224            # list values are tuples, (value, name)
1225            elif isinstance(freqs[-1], dict):
1226                #sel = selector()
1227                #savesel = self._getselection()
1228                #iflist = self.getifnos()
1229                #for i in xrange(len(freqs)):
1230                #    sel.set_ifs(iflist[i])
1231                #    self._setselection(sel)
1232                #    self._setrestfreqs(freqs[i], "",unit)
1233                #self._setselection(savesel)
1234                self._setrestfreqs(freqs["value"],
1235                                   freqs["name"], "MHz")
1236            elif isinstance(freqs[-1], list) or isinstance(freqs[-1], tuple):
1237                sel = selector()
1238                savesel = self._getselection()
1239                iflist = self.getifnos()
1240                if len(freqs)>len(iflist):
1241                    raise ValueError("number of elements in list of list exeeds the current IF selections")
1242                for i in xrange(len(freqs)):
1243                    sel.set_ifs(iflist[i])
1244                    self._setselection(sel)
1245                    self._setrestfreqs(freqs[i]["value"],
1246                                       freqs[i]["name"], "MHz")
1247                self._setselection(savesel)
1248        # freqs are to be taken from a linecatalog
1249        elif isinstance(freqs, linecatalog):
1250            sel = selector()
1251            savesel = self._getselection()
1252            for i in xrange(freqs.nrow()):
1253                sel.set_ifs(iflist[i])
1254                self._setselection(sel)
1255                self._setrestfreqs(freqs.get_frequency(i),
1256                                   freqs.get_name(i), "MHz")
1257                # ensure that we are not iterating past nIF
1258                if i == self.nif()-1: break
1259            self._setselection(savesel)
1260        else:
1261            return
1262        self._add_history("set_restfreqs", varlist)
1263
1264    def shift_refpix(self, delta):
1265        """
1266        Shift the reference pixel of the Spectra Coordinate by an
1267        integer amount.
1268        Parameters:
1269            delta:   the amount to shift by
1270        Note:
1271            Be careful using this with broadband data.
1272        """
1273        Scantable.shift_refpix(self, delta)
1274
1275    def history(self, filename=None):
1276        """
1277        Print the history. Optionally to a file.
1278        Parameters:
1279            filename:    The name  of the file to save the history to.
1280        """
1281        hist = list(self._gethistory())
1282        out = "-"*80
1283        for h in hist:
1284            if h.startswith("---"):
1285                out += "\n"+h
1286            else:
1287                items = h.split("##")
1288                date = items[0]
1289                func = items[1]
1290                items = items[2:]
1291                out += "\n"+date+"\n"
1292                out += "Function: %s\n  Parameters:" % (func)
1293                for i in items:
1294                    s = i.split("=")
1295                    out += "\n   %s = %s" % (s[0], s[1])
1296                out += "\n"+"-"*80
1297        if filename is not None:
1298            if filename is "":
1299                filename = 'scantable_history.txt'
1300            import os
1301            filename = os.path.expandvars(os.path.expanduser(filename))
1302            if not os.path.isdir(filename):
1303                data = open(filename, 'w')
1304                data.write(out)
1305                data.close()
1306            else:
1307                msg = "Illegal file name '%s'." % (filename)
1308                if rcParams['verbose']:
1309                    #print msg
1310                    asaplog.push( msg )
1311                    print_log( 'ERROR' )
1312                else:
1313                    raise IOError(msg)
1314        if rcParams['verbose']:
1315            try:
1316                from IPython.genutils import page as pager
1317            except ImportError:
1318                from pydoc import pager
1319            pager(out)
1320        else:
1321            return out
1322        return
1323    #
1324    # Maths business
1325    #
1326    #@print_log_dec
1327    def average_time(self, mask=None, scanav=False, weight='tint', align=False):
1328        """
1329        Return the (time) weighted average of a scan.
1330        Note:
1331            in channels only - align if necessary
1332        Parameters:
1333            mask:     an optional mask (only used for 'var' and 'tsys'
1334                      weighting)
1335            scanav:   True averages each scan separately
1336                      False (default) averages all scans together,
1337            weight:   Weighting scheme.
1338                      'none'     (mean no weight)
1339                      'var'      (1/var(spec) weighted)
1340                      'tsys'     (1/Tsys**2 weighted)
1341                      'tint'     (integration time weighted)
1342                      'tintsys'  (Tint/Tsys**2)
1343                      'median'   ( median averaging)
1344                      The default is 'tint'
1345            align:    align the spectra in velocity before averaging. It takes
1346                      the time of the first spectrum as reference time.
1347        Example:
1348            # time average the scantable without using a mask
1349            newscan = scan.average_time()
1350        """
1351        varlist = vars()
1352        weight = weight or 'TINT'
1353        mask = mask or ()
1354        scanav = (scanav and 'SCAN') or 'NONE'
1355        scan = (self, )
1356        try:
1357            if align:
1358                scan = (self.freq_align(insitu=False), )
1359            s = None
1360            if weight.upper() == 'MEDIAN':
1361                s = scantable(self._math._averagechannel(scan[0], 'MEDIAN',
1362                                                         scanav))
1363            else:
1364                s = scantable(self._math._average(scan, mask, weight.upper(),
1365                              scanav))
1366        except RuntimeError, msg:
1367            if rcParams['verbose']:
1368                #print msg
1369                print_log()
1370                asaplog.push( str(msg) )
1371                print_log( 'ERROR' )
1372                return
1373            else: raise
1374        s._add_history("average_time", varlist)
1375        print_log()
1376        return s
1377
1378    #@print_log_dec
1379    def convert_flux(self, jyperk=None, eta=None, d=None, insitu=None):
1380        """
1381        Return a scan where all spectra are converted to either
1382        Jansky or Kelvin depending upon the flux units of the scan table.
1383        By default the function tries to look the values up internally.
1384        If it can't find them (or if you want to over-ride), you must
1385        specify EITHER jyperk OR eta (and D which it will try to look up
1386        also if you don't set it). jyperk takes precedence if you set both.
1387        Parameters:
1388            jyperk:      the Jy / K conversion factor
1389            eta:         the aperture efficiency
1390            d:           the geomtric diameter (metres)
1391            insitu:      if False a new scantable is returned.
1392                         Otherwise, the scaling is done in-situ
1393                         The default is taken from .asaprc (False)
1394        """
1395        if insitu is None: insitu = rcParams['insitu']
1396        self._math._setinsitu(insitu)
1397        varlist = vars()
1398        jyperk = jyperk or -1.0
1399        d = d or -1.0
1400        eta = eta or -1.0
1401        s = scantable(self._math._convertflux(self, d, eta, jyperk))
1402        s._add_history("convert_flux", varlist)
1403        print_log()
1404        if insitu: self._assign(s)
1405        else: return s
1406
1407    #@print_log_dec
1408    def gain_el(self, poly=None, filename="", method="linear", insitu=None):
1409        """
1410        Return a scan after applying a gain-elevation correction.
1411        The correction can be made via either a polynomial or a
1412        table-based interpolation (and extrapolation if necessary).
1413        You specify polynomial coefficients, an ascii table or neither.
1414        If you specify neither, then a polynomial correction will be made
1415        with built in coefficients known for certain telescopes (an error
1416        will occur if the instrument is not known).
1417        The data and Tsys are *divided* by the scaling factors.
1418        Parameters:
1419            poly:        Polynomial coefficients (default None) to compute a
1420                         gain-elevation correction as a function of
1421                         elevation (in degrees).
1422            filename:    The name of an ascii file holding correction factors.
1423                         The first row of the ascii file must give the column
1424                         names and these MUST include columns
1425                         "ELEVATION" (degrees) and "FACTOR" (multiply data
1426                         by this) somewhere.
1427                         The second row must give the data type of the
1428                         column. Use 'R' for Real and 'I' for Integer.
1429                         An example file would be
1430                         (actual factors are arbitrary) :
1431
1432                         TIME ELEVATION FACTOR
1433                         R R R
1434                         0.1 0 0.8
1435                         0.2 20 0.85
1436                         0.3 40 0.9
1437                         0.4 60 0.85
1438                         0.5 80 0.8
1439                         0.6 90 0.75
1440            method:      Interpolation method when correcting from a table.
1441                         Values are  "nearest", "linear" (default), "cubic"
1442                         and "spline"
1443            insitu:      if False a new scantable is returned.
1444                         Otherwise, the scaling is done in-situ
1445                         The default is taken from .asaprc (False)
1446        """
1447
1448        if insitu is None: insitu = rcParams['insitu']
1449        self._math._setinsitu(insitu)
1450        varlist = vars()
1451        poly = poly or ()
1452        from os.path import expandvars
1453        filename = expandvars(filename)
1454        s = scantable(self._math._gainel(self, poly, filename, method))
1455        s._add_history("gain_el", varlist)
1456        print_log()
1457        if insitu:
1458            self._assign(s)
1459        else:
1460            return s
1461
1462    #@print_log_dec
1463    def freq_align(self, reftime=None, method='cubic', insitu=None):
1464        """
1465        Return a scan where all rows have been aligned in frequency/velocity.
1466        The alignment frequency frame (e.g. LSRK) is that set by function
1467        set_freqframe.
1468        Parameters:
1469            reftime:     reference time to align at. By default, the time of
1470                         the first row of data is used.
1471            method:      Interpolation method for regridding the spectra.
1472                         Choose from "nearest", "linear", "cubic" (default)
1473                         and "spline"
1474            insitu:      if False a new scantable is returned.
1475                         Otherwise, the scaling is done in-situ
1476                         The default is taken from .asaprc (False)
1477        """
1478        if insitu is None: insitu = rcParams["insitu"]
1479        self._math._setinsitu(insitu)
1480        varlist = vars()
1481        reftime = reftime or ""
1482        s = scantable(self._math._freq_align(self, reftime, method))
1483        s._add_history("freq_align", varlist)
1484        print_log()
1485        if insitu: self._assign(s)
1486        else: return s
1487
1488    #@print_log_dec
1489    def opacity(self, tau=None, insitu=None):
1490        """
1491        Apply an opacity correction. The data
1492        and Tsys are multiplied by the correction factor.
1493        Parameters:
1494            tau:         (list of) opacity from which the correction factor is
1495                         exp(tau*ZD)
1496                         where ZD is the zenith-distance.
1497                         If a list is provided, it has to be of length nIF,
1498                         nIF*nPol or 1 and in order of IF/POL, e.g.
1499                         [opif0pol0, opif0pol1, opif1pol0 ...]
1500                         if tau is `None` the opacities are determined from a
1501                         model.
1502            insitu:      if False a new scantable is returned.
1503                         Otherwise, the scaling is done in-situ
1504                         The default is taken from .asaprc (False)
1505        """
1506        if insitu is None: insitu = rcParams['insitu']
1507        self._math._setinsitu(insitu)
1508        varlist = vars()
1509        if not hasattr(tau, "__len__"):
1510            tau = [tau]
1511        s = scantable(self._math._opacity(self, tau))
1512        s._add_history("opacity", varlist)
1513        print_log()
1514        if insitu: self._assign(s)
1515        else: return s
1516
1517    #@print_log_dec
1518    def bin(self, width=5, insitu=None):
1519        """
1520        Return a scan where all spectra have been binned up.
1521        Parameters:
1522            width:       The bin width (default=5) in pixels
1523            insitu:      if False a new scantable is returned.
1524                         Otherwise, the scaling is done in-situ
1525                         The default is taken from .asaprc (False)
1526        """
1527        if insitu is None: insitu = rcParams['insitu']
1528        self._math._setinsitu(insitu)
1529        varlist = vars()
1530        s = scantable(self._math._bin(self, width))
1531        s._add_history("bin", varlist)
1532        print_log()
1533        if insitu:
1534            self._assign(s)
1535        else:
1536            return s
1537
1538    #@print_log_dec
1539    def resample(self, width=5, method='cubic', insitu=None):
1540        """
1541        Return a scan where all spectra have been binned up.
1542       
1543        Parameters:
1544            width:       The bin width (default=5) in pixels
1545            method:      Interpolation method when correcting from a table.
1546                         Values are  "nearest", "linear", "cubic" (default)
1547                         and "spline"
1548            insitu:      if False a new scantable is returned.
1549                         Otherwise, the scaling is done in-situ
1550                         The default is taken from .asaprc (False)
1551        """
1552        if insitu is None: insitu = rcParams['insitu']
1553        self._math._setinsitu(insitu)
1554        varlist = vars()
1555        s = scantable(self._math._resample(self, method, width))
1556        s._add_history("resample", varlist)
1557        print_log()
1558        if insitu: self._assign(s)
1559        else: return s
1560
1561    #@print_log_dec
1562    def average_pol(self, mask=None, weight='none'):
1563        """
1564        Average the Polarisations together.
1565        Parameters:
1566            mask:        An optional mask defining the region, where the
1567                         averaging will be applied. The output will have all
1568                         specified points masked.
1569            weight:      Weighting scheme. 'none' (default), 'var' (1/var(spec)
1570                         weighted), or 'tsys' (1/Tsys**2 weighted)
1571        """
1572        varlist = vars()
1573        mask = mask or ()
1574        s = scantable(self._math._averagepol(self, mask, weight.upper()))
1575        s._add_history("average_pol", varlist)
1576        print_log()
1577        return s
1578
1579    #@print_log_dec
1580    def average_beam(self, mask=None, weight='none'):
1581        """
1582        Average the Beams together.
1583        Parameters:
1584            mask:        An optional mask defining the region, where the
1585                         averaging will be applied. The output will have all
1586                         specified points masked.
1587            weight:      Weighting scheme. 'none' (default), 'var' (1/var(spec)
1588                         weighted), or 'tsys' (1/Tsys**2 weighted)
1589        """
1590        varlist = vars()
1591        mask = mask or ()
1592        s = scantable(self._math._averagebeams(self, mask, weight.upper()))
1593        s._add_history("average_beam", varlist)
1594        print_log()
1595        return s
1596
1597    def parallactify(self, pflag):
1598        """
1599        Set a flag to inidcate whether this data should be treated as having
1600        been 'parallactified' (total phase == 0.0)
1601        Parameters:
1602            pflag:  Bool inidcating whether to turn this on (True) or
1603                    off (False)
1604        """
1605        varlist = vars()
1606        self._parallactify(pflag)
1607        self._add_history("parallactify", varlist)
1608
1609    #@print_log_dec
1610    def convert_pol(self, poltype=None):
1611        """
1612        Convert the data to a different polarisation type.
1613        Note that you will need cross-polarisation terms for most conversions.
1614        Parameters:
1615            poltype:    The new polarisation type. Valid types are:
1616                        "linear", "circular", "stokes" and "linpol"
1617        """
1618        varlist = vars()
1619        try:
1620            s = scantable(self._math._convertpol(self, poltype))
1621        except RuntimeError, msg:
1622            if rcParams['verbose']:
1623                #print msg
1624                print_log()
1625                asaplog.push( str(msg) )
1626                print_log( 'ERROR' )
1627                return
1628            else:
1629                raise
1630        s._add_history("convert_pol", varlist)
1631        print_log()
1632        return s
1633
1634    #@print_log_dec
1635    def smooth(self, kernel="hanning", width=5.0, order=2, plot=False, insitu=None):
1636        """
1637        Smooth the spectrum by the specified kernel (conserving flux).
1638        Parameters:
1639            kernel:     The type of smoothing kernel. Select from
1640                        'hanning' (default), 'gaussian', 'boxcar', 'rmedian'
1641                        or 'poly'
1642            width:      The width of the kernel in pixels. For hanning this is
1643                        ignored otherwise it defauls to 5 pixels.
1644                        For 'gaussian' it is the Full Width Half
1645                        Maximum. For 'boxcar' it is the full width.
1646                        For 'rmedian' and 'poly' it is the half width.
1647            order:      Optional parameter for 'poly' kernel (default is 2), to
1648                        specify the order of the polnomial. Ignored by all other
1649                        kernels.
1650            plot:       plot the original and the smoothed spectra.
1651                        In this each indivual fit has to be approved, by
1652                        typing 'y' or 'n'
1653            insitu:     if False a new scantable is returned.
1654                        Otherwise, the scaling is done in-situ
1655                        The default is taken from .asaprc (False)
1656        Example:
1657             none
1658        """
1659        if insitu is None: insitu = rcParams['insitu']
1660        self._math._setinsitu(insitu)
1661        varlist = vars()
1662
1663        if plot: orgscan = self.copy()
1664
1665        s = scantable(self._math._smooth(self, kernel.lower(), width, order))
1666        s._add_history("smooth", varlist)
1667
1668        if plot:
1669            if rcParams['plotter.gui']:
1670                from asap.asaplotgui import asaplotgui as asaplot
1671            else:
1672                from asap.asaplot import asaplot
1673            self._p=asaplot()
1674            self._p.set_panels()
1675            ylab=s._get_ordinate_label()
1676            #self._p.palette(0,["#777777","red"])
1677            for r in xrange(s.nrow()):
1678                xsm=s._getabcissa(r)
1679                ysm=s._getspectrum(r)
1680                xorg=orgscan._getabcissa(r)
1681                yorg=orgscan._getspectrum(r)
1682                self._p.clear()
1683                self._p.hold()
1684                self._p.set_axes('ylabel',ylab)
1685                self._p.set_axes('xlabel',s._getabcissalabel(r))
1686                self._p.set_axes('title',s._getsourcename(r))
1687                self._p.set_line(label='Original',color="#777777")
1688                self._p.plot(xorg,yorg)
1689                self._p.set_line(label='Smoothed',color="red")
1690                self._p.plot(xsm,ysm)
1691                ### Ugly part for legend
1692                for i in [0,1]:
1693                    self._p.subplots[0]['lines'].append([self._p.subplots[0]['axes'].lines[i]])
1694                self._p.release()
1695                ### Ugly part for legend
1696                self._p.subplots[0]['lines']=[]
1697                res = raw_input("Accept smoothing ([y]/n): ")
1698                if res.upper() == 'N':
1699                    s._setspectrum(yorg, r)
1700            self._p.unmap()
1701            self._p = None
1702            del orgscan
1703
1704        print_log()
1705        if insitu: self._assign(s)
1706        else: return s
1707
1708    #@print_log_dec
1709    def poly_baseline(self, mask=None, order=0, plot=False, uselin=False,
1710                      insitu=None):
1711        """
1712        Return a scan which has been baselined (all rows) by a polynomial.
1713        Parameters:
1714            mask:       an optional mask
1715            order:      the order of the polynomial (default is 0)
1716            plot:       plot the fit and the residual. In this each
1717                        indivual fit has to be approved, by typing 'y'
1718                        or 'n'
1719            uselin:     use linear polynomial fit
1720            insitu:     if False a new scantable is returned.
1721                        Otherwise, the scaling is done in-situ
1722                        The default is taken from .asaprc (False)
1723        Example:
1724            # return a scan baselined by a third order polynomial,
1725            # not using a mask
1726            bscan = scan.poly_baseline(order=3)
1727        """
1728        if insitu is None: insitu = rcParams['insitu']
1729        if not insitu:
1730            workscan = self.copy()
1731        else:
1732            workscan = self
1733        varlist = vars()
1734        if mask is None:
1735            mask = [True for i in xrange(self.nchan(-1))]
1736       
1737        from asap.asapfitter import fitter
1738        try:
1739            f = fitter()
1740            if uselin:
1741                f.set_function(lpoly=order)
1742            else:
1743                f.set_function(poly=order)
1744
1745            rows = range(workscan.nrow())
1746            if len(rows) > 0:
1747                self.blpars = []
1748           
1749            for r in rows:
1750                # take into account flagtra info (CAS-1434)
1751                flagtra = workscan._getmask(r)
1752                actualmask = mask[:]
1753                if len(actualmask) == 0:
1754                    actualmask = list(flagtra[:])
1755                else:
1756                    if len(actualmask) != len(flagtra):
1757                        raise RuntimeError, "Mask and flagtra have different length"
1758                    else:
1759                        for i in range(0, len(actualmask)):
1760                            actualmask[i] = actualmask[i] and flagtra[i]
1761                f.set_scan(workscan, actualmask)
1762                f.x = workscan._getabcissa(r)
1763                f.y = workscan._getspectrum(r)
1764                f.data = None
1765                f.fit()
1766                if plot:
1767                    f.plot(residual=True)
1768                    x = raw_input("Accept fit ( [y]/n ): ")
1769                    if x.upper() == 'N':
1770                        self.blpars.append(None)
1771                        continue
1772                workscan._setspectrum(f.fitter.getresidual(), r)
1773                self.blpars.append(f.get_parameters())
1774
1775            if plot:
1776                f._p.unmap()
1777                f._p = None
1778            workscan._add_history("poly_baseline", varlist)
1779            print_log()
1780            if insitu: self._assign(workscan)
1781            else: return workscan
1782        except RuntimeError:
1783            msg = "The fit failed, possibly because it didn't converge."
1784            if rcParams['verbose']:
1785                #print msg
1786                print_log()
1787                asaplog.push( str(msg) )
1788                print_log( 'ERROR' )
1789                return
1790            else:
1791                raise RuntimeError(msg)
1792
1793
1794    def auto_poly_baseline(self, mask=[], edge=(0, 0), order=0,
1795                           threshold=3, chan_avg_limit=1, plot=False,
1796                           insitu=None):
1797        """
1798        Return a scan which has been baselined (all rows) by a polynomial.
1799        Spectral lines are detected first using linefinder and masked out
1800        to avoid them affecting the baseline solution.
1801
1802        Parameters:
1803            mask:       an optional mask retreived from scantable
1804            edge:       an optional number of channel to drop at
1805                        the edge of spectrum. If only one value is
1806                        specified, the same number will be dropped from
1807                        both sides of the spectrum. Default is to keep
1808                        all channels. Nested tuples represent individual
1809                        edge selection for different IFs (a number of spectral
1810                        channels can be different)
1811            order:      the order of the polynomial (default is 0)
1812            threshold:  the threshold used by line finder. It is better to
1813                        keep it large as only strong lines affect the
1814                        baseline solution.
1815            chan_avg_limit:
1816                        a maximum number of consequtive spectral channels to
1817                        average during the search of weak and broad lines.
1818                        The default is no averaging (and no search for weak
1819                        lines). If such lines can affect the fitted baseline
1820                        (e.g. a high order polynomial is fitted), increase this
1821                        parameter (usually values up to 8 are reasonable). Most
1822                        users of this method should find the default value
1823                        sufficient.
1824            plot:       plot the fit and the residual. In this each
1825                        indivual fit has to be approved, by typing 'y'
1826                        or 'n'
1827            insitu:     if False a new scantable is returned.
1828                        Otherwise, the scaling is done in-situ
1829                        The default is taken from .asaprc (False)
1830
1831        Example:
1832            scan2=scan.auto_poly_baseline(order=7)
1833        """
1834        if insitu is None: insitu = rcParams['insitu']
1835        varlist = vars()
1836        from asap.asapfitter import fitter
1837        from asap.asaplinefind import linefinder
1838        from asap import _is_sequence_or_number as _is_valid
1839
1840        # check whether edge is set up for each IF individually
1841        individualedge = False;
1842        if len(edge) > 1:
1843            if isinstance(edge[0], list) or isinstance(edge[0], tuple):
1844                individualedge = True;
1845
1846        if not _is_valid(edge, int) and not individualedge:
1847            raise ValueError, "Parameter 'edge' has to be an integer or a \
1848            pair of integers specified as a tuple. Nested tuples are allowed \
1849            to make individual selection for different IFs."
1850
1851        curedge = (0, 0)
1852        if individualedge:
1853            for edgepar in edge:
1854                if not _is_valid(edgepar, int):
1855                    raise ValueError, "Each element of the 'edge' tuple has \
1856                                       to be a pair of integers or an integer."
1857        else:
1858            curedge = edge;
1859
1860        # setup fitter
1861        f = fitter()
1862        f.set_function(poly=order)
1863
1864        # setup line finder
1865        fl = linefinder()
1866        fl.set_options(threshold=threshold,avg_limit=chan_avg_limit)
1867
1868        if not insitu:
1869            workscan = self.copy()
1870        else:
1871            workscan = self
1872
1873        fl.set_scan(workscan)
1874
1875        rows = range(workscan.nrow())
1876        # Save parameters of baseline fits & masklists as a class attribute.
1877        # NOTICE: It does not reflect changes in scantable!
1878        if len(rows) > 0:
1879            self.blpars=[]
1880            self.masklists=[]
1881        asaplog.push("Processing:")
1882        for r in rows:
1883            msg = " Scan[%d] Beam[%d] IF[%d] Pol[%d] Cycle[%d]" % \
1884                (workscan.getscan(r), workscan.getbeam(r), workscan.getif(r), \
1885                 workscan.getpol(r), workscan.getcycle(r))
1886            asaplog.push(msg, False)
1887
1888            # figure out edge parameter
1889            if individualedge:
1890                if len(edge) >= workscan.getif(r):
1891                    raise RuntimeError, "Number of edge elements appear to " \
1892                                        "be less than the number of IFs"
1893                    curedge = edge[workscan.getif(r)]
1894
1895            # take into account flagtra info (CAS-1434)
1896            flagtra = workscan._getmask(r)
1897            actualmask = mask[:]
1898            if len(actualmask) == 0:
1899                actualmask = list(flagtra[:])
1900            else:
1901                if len(actualmask) != len(flagtra):
1902                    raise RuntimeError, "Mask and flagtra have different length"
1903                else:
1904                    for i in range(0, len(actualmask)):
1905                        actualmask[i] = actualmask[i] and flagtra[i]
1906
1907            # setup line finder
1908            fl.find_lines(r, actualmask, curedge)
1909            outmask=fl.get_mask()
1910            f.set_scan(workscan, fl.get_mask())
1911            f.x = workscan._getabcissa(r)
1912            f.y = workscan._getspectrum(r)
1913            f.data = None
1914            f.fit()
1915           
1916            # Show mask list
1917            masklist=workscan.get_masklist(fl.get_mask(),row=r)
1918            msg = "mask range: "+str(masklist)
1919            asaplog.push(msg, False)
1920
1921            if plot:
1922                f.plot(residual=True)
1923                x = raw_input("Accept fit ( [y]/n ): ")
1924                if x.upper() == 'N':
1925                    self.blpars.append(None)
1926                    self.masklists.append(None)
1927                    continue
1928
1929            workscan._setspectrum(f.fitter.getresidual(), r)
1930            self.blpars.append(f.get_parameters())
1931            self.masklists.append(masklist)
1932        if plot:
1933            f._p.unmap()
1934            f._p = None
1935        workscan._add_history("auto_poly_baseline", varlist)
1936        if insitu:
1937            self._assign(workscan)
1938        else:
1939            return workscan
1940
1941    #@print_log_dec
1942    def rotate_linpolphase(self, angle):
1943        """
1944        Rotate the phase of the complex polarization O=Q+iU correlation.
1945        This is always done in situ in the raw data.  So if you call this
1946        function more than once then each call rotates the phase further.
1947        Parameters:
1948            angle:   The angle (degrees) to rotate (add) by.
1949        Examples:
1950            scan.rotate_linpolphase(2.3)
1951        """
1952        varlist = vars()
1953        self._math._rotate_linpolphase(self, angle)
1954        self._add_history("rotate_linpolphase", varlist)
1955        print_log()
1956        return
1957
1958    #@print_log_dec
1959    def rotate_xyphase(self, angle):
1960        """
1961        Rotate the phase of the XY correlation.  This is always done in situ
1962        in the data.  So if you call this function more than once
1963        then each call rotates the phase further.
1964        Parameters:
1965            angle:   The angle (degrees) to rotate (add) by.
1966        Examples:
1967            scan.rotate_xyphase(2.3)
1968        """
1969        varlist = vars()
1970        self._math._rotate_xyphase(self, angle)
1971        self._add_history("rotate_xyphase", varlist)
1972        print_log()
1973        return
1974
1975    #@print_log_dec
1976    def swap_linears(self):
1977        """
1978        Swap the linear polarisations XX and YY, or better the first two
1979        polarisations as this also works for ciculars.
1980        """
1981        varlist = vars()
1982        self._math._swap_linears(self)
1983        self._add_history("swap_linears", varlist)
1984        print_log()
1985        return
1986
1987    #@print_log_dec
1988    def invert_phase(self):
1989        """
1990        Invert the phase of the complex polarisation
1991        """
1992        varlist = vars()
1993        self._math._invert_phase(self)
1994        self._add_history("invert_phase", varlist)
1995        print_log()
1996        return
1997
1998    #@print_log_dec
1999    def add(self, offset, insitu=None):
2000        """
2001        Return a scan where all spectra have the offset added
2002        Parameters:
2003            offset:      the offset
2004            insitu:      if False a new scantable is returned.
2005                         Otherwise, the scaling is done in-situ
2006                         The default is taken from .asaprc (False)
2007        """
2008        if insitu is None: insitu = rcParams['insitu']
2009        self._math._setinsitu(insitu)
2010        varlist = vars()
2011        s = scantable(self._math._unaryop(self, offset, "ADD", False))
2012        s._add_history("add", varlist)
2013        print_log()
2014        if insitu:
2015            self._assign(s)
2016        else:
2017            return s
2018
2019    #@print_log_dec
2020    def scale(self, factor, tsys=True, insitu=None):
2021        """
2022        Return a scan where all spectra are scaled by the give 'factor'
2023        Parameters:
2024            factor:      the scaling factor (float or 1D float list)
2025            insitu:      if False a new scantable is returned.
2026                         Otherwise, the scaling is done in-situ
2027                         The default is taken from .asaprc (False)
2028            tsys:        if True (default) then apply the operation to Tsys
2029                         as well as the data
2030        """
2031        if insitu is None: insitu = rcParams['insitu']
2032        self._math._setinsitu(insitu)
2033        varlist = vars()
2034        s = None
2035        import numpy
2036        if isinstance(factor, list) or isinstance(factor, numpy.ndarray):
2037            if isinstance(factor[0], list) or isinstance(factor[0], numpy.ndarray):
2038                from asapmath import _array2dOp
2039                s = _array2dOp( self.copy(), factor, "MUL", tsys )
2040            else:
2041                s = scantable( self._math._arrayop( self.copy(), factor, "MUL", tsys ) )
2042        else:
2043            s = scantable(self._math._unaryop(self.copy(), factor, "MUL", tsys))
2044        s._add_history("scale", varlist)
2045        print_log()
2046        if insitu:
2047            self._assign(s)
2048        else:
2049            return s
2050
2051    def set_sourcetype(self, match, matchtype="pattern",
2052                       sourcetype="reference"):
2053        """
2054        Set the type of the source to be an source or reference scan
2055        using the provided pattern:
2056        Parameters:
2057            match:          a Unix style pattern, regular expression or selector
2058            matchtype:      'pattern' (default) UNIX style pattern or
2059                            'regex' regular expression
2060            sourcetype:     the type of the source to use (source/reference)
2061        """
2062        varlist = vars()
2063        basesel = self.get_selection()
2064        stype = -1
2065        if sourcetype.lower().startswith("r"):
2066            stype = 1
2067        elif sourcetype.lower().startswith("s"):
2068            stype = 0
2069        else:
2070            raise ValueError("Illegal sourcetype use s(ource) or r(eference)")
2071        if matchtype.lower().startswith("p"):
2072            matchtype = "pattern"
2073        elif matchtype.lower().startswith("r"):
2074            matchtype = "regex"
2075        else:
2076            raise ValueError("Illegal matchtype, use p(attern) or r(egex)")
2077        sel = selector()
2078        if isinstance(match, selector):
2079            sel = match
2080        else:
2081            sel.set_query("SRCNAME == %s('%s')" % (matchtype, match))
2082        self.set_selection(basesel+sel)
2083        self._setsourcetype(stype)
2084        self.set_selection(basesel)
2085        self._add_history("set_sourcetype", varlist)
2086
2087    #@print_log_dec
2088    def auto_quotient(self, preserve=True, mode='paired', verify=False):
2089        """
2090        This function allows to build quotients automatically.
2091        It assumes the observation to have the same number of
2092        "ons" and "offs"
2093        Parameters:
2094            preserve:       you can preserve (default) the continuum or
2095                            remove it.  The equations used are
2096                            preserve: Output = Toff * (on/off) - Toff
2097                            remove:   Output = Toff * (on/off) - Ton
2098            mode:           the on/off detection mode
2099                            'paired' (default)
2100                            identifies 'off' scans by the
2101                            trailing '_R' (Mopra/Parkes) or
2102                            '_e'/'_w' (Tid) and matches
2103                            on/off pairs from the observing pattern
2104                            'time'
2105                            finds the closest off in time
2106
2107        """
2108        modes = ["time", "paired"]
2109        if not mode in modes:
2110            msg = "please provide valid mode. Valid modes are %s" % (modes)
2111            raise ValueError(msg)
2112        varlist = vars()
2113        s = None
2114        if mode.lower() == "paired":
2115            basesel = self.get_selection()
2116            sel = selector()+basesel
2117            sel.set_query("SRCTYPE==1")
2118            self.set_selection(sel)
2119            offs = self.copy()
2120            sel.set_query("SRCTYPE==0")
2121            self.set_selection(sel)
2122            ons = self.copy()
2123            s = scantable(self._math._quotient(ons, offs, preserve))
2124            self.set_selection(basesel)
2125        elif mode.lower() == "time":
2126            s = scantable(self._math._auto_quotient(self, mode, preserve))
2127        s._add_history("auto_quotient", varlist)
2128        print_log()
2129        return s
2130
2131    #@print_log_dec
2132    def mx_quotient(self, mask = None, weight='median', preserve=True):
2133        """
2134        Form a quotient using "off" beams when observing in "MX" mode.
2135        Parameters:
2136            mask:           an optional mask to be used when weight == 'stddev'
2137            weight:         How to average the off beams.  Default is 'median'.
2138            preserve:       you can preserve (default) the continuum or
2139                            remove it.  The equations used are
2140                            preserve: Output = Toff * (on/off) - Toff
2141                            remove:   Output = Toff * (on/off) - Ton
2142        """
2143        mask = mask or ()
2144        varlist = vars()
2145        on = scantable(self._math._mx_extract(self, 'on'))
2146        preoff = scantable(self._math._mx_extract(self, 'off'))
2147        off = preoff.average_time(mask=mask, weight=weight, scanav=False)
2148        from asapmath  import quotient
2149        q = quotient(on, off, preserve)
2150        q._add_history("mx_quotient", varlist)
2151        print_log()
2152        return q
2153
2154    #@print_log_dec
2155    def freq_switch(self, insitu=None):
2156        """
2157        Apply frequency switching to the data.
2158        Parameters:
2159            insitu:      if False a new scantable is returned.
2160                         Otherwise, the swictching is done in-situ
2161                         The default is taken from .asaprc (False)
2162        Example:
2163            none
2164        """
2165        if insitu is None: insitu = rcParams['insitu']
2166        self._math._setinsitu(insitu)
2167        varlist = vars()
2168        s = scantable(self._math._freqswitch(self))
2169        s._add_history("freq_switch", varlist)
2170        print_log()
2171        if insitu: self._assign(s)
2172        else: return s
2173
2174    #@print_log_dec
2175    def recalc_azel(self):
2176        """
2177        Recalculate the azimuth and elevation for each position.
2178        Parameters:
2179            none
2180        Example:
2181        """
2182        varlist = vars()
2183        self._recalcazel()
2184        self._add_history("recalc_azel", varlist)
2185        print_log()
2186        return
2187
2188    #@print_log_dec
2189    def __add__(self, other):
2190        """
2191        implicit on all axes and on Tsys
2192        """
2193        return self._operation( other, "ADD" )
2194
2195    #@print_log_dec
2196    def __sub__(self, other):
2197        """
2198        implicit on all axes and on Tsys
2199        """
2200        return self._operation( other, 'SUB' )
2201
2202    #@print_log_dec
2203    def __mul__(self, other):
2204        """
2205        implicit on all axes and on Tsys
2206        """
2207        return self._operation( other, 'MUL' )
2208
2209    #@print_log_dec
2210    def __div__(self, other):
2211        """
2212        implicit on all axes and on Tsys
2213        """
2214        return self._operation( other, 'DIV' )
2215
2216    def get_fit(self, row=0):
2217        """
2218        Print or return the stored fits for a row in the scantable
2219        Parameters:
2220            row:    the row which the fit has been applied to.
2221        """
2222        if row > self.nrow():
2223            return
2224        from asap.asapfit import asapfit
2225        fit = asapfit(self._getfit(row))
2226        if rcParams['verbose']:
2227            #print fit
2228            asaplog.push( '%s' %(fit) )
2229            print_log()
2230            return
2231        else:
2232            return fit.as_dict()
2233
2234    def flag_nans(self):
2235        """
2236        Utility function to flag NaN values in the scantable.
2237        """
2238        import numpy
2239        basesel = self.get_selection()
2240        for i in range(self.nrow()):
2241            sel = self.get_row_selector(i)
2242            self.set_selection(basesel+sel)
2243            nans = numpy.isnan(self._getspectrum(0))
2244        if numpy.any(nans):
2245            bnans = [ bool(v) for v in nans]
2246            self.flag(bnans)
2247        self.set_selection(basesel)
2248
2249    def get_row_selector(self, rowno):
2250        return selector(beams=self.getbeam(rowno),
2251                        ifs=self.getif(rowno),
2252                        pols=self.getpol(rowno),
2253                        scans=self.getscan(rowno),
2254                        cycles=self.getcycle(rowno))
2255
2256    def _add_history(self, funcname, parameters):
2257        if not rcParams['scantable.history']:
2258            return
2259        # create date
2260        sep = "##"
2261        from datetime import datetime
2262        dstr = datetime.now().strftime('%Y/%m/%d %H:%M:%S')
2263        hist = dstr+sep
2264        hist += funcname+sep#cdate+sep
2265        if parameters.has_key('self'): del parameters['self']
2266        for k, v in parameters.iteritems():
2267            if type(v) is dict:
2268                for k2, v2 in v.iteritems():
2269                    hist += k2
2270                    hist += "="
2271                    if isinstance(v2, scantable):
2272                        hist += 'scantable'
2273                    elif k2 == 'mask':
2274                        if isinstance(v2, list) or isinstance(v2, tuple):
2275                            hist += str(self._zip_mask(v2))
2276                        else:
2277                            hist += str(v2)
2278                    else:
2279                        hist += str(v2)
2280            else:
2281                hist += k
2282                hist += "="
2283                if isinstance(v, scantable):
2284                    hist += 'scantable'
2285                elif k == 'mask':
2286                    if isinstance(v, list) or isinstance(v, tuple):
2287                        hist += str(self._zip_mask(v))
2288                    else:
2289                        hist += str(v)
2290                else:
2291                    hist += str(v)
2292            hist += sep
2293        hist = hist[:-2] # remove trailing '##'
2294        self._addhistory(hist)
2295
2296
2297    def _zip_mask(self, mask):
2298        mask = list(mask)
2299        i = 0
2300        segments = []
2301        while mask[i:].count(1):
2302            i += mask[i:].index(1)
2303            if mask[i:].count(0):
2304                j = i + mask[i:].index(0)
2305            else:
2306                j = len(mask)
2307            segments.append([i, j])
2308            i = j
2309        return segments
2310
2311    def _get_ordinate_label(self):
2312        fu = "("+self.get_fluxunit()+")"
2313        import re
2314        lbl = "Intensity"
2315        if re.match(".K.", fu):
2316            lbl = "Brightness Temperature "+ fu
2317        elif re.match(".Jy.", fu):
2318            lbl = "Flux density "+ fu
2319        return lbl
2320
2321    def _check_ifs(self):
2322        nchans = [self.nchan(i) for i in range(self.nif(-1))]
2323        nchans = filter(lambda t: t > 0, nchans)
2324        return (sum(nchans)/len(nchans) == nchans[0])
2325
2326    def _fill(self, names, unit, average, getpt, antenna):
2327        import os
2328        from asap._asap import stfiller
2329        first = True
2330        fullnames = []
2331        for name in names:
2332            name = os.path.expandvars(name)
2333            name = os.path.expanduser(name)
2334            if not os.path.exists(name):
2335                msg = "File '%s' does not exists" % (name)
2336                if rcParams['verbose']:
2337                    asaplog.push(msg)
2338                    #print asaplog.pop().strip()
2339                    print_log( 'ERROR' )
2340                    return
2341                raise IOError(msg)
2342            fullnames.append(name)
2343        if average:
2344            asaplog.push('Auto averaging integrations')
2345        stype = int(rcParams['scantable.storage'].lower() == 'disk')
2346        for name in fullnames:
2347            tbl = Scantable(stype)
2348            r = stfiller(tbl)
2349            rx = rcParams['scantable.reference']
2350            r._setreferenceexpr(rx)
2351            msg = "Importing %s..." % (name)
2352            asaplog.push(msg, False)
2353            print_log()
2354            r._open(name, antenna, -1, -1, getpt)
2355            r._read()
2356            if average:
2357                tbl = self._math._average((tbl, ), (), 'NONE', 'SCAN')
2358            if not first:
2359                tbl = self._math._merge([self, tbl])
2360            Scantable.__init__(self, tbl)
2361            r._close()
2362            del r, tbl
2363            first = False
2364        if unit is not None:
2365            self.set_fluxunit(unit)
2366        #self.set_freqframe(rcParams['scantable.freqframe'])
2367
2368    def __getitem__(self, key):
2369        if key < 0:
2370            key += self.nrow()
2371        if key >= self.nrow():
2372            raise IndexError("Row index out of range.")
2373        return self._getspectrum(key)
2374
2375    def __setitem__(self, key, value):
2376        if key < 0:
2377            key += self.nrow()
2378        if key >= self.nrow():
2379            raise IndexError("Row index out of range.")
2380        if not hasattr(value, "__len__") or \
2381                len(value) > self.nchan(self.getif(key)):
2382            raise ValueError("Spectrum length doesn't match.")
2383        return self._setspectrum(value, key)
2384
2385    def __len__(self):
2386        return self.nrow()
2387
2388    def __iter__(self):
2389        for i in range(len(self)):
2390            yield self[i]
2391
2392    def _operation(self, other, opmode):
2393        varlist = vars()
2394        s = None
2395        import numpy
2396        if isinstance(other, scantable):
2397            s = scantable(self._math._binaryop(self.copy(), other, opmode))
2398        elif isinstance(other, float) or isinstance(other, int):
2399            if opmode == 'DIV' and float(other) == 0.0:
2400                raise ZeroDivisionError("Dividing by zero is not recommended")
2401            s = scantable(self._math._unaryop(self.copy(), other, opmode, False))
2402        elif isinstance(other, list) or isinstance(other, numpy.ndarray):
2403            if isinstance(other[0], list) or isinstance(other[0], numpy.ndarray):
2404                from asapmath import _array2dOp
2405                s = _array2dOp( self.copy(), other, opmode, False )
2406            else:
2407                s = scantable(self._math._arrayop(self.copy(), other, opmode, False))
2408        else:
2409            raise TypeError("Other input is not a scantable or float value or float list")
2410        opdic = {}
2411        opdic['ADD'] = '+'
2412        opdic['SUB'] = '-'
2413        opdic['MUL'] = '*'
2414        opdic['DIV'] = '/'
2415        s._add_history("operator %s" % opdic[opmode], varlist)
2416        print_log()
2417        return s
2418
2419       
Note: See TracBrowser for help on using the repository browser.