source: trunk/python/scantable.py @ 2591

Last change on this file since 2591 was 2591, checked in by WataruKawasaki, 12 years ago

New Development: Yes

JIRA Issue: Yes CSV-1869

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs:

Put in Release Notes: No

Module(s): asap

Description: added a scantable function to get/set moleculesID columns data.


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