source: trunk/python/scantable.py @ 1950

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

New Development: No

JIRA Issue: No

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs:

Put in Release Notes: No

Module(s): scantable

Description: a modification to make scantable.get_time a bit more effective.


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