source: trunk/python/scantable.py @ 2431

Last change on this file since 2431 was 2431, checked in by Kana Sugimoto, 12 years ago

New Development: No

JIRA Issue: Yes (CAS-2818)

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs: unit test: sdaverage[test900]

Put in Release Notes: No

Module(s):

Description:

Bug fixes and improvements of Scantable::regridChannel.

  • fixed bugs in regridding abcissa of spectra.
  • take channel flag into account when regridding spectra.
  • flag a regridded channel only when all channels mapped to it is flagged or no channel is mapped to it.


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