source: trunk/python/scantable.py @ 2767

Last change on this file since 2767 was 2767, checked in by WataruKawasaki, 11 years ago

New Development: Yes

JIRA Issue: Yes CAS-4794

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs:

Put in Release Notes: No

Module(s): sd

Description: functions to apply/write STBaselineTable in which baseline parameters stored.


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