source: trunk/python/scantable.py @ 2926

Last change on this file since 2926 was 2926, checked in by WataruKawasaki, 10 years ago

New Development: No

JIRA Issue: No

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs:

Put in Release Notes: No

Module(s): sd

Description: modified an error message from get_first_rowno_by_if().


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