source: trunk/python/scantable.py @ 2013

Last change on this file since 2013 was 2013, checked in by Kana Sugimoto, 13 years ago

New Development: Yes

JIRA Issue: Yes (CAS-1425)

Ready for Test: Yes

Interface Changes: Yes

What Interface Changed: Two new methods are added to scantable class, i.e., parse_maskexpr and _parse_selection.

Test Programs:

scan=asap.scantable('FILENAME')
scan.parse_maskexpr('<2:-58~1100,2~13:<1200.;>9000.,15')

Put in Release Notes: No

Module(s): scantable

Description:

Added a new public method scantable.parse_maskexpr and an internal method
scantable._parse_selection. scantable..parse_maskexpr parses a CASA type channel
selection syntax string and returns a dictionary of valid IF (key) and masklist
combinations. See help for more details


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