source: branches/alma/python/scantable.py @ 1627

Last change on this file since 1627 was 1627, checked in by Takeshi Nakazato, 15 years ago

New Development: No

JIRA Issue: No

Ready to Release: Yes

Interface Changes: No

What Interface Changed: Please list interface changes

Test Programs: List test programs

Put in Release Notes: No

Module(s): Module Names change impacts.

Description: Describe your changes here...

Bug fix.

asaplog.push( msg ) in the except section is not correct.
I have modified it to asaplog.push( msg.message ).


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