source: trunk/python/scantable.py @ 2178

Last change on this file since 2178 was 2178, checked in by Malte Marquarding, 13 years ago

Removed redundant argument 'verbose'

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 128.1 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)
[2029]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):
[2178]339        return Scantable._summary(self)
[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        """
[2178]351        info = Scantable._summary(self)
[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.
[2177]1085        Flagged data in the scantable get 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
[2177]1122    def fft(self, getrealimag=False, rowno=[]):
1123        """\
1124        Apply FFT to the spectra.
1125        Flagged data in the scantable get interpolated over the region.
1126
1127        Parameters:
1128       
1129            getrealimag:    If True, returns the real and imaginary part
1130                            values of the complex results.
1131                            If False (the default), returns the amplitude
1132                            (absolute value) normalised with Ndata/2 and
1133                            phase (argument, in unit of radian).
1134
1135            rowno:          The row number(s) to be processed. int, list
1136                            and tuple are accepted. By default ([]), FFT
1137                            is applied to the whole data.
1138
1139        Returns:
1140
1141            A dictionary containing two keys, i.e., 'real' and 'imag' for
1142            getrealimag = True, or 'ampl' and 'phase' for getrealimag = False,
1143            respectively.
1144            The value for each key is a list of lists containing the FFT
1145            results from each spectrum.
1146           
1147        """
1148        if getrealimag:
1149            res = {"real":[], "imag":[]}  # return real and imaginary values
1150        else:
1151            res = {"ampl":[], "phase":[]} # return amplitude and phase(argument)
1152       
1153        if isinstance(rowno, int):
1154            rowno = [rowno]
1155        elif not (isinstance(rowno, list) or isinstance(rowno, tuple)):
1156            raise TypeError("The row number(s) should be int, list or tuple.")
1157       
1158        nrow = len(rowno)
1159        if nrow == 0: nrow = self.nrow()  # by default, calculate for all rows.
1160       
1161        fspec = self._math._fft(self, rowno, getrealimag)
1162        nspec = len(fspec)/nrow
1163       
1164        i = 0
1165        while (i < nrow):
1166            v1 = []
1167            v2 = []
1168           
1169            j = 0
1170            while (j < nspec):
1171                k = i*nspec + j
1172                v1.append(fspec[k])
1173                v2.append(fspec[k+1])
1174                j += 2
1175           
1176            if getrealimag:
1177                res["real"].append(v1)
1178                res["imag"].append(v2)
1179            else:
1180                res["ampl"].append(v1)
1181                res["phase"].append(v2)
1182           
1183            i += 1
1184
1185        return res
1186
1187    @asaplog_post_dec
[113]1188    def create_mask(self, *args, **kwargs):
[1846]1189        """\
[1118]1190        Compute and return a mask based on [min, max] windows.
[189]1191        The specified windows are to be INCLUDED, when the mask is
[113]1192        applied.
[1846]1193
[102]1194        Parameters:
[1846]1195
[1118]1196            [min, max], [min2, max2], ...
[1024]1197                Pairs of start/end points (inclusive)specifying the regions
[102]1198                to be masked
[1855]1199
[189]1200            invert:     optional argument. If specified as True,
1201                        return an inverted mask, i.e. the regions
1202                        specified are EXCLUDED
[1855]1203
[513]1204            row:        create the mask using the specified row for
1205                        unit conversions, default is row=0
1206                        only necessary if frequency varies over rows.
[1846]1207
1208        Examples::
1209
[113]1210            scan.set_unit('channel')
[1846]1211            # a)
[1118]1212            msk = scan.create_mask([400, 500], [800, 900])
[189]1213            # masks everything outside 400 and 500
[113]1214            # and 800 and 900 in the unit 'channel'
1215
[1846]1216            # b)
[1118]1217            msk = scan.create_mask([400, 500], [800, 900], invert=True)
[189]1218            # masks the regions between 400 and 500
[113]1219            # and 800 and 900 in the unit 'channel'
[1846]1220
1221            # c)
1222            #mask only channel 400
[1554]1223            msk =  scan.create_mask([400])
[1846]1224
[102]1225        """
[1554]1226        row = kwargs.get("row", 0)
[513]1227        data = self._getabcissa(row)
[113]1228        u = self._getcoordinfo()[0]
[1859]1229        if u == "":
1230            u = "channel"
1231        msg = "The current mask window unit is %s" % u
1232        i = self._check_ifs()
1233        if not i:
1234            msg += "\nThis mask is only valid for IF=%d" % (self.getif(i))
1235        asaplog.push(msg)
[102]1236        n = self.nchan()
[1295]1237        msk = _n_bools(n, False)
[710]1238        # test if args is a 'list' or a 'normal *args - UGLY!!!
1239
[1118]1240        ws = (isinstance(args[-1][-1], int) or isinstance(args[-1][-1], float)) \
1241             and args or args[0]
[710]1242        for window in ws:
[1554]1243            if len(window) == 1:
1244                window = [window[0], window[0]]
1245            if len(window) == 0 or  len(window) > 2:
1246                raise ValueError("A window needs to be defined as [start(, end)]")
[1545]1247            if window[0] > window[1]:
1248                tmp = window[0]
1249                window[0] = window[1]
1250                window[1] = tmp
[102]1251            for i in range(n):
[1024]1252                if data[i] >= window[0] and data[i] <= window[1]:
[1295]1253                    msk[i] = True
[113]1254        if kwargs.has_key('invert'):
1255            if kwargs.get('invert'):
[1295]1256                msk = mask_not(msk)
[102]1257        return msk
[710]1258
[1931]1259    def get_masklist(self, mask=None, row=0, silent=False):
[1846]1260        """\
[1819]1261        Compute and return a list of mask windows, [min, max].
[1846]1262
[1819]1263        Parameters:
[1846]1264
[1819]1265            mask:       channel mask, created with create_mask.
[1855]1266
[1819]1267            row:        calcutate the masklist using the specified row
1268                        for unit conversions, default is row=0
1269                        only necessary if frequency varies over rows.
[1846]1270
[1819]1271        Returns:
[1846]1272
[1819]1273            [min, max], [min2, max2], ...
1274                Pairs of start/end points (inclusive)specifying
1275                the masked regions
[1846]1276
[1819]1277        """
1278        if not (isinstance(mask,list) or isinstance(mask, tuple)):
1279            raise TypeError("The mask should be list or tuple.")
1280        if len(mask) < 2:
1281            raise TypeError("The mask elements should be > 1")
1282        if self.nchan() != len(mask):
1283            msg = "Number of channels in scantable != number of mask elements"
1284            raise TypeError(msg)
1285        data = self._getabcissa(row)
1286        u = self._getcoordinfo()[0]
[1859]1287        if u == "":
1288            u = "channel"
1289        msg = "The current mask window unit is %s" % u
1290        i = self._check_ifs()
1291        if not i:
1292            msg += "\nThis mask is only valid for IF=%d" % (self.getif(i))
[1931]1293        if not silent:
1294            asaplog.push(msg)
[1819]1295        masklist=[]
1296        ist, ien = None, None
1297        ist, ien=self.get_mask_indices(mask)
1298        if ist is not None and ien is not None:
1299            for i in xrange(len(ist)):
1300                range=[data[ist[i]],data[ien[i]]]
1301                range.sort()
1302                masklist.append([range[0],range[1]])
1303        return masklist
1304
1305    def get_mask_indices(self, mask=None):
[1846]1306        """\
[1819]1307        Compute and Return lists of mask start indices and mask end indices.
[1855]1308
1309        Parameters:
1310
[1819]1311            mask:       channel mask, created with create_mask.
[1846]1312
[1819]1313        Returns:
[1846]1314
[1819]1315            List of mask start indices and that of mask end indices,
1316            i.e., [istart1,istart2,....], [iend1,iend2,....].
[1846]1317
[1819]1318        """
1319        if not (isinstance(mask,list) or isinstance(mask, tuple)):
1320            raise TypeError("The mask should be list or tuple.")
1321        if len(mask) < 2:
1322            raise TypeError("The mask elements should be > 1")
1323        istart=[]
1324        iend=[]
1325        if mask[0]: istart.append(0)
1326        for i in range(len(mask)-1):
1327            if not mask[i] and mask[i+1]:
1328                istart.append(i+1)
1329            elif mask[i] and not mask[i+1]:
1330                iend.append(i)
1331        if mask[len(mask)-1]: iend.append(len(mask)-1)
1332        if len(istart) != len(iend):
1333            raise RuntimeError("Numbers of mask start != mask end.")
1334        for i in range(len(istart)):
1335            if istart[i] > iend[i]:
1336                raise RuntimeError("Mask start index > mask end index")
1337                break
1338        return istart,iend
1339
[2013]1340    @asaplog_post_dec
1341    def parse_maskexpr(self,maskstring):
1342        """
1343        Parse CASA type mask selection syntax (IF dependent).
1344
1345        Parameters:
1346            maskstring : A string mask selection expression.
1347                         A comma separated selections mean different IF -
1348                         channel combinations. IFs and channel selections
1349                         are partitioned by a colon, ':'.
1350                     examples:
[2015]1351                         ''          = all IFs (all channels)
[2013]1352                         '<2,4~6,9'  = IFs 0,1,4,5,6,9 (all channels)
1353                         '3:3~45;60' = channels 3 to 45 and 60 in IF 3
1354                         '0~1:2~6,8' = channels 2 to 6 in IFs 0,1, and
1355                                       all channels in IF8
1356        Returns:
1357        A dictionary of selected (valid) IF and masklist pairs,
1358        e.g. {'0': [[50,250],[350,462]], '2': [[100,400],[550,974]]}
1359        """
1360        if not isinstance(maskstring,str):
1361            asaplog.post()
1362            asaplog.push("Invalid mask expression")
1363            asaplog.post("ERROR")
1364       
1365        valid_ifs = self.getifnos()
1366        frequnit = self.get_unit()
1367        seldict = {}
[2015]1368        if maskstring == "":
1369            maskstring = str(valid_ifs)[1:-1]
[2013]1370        ## split each selection
1371        sellist = maskstring.split(',')
1372        for currselstr in sellist:
1373            selset = currselstr.split(':')
1374            # spw and mask string (may include ~, < or >)
1375            spwmasklist = self._parse_selection(selset[0],typestr='integer',
1376                                               offset=1,minval=min(valid_ifs),
1377                                               maxval=max(valid_ifs))
1378            for spwlist in spwmasklist:
1379                selspws = []
1380                for ispw in range(spwlist[0],spwlist[1]+1):
1381                    # Put into the list only if ispw exists
1382                    if valid_ifs.count(ispw):
1383                        selspws.append(ispw)
1384            del spwmasklist, spwlist
1385
1386            # parse frequency mask list
1387            if len(selset) > 1:
1388                freqmasklist = self._parse_selection(selset[1],typestr='float',
1389                                                    offset=0.)
1390            else:
1391                # want to select the whole spectrum
1392                freqmasklist = [None]
1393
1394            ## define a dictionary of spw - masklist combination
1395            for ispw in selspws:
1396                #print "working on", ispw
1397                spwstr = str(ispw)
1398                if len(selspws) == 0:
1399                    # empty spw
1400                    continue
1401                else:
1402                    ## want to get min and max of the spw and
1403                    ## offset to set for '<' and '>'
1404                    if frequnit == 'channel':
1405                        minfreq = 0
1406                        maxfreq = self.nchan(ifno=ispw)
1407                        offset = 0.5
1408                    else:
1409                        ## This is ugly part. need improvement
1410                        for ifrow in xrange(self.nrow()):
1411                            if self.getif(ifrow) == ispw:
1412                                #print "IF",ispw,"found in row =",ifrow
1413                                break
1414                        freqcoord = self.get_coordinate(ifrow)
1415                        freqs = self._getabcissa(ifrow)
1416                        minfreq = min(freqs)
1417                        maxfreq = max(freqs)
1418                        if len(freqs) == 1:
1419                            offset = 0.5
1420                        elif frequnit.find('Hz') > 0:
1421                            offset = abs(freqcoord.to_frequency(1,unit=frequnit)
1422                                      -freqcoord.to_frequency(0,unit=frequnit))*0.5
1423                        elif frequnit.find('m/s') > 0:
1424                            offset = abs(freqcoord.to_velocity(1,unit=frequnit)
1425                                      -freqcoord.to_velocity(0,unit=frequnit))*0.5
1426                        else:
1427                            asaplog.post()
1428                            asaplog.push("Invalid frequency unit")
1429                            asaplog.post("ERROR")
1430                        del freqs, freqcoord, ifrow
1431                    for freq in freqmasklist:
1432                        selmask = freq or [minfreq, maxfreq]
1433                        if selmask[0] == None:
1434                            ## selection was "<freq[1]".
1435                            if selmask[1] < minfreq:
1436                                ## avoid adding region selection
1437                                selmask = None
1438                            else:
1439                                selmask = [minfreq,selmask[1]-offset]
1440                        elif selmask[1] == None:
1441                            ## selection was ">freq[0]"
1442                            if selmask[0] > maxfreq:
1443                                ## avoid adding region selection
1444                                selmask = None
1445                            else:
1446                                selmask = [selmask[0]+offset,maxfreq]
1447                        if selmask:
1448                            if not seldict.has_key(spwstr):
1449                                # new spw selection
1450                                seldict[spwstr] = []
1451                            seldict[spwstr] += [selmask]
1452                    del minfreq,maxfreq,offset,freq,selmask
1453                del spwstr
1454            del freqmasklist
1455        del valid_ifs
1456        if len(seldict) == 0:
1457            asaplog.post()
1458            asaplog.push("No valid selection in the mask expression: "+maskstring)
1459            asaplog.post("WARN")
1460            return None
1461        msg = "Selected masklist:\n"
1462        for sif, lmask in seldict.iteritems():
1463            msg += "   IF"+sif+" - "+str(lmask)+"\n"
1464        asaplog.push(msg)
1465        return seldict
1466
1467    def _parse_selection(self,selstr,typestr='float',offset=0.,minval=None,maxval=None):
1468        """
1469        Parameters:
1470            selstr :    The Selection string, e.g., '<3;5~7;100~103;9'
1471            typestr :   The type of the values in returned list
1472                        ('integer' or 'float')
1473            offset :    The offset value to subtract from or add to
1474                        the boundary value if the selection string
1475                        includes '<' or '>'
1476            minval, maxval :  The minimum/maximum values to set if the
1477                              selection string includes '<' or '>'.
1478                              The list element is filled with None by default.
1479        Returns:
1480            A list of min/max pair of selections.
1481        Example:
1482            _parseSelection('<3;5~7;9',typestr='int',offset=1,minval=0)
1483            returns [[0,2],[5,7],[9,9]]
1484        """
1485        selgroups = selstr.split(';')
1486        sellists = []
1487        if typestr.lower().startswith('int'):
1488            formatfunc = int
1489        else:
1490            formatfunc = float
1491       
1492        for currsel in  selgroups:
1493            if currsel.find('~') > 0:
1494                minsel = formatfunc(currsel.split('~')[0].strip())
1495                maxsel = formatfunc(currsel.split('~')[1].strip())
1496            elif currsel.strip().startswith('<'):
1497                minsel = minval
1498                maxsel = formatfunc(currsel.split('<')[1].strip()) \
1499                         - formatfunc(offset)
1500            elif currsel.strip().startswith('>'):
1501                minsel = formatfunc(currsel.split('>')[1].strip()) \
1502                         + formatfunc(offset)
1503                maxsel = maxval
1504            else:
1505                minsel = formatfunc(currsel)
1506                maxsel = formatfunc(currsel)
1507            sellists.append([minsel,maxsel])
1508        return sellists
1509
[1819]1510#    def get_restfreqs(self):
1511#        """
1512#        Get the restfrequency(s) stored in this scantable.
1513#        The return value(s) are always of unit 'Hz'
1514#        Parameters:
1515#            none
1516#        Returns:
1517#            a list of doubles
1518#        """
1519#        return list(self._getrestfreqs())
1520
1521    def get_restfreqs(self, ids=None):
[1846]1522        """\
[256]1523        Get the restfrequency(s) stored in this scantable.
1524        The return value(s) are always of unit 'Hz'
[1846]1525
[256]1526        Parameters:
[1846]1527
[1819]1528            ids: (optional) a list of MOLECULE_ID for that restfrequency(s) to
1529                 be retrieved
[1846]1530
[256]1531        Returns:
[1846]1532
[1819]1533            dictionary containing ids and a list of doubles for each id
[1846]1534
[256]1535        """
[1819]1536        if ids is None:
1537            rfreqs={}
1538            idlist = self.getmolnos()
1539            for i in idlist:
1540                rfreqs[i]=list(self._getrestfreqs(i))
1541            return rfreqs
1542        else:
1543            if type(ids)==list or type(ids)==tuple:
1544                rfreqs={}
1545                for i in ids:
1546                    rfreqs[i]=list(self._getrestfreqs(i))
1547                return rfreqs
1548            else:
1549                return list(self._getrestfreqs(ids))
1550            #return list(self._getrestfreqs(ids))
[102]1551
[931]1552    def set_restfreqs(self, freqs=None, unit='Hz'):
[1846]1553        """\
[931]1554        Set or replace the restfrequency specified and
[1938]1555        if the 'freqs' argument holds a scalar,
[931]1556        then that rest frequency will be applied to all the selected
1557        data.  If the 'freqs' argument holds
1558        a vector, then it MUST be of equal or smaller length than
1559        the number of IFs (and the available restfrequencies will be
1560        replaced by this vector).  In this case, *all* data have
1561        the restfrequency set per IF according
1562        to the corresponding value you give in the 'freqs' vector.
[1118]1563        E.g. 'freqs=[1e9, 2e9]'  would mean IF 0 gets restfreq 1e9 and
[931]1564        IF 1 gets restfreq 2e9.
[1846]1565
[1395]1566        You can also specify the frequencies via a linecatalog.
[1153]1567
[931]1568        Parameters:
[1846]1569
[931]1570            freqs:   list of rest frequency values or string idenitfiers
[1855]1571
[931]1572            unit:    unit for rest frequency (default 'Hz')
[402]1573
[1846]1574
1575        Example::
1576
[1819]1577            # set the given restfrequency for the all currently selected IFs
[931]1578            scan.set_restfreqs(freqs=1.4e9)
[1845]1579            # set restfrequencies for the n IFs  (n > 1) in the order of the
1580            # list, i.e
1581            # IF0 -> 1.4e9, IF1 ->  1.41e9, IF3 -> 1.42e9
1582            # len(list_of_restfreqs) == nIF
1583            # for nIF == 1 the following will set multiple restfrequency for
1584            # that IF
[1819]1585            scan.set_restfreqs(freqs=[1.4e9, 1.41e9, 1.42e9])
[1845]1586            # set multiple restfrequencies per IF. as a list of lists where
1587            # the outer list has nIF elements, the inner s arbitrary
1588            scan.set_restfreqs(freqs=[[1.4e9, 1.41e9], [1.67e9]])
[391]1589
[1846]1590       *Note*:
[1845]1591
[931]1592            To do more sophisticate Restfrequency setting, e.g. on a
1593            source and IF basis, use scantable.set_selection() before using
[1846]1594            this function::
[931]1595
[1846]1596                # provided your scantable is called scan
1597                selection = selector()
1598                selection.set_name("ORION*")
1599                selection.set_ifs([1])
1600                scan.set_selection(selection)
1601                scan.set_restfreqs(freqs=86.6e9)
1602
[931]1603        """
1604        varlist = vars()
[1157]1605        from asap import linecatalog
1606        # simple  value
[1118]1607        if isinstance(freqs, int) or isinstance(freqs, float):
[1845]1608            self._setrestfreqs([freqs], [""], unit)
[1157]1609        # list of values
[1118]1610        elif isinstance(freqs, list) or isinstance(freqs, tuple):
[1157]1611            # list values are scalars
[1118]1612            if isinstance(freqs[-1], int) or isinstance(freqs[-1], float):
[1845]1613                if len(freqs) == 1:
1614                    self._setrestfreqs(freqs, [""], unit)
1615                else:
1616                    # allow the 'old' mode of setting mulitple IFs
1617                    sel = selector()
1618                    savesel = self._getselection()
1619                    iflist = self.getifnos()
1620                    if len(freqs)>len(iflist):
1621                        raise ValueError("number of elements in list of list "
1622                                         "exeeds the current IF selections")
1623                    iflist = self.getifnos()
1624                    for i, fval in enumerate(freqs):
1625                        sel.set_ifs(iflist[i])
1626                        self._setselection(sel)
1627                        self._setrestfreqs([fval], [""], unit)
1628                    self._setselection(savesel)
1629
1630            # list values are dict, {'value'=, 'name'=)
[1157]1631            elif isinstance(freqs[-1], dict):
[1845]1632                values = []
1633                names = []
1634                for d in freqs:
1635                    values.append(d["value"])
1636                    names.append(d["name"])
1637                self._setrestfreqs(values, names, unit)
[1819]1638            elif isinstance(freqs[-1], list) or isinstance(freqs[-1], tuple):
[1157]1639                sel = selector()
1640                savesel = self._getselection()
[1322]1641                iflist = self.getifnos()
[1819]1642                if len(freqs)>len(iflist):
[1845]1643                    raise ValueError("number of elements in list of list exeeds"
1644                                     " the current IF selections")
1645                for i, fval in enumerate(freqs):
[1322]1646                    sel.set_ifs(iflist[i])
[1259]1647                    self._setselection(sel)
[1845]1648                    self._setrestfreqs(fval, [""], unit)
[1157]1649                self._setselection(savesel)
1650        # freqs are to be taken from a linecatalog
[1153]1651        elif isinstance(freqs, linecatalog):
1652            sel = selector()
1653            savesel = self._getselection()
1654            for i in xrange(freqs.nrow()):
[1322]1655                sel.set_ifs(iflist[i])
[1153]1656                self._setselection(sel)
[1845]1657                self._setrestfreqs([freqs.get_frequency(i)],
1658                                   [freqs.get_name(i)], "MHz")
[1153]1659                # ensure that we are not iterating past nIF
1660                if i == self.nif()-1: break
1661            self._setselection(savesel)
[931]1662        else:
1663            return
1664        self._add_history("set_restfreqs", varlist)
1665
[1360]1666    def shift_refpix(self, delta):
[1846]1667        """\
[1589]1668        Shift the reference pixel of the Spectra Coordinate by an
1669        integer amount.
[1846]1670
[1589]1671        Parameters:
[1846]1672
[1589]1673            delta:   the amount to shift by
[1846]1674
1675        *Note*:
1676
[1589]1677            Be careful using this with broadband data.
[1846]1678
[1360]1679        """
[1731]1680        Scantable.shift_refpix(self, delta)
[931]1681
[1862]1682    @asaplog_post_dec
[1259]1683    def history(self, filename=None):
[1846]1684        """\
[1259]1685        Print the history. Optionally to a file.
[1846]1686
[1348]1687        Parameters:
[1846]1688
[1928]1689            filename:    The name of the file to save the history to.
[1846]1690
[1259]1691        """
[484]1692        hist = list(self._gethistory())
[794]1693        out = "-"*80
[484]1694        for h in hist:
[489]1695            if h.startswith("---"):
[1857]1696                out = "\n".join([out, h])
[489]1697            else:
1698                items = h.split("##")
1699                date = items[0]
1700                func = items[1]
1701                items = items[2:]
[794]1702                out += "\n"+date+"\n"
1703                out += "Function: %s\n  Parameters:" % (func)
[489]1704                for i in items:
[1938]1705                    if i == '':
1706                        continue
[489]1707                    s = i.split("=")
[1118]1708                    out += "\n   %s = %s" % (s[0], s[1])
[1857]1709                out = "\n".join([out, "-"*80])
[1259]1710        if filename is not None:
1711            if filename is "":
1712                filename = 'scantable_history.txt'
1713            import os
1714            filename = os.path.expandvars(os.path.expanduser(filename))
1715            if not os.path.isdir(filename):
1716                data = open(filename, 'w')
1717                data.write(out)
1718                data.close()
1719            else:
1720                msg = "Illegal file name '%s'." % (filename)
[1859]1721                raise IOError(msg)
1722        return page(out)
[513]1723    #
1724    # Maths business
1725    #
[1862]1726    @asaplog_post_dec
[931]1727    def average_time(self, mask=None, scanav=False, weight='tint', align=False):
[1846]1728        """\
[1070]1729        Return the (time) weighted average of a scan.
[1846]1730
1731        *Note*:
1732
[1070]1733            in channels only - align if necessary
[1846]1734
[513]1735        Parameters:
[1846]1736
[513]1737            mask:     an optional mask (only used for 'var' and 'tsys'
1738                      weighting)
[1855]1739
[558]1740            scanav:   True averages each scan separately
1741                      False (default) averages all scans together,
[1855]1742
[1099]1743            weight:   Weighting scheme.
1744                      'none'     (mean no weight)
1745                      'var'      (1/var(spec) weighted)
1746                      'tsys'     (1/Tsys**2 weighted)
1747                      'tint'     (integration time weighted)
1748                      'tintsys'  (Tint/Tsys**2)
1749                      'median'   ( median averaging)
[535]1750                      The default is 'tint'
[1855]1751
[931]1752            align:    align the spectra in velocity before averaging. It takes
1753                      the time of the first spectrum as reference time.
[1846]1754
1755        Example::
1756
[513]1757            # time average the scantable without using a mask
[710]1758            newscan = scan.average_time()
[1846]1759
[513]1760        """
1761        varlist = vars()
[1593]1762        weight = weight or 'TINT'
1763        mask = mask or ()
1764        scanav = (scanav and 'SCAN') or 'NONE'
[1118]1765        scan = (self, )
[1859]1766
1767        if align:
1768            scan = (self.freq_align(insitu=False), )
1769        s = None
1770        if weight.upper() == 'MEDIAN':
1771            s = scantable(self._math._averagechannel(scan[0], 'MEDIAN',
1772                                                     scanav))
1773        else:
1774            s = scantable(self._math._average(scan, mask, weight.upper(),
1775                          scanav))
[1099]1776        s._add_history("average_time", varlist)
[513]1777        return s
[710]1778
[1862]1779    @asaplog_post_dec
[876]1780    def convert_flux(self, jyperk=None, eta=None, d=None, insitu=None):
[1846]1781        """\
[513]1782        Return a scan where all spectra are converted to either
1783        Jansky or Kelvin depending upon the flux units of the scan table.
1784        By default the function tries to look the values up internally.
1785        If it can't find them (or if you want to over-ride), you must
1786        specify EITHER jyperk OR eta (and D which it will try to look up
1787        also if you don't set it). jyperk takes precedence if you set both.
[1846]1788
[513]1789        Parameters:
[1846]1790
[513]1791            jyperk:      the Jy / K conversion factor
[1855]1792
[513]1793            eta:         the aperture efficiency
[1855]1794
[1928]1795            d:           the geometric diameter (metres)
[1855]1796
[513]1797            insitu:      if False a new scantable is returned.
1798                         Otherwise, the scaling is done in-situ
1799                         The default is taken from .asaprc (False)
[1846]1800
[513]1801        """
1802        if insitu is None: insitu = rcParams['insitu']
[876]1803        self._math._setinsitu(insitu)
[513]1804        varlist = vars()
[1593]1805        jyperk = jyperk or -1.0
1806        d = d or -1.0
1807        eta = eta or -1.0
[876]1808        s = scantable(self._math._convertflux(self, d, eta, jyperk))
1809        s._add_history("convert_flux", varlist)
1810        if insitu: self._assign(s)
1811        else: return s
[513]1812
[1862]1813    @asaplog_post_dec
[876]1814    def gain_el(self, poly=None, filename="", method="linear", insitu=None):
[1846]1815        """\
[513]1816        Return a scan after applying a gain-elevation correction.
1817        The correction can be made via either a polynomial or a
1818        table-based interpolation (and extrapolation if necessary).
1819        You specify polynomial coefficients, an ascii table or neither.
1820        If you specify neither, then a polynomial correction will be made
1821        with built in coefficients known for certain telescopes (an error
1822        will occur if the instrument is not known).
1823        The data and Tsys are *divided* by the scaling factors.
[1846]1824
[513]1825        Parameters:
[1846]1826
[513]1827            poly:        Polynomial coefficients (default None) to compute a
1828                         gain-elevation correction as a function of
1829                         elevation (in degrees).
[1855]1830
[513]1831            filename:    The name of an ascii file holding correction factors.
1832                         The first row of the ascii file must give the column
1833                         names and these MUST include columns
1834                         "ELEVATION" (degrees) and "FACTOR" (multiply data
1835                         by this) somewhere.
1836                         The second row must give the data type of the
1837                         column. Use 'R' for Real and 'I' for Integer.
1838                         An example file would be
1839                         (actual factors are arbitrary) :
1840
1841                         TIME ELEVATION FACTOR
1842                         R R R
1843                         0.1 0 0.8
1844                         0.2 20 0.85
1845                         0.3 40 0.9
1846                         0.4 60 0.85
1847                         0.5 80 0.8
1848                         0.6 90 0.75
[1855]1849
[513]1850            method:      Interpolation method when correcting from a table.
1851                         Values are  "nearest", "linear" (default), "cubic"
1852                         and "spline"
[1855]1853
[513]1854            insitu:      if False a new scantable is returned.
1855                         Otherwise, the scaling is done in-situ
1856                         The default is taken from .asaprc (False)
[1846]1857
[513]1858        """
1859
1860        if insitu is None: insitu = rcParams['insitu']
[876]1861        self._math._setinsitu(insitu)
[513]1862        varlist = vars()
[1593]1863        poly = poly or ()
[513]1864        from os.path import expandvars
1865        filename = expandvars(filename)
[876]1866        s = scantable(self._math._gainel(self, poly, filename, method))
1867        s._add_history("gain_el", varlist)
[1593]1868        if insitu:
1869            self._assign(s)
1870        else:
1871            return s
[710]1872
[1862]1873    @asaplog_post_dec
[931]1874    def freq_align(self, reftime=None, method='cubic', insitu=None):
[1846]1875        """\
[513]1876        Return a scan where all rows have been aligned in frequency/velocity.
1877        The alignment frequency frame (e.g. LSRK) is that set by function
1878        set_freqframe.
[1846]1879
[513]1880        Parameters:
[1855]1881
[513]1882            reftime:     reference time to align at. By default, the time of
1883                         the first row of data is used.
[1855]1884
[513]1885            method:      Interpolation method for regridding the spectra.
1886                         Choose from "nearest", "linear", "cubic" (default)
1887                         and "spline"
[1855]1888
[513]1889            insitu:      if False a new scantable is returned.
1890                         Otherwise, the scaling is done in-situ
1891                         The default is taken from .asaprc (False)
[1846]1892
[513]1893        """
[931]1894        if insitu is None: insitu = rcParams["insitu"]
[876]1895        self._math._setinsitu(insitu)
[513]1896        varlist = vars()
[1593]1897        reftime = reftime or ""
[931]1898        s = scantable(self._math._freq_align(self, reftime, method))
[876]1899        s._add_history("freq_align", varlist)
1900        if insitu: self._assign(s)
1901        else: return s
[513]1902
[1862]1903    @asaplog_post_dec
[1725]1904    def opacity(self, tau=None, insitu=None):
[1846]1905        """\
[513]1906        Apply an opacity correction. The data
1907        and Tsys are multiplied by the correction factor.
[1846]1908
[513]1909        Parameters:
[1855]1910
[1689]1911            tau:         (list of) opacity from which the correction factor is
[513]1912                         exp(tau*ZD)
[1689]1913                         where ZD is the zenith-distance.
1914                         If a list is provided, it has to be of length nIF,
1915                         nIF*nPol or 1 and in order of IF/POL, e.g.
1916                         [opif0pol0, opif0pol1, opif1pol0 ...]
[1725]1917                         if tau is `None` the opacities are determined from a
1918                         model.
[1855]1919
[513]1920            insitu:      if False a new scantable is returned.
1921                         Otherwise, the scaling is done in-situ
1922                         The default is taken from .asaprc (False)
[1846]1923
[513]1924        """
1925        if insitu is None: insitu = rcParams['insitu']
[876]1926        self._math._setinsitu(insitu)
[513]1927        varlist = vars()
[1689]1928        if not hasattr(tau, "__len__"):
1929            tau = [tau]
[876]1930        s = scantable(self._math._opacity(self, tau))
1931        s._add_history("opacity", varlist)
1932        if insitu: self._assign(s)
1933        else: return s
[513]1934
[1862]1935    @asaplog_post_dec
[513]1936    def bin(self, width=5, insitu=None):
[1846]1937        """\
[513]1938        Return a scan where all spectra have been binned up.
[1846]1939
[1348]1940        Parameters:
[1846]1941
[513]1942            width:       The bin width (default=5) in pixels
[1855]1943
[513]1944            insitu:      if False a new scantable is returned.
1945                         Otherwise, the scaling is done in-situ
1946                         The default is taken from .asaprc (False)
[1846]1947
[513]1948        """
1949        if insitu is None: insitu = rcParams['insitu']
[876]1950        self._math._setinsitu(insitu)
[513]1951        varlist = vars()
[876]1952        s = scantable(self._math._bin(self, width))
[1118]1953        s._add_history("bin", varlist)
[1589]1954        if insitu:
1955            self._assign(s)
1956        else:
1957            return s
[513]1958
[1862]1959    @asaplog_post_dec
[513]1960    def resample(self, width=5, method='cubic', insitu=None):
[1846]1961        """\
[1348]1962        Return a scan where all spectra have been binned up.
[1573]1963
[1348]1964        Parameters:
[1846]1965
[513]1966            width:       The bin width (default=5) in pixels
[1855]1967
[513]1968            method:      Interpolation method when correcting from a table.
1969                         Values are  "nearest", "linear", "cubic" (default)
1970                         and "spline"
[1855]1971
[513]1972            insitu:      if False a new scantable is returned.
1973                         Otherwise, the scaling is done in-situ
1974                         The default is taken from .asaprc (False)
[1846]1975
[513]1976        """
1977        if insitu is None: insitu = rcParams['insitu']
[876]1978        self._math._setinsitu(insitu)
[513]1979        varlist = vars()
[876]1980        s = scantable(self._math._resample(self, method, width))
[1118]1981        s._add_history("resample", varlist)
[876]1982        if insitu: self._assign(s)
1983        else: return s
[513]1984
[1862]1985    @asaplog_post_dec
[946]1986    def average_pol(self, mask=None, weight='none'):
[1846]1987        """\
[946]1988        Average the Polarisations together.
[1846]1989
[946]1990        Parameters:
[1846]1991
[946]1992            mask:        An optional mask defining the region, where the
1993                         averaging will be applied. The output will have all
1994                         specified points masked.
[1855]1995
[946]1996            weight:      Weighting scheme. 'none' (default), 'var' (1/var(spec)
1997                         weighted), or 'tsys' (1/Tsys**2 weighted)
[1846]1998
[946]1999        """
2000        varlist = vars()
[1593]2001        mask = mask or ()
[1010]2002        s = scantable(self._math._averagepol(self, mask, weight.upper()))
[1118]2003        s._add_history("average_pol", varlist)
[992]2004        return s
[513]2005
[1862]2006    @asaplog_post_dec
[1145]2007    def average_beam(self, mask=None, weight='none'):
[1846]2008        """\
[1145]2009        Average the Beams together.
[1846]2010
[1145]2011        Parameters:
2012            mask:        An optional mask defining the region, where the
2013                         averaging will be applied. The output will have all
2014                         specified points masked.
[1855]2015
[1145]2016            weight:      Weighting scheme. 'none' (default), 'var' (1/var(spec)
2017                         weighted), or 'tsys' (1/Tsys**2 weighted)
[1846]2018
[1145]2019        """
2020        varlist = vars()
[1593]2021        mask = mask or ()
[1145]2022        s = scantable(self._math._averagebeams(self, mask, weight.upper()))
2023        s._add_history("average_beam", varlist)
2024        return s
2025
[1586]2026    def parallactify(self, pflag):
[1846]2027        """\
[1843]2028        Set a flag to indicate whether this data should be treated as having
[1617]2029        been 'parallactified' (total phase == 0.0)
[1846]2030
[1617]2031        Parameters:
[1855]2032
[1843]2033            pflag:  Bool indicating whether to turn this on (True) or
[1617]2034                    off (False)
[1846]2035
[1617]2036        """
[1586]2037        varlist = vars()
2038        self._parallactify(pflag)
2039        self._add_history("parallactify", varlist)
2040
[1862]2041    @asaplog_post_dec
[992]2042    def convert_pol(self, poltype=None):
[1846]2043        """\
[992]2044        Convert the data to a different polarisation type.
[1565]2045        Note that you will need cross-polarisation terms for most conversions.
[1846]2046
[992]2047        Parameters:
[1855]2048
[992]2049            poltype:    The new polarisation type. Valid types are:
[1565]2050                        "linear", "circular", "stokes" and "linpol"
[1846]2051
[992]2052        """
2053        varlist = vars()
[1859]2054        s = scantable(self._math._convertpol(self, poltype))
[1118]2055        s._add_history("convert_pol", varlist)
[992]2056        return s
2057
[1862]2058    @asaplog_post_dec
[1819]2059    def smooth(self, kernel="hanning", width=5.0, order=2, plot=False, insitu=None):
[1846]2060        """\
[513]2061        Smooth the spectrum by the specified kernel (conserving flux).
[1846]2062
[513]2063        Parameters:
[1846]2064
[513]2065            kernel:     The type of smoothing kernel. Select from
[1574]2066                        'hanning' (default), 'gaussian', 'boxcar', 'rmedian'
2067                        or 'poly'
[1855]2068
[513]2069            width:      The width of the kernel in pixels. For hanning this is
2070                        ignored otherwise it defauls to 5 pixels.
2071                        For 'gaussian' it is the Full Width Half
2072                        Maximum. For 'boxcar' it is the full width.
[1574]2073                        For 'rmedian' and 'poly' it is the half width.
[1855]2074
[1574]2075            order:      Optional parameter for 'poly' kernel (default is 2), to
2076                        specify the order of the polnomial. Ignored by all other
2077                        kernels.
[1855]2078
[1819]2079            plot:       plot the original and the smoothed spectra.
2080                        In this each indivual fit has to be approved, by
2081                        typing 'y' or 'n'
[1855]2082
[513]2083            insitu:     if False a new scantable is returned.
2084                        Otherwise, the scaling is done in-situ
2085                        The default is taken from .asaprc (False)
[1846]2086
[513]2087        """
2088        if insitu is None: insitu = rcParams['insitu']
[876]2089        self._math._setinsitu(insitu)
[513]2090        varlist = vars()
[1819]2091
2092        if plot: orgscan = self.copy()
2093
[1574]2094        s = scantable(self._math._smooth(self, kernel.lower(), width, order))
[876]2095        s._add_history("smooth", varlist)
[1819]2096
2097        if plot:
[2150]2098            from asap.asapplotter import new_asaplot
2099            theplot = new_asaplot(rcParams['plotter.gui'])
2100            theplot.set_panels()
[1819]2101            ylab=s._get_ordinate_label()
[2150]2102            #theplot.palette(0,["#777777","red"])
[1819]2103            for r in xrange(s.nrow()):
2104                xsm=s._getabcissa(r)
2105                ysm=s._getspectrum(r)
2106                xorg=orgscan._getabcissa(r)
2107                yorg=orgscan._getspectrum(r)
[2150]2108                theplot.clear()
2109                theplot.hold()
2110                theplot.set_axes('ylabel',ylab)
2111                theplot.set_axes('xlabel',s._getabcissalabel(r))
2112                theplot.set_axes('title',s._getsourcename(r))
2113                theplot.set_line(label='Original',color="#777777")
2114                theplot.plot(xorg,yorg)
2115                theplot.set_line(label='Smoothed',color="red")
2116                theplot.plot(xsm,ysm)
[1819]2117                ### Ugly part for legend
2118                for i in [0,1]:
[2150]2119                    theplot.subplots[0]['lines'].append([theplot.subplots[0]['axes'].lines[i]])
2120                theplot.release()
[1819]2121                ### Ugly part for legend
[2150]2122                theplot.subplots[0]['lines']=[]
[1819]2123                res = raw_input("Accept smoothing ([y]/n): ")
2124                if res.upper() == 'N':
2125                    s._setspectrum(yorg, r)
[2150]2126            theplot.quit()
2127            del theplot
[1819]2128            del orgscan
2129
[876]2130        if insitu: self._assign(s)
2131        else: return s
[513]2132
[2012]2133
[1862]2134    @asaplog_post_dec
[2081]2135    def sinusoid_baseline(self, insitu=None, mask=None, nwave=None, maxwavelength=None,
2136                          clipthresh=None, clipniter=None, plot=None, getresidual=None, outlog=None, blfile=None):
[2047]2137        """\
[2094]2138        Return a scan which has been baselined (all rows) with sinusoidal functions.
[2047]2139        Parameters:
[2081]2140            insitu:        If False a new scantable is returned.
2141                           Otherwise, the scaling is done in-situ
2142                           The default is taken from .asaprc (False)
2143            mask:          An optional mask
2144            nwave:         the maximum wave number of sinusoids within
2145                           maxwavelength * (spectral range).
2146                           The default is 3 (i.e., sinusoids with wave
2147                           number of 0(=constant), 1, 2, and 3 are
2148                           used for fitting). Also it is possible to
2149                           explicitly specify all the wave numbers to
2150                           be used, by giving a list including them
2151                           (e.g. [0,1,2,15,16]).
2152            maxwavelength: the longest sinusoidal wavelength. The
2153                           default is 1.0 (unit: spectral range).
2154            clipthresh:    Clipping threshold. (default is 3.0, unit: sigma)
[2129]2155            clipniter:     maximum number of iteration of 'clipthresh'-sigma clipping (default is 0)
[2081]2156            plot:      *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
2157                           plot the fit and the residual. In this each
2158                           indivual fit has to be approved, by typing 'y'
2159                           or 'n'
2160            getresidual:   if False, returns best-fit values instead of
2161                           residual. (default is True)
2162            outlog:        Output the coefficients of the best-fit
2163                           function to logger (default is False)
2164            blfile:        Name of a text file in which the best-fit
2165                           parameter values to be written
2166                           (default is "": no file/logger output)
[2047]2167
2168        Example:
2169            # return a scan baselined by a combination of sinusoidal curves having
[2081]2170            # wave numbers in spectral window up to 10,
[2047]2171            # also with 3-sigma clipping, iteration up to 4 times
[2081]2172            bscan = scan.sinusoid_baseline(nwave=10,clipthresh=3.0,clipniter=4)
2173
2174        Note:
2175            The best-fit parameter values output in logger and/or blfile are now
2176            based on specunit of 'channel'.
[2047]2177        """
2178       
2179        varlist = vars()
2180       
2181        if insitu is None: insitu = rcParams["insitu"]
2182        if insitu:
2183            workscan = self
2184        else:
2185            workscan = self.copy()
2186
2187        nchan = workscan.nchan()
2188       
[2081]2189        if mask          is None: mask          = [True for i in xrange(nchan)]
2190        if nwave         is None: nwave         = 3
2191        if maxwavelength is None: maxwavelength = 1.0
2192        if clipthresh    is None: clipthresh    = 3.0
[2129]2193        if clipniter     is None: clipniter     = 0
[2081]2194        if plot          is None: plot          = False
2195        if getresidual   is None: getresidual   = True
2196        if outlog        is None: outlog        = False
2197        if blfile        is None: blfile        = ""
[2047]2198
[2081]2199        if isinstance(nwave, int):
2200            in_nwave = nwave
2201            nwave = []
2202            for i in xrange(in_nwave+1): nwave.append(i)
2203       
[2047]2204        outblfile = (blfile != "") and os.path.exists(os.path.expanduser(os.path.expandvars(blfile)))
2205       
2206        try:
[2081]2207            #CURRENTLY, PLOT=true is UNAVAILABLE UNTIL sinusoidal fitting is implemented as a fitter method.
2208            workscan._sinusoid_baseline(mask, nwave, maxwavelength, clipthresh, clipniter, getresidual, outlog, blfile)
[2047]2209           
2210            workscan._add_history("sinusoid_baseline", varlist)
2211           
2212            if insitu:
2213                self._assign(workscan)
2214            else:
2215                return workscan
2216           
2217        except RuntimeError, e:
2218            msg = "The fit failed, possibly because it didn't converge."
2219            if rcParams["verbose"]:
2220                asaplog.push(str(e))
2221                asaplog.push(str(msg))
2222                return
2223            else:
2224                raise RuntimeError(str(e)+'\n'+msg)
2225
2226
[2081]2227    def auto_sinusoid_baseline(self, insitu=None, mask=None, nwave=None, maxwavelength=None,
[2047]2228                               clipthresh=None, clipniter=None, edge=None, threshold=None,
[2081]2229                               chan_avg_limit=None, plot=None, getresidual=None, outlog=None, blfile=None):
[2047]2230        """\
[2094]2231        Return a scan which has been baselined (all rows) with sinusoidal functions.
[2047]2232        Spectral lines are detected first using linefinder and masked out
2233        to avoid them affecting the baseline solution.
2234
2235        Parameters:
[2081]2236            insitu:        if False a new scantable is returned.
2237                           Otherwise, the scaling is done in-situ
2238                           The default is taken from .asaprc (False)
2239            mask:          an optional mask retreived from scantable
2240            nwave:         the maximum wave number of sinusoids within
2241                           maxwavelength * (spectral range).
2242                           The default is 3 (i.e., sinusoids with wave
2243                           number of 0(=constant), 1, 2, and 3 are
2244                           used for fitting). Also it is possible to
2245                           explicitly specify all the wave numbers to
2246                           be used, by giving a list including them
2247                           (e.g. [0,1,2,15,16]).
2248            maxwavelength: the longest sinusoidal wavelength. The
2249                           default is 1.0 (unit: spectral range).
2250            clipthresh:    Clipping threshold. (default is 3.0, unit: sigma)
[2129]2251            clipniter:     maximum number of iteration of 'clipthresh'-sigma clipping (default is 0)
[2081]2252            edge:          an optional number of channel to drop at
2253                           the edge of spectrum. If only one value is
2254                           specified, the same number will be dropped
2255                           from both sides of the spectrum. Default
2256                           is to keep all channels. Nested tuples
2257                           represent individual edge selection for
2258                           different IFs (a number of spectral channels
2259                           can be different)
2260            threshold:     the threshold used by line finder. It is
2261                           better to keep it large as only strong lines
2262                           affect the baseline solution.
2263            chan_avg_limit:a maximum number of consequtive spectral
2264                           channels to average during the search of
2265                           weak and broad lines. The default is no
2266                           averaging (and no search for weak lines).
2267                           If such lines can affect the fitted baseline
2268                           (e.g. a high order polynomial is fitted),
2269                           increase this parameter (usually values up
2270                           to 8 are reasonable). Most users of this
2271                           method should find the default value sufficient.
2272            plot:      *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
2273                           plot the fit and the residual. In this each
2274                           indivual fit has to be approved, by typing 'y'
2275                           or 'n'
2276            getresidual:   if False, returns best-fit values instead of
2277                           residual. (default is True)
2278            outlog:        Output the coefficients of the best-fit
2279                           function to logger (default is False)
2280            blfile:        Name of a text file in which the best-fit
2281                           parameter values to be written
2282                           (default is "": no file/logger output)
[2047]2283
2284        Example:
[2081]2285            bscan = scan.auto_sinusoid_baseline(nwave=10, insitu=False)
2286       
2287        Note:
2288            The best-fit parameter values output in logger and/or blfile are now
2289            based on specunit of 'channel'.
[2047]2290        """
2291
2292        varlist = vars()
2293
2294        if insitu is None: insitu = rcParams['insitu']
2295        if insitu:
2296            workscan = self
2297        else:
2298            workscan = self.copy()
2299
2300        nchan = workscan.nchan()
2301       
[2081]2302        if mask           is None: mask           = [True for i in xrange(nchan)]
2303        if nwave          is None: nwave          = 3
2304        if maxwavelength  is None: maxwavelength  = 1.0
2305        if clipthresh     is None: clipthresh     = 3.0
[2129]2306        if clipniter      is None: clipniter      = 0
[2081]2307        if edge           is None: edge           = (0,0)
2308        if threshold      is None: threshold      = 3
[2047]2309        if chan_avg_limit is None: chan_avg_limit = 1
[2081]2310        if plot           is None: plot           = False
2311        if getresidual    is None: getresidual    = True
2312        if outlog         is None: outlog         = False
2313        if blfile         is None: blfile         = ""
[2047]2314
2315        outblfile = (blfile != "") and os.path.exists(os.path.expanduser(os.path.expandvars(blfile)))
2316       
2317        from asap.asaplinefind import linefinder
2318        from asap import _is_sequence_or_number as _is_valid
2319
[2081]2320        if isinstance(nwave, int):
2321            in_nwave = nwave
2322            nwave = []
2323            for i in xrange(in_nwave+1): nwave.append(i)
2324
[2047]2325        if not (isinstance(edge, list) or isinstance(edge, tuple)): edge = [ edge ]
2326        individualedge = False;
2327        if len(edge) > 1: individualedge = isinstance(edge[0], list) or isinstance(edge[0], tuple)
2328
2329        if individualedge:
2330            for edgepar in edge:
2331                if not _is_valid(edgepar, int):
2332                    raise ValueError, "Each element of the 'edge' tuple has \
2333                                       to be a pair of integers or an integer."
2334        else:
2335            if not _is_valid(edge, int):
2336                raise ValueError, "Parameter 'edge' has to be an integer or a \
2337                                   pair of integers specified as a tuple. \
2338                                   Nested tuples are allowed \
2339                                   to make individual selection for different IFs."
2340
2341            if len(edge) > 1:
2342                curedge = edge
2343            else:
2344                curedge = edge + edge
2345
2346        try:
[2081]2347            #CURRENTLY, PLOT=true is UNAVAILABLE UNTIL sinusoidal fitting is implemented as a fitter method.
[2047]2348            if individualedge:
2349                curedge = []
2350                for i in xrange(len(edge)):
2351                    curedge += edge[i]
2352               
[2081]2353            workscan._auto_sinusoid_baseline(mask, nwave, maxwavelength, clipthresh, clipniter, curedge, threshold, chan_avg_limit, getresidual, outlog, blfile)
[2047]2354
2355            workscan._add_history("auto_sinusoid_baseline", varlist)
2356           
2357            if insitu:
2358                self._assign(workscan)
2359            else:
2360                return workscan
2361           
2362        except RuntimeError, e:
2363            msg = "The fit failed, possibly because it didn't converge."
2364            if rcParams["verbose"]:
2365                asaplog.push(str(e))
2366                asaplog.push(str(msg))
2367                return
2368            else:
2369                raise RuntimeError(str(e)+'\n'+msg)
2370
2371
2372    @asaplog_post_dec
[2094]2373    def cspline_baseline(self, insitu=None, mask=None, npiece=None,
2374                         clipthresh=None, clipniter=None, plot=None, getresidual=None, outlog=None, blfile=None):
[1846]2375        """\
[2012]2376        Return a scan which has been baselined (all rows) by cubic spline function (piecewise cubic polynomial).
[513]2377        Parameters:
[2012]2378            insitu:     If False a new scantable is returned.
2379                        Otherwise, the scaling is done in-situ
2380                        The default is taken from .asaprc (False)
2381            mask:       An optional mask
2382            npiece:     Number of pieces. (default is 2)
2383            clipthresh: Clipping threshold. (default is 3.0, unit: sigma)
[2129]2384            clipniter:  maximum number of iteration of 'clipthresh'-sigma clipping (default is 0)
[2012]2385            plot:   *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
2386                        plot the fit and the residual. In this each
2387                        indivual fit has to be approved, by typing 'y'
2388                        or 'n'
[2094]2389            getresidual:if False, returns best-fit values instead of
2390                        residual. (default is True)
[2012]2391            outlog:     Output the coefficients of the best-fit
2392                        function to logger (default is False)
2393            blfile:     Name of a text file in which the best-fit
2394                        parameter values to be written
2395                        (default is "": no file/logger output)
[1846]2396
[2012]2397        Example:
2398            # return a scan baselined by a cubic spline consisting of 2 pieces (i.e., 1 internal knot),
2399            # also with 3-sigma clipping, iteration up to 4 times
2400            bscan = scan.cspline_baseline(npiece=2,clipthresh=3.0,clipniter=4)
[2081]2401       
2402        Note:
2403            The best-fit parameter values output in logger and/or blfile are now
2404            based on specunit of 'channel'.
[2012]2405        """
2406       
2407        varlist = vars()
2408       
2409        if insitu is None: insitu = rcParams["insitu"]
2410        if insitu:
2411            workscan = self
2412        else:
2413            workscan = self.copy()
[1855]2414
[2012]2415        nchan = workscan.nchan()
2416       
[2094]2417        if mask        is None: mask        = [True for i in xrange(nchan)]
2418        if npiece      is None: npiece      = 2
2419        if clipthresh  is None: clipthresh  = 3.0
[2129]2420        if clipniter   is None: clipniter   = 0
[2094]2421        if plot        is None: plot        = False
2422        if getresidual is None: getresidual = True
2423        if outlog      is None: outlog      = False
2424        if blfile      is None: blfile      = ""
[1855]2425
[2012]2426        outblfile = (blfile != "") and os.path.exists(os.path.expanduser(os.path.expandvars(blfile)))
2427       
2428        try:
2429            #CURRENTLY, PLOT=true UNAVAILABLE UNTIL cubic spline fitting is implemented as a fitter method.
[2094]2430            workscan._cspline_baseline(mask, npiece, clipthresh, clipniter, getresidual, outlog, blfile)
[2012]2431           
2432            workscan._add_history("cspline_baseline", varlist)
2433           
2434            if insitu:
2435                self._assign(workscan)
2436            else:
2437                return workscan
2438           
2439        except RuntimeError, e:
2440            msg = "The fit failed, possibly because it didn't converge."
2441            if rcParams["verbose"]:
2442                asaplog.push(str(e))
2443                asaplog.push(str(msg))
2444                return
2445            else:
2446                raise RuntimeError(str(e)+'\n'+msg)
[1855]2447
2448
[2012]2449    def auto_cspline_baseline(self, insitu=None, mask=None, npiece=None, clipthresh=None,
2450                              clipniter=None, edge=None, threshold=None,
[2094]2451                              chan_avg_limit=None, getresidual=None, plot=None, outlog=None, blfile=None):
[2012]2452        """\
2453        Return a scan which has been baselined (all rows) by cubic spline
2454        function (piecewise cubic polynomial).
2455        Spectral lines are detected first using linefinder and masked out
2456        to avoid them affecting the baseline solution.
2457
2458        Parameters:
[794]2459            insitu:     if False a new scantable is returned.
2460                        Otherwise, the scaling is done in-situ
2461                        The default is taken from .asaprc (False)
[2012]2462            mask:       an optional mask retreived from scantable
2463            npiece:     Number of pieces. (default is 2)
2464            clipthresh: Clipping threshold. (default is 3.0, unit: sigma)
[2129]2465            clipniter:  maximum number of iteration of 'clipthresh'-sigma clipping (default is 0)
[2012]2466            edge:       an optional number of channel to drop at
2467                        the edge of spectrum. If only one value is
2468                        specified, the same number will be dropped
2469                        from both sides of the spectrum. Default
2470                        is to keep all channels. Nested tuples
2471                        represent individual edge selection for
2472                        different IFs (a number of spectral channels
2473                        can be different)
2474            threshold:  the threshold used by line finder. It is
2475                        better to keep it large as only strong lines
2476                        affect the baseline solution.
2477            chan_avg_limit:
2478                        a maximum number of consequtive spectral
2479                        channels to average during the search of
2480                        weak and broad lines. The default is no
2481                        averaging (and no search for weak lines).
2482                        If such lines can affect the fitted baseline
2483                        (e.g. a high order polynomial is fitted),
2484                        increase this parameter (usually values up
2485                        to 8 are reasonable). Most users of this
2486                        method should find the default value sufficient.
2487            plot:   *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
2488                        plot the fit and the residual. In this each
2489                        indivual fit has to be approved, by typing 'y'
2490                        or 'n'
[2094]2491            getresidual:if False, returns best-fit values instead of
2492                        residual. (default is True)
[2012]2493            outlog:     Output the coefficients of the best-fit
2494                        function to logger (default is False)
2495            blfile:     Name of a text file in which the best-fit
2496                        parameter values to be written
2497                        (default is "": no file/logger output)
[1846]2498
[1907]2499        Example:
[2012]2500            bscan = scan.auto_cspline_baseline(npiece=3, insitu=False)
[2081]2501       
2502        Note:
2503            The best-fit parameter values output in logger and/or blfile are now
2504            based on specunit of 'channel'.
[2012]2505        """
[1846]2506
[2012]2507        varlist = vars()
2508
[513]2509        if insitu is None: insitu = rcParams['insitu']
[2012]2510        if insitu:
2511            workscan = self
2512        else:
[1819]2513            workscan = self.copy()
[2012]2514
2515        nchan = workscan.nchan()
2516       
[2094]2517        if mask           is None: mask           = [True for i in xrange(nchan)]
2518        if npiece         is None: npiece         = 2
2519        if clipthresh     is None: clipthresh     = 3.0
[2129]2520        if clipniter      is None: clipniter      = 0
[2094]2521        if edge           is None: edge           = (0, 0)
2522        if threshold      is None: threshold      = 3
[2012]2523        if chan_avg_limit is None: chan_avg_limit = 1
[2094]2524        if plot           is None: plot           = False
2525        if getresidual    is None: getresidual    = True
2526        if outlog         is None: outlog         = False
2527        if blfile         is None: blfile         = ""
[2012]2528
2529        outblfile = (blfile != "") and os.path.exists(os.path.expanduser(os.path.expandvars(blfile)))
2530       
2531        from asap.asaplinefind import linefinder
2532        from asap import _is_sequence_or_number as _is_valid
2533
2534        if not (isinstance(edge, list) or isinstance(edge, tuple)): edge = [ edge ]
2535        individualedge = False;
2536        if len(edge) > 1: individualedge = isinstance(edge[0], list) or isinstance(edge[0], tuple)
2537
2538        if individualedge:
2539            for edgepar in edge:
2540                if not _is_valid(edgepar, int):
2541                    raise ValueError, "Each element of the 'edge' tuple has \
2542                                       to be a pair of integers or an integer."
[1819]2543        else:
[2012]2544            if not _is_valid(edge, int):
2545                raise ValueError, "Parameter 'edge' has to be an integer or a \
2546                                   pair of integers specified as a tuple. \
2547                                   Nested tuples are allowed \
2548                                   to make individual selection for different IFs."
[1819]2549
[2012]2550            if len(edge) > 1:
2551                curedge = edge
[1391]2552            else:
[2012]2553                curedge = edge + edge
[1819]2554
[2012]2555        try:
2556            #CURRENTLY, PLOT=true UNAVAILABLE UNTIL cubic spline fitting is implemented as a fitter method.
2557            if individualedge:
2558                curedge = []
2559                for i in xrange(len(edge)):
2560                    curedge += edge[i]
2561               
[2094]2562            workscan._auto_cspline_baseline(mask, npiece, clipthresh, clipniter, curedge, threshold, chan_avg_limit, getresidual, outlog, blfile)
[2012]2563
2564            workscan._add_history("auto_cspline_baseline", varlist)
[1907]2565           
[1856]2566            if insitu:
2567                self._assign(workscan)
2568            else:
2569                return workscan
[2012]2570           
2571        except RuntimeError, e:
[1217]2572            msg = "The fit failed, possibly because it didn't converge."
[2012]2573            if rcParams["verbose"]:
2574                asaplog.push(str(e))
2575                asaplog.push(str(msg))
2576                return
2577            else:
2578                raise RuntimeError(str(e)+'\n'+msg)
[513]2579
[2012]2580
[1931]2581    @asaplog_post_dec
[2094]2582    def poly_baseline(self, insitu=None, mask=None, order=None, plot=None, getresidual=None, outlog=None, blfile=None):
[1907]2583        """\
2584        Return a scan which has been baselined (all rows) by a polynomial.
2585        Parameters:
[2012]2586            insitu:     if False a new scantable is returned.
2587                        Otherwise, the scaling is done in-situ
2588                        The default is taken from .asaprc (False)
[1907]2589            mask:       an optional mask
2590            order:      the order of the polynomial (default is 0)
2591            plot:       plot the fit and the residual. In this each
2592                        indivual fit has to be approved, by typing 'y'
[2012]2593                        or 'n'
[2094]2594            getresidual:if False, returns best-fit values instead of
2595                        residual. (default is True)
[2012]2596            outlog:     Output the coefficients of the best-fit
2597                        function to logger (default is False)
2598            blfile:     Name of a text file in which the best-fit
2599                        parameter values to be written
2600                        (default is "": no file/logger output)
2601
[1907]2602        Example:
2603            # return a scan baselined by a third order polynomial,
2604            # not using a mask
2605            bscan = scan.poly_baseline(order=3)
2606        """
[1931]2607       
2608        varlist = vars()
2609       
[1907]2610        if insitu is None: insitu = rcParams["insitu"]
2611        if insitu:
2612            workscan = self
2613        else:
2614            workscan = self.copy()
2615
2616        nchan = workscan.nchan()
2617       
[2094]2618        if mask        is None: mask        = [True for i in xrange(nchan)]
2619        if order       is None: order       = 0
2620        if plot        is None: plot        = False
2621        if getresidual is None: getresidual = True
2622        if outlog      is None: outlog      = False
2623        if blfile      is None: blfile      = ""
[1907]2624
[2012]2625        outblfile = (blfile != "") and os.path.exists(os.path.expanduser(os.path.expandvars(blfile)))
2626       
[1907]2627        try:
[2012]2628            rows = xrange(workscan.nrow())
[1907]2629           
[2012]2630            #if len(rows) > 0: workscan._init_blinfo()
[1907]2631
[2012]2632            if plot:
2633                if outblfile: blf = open(blfile, "a")
2634               
[1907]2635                f = fitter()
2636                f.set_function(lpoly=order)
2637                for r in rows:
2638                    f.x = workscan._getabcissa(r)
2639                    f.y = workscan._getspectrum(r)
2640                    f.mask = mask_and(mask, workscan._getmask(r))    # (CAS-1434)
2641                    f.data = None
2642                    f.fit()
2643                   
2644                    f.plot(residual=True)
2645                    accept_fit = raw_input("Accept fit ( [y]/n ): ")
2646                    if accept_fit.upper() == "N":
[2012]2647                        #workscan._append_blinfo(None, None, None)
[1907]2648                        continue
[2012]2649                   
2650                    blpars = f.get_parameters()
2651                    masklist = workscan.get_masklist(f.mask, row=r, silent=True)
2652                    #workscan._append_blinfo(blpars, masklist, f.mask)
[2094]2653                    workscan._setspectrum((f.fitter.getresidual() if getresidual else f.fitter.getfit()), r)
[1907]2654                   
[2012]2655                    if outblfile:
2656                        rms = workscan.get_rms(f.mask, r)
2657                        dataout = workscan.format_blparams_row(blpars["params"], blpars["fixed"], rms, str(masklist), r, True)
2658                        blf.write(dataout)
2659
[1907]2660                f._p.unmap()
2661                f._p = None
[2012]2662
2663                if outblfile: blf.close()
[1907]2664            else:
[2094]2665                workscan._poly_baseline(mask, order, getresidual, outlog, blfile)
[1907]2666           
2667            workscan._add_history("poly_baseline", varlist)
2668           
2669            if insitu:
2670                self._assign(workscan)
2671            else:
2672                return workscan
2673           
[1919]2674        except RuntimeError, e:
[1907]2675            msg = "The fit failed, possibly because it didn't converge."
2676            if rcParams["verbose"]:
[1919]2677                asaplog.push(str(e))
[1907]2678                asaplog.push(str(msg))
2679                return
2680            else:
[1919]2681                raise RuntimeError(str(e)+'\n'+msg)
[1907]2682
2683
[2012]2684    def auto_poly_baseline(self, insitu=None, mask=None, order=None, edge=None, threshold=None,
[2094]2685                           chan_avg_limit=None, plot=None, getresidual=None, outlog=None, blfile=None):
[1846]2686        """\
[1931]2687        Return a scan which has been baselined (all rows) by a polynomial.
[880]2688        Spectral lines are detected first using linefinder and masked out
2689        to avoid them affecting the baseline solution.
2690
2691        Parameters:
[2012]2692            insitu:     if False a new scantable is returned.
2693                        Otherwise, the scaling is done in-situ
2694                        The default is taken from .asaprc (False)
[880]2695            mask:       an optional mask retreived from scantable
2696            order:      the order of the polynomial (default is 0)
[2012]2697            edge:       an optional number of channel to drop at
2698                        the edge of spectrum. If only one value is
2699                        specified, the same number will be dropped
2700                        from both sides of the spectrum. Default
2701                        is to keep all channels. Nested tuples
2702                        represent individual edge selection for
2703                        different IFs (a number of spectral channels
2704                        can be different)
2705            threshold:  the threshold used by line finder. It is
2706                        better to keep it large as only strong lines
2707                        affect the baseline solution.
[1280]2708            chan_avg_limit:
[2012]2709                        a maximum number of consequtive spectral
2710                        channels to average during the search of
2711                        weak and broad lines. The default is no
2712                        averaging (and no search for weak lines).
2713                        If such lines can affect the fitted baseline
2714                        (e.g. a high order polynomial is fitted),
2715                        increase this parameter (usually values up
2716                        to 8 are reasonable). Most users of this
2717                        method should find the default value sufficient.
[1061]2718            plot:       plot the fit and the residual. In this each
2719                        indivual fit has to be approved, by typing 'y'
2720                        or 'n'
[2094]2721            getresidual:if False, returns best-fit values instead of
2722                        residual. (default is True)
[2012]2723            outlog:     Output the coefficients of the best-fit
2724                        function to logger (default is False)
2725            blfile:     Name of a text file in which the best-fit
2726                        parameter values to be written
2727                        (default is "": no file/logger output)
[1846]2728
[2012]2729        Example:
2730            bscan = scan.auto_poly_baseline(order=7, insitu=False)
2731        """
[880]2732
[2012]2733        varlist = vars()
[1846]2734
[2012]2735        if insitu is None: insitu = rcParams['insitu']
2736        if insitu:
2737            workscan = self
2738        else:
2739            workscan = self.copy()
[1846]2740
[2012]2741        nchan = workscan.nchan()
2742       
[2094]2743        if mask           is None: mask           = [True for i in xrange(nchan)]
2744        if order          is None: order          = 0
2745        if edge           is None: edge           = (0, 0)
2746        if threshold      is None: threshold      = 3
[2012]2747        if chan_avg_limit is None: chan_avg_limit = 1
[2094]2748        if plot           is None: plot           = False
2749        if getresidual    is None: getresidual    = True
2750        if outlog         is None: outlog         = False
2751        if blfile         is None: blfile         = ""
[1846]2752
[2012]2753        outblfile = (blfile != "") and os.path.exists(os.path.expanduser(os.path.expandvars(blfile)))
2754       
[880]2755        from asap.asaplinefind import linefinder
2756        from asap import _is_sequence_or_number as _is_valid
2757
[2012]2758        if not (isinstance(edge, list) or isinstance(edge, tuple)): edge = [ edge ]
[1118]2759        individualedge = False;
[2012]2760        if len(edge) > 1: individualedge = isinstance(edge[0], list) or isinstance(edge[0], tuple)
[907]2761
[1118]2762        if individualedge:
2763            for edgepar in edge:
2764                if not _is_valid(edgepar, int):
2765                    raise ValueError, "Each element of the 'edge' tuple has \
2766                                       to be a pair of integers or an integer."
[907]2767        else:
[2012]2768            if not _is_valid(edge, int):
2769                raise ValueError, "Parameter 'edge' has to be an integer or a \
2770                                   pair of integers specified as a tuple. \
2771                                   Nested tuples are allowed \
2772                                   to make individual selection for different IFs."
[880]2773
[2012]2774            if len(edge) > 1:
2775                curedge = edge
2776            else:
2777                curedge = edge + edge
[1907]2778
[2012]2779        try:
2780            rows = xrange(workscan.nrow())
2781           
2782            #if len(rows) > 0: workscan._init_blinfo()
[880]2783
[2012]2784            if plot:
2785                if outblfile: blf = open(blfile, "a")
2786               
2787                fl = linefinder()
2788                fl.set_options(threshold=threshold,avg_limit=chan_avg_limit)
2789                fl.set_scan(workscan)
2790                f = fitter()
2791                f.set_function(lpoly=order)
[880]2792
[2012]2793                for r in rows:
2794                    if individualedge:
2795                        if len(edge) <= workscan.getif(r):
2796                            raise RuntimeError, "Number of edge elements appear to " \
2797                                  "be less than the number of IFs"
2798                        else:
2799                            curedge = edge[workscan.getif(r)]
[907]2800
[2012]2801                    fl.find_lines(r, mask_and(mask, workscan._getmask(r)), curedge)  # (CAS-1434)
2802
2803                    f.x = workscan._getabcissa(r)
2804                    f.y = workscan._getspectrum(r)
2805                    f.mask = fl.get_mask()
2806                    f.data = None
2807                    f.fit()
2808
2809                    f.plot(residual=True)
2810                    accept_fit = raw_input("Accept fit ( [y]/n ): ")
2811                    if accept_fit.upper() == "N":
2812                        #workscan._append_blinfo(None, None, None)
2813                        continue
2814
2815                    blpars = f.get_parameters()
2816                    masklist = workscan.get_masklist(f.mask, row=r, silent=True)
2817                    #workscan._append_blinfo(blpars, masklist, f.mask)
[2094]2818                    workscan._setspectrum((f.fitter.getresidual() if getresidual else f.fitter.getfit()), r)
[2012]2819
2820                    if outblfile:
2821                        rms = workscan.get_rms(f.mask, r)
2822                        dataout = workscan.format_blparams_row(blpars["params"], blpars["fixed"], rms, str(masklist), r, True)
2823                        blf.write(dataout)
2824                   
2825                f._p.unmap()
2826                f._p = None
2827
2828                if outblfile: blf.close()
2829               
2830            else:
2831                if individualedge:
2832                    curedge = []
2833                    for i in xrange(len(edge)):
2834                        curedge += edge[i]
2835               
[2094]2836                workscan._auto_poly_baseline(mask, order, curedge, threshold, chan_avg_limit, getresidual, outlog, blfile)
[2012]2837
2838            workscan._add_history("auto_poly_baseline", varlist)
2839           
2840            if insitu:
2841                self._assign(workscan)
2842            else:
2843                return workscan
2844           
2845        except RuntimeError, e:
2846            msg = "The fit failed, possibly because it didn't converge."
2847            if rcParams["verbose"]:
2848                asaplog.push(str(e))
2849                asaplog.push(str(msg))
2850                return
2851            else:
2852                raise RuntimeError(str(e)+'\n'+msg)
2853
2854
2855    ### OBSOLETE ##################################################################
2856    @asaplog_post_dec
2857    def old_poly_baseline(self, mask=None, order=0, plot=False, uselin=False, insitu=None, rows=None):
2858        """
2859        Return a scan which has been baselined (all rows) by a polynomial.
[1907]2860       
[2012]2861        Parameters:
2862
2863            mask:       an optional mask
2864
2865            order:      the order of the polynomial (default is 0)
2866
2867            plot:       plot the fit and the residual. In this each
2868                        indivual fit has to be approved, by typing 'y'
2869                        or 'n'
2870
2871            uselin:     use linear polynomial fit
2872
2873            insitu:     if False a new scantable is returned.
2874                        Otherwise, the scaling is done in-situ
2875                        The default is taken from .asaprc (False)
2876
2877            rows:       row numbers of spectra to be processed.
2878                        (default is None: for all rows)
[1907]2879       
[2012]2880        Example:
2881            # return a scan baselined by a third order polynomial,
2882            # not using a mask
2883            bscan = scan.poly_baseline(order=3)
[907]2884
[2012]2885        """
2886        if insitu is None: insitu = rcParams['insitu']
2887        if not insitu:
2888            workscan = self.copy()
2889        else:
2890            workscan = self
2891        varlist = vars()
2892        if mask is None:
2893            mask = [True for i in xrange(self.nchan())]
[919]2894
[2012]2895        try:
2896            f = fitter()
2897            if uselin:
2898                f.set_function(lpoly=order)
2899            else:
2900                f.set_function(poly=order)
[1819]2901
[2012]2902            if rows == None:
2903                rows = xrange(workscan.nrow())
2904            elif isinstance(rows, int):
2905                rows = [ rows ]
[1907]2906           
[2012]2907            if len(rows) > 0:
2908                self.blpars = []
2909                self.masklists = []
2910                self.actualmask = []
2911           
2912            for r in rows:
2913                f.x = workscan._getabcissa(r)
2914                f.y = workscan._getspectrum(r)
2915                f.mask = mask_and(mask, workscan._getmask(r))    # (CAS-1434)
2916                f.data = None
2917                f.fit()
2918                if plot:
2919                    f.plot(residual=True)
2920                    x = raw_input("Accept fit ( [y]/n ): ")
2921                    if x.upper() == 'N':
2922                        self.blpars.append(None)
2923                        self.masklists.append(None)
2924                        self.actualmask.append(None)
2925                        continue
2926                workscan._setspectrum(f.fitter.getresidual(), r)
2927                self.blpars.append(f.get_parameters())
2928                self.masklists.append(workscan.get_masklist(f.mask, row=r, silent=True))
2929                self.actualmask.append(f.mask)
[1819]2930
[1061]2931            if plot:
[2012]2932                f._p.unmap()
2933                f._p = None
2934            workscan._add_history("poly_baseline", varlist)
2935            if insitu:
2936                self._assign(workscan)
2937            else:
2938                return workscan
2939        except RuntimeError:
2940            msg = "The fit failed, possibly because it didn't converge."
2941            raise RuntimeError(msg)
[1819]2942
[2012]2943    def _init_blinfo(self):
2944        """\
2945        Initialise the following three auxiliary members:
2946           blpars : parameters of the best-fit baseline,
2947           masklists : mask data (edge positions of masked channels) and
2948           actualmask : mask data (in boolean list),
2949        to keep for use later (including output to logger/text files).
2950        Used by poly_baseline() and auto_poly_baseline() in case of
2951        'plot=True'.
2952        """
2953        self.blpars = []
2954        self.masklists = []
2955        self.actualmask = []
2956        return
[880]2957
[2012]2958    def _append_blinfo(self, data_blpars, data_masklists, data_actualmask):
2959        """\
2960        Append baseline-fitting related info to blpars, masklist and
2961        actualmask.
2962        """
2963        self.blpars.append(data_blpars)
2964        self.masklists.append(data_masklists)
2965        self.actualmask.append(data_actualmask)
2966        return
2967       
[1862]2968    @asaplog_post_dec
[914]2969    def rotate_linpolphase(self, angle):
[1846]2970        """\
[914]2971        Rotate the phase of the complex polarization O=Q+iU correlation.
2972        This is always done in situ in the raw data.  So if you call this
2973        function more than once then each call rotates the phase further.
[1846]2974
[914]2975        Parameters:
[1846]2976
[914]2977            angle:   The angle (degrees) to rotate (add) by.
[1846]2978
2979        Example::
2980
[914]2981            scan.rotate_linpolphase(2.3)
[1846]2982
[914]2983        """
2984        varlist = vars()
[936]2985        self._math._rotate_linpolphase(self, angle)
[914]2986        self._add_history("rotate_linpolphase", varlist)
2987        return
[710]2988
[1862]2989    @asaplog_post_dec
[914]2990    def rotate_xyphase(self, angle):
[1846]2991        """\
[914]2992        Rotate the phase of the XY correlation.  This is always done in situ
2993        in the data.  So if you call this function more than once
2994        then each call rotates the phase further.
[1846]2995
[914]2996        Parameters:
[1846]2997
[914]2998            angle:   The angle (degrees) to rotate (add) by.
[1846]2999
3000        Example::
3001
[914]3002            scan.rotate_xyphase(2.3)
[1846]3003
[914]3004        """
3005        varlist = vars()
[936]3006        self._math._rotate_xyphase(self, angle)
[914]3007        self._add_history("rotate_xyphase", varlist)
3008        return
3009
[1862]3010    @asaplog_post_dec
[914]3011    def swap_linears(self):
[1846]3012        """\
[1573]3013        Swap the linear polarisations XX and YY, or better the first two
[1348]3014        polarisations as this also works for ciculars.
[914]3015        """
3016        varlist = vars()
[936]3017        self._math._swap_linears(self)
[914]3018        self._add_history("swap_linears", varlist)
3019        return
3020
[1862]3021    @asaplog_post_dec
[914]3022    def invert_phase(self):
[1846]3023        """\
[914]3024        Invert the phase of the complex polarisation
3025        """
3026        varlist = vars()
[936]3027        self._math._invert_phase(self)
[914]3028        self._add_history("invert_phase", varlist)
3029        return
3030
[1862]3031    @asaplog_post_dec
[876]3032    def add(self, offset, insitu=None):
[1846]3033        """\
[513]3034        Return a scan where all spectra have the offset added
[1846]3035
[513]3036        Parameters:
[1846]3037
[513]3038            offset:      the offset
[1855]3039
[513]3040            insitu:      if False a new scantable is returned.
3041                         Otherwise, the scaling is done in-situ
3042                         The default is taken from .asaprc (False)
[1846]3043
[513]3044        """
3045        if insitu is None: insitu = rcParams['insitu']
[876]3046        self._math._setinsitu(insitu)
[513]3047        varlist = vars()
[876]3048        s = scantable(self._math._unaryop(self, offset, "ADD", False))
[1118]3049        s._add_history("add", varlist)
[876]3050        if insitu:
3051            self._assign(s)
3052        else:
[513]3053            return s
3054
[1862]3055    @asaplog_post_dec
[1308]3056    def scale(self, factor, tsys=True, insitu=None):
[1846]3057        """\
3058
[1938]3059        Return a scan where all spectra are scaled by the given 'factor'
[1846]3060
[513]3061        Parameters:
[1846]3062
[1819]3063            factor:      the scaling factor (float or 1D float list)
[1855]3064
[513]3065            insitu:      if False a new scantable is returned.
3066                         Otherwise, the scaling is done in-situ
3067                         The default is taken from .asaprc (False)
[1855]3068
[513]3069            tsys:        if True (default) then apply the operation to Tsys
3070                         as well as the data
[1846]3071
[513]3072        """
3073        if insitu is None: insitu = rcParams['insitu']
[876]3074        self._math._setinsitu(insitu)
[513]3075        varlist = vars()
[1819]3076        s = None
3077        import numpy
3078        if isinstance(factor, list) or isinstance(factor, numpy.ndarray):
3079            if isinstance(factor[0], list) or isinstance(factor[0], numpy.ndarray):
3080                from asapmath import _array2dOp
3081                s = _array2dOp( self.copy(), factor, "MUL", tsys )
3082            else:
3083                s = scantable( self._math._arrayop( self.copy(), factor, "MUL", tsys ) )
3084        else:
3085            s = scantable(self._math._unaryop(self.copy(), factor, "MUL", tsys))
[1118]3086        s._add_history("scale", varlist)
[876]3087        if insitu:
3088            self._assign(s)
3089        else:
[513]3090            return s
3091
[1504]3092    def set_sourcetype(self, match, matchtype="pattern",
3093                       sourcetype="reference"):
[1846]3094        """\
[1502]3095        Set the type of the source to be an source or reference scan
[1846]3096        using the provided pattern.
3097
[1502]3098        Parameters:
[1846]3099
[1504]3100            match:          a Unix style pattern, regular expression or selector
[1855]3101
[1504]3102            matchtype:      'pattern' (default) UNIX style pattern or
3103                            'regex' regular expression
[1855]3104
[1502]3105            sourcetype:     the type of the source to use (source/reference)
[1846]3106
[1502]3107        """
3108        varlist = vars()
3109        basesel = self.get_selection()
3110        stype = -1
3111        if sourcetype.lower().startswith("r"):
3112            stype = 1
3113        elif sourcetype.lower().startswith("s"):
3114            stype = 0
[1504]3115        else:
[1502]3116            raise ValueError("Illegal sourcetype use s(ource) or r(eference)")
[1504]3117        if matchtype.lower().startswith("p"):
3118            matchtype = "pattern"
3119        elif matchtype.lower().startswith("r"):
3120            matchtype = "regex"
3121        else:
3122            raise ValueError("Illegal matchtype, use p(attern) or r(egex)")
[1502]3123        sel = selector()
3124        if isinstance(match, selector):
3125            sel = match
3126        else:
[1504]3127            sel.set_query("SRCNAME == %s('%s')" % (matchtype, match))
[1502]3128        self.set_selection(basesel+sel)
3129        self._setsourcetype(stype)
3130        self.set_selection(basesel)
[1573]3131        self._add_history("set_sourcetype", varlist)
[1502]3132
[1862]3133    @asaplog_post_dec
[1857]3134    @preserve_selection
[1819]3135    def auto_quotient(self, preserve=True, mode='paired', verify=False):
[1846]3136        """\
[670]3137        This function allows to build quotients automatically.
[1819]3138        It assumes the observation to have the same number of
[670]3139        "ons" and "offs"
[1846]3140
[670]3141        Parameters:
[1846]3142
[710]3143            preserve:       you can preserve (default) the continuum or
3144                            remove it.  The equations used are
[1857]3145
[670]3146                            preserve: Output = Toff * (on/off) - Toff
[1857]3147
[1070]3148                            remove:   Output = Toff * (on/off) - Ton
[1855]3149
[1573]3150            mode:           the on/off detection mode
[1348]3151                            'paired' (default)
3152                            identifies 'off' scans by the
3153                            trailing '_R' (Mopra/Parkes) or
3154                            '_e'/'_w' (Tid) and matches
3155                            on/off pairs from the observing pattern
[1502]3156                            'time'
3157                            finds the closest off in time
[1348]3158
[1857]3159        .. todo:: verify argument is not implemented
3160
[670]3161        """
[1857]3162        varlist = vars()
[1348]3163        modes = ["time", "paired"]
[670]3164        if not mode in modes:
[876]3165            msg = "please provide valid mode. Valid modes are %s" % (modes)
3166            raise ValueError(msg)
[1348]3167        s = None
3168        if mode.lower() == "paired":
[1857]3169            sel = self.get_selection()
[1875]3170            sel.set_query("SRCTYPE==psoff")
[1356]3171            self.set_selection(sel)
[1348]3172            offs = self.copy()
[1875]3173            sel.set_query("SRCTYPE==pson")
[1356]3174            self.set_selection(sel)
[1348]3175            ons = self.copy()
3176            s = scantable(self._math._quotient(ons, offs, preserve))
3177        elif mode.lower() == "time":
3178            s = scantable(self._math._auto_quotient(self, mode, preserve))
[1118]3179        s._add_history("auto_quotient", varlist)
[876]3180        return s
[710]3181
[1862]3182    @asaplog_post_dec
[1145]3183    def mx_quotient(self, mask = None, weight='median', preserve=True):
[1846]3184        """\
[1143]3185        Form a quotient using "off" beams when observing in "MX" mode.
[1846]3186
[1143]3187        Parameters:
[1846]3188
[1145]3189            mask:           an optional mask to be used when weight == 'stddev'
[1855]3190
[1143]3191            weight:         How to average the off beams.  Default is 'median'.
[1855]3192
[1145]3193            preserve:       you can preserve (default) the continuum or
[1855]3194                            remove it.  The equations used are:
[1846]3195
[1855]3196                                preserve: Output = Toff * (on/off) - Toff
3197
3198                                remove:   Output = Toff * (on/off) - Ton
3199
[1217]3200        """
[1593]3201        mask = mask or ()
[1141]3202        varlist = vars()
3203        on = scantable(self._math._mx_extract(self, 'on'))
[1143]3204        preoff = scantable(self._math._mx_extract(self, 'off'))
3205        off = preoff.average_time(mask=mask, weight=weight, scanav=False)
[1217]3206        from asapmath  import quotient
[1145]3207        q = quotient(on, off, preserve)
[1143]3208        q._add_history("mx_quotient", varlist)
[1217]3209        return q
[513]3210
[1862]3211    @asaplog_post_dec
[718]3212    def freq_switch(self, insitu=None):
[1846]3213        """\
[718]3214        Apply frequency switching to the data.
[1846]3215
[718]3216        Parameters:
[1846]3217
[718]3218            insitu:      if False a new scantable is returned.
3219                         Otherwise, the swictching is done in-situ
3220                         The default is taken from .asaprc (False)
[1846]3221
[718]3222        """
3223        if insitu is None: insitu = rcParams['insitu']
[876]3224        self._math._setinsitu(insitu)
[718]3225        varlist = vars()
[876]3226        s = scantable(self._math._freqswitch(self))
[1118]3227        s._add_history("freq_switch", varlist)
[1856]3228        if insitu:
3229            self._assign(s)
3230        else:
3231            return s
[718]3232
[1862]3233    @asaplog_post_dec
[780]3234    def recalc_azel(self):
[1846]3235        """Recalculate the azimuth and elevation for each position."""
[780]3236        varlist = vars()
[876]3237        self._recalcazel()
[780]3238        self._add_history("recalc_azel", varlist)
3239        return
3240
[1862]3241    @asaplog_post_dec
[513]3242    def __add__(self, other):
3243        varlist = vars()
3244        s = None
3245        if isinstance(other, scantable):
[1573]3246            s = scantable(self._math._binaryop(self, other, "ADD"))
[513]3247        elif isinstance(other, float):
[876]3248            s = scantable(self._math._unaryop(self, other, "ADD", False))
[2144]3249        elif isinstance(other, list) or isinstance(other, numpy.ndarray):
3250            if isinstance(other[0], list) or isinstance(other[0], numpy.ndarray):
3251                from asapmath import _array2dOp
3252                s = _array2dOp( self.copy(), other, "ADD", False )
3253            else:
3254                s = scantable( self._math._arrayop( self.copy(), other, "ADD", False ) )
[513]3255        else:
[718]3256            raise TypeError("Other input is not a scantable or float value")
[513]3257        s._add_history("operator +", varlist)
3258        return s
3259
[1862]3260    @asaplog_post_dec
[513]3261    def __sub__(self, other):
3262        """
3263        implicit on all axes and on Tsys
3264        """
3265        varlist = vars()
3266        s = None
3267        if isinstance(other, scantable):
[1588]3268            s = scantable(self._math._binaryop(self, other, "SUB"))
[513]3269        elif isinstance(other, float):
[876]3270            s = scantable(self._math._unaryop(self, other, "SUB", False))
[2144]3271        elif isinstance(other, list) or isinstance(other, numpy.ndarray):
3272            if isinstance(other[0], list) or isinstance(other[0], numpy.ndarray):
3273                from asapmath import _array2dOp
3274                s = _array2dOp( self.copy(), other, "SUB", False )
3275            else:
3276                s = scantable( self._math._arrayop( self.copy(), other, "SUB", False ) )
[513]3277        else:
[718]3278            raise TypeError("Other input is not a scantable or float value")
[513]3279        s._add_history("operator -", varlist)
3280        return s
[710]3281
[1862]3282    @asaplog_post_dec
[513]3283    def __mul__(self, other):
3284        """
3285        implicit on all axes and on Tsys
3286        """
3287        varlist = vars()
3288        s = None
3289        if isinstance(other, scantable):
[1588]3290            s = scantable(self._math._binaryop(self, other, "MUL"))
[513]3291        elif isinstance(other, float):
[876]3292            s = scantable(self._math._unaryop(self, other, "MUL", False))
[2144]3293        elif isinstance(other, list) or isinstance(other, numpy.ndarray):
3294            if isinstance(other[0], list) or isinstance(other[0], numpy.ndarray):
3295                from asapmath import _array2dOp
3296                s = _array2dOp( self.copy(), other, "MUL", False )
3297            else:
3298                s = scantable( self._math._arrayop( self.copy(), other, "MUL", False ) )
[513]3299        else:
[718]3300            raise TypeError("Other input is not a scantable or float value")
[513]3301        s._add_history("operator *", varlist)
3302        return s
3303
[710]3304
[1862]3305    @asaplog_post_dec
[513]3306    def __div__(self, other):
3307        """
3308        implicit on all axes and on Tsys
3309        """
3310        varlist = vars()
3311        s = None
3312        if isinstance(other, scantable):
[1589]3313            s = scantable(self._math._binaryop(self, other, "DIV"))
[513]3314        elif isinstance(other, float):
3315            if other == 0.0:
[718]3316                raise ZeroDivisionError("Dividing by zero is not recommended")
[876]3317            s = scantable(self._math._unaryop(self, other, "DIV", False))
[2144]3318        elif isinstance(other, list) or isinstance(other, numpy.ndarray):
3319            if isinstance(other[0], list) or isinstance(other[0], numpy.ndarray):
3320                from asapmath import _array2dOp
3321                s = _array2dOp( self.copy(), other, "DIV", False )
3322            else:
3323                s = scantable( self._math._arrayop( self.copy(), other, "DIV", False ) )
[513]3324        else:
[718]3325            raise TypeError("Other input is not a scantable or float value")
[513]3326        s._add_history("operator /", varlist)
3327        return s
3328
[1862]3329    @asaplog_post_dec
[530]3330    def get_fit(self, row=0):
[1846]3331        """\
[530]3332        Print or return the stored fits for a row in the scantable
[1846]3333
[530]3334        Parameters:
[1846]3335
[530]3336            row:    the row which the fit has been applied to.
[1846]3337
[530]3338        """
3339        if row > self.nrow():
3340            return
[976]3341        from asap.asapfit import asapfit
[530]3342        fit = asapfit(self._getfit(row))
[1859]3343        asaplog.push( '%s' %(fit) )
3344        return fit.as_dict()
[530]3345
[1483]3346    def flag_nans(self):
[1846]3347        """\
[1483]3348        Utility function to flag NaN values in the scantable.
3349        """
3350        import numpy
3351        basesel = self.get_selection()
3352        for i in range(self.nrow()):
[1589]3353            sel = self.get_row_selector(i)
3354            self.set_selection(basesel+sel)
[1483]3355            nans = numpy.isnan(self._getspectrum(0))
3356        if numpy.any(nans):
3357            bnans = [ bool(v) for v in nans]
3358            self.flag(bnans)
3359        self.set_selection(basesel)
3360
[1588]3361    def get_row_selector(self, rowno):
[1992]3362        #return selector(beams=self.getbeam(rowno),
3363        #                ifs=self.getif(rowno),
3364        #                pols=self.getpol(rowno),
3365        #                scans=self.getscan(rowno),
3366        #                cycles=self.getcycle(rowno))
3367        return selector(rows=[rowno])
[1573]3368
[484]3369    def _add_history(self, funcname, parameters):
[1435]3370        if not rcParams['scantable.history']:
3371            return
[484]3372        # create date
3373        sep = "##"
3374        from datetime import datetime
3375        dstr = datetime.now().strftime('%Y/%m/%d %H:%M:%S')
3376        hist = dstr+sep
3377        hist += funcname+sep#cdate+sep
3378        if parameters.has_key('self'): del parameters['self']
[1118]3379        for k, v in parameters.iteritems():
[484]3380            if type(v) is dict:
[1118]3381                for k2, v2 in v.iteritems():
[484]3382                    hist += k2
3383                    hist += "="
[1118]3384                    if isinstance(v2, scantable):
[484]3385                        hist += 'scantable'
3386                    elif k2 == 'mask':
[1118]3387                        if isinstance(v2, list) or isinstance(v2, tuple):
[513]3388                            hist += str(self._zip_mask(v2))
3389                        else:
3390                            hist += str(v2)
[484]3391                    else:
[513]3392                        hist += str(v2)
[484]3393            else:
3394                hist += k
3395                hist += "="
[1118]3396                if isinstance(v, scantable):
[484]3397                    hist += 'scantable'
3398                elif k == 'mask':
[1118]3399                    if isinstance(v, list) or isinstance(v, tuple):
[513]3400                        hist += str(self._zip_mask(v))
3401                    else:
3402                        hist += str(v)
[484]3403                else:
3404                    hist += str(v)
3405            hist += sep
3406        hist = hist[:-2] # remove trailing '##'
3407        self._addhistory(hist)
3408
[710]3409
[484]3410    def _zip_mask(self, mask):
3411        mask = list(mask)
3412        i = 0
3413        segments = []
3414        while mask[i:].count(1):
3415            i += mask[i:].index(1)
3416            if mask[i:].count(0):
3417                j = i + mask[i:].index(0)
3418            else:
[710]3419                j = len(mask)
[1118]3420            segments.append([i, j])
[710]3421            i = j
[484]3422        return segments
[714]3423
[626]3424    def _get_ordinate_label(self):
3425        fu = "("+self.get_fluxunit()+")"
3426        import re
3427        lbl = "Intensity"
[1118]3428        if re.match(".K.", fu):
[626]3429            lbl = "Brightness Temperature "+ fu
[1118]3430        elif re.match(".Jy.", fu):
[626]3431            lbl = "Flux density "+ fu
3432        return lbl
[710]3433
[876]3434    def _check_ifs(self):
[1986]3435        #nchans = [self.nchan(i) for i in range(self.nif(-1))]
3436        nchans = [self.nchan(i) for i in self.getifnos()]
[2004]3437        nchans = filter(lambda t: t > 0, nchans)
[876]3438        return (sum(nchans)/len(nchans) == nchans[0])
[976]3439
[1862]3440    @asaplog_post_dec
[1916]3441    #def _fill(self, names, unit, average, getpt, antenna):
3442    def _fill(self, names, unit, average, opts={}):
[976]3443        first = True
3444        fullnames = []
3445        for name in names:
3446            name = os.path.expandvars(name)
3447            name = os.path.expanduser(name)
3448            if not os.path.exists(name):
3449                msg = "File '%s' does not exists" % (name)
3450                raise IOError(msg)
3451            fullnames.append(name)
3452        if average:
3453            asaplog.push('Auto averaging integrations')
[1079]3454        stype = int(rcParams['scantable.storage'].lower() == 'disk')
[976]3455        for name in fullnames:
[1073]3456            tbl = Scantable(stype)
[2004]3457            if is_ms( name ):
3458                r = msfiller( tbl )
3459            else:
3460                r = filler( tbl )
3461                rx = rcParams['scantable.reference']
3462                r.setreferenceexpr(rx)
3463            #r = filler(tbl)
3464            #rx = rcParams['scantable.reference']
3465            #r.setreferenceexpr(rx)
[976]3466            msg = "Importing %s..." % (name)
[1118]3467            asaplog.push(msg, False)
[1916]3468            #opts = {'ms': {'antenna' : antenna, 'getpt': getpt} }
[1904]3469            r.open(name, opts)# antenna, -1, -1, getpt)
[1843]3470            r.fill()
[976]3471            if average:
[1118]3472                tbl = self._math._average((tbl, ), (), 'NONE', 'SCAN')
[976]3473            if not first:
3474                tbl = self._math._merge([self, tbl])
3475            Scantable.__init__(self, tbl)
[1843]3476            r.close()
[1118]3477            del r, tbl
[976]3478            first = False
[1861]3479            #flush log
3480        asaplog.post()
[976]3481        if unit is not None:
3482            self.set_fluxunit(unit)
[1824]3483        if not is_casapy():
3484            self.set_freqframe(rcParams['scantable.freqframe'])
[976]3485
[2012]3486
[1402]3487    def __getitem__(self, key):
3488        if key < 0:
3489            key += self.nrow()
3490        if key >= self.nrow():
3491            raise IndexError("Row index out of range.")
3492        return self._getspectrum(key)
3493
3494    def __setitem__(self, key, value):
3495        if key < 0:
3496            key += self.nrow()
3497        if key >= self.nrow():
3498            raise IndexError("Row index out of range.")
3499        if not hasattr(value, "__len__") or \
3500                len(value) > self.nchan(self.getif(key)):
3501            raise ValueError("Spectrum length doesn't match.")
3502        return self._setspectrum(value, key)
3503
3504    def __len__(self):
3505        return self.nrow()
3506
3507    def __iter__(self):
3508        for i in range(len(self)):
3509            yield self[i]
Note: See TracBrowser for help on using the repository browser.