source: trunk/python/scantable.py @ 2932

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

New Development: No

JIRA Issue: Yes CAS-6465

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs:

Put in Release Notes: No

Module(s): sd

Description: modified sd.scantable.parse_{idx|spw}_selection() so that no duplicated values appear in returned values.


  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 206.5 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 information.
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        if row >= len(self):
1112            raise IndexError("row out of range")
1113        values = self._get_column(self._get_weather, row)
1114        if row > -1:
1115            return {'temperature': values[0],
1116                    'pressure': values[1], 'humidity' : values[2],
1117                    'windspeed' : values[3], 'windaz' : values[4]
1118                    }
1119        else:
1120            out = []
1121            for r in values:
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 in valid_ifs:
2148                # remove duplicated chennal ranges
2149                for i in reversed(xrange(len(res[spw]))):
2150                    for j in xrange(i):
2151                        if ((res[spw][i][0]-res[spw][j][1])*(res[spw][i][1]-res[spw][j][0]) <= 0):
2152                            res[spw][j][0] = min(res[spw][i][0], res[spw][j][0])
2153                            res[spw][j][1] = max(res[spw][i][1], res[spw][j][1])
2154                            res[spw].pop(i)
2155                            break
2156            else:
2157                del res[spw]
2158
2159        if len(res) == 0:
2160            raise RuntimeError("No valid spw.")
2161       
2162        # restore original values
2163        self.set_unit(orig_unit)
2164        if restfreq is not None:
2165            self._setmolidcol_list(orig_molids)
2166        if frame is not None:
2167            self.set_freqframe(orig_frame)
2168        if doppler is not None:
2169            self.set_doppler(orig_doppler)
2170       
2171        return res
2172   
2173    @asaplog_post_dec
2174    def get_first_rowno_by_if(self, ifno):
2175        found = False
2176        for irow in xrange(self.nrow()):
2177            if (self.getif(irow) == ifno):
2178                res = irow
2179                found = True
2180                break
2181
2182        if not found: raise RuntimeError("No valid spw.")
2183       
2184        return res
2185
2186    @asaplog_post_dec
2187    def _get_coordinate_list(self):
2188        res = []
2189        spws = self.getifnos()
2190        for spw in spws:
2191            elem = {}
2192            elem['if']    = spw
2193            elem['coord'] = self.get_coordinate(self.get_first_rowno_by_if(spw))
2194            res.append(elem)
2195
2196        return res
2197   
2198    @asaplog_post_dec
2199    def parse_maskexpr(self, maskstring):
2200        """
2201        Parse CASA type mask selection syntax (IF dependent).
2202
2203        Parameters:
2204            maskstring : A string mask selection expression.
2205                         A comma separated selections mean different IF -
2206                         channel combinations. IFs and channel selections
2207                         are partitioned by a colon, ':'.
2208                     examples:
2209                         ''          = all IFs (all channels)
2210                         '<2,4~6,9'  = IFs 0,1,4,5,6,9 (all channels)
2211                         '3:3~45;60' = channels 3 to 45 and 60 in IF 3
2212                         '0~1:2~6,8' = channels 2 to 6 in IFs 0,1, and
2213                                       all channels in IF8
2214        Returns:
2215        A dictionary of selected (valid) IF and masklist pairs,
2216        e.g. {'0': [[50,250],[350,462]], '2': [[100,400],[550,974]]}
2217        """
2218        if not isinstance(maskstring,str):
2219            asaplog.post()
2220            asaplog.push("Mask expression should be a string.")
2221            asaplog.post("ERROR")
2222       
2223        valid_ifs = self.getifnos()
2224        frequnit = self.get_unit()
2225        seldict = {}
2226        if maskstring == "":
2227            maskstring = str(valid_ifs)[1:-1]
2228        ## split each selection "IF range[:CHAN range]"
2229        # split maskstring by "<spaces>,<spaces>"
2230        comma_sep = re.compile('\s*,\s*')
2231        sellist = comma_sep.split(maskstring)
2232        # separator by "<spaces>:<spaces>"
2233        collon_sep = re.compile('\s*:\s*')
2234        for currselstr in sellist:
2235            selset = collon_sep.split(currselstr)
2236            # spw and mask string (may include ~, < or >)
2237            spwmasklist = self._parse_selection(selset[0], typestr='integer',
2238                                                minval=min(valid_ifs),
2239                                                maxval=max(valid_ifs))
2240            for spwlist in spwmasklist:
2241                selspws = []
2242                for ispw in range(spwlist[0],spwlist[1]+1):
2243                    # Put into the list only if ispw exists
2244                    if valid_ifs.count(ispw):
2245                        selspws.append(ispw)
2246            del spwmasklist, spwlist
2247
2248            # parse frequency mask list
2249            if len(selset) > 1:
2250                freqmasklist = self._parse_selection(selset[1], typestr='float',
2251                                                     offset=0.)
2252            else:
2253                # want to select the whole spectrum
2254                freqmasklist = [None]
2255
2256            ## define a dictionary of spw - masklist combination
2257            for ispw in selspws:
2258                #print "working on", ispw
2259                spwstr = str(ispw)
2260                if len(selspws) == 0:
2261                    # empty spw
2262                    continue
2263                else:
2264                    ## want to get min and max of the spw and
2265                    ## offset to set for '<' and '>'
2266                    if frequnit == 'channel':
2267                        minfreq = 0
2268                        maxfreq = self.nchan(ifno=ispw)
2269                        offset = 0.5
2270                    else:
2271                        ## This is ugly part. need improvement
2272                        for ifrow in xrange(self.nrow()):
2273                            if self.getif(ifrow) == ispw:
2274                                #print "IF",ispw,"found in row =",ifrow
2275                                break
2276                        freqcoord = self.get_coordinate(ifrow)
2277                        freqs = self._getabcissa(ifrow)
2278                        minfreq = min(freqs)
2279                        maxfreq = max(freqs)
2280                        if len(freqs) == 1:
2281                            offset = 0.5
2282                        elif frequnit.find('Hz') > 0:
2283                            offset = abs(freqcoord.to_frequency(1,
2284                                                                unit=frequnit)
2285                                         -freqcoord.to_frequency(0,
2286                                                                 unit=frequnit)
2287                                         )*0.5
2288                        elif frequnit.find('m/s') > 0:
2289                            offset = abs(freqcoord.to_velocity(1,
2290                                                               unit=frequnit)
2291                                         -freqcoord.to_velocity(0,
2292                                                                unit=frequnit)
2293                                         )*0.5
2294                        else:
2295                            asaplog.post()
2296                            asaplog.push("Invalid frequency unit")
2297                            asaplog.post("ERROR")
2298                        del freqs, freqcoord, ifrow
2299                    for freq in freqmasklist:
2300                        selmask = freq or [minfreq, maxfreq]
2301                        if selmask[0] == None:
2302                            ## selection was "<freq[1]".
2303                            if selmask[1] < minfreq:
2304                                ## avoid adding region selection
2305                                selmask = None
2306                            else:
2307                                selmask = [minfreq,selmask[1]-offset]
2308                        elif selmask[1] == None:
2309                            ## selection was ">freq[0]"
2310                            if selmask[0] > maxfreq:
2311                                ## avoid adding region selection
2312                                selmask = None
2313                            else:
2314                                selmask = [selmask[0]+offset,maxfreq]
2315                        if selmask:
2316                            if not seldict.has_key(spwstr):
2317                                # new spw selection
2318                                seldict[spwstr] = []
2319                            seldict[spwstr] += [selmask]
2320                    del minfreq,maxfreq,offset,freq,selmask
2321                del spwstr
2322            del freqmasklist
2323        del valid_ifs
2324        if len(seldict) == 0:
2325            asaplog.post()
2326            asaplog.push("No valid selection in the mask expression: "
2327                         +maskstring)
2328            asaplog.post("WARN")
2329            return None
2330        msg = "Selected masklist:\n"
2331        for sif, lmask in seldict.iteritems():
2332            msg += "   IF"+sif+" - "+str(lmask)+"\n"
2333        asaplog.push(msg)
2334        return seldict
2335
2336    @asaplog_post_dec
2337    def parse_idx_selection(self, mode, selexpr):
2338        """
2339        Parse CASA type mask selection syntax of SCANNO, IFNO, POLNO,
2340        BEAMNO, and row number
2341
2342        Parameters:
2343            mode       : which column to select.
2344                         ['scan',|'if'|'pol'|'beam'|'row']
2345            selexpr    : A comma separated selection expression.
2346                     examples:
2347                         ''          = all (returns [])
2348                         '<2,4~6,9'  = indices less than 2, 4 to 6 and 9
2349                                       (returns [0,1,4,5,6,9])
2350        Returns:
2351        A List of selected indices
2352        """
2353        if selexpr == "":
2354            return []
2355        valid_modes = {'s': 'scan', 'i': 'if', 'p': 'pol',
2356                       'b': 'beam', 'r': 'row'}
2357        smode =  mode.lower()[0]
2358        if not (smode in valid_modes.keys()):
2359            msg = "Invalid mode '%s'. Valid modes are %s" %\
2360                  (mode, str(valid_modes.values()))
2361            asaplog.post()
2362            asaplog.push(msg)
2363            asaplog.post("ERROR")
2364        mode = valid_modes[smode]
2365        minidx = None
2366        maxidx = None
2367        if smode == 'r':
2368            minidx = 0
2369            maxidx = self.nrow()
2370        else:
2371            idx = getattr(self,"get"+mode+"nos")()
2372            minidx = min(idx)
2373            maxidx = max(idx)
2374            del idx
2375        # split selexpr by "<spaces>,<spaces>"
2376        comma_sep = re.compile('\s*,\s*')
2377        sellist = comma_sep.split(selexpr)
2378        idxlist = []
2379        for currselstr in sellist:
2380            # single range (may include ~, < or >)
2381            currlist = self._parse_selection(currselstr, typestr='integer',
2382                                             minval=minidx,maxval=maxidx)
2383            for thelist in currlist:
2384                idxlist += range(thelist[0],thelist[1]+1)
2385        # remove duplicated elements after first ones
2386        for i in reversed(xrange(len(idxlist))):
2387            if idxlist.index(idxlist[i]) < i:
2388                idxlist.pop(i)
2389        msg = "Selected %s: %s" % (mode.upper()+"NO", str(idxlist))
2390        asaplog.push(msg)
2391        return idxlist
2392
2393    def _parse_selection(self, selstr, typestr='float', offset=0.,
2394                         minval=None, maxval=None):
2395        """
2396        Parameters:
2397            selstr :    The Selection string, e.g., '<3;5~7;100~103;9'
2398            typestr :   The type of the values in returned list
2399                        ('integer' or 'float')
2400            offset :    The offset value to subtract from or add to
2401                        the boundary value if the selection string
2402                        includes '<' or '>' [Valid only for typestr='float']
2403            minval, maxval :  The minimum/maximum values to set if the
2404                              selection string includes '<' or '>'.
2405                              The list element is filled with None by default.
2406        Returns:
2407            A list of min/max pair of selections.
2408        Example:
2409            _parse_selection('<3;5~7;9',typestr='int',minval=0)
2410            --> returns [[0,2],[5,7],[9,9]]
2411            _parse_selection('<3;5~7;9',typestr='float',offset=0.5,minval=0)
2412            --> returns [[0.,2.5],[5.0,7.0],[9.,9.]]
2413        """
2414        # split selstr by '<spaces>;<spaces>'
2415        semi_sep = re.compile('\s*;\s*')
2416        selgroups = semi_sep.split(selstr)
2417        sellists = []
2418        if typestr.lower().startswith('int'):
2419            formatfunc = int
2420            offset = 1
2421        else:
2422            formatfunc = float
2423       
2424        for currsel in  selgroups:
2425            if currsel.strip() == '*' or len(currsel.strip()) == 0:
2426                minsel = minval
2427                maxsel = maxval
2428            if currsel.find('~') > 0:
2429                # val0 <= x <= val1
2430                minsel = formatfunc(currsel.split('~')[0].strip())
2431                maxsel = formatfunc(currsel.split('~')[1].strip())
2432            elif currsel.strip().find('<=') > -1:
2433                bound = currsel.split('<=')
2434                try: # try "x <= val"
2435                    minsel = minval
2436                    maxsel = formatfunc(bound[1].strip())
2437                except ValueError: # now "val <= x"
2438                    minsel = formatfunc(bound[0].strip())
2439                    maxsel = maxval
2440            elif currsel.strip().find('>=') > -1:
2441                bound = currsel.split('>=')
2442                try: # try "x >= val"
2443                    minsel = formatfunc(bound[1].strip())
2444                    maxsel = maxval
2445                except ValueError: # now "val >= x"
2446                    minsel = minval
2447                    maxsel = formatfunc(bound[0].strip())
2448            elif currsel.strip().find('<') > -1:
2449                bound = currsel.split('<')
2450                try: # try "x < val"
2451                    minsel = minval
2452                    maxsel = formatfunc(bound[1].strip()) \
2453                             - formatfunc(offset)
2454                except ValueError: # now "val < x"
2455                    minsel = formatfunc(bound[0].strip()) \
2456                         + formatfunc(offset)
2457                    maxsel = maxval
2458            elif currsel.strip().find('>') > -1:
2459                bound = currsel.split('>')
2460                try: # try "x > val"
2461                    minsel = formatfunc(bound[1].strip()) \
2462                             + formatfunc(offset)
2463                    maxsel = maxval
2464                except ValueError: # now "val > x"
2465                    minsel = minval
2466                    maxsel = formatfunc(bound[0].strip()) \
2467                             - formatfunc(offset)
2468            else:
2469                minsel = formatfunc(currsel)
2470                maxsel = formatfunc(currsel)
2471            sellists.append([minsel,maxsel])
2472        return sellists
2473
2474#    def get_restfreqs(self):
2475#        """
2476#        Get the restfrequency(s) stored in this scantable.
2477#        The return value(s) are always of unit 'Hz'
2478#        Parameters:
2479#            none
2480#        Returns:
2481#            a list of doubles
2482#        """
2483#        return list(self._getrestfreqs())
2484
2485    def get_restfreqs(self, ids=None):
2486        """\
2487        Get the restfrequency(s) stored in this scantable.
2488        The return value(s) are always of unit 'Hz'
2489
2490        Parameters:
2491
2492            ids: (optional) a list of MOLECULE_ID for that restfrequency(s) to
2493                 be retrieved
2494
2495        Returns:
2496
2497            dictionary containing ids and a list of doubles for each id
2498
2499        """
2500        if ids is None:
2501            rfreqs = {}
2502            idlist = self.getmolnos()
2503            for i in idlist:
2504                rfreqs[i] = list(self._getrestfreqs(i))
2505            return rfreqs
2506        else:
2507            if type(ids) == list or type(ids) == tuple:
2508                rfreqs = {}
2509                for i in ids:
2510                    rfreqs[i] = list(self._getrestfreqs(i))
2511                return rfreqs
2512            else:
2513                return list(self._getrestfreqs(ids))
2514
2515    @asaplog_post_dec
2516    def set_restfreqs(self, freqs=None, unit='Hz'):
2517        """\
2518        Set or replace the restfrequency specified and
2519        if the 'freqs' argument holds a scalar,
2520        then that rest frequency will be applied to all the selected
2521        data.  If the 'freqs' argument holds
2522        a vector, then it MUST be of equal or smaller length than
2523        the number of IFs (and the available restfrequencies will be
2524        replaced by this vector).  In this case, *all* data have
2525        the restfrequency set per IF according
2526        to the corresponding value you give in the 'freqs' vector.
2527        E.g. 'freqs=[1e9, 2e9]'  would mean IF 0 gets restfreq 1e9 and
2528        IF 1 gets restfreq 2e9.
2529
2530        You can also specify the frequencies via a linecatalog.
2531
2532        Parameters:
2533
2534            freqs:   list of rest frequency values or string idenitfiers
2535
2536            unit:    unit for rest frequency (default 'Hz')
2537
2538
2539        Example::
2540
2541            # set the given restfrequency for the all currently selected IFs
2542            scan.set_restfreqs(freqs=1.4e9)
2543            # set restfrequencies for the n IFs  (n > 1) in the order of the
2544            # list, i.e
2545            # IF0 -> 1.4e9, IF1 ->  1.41e9, IF3 -> 1.42e9
2546            # len(list_of_restfreqs) == nIF
2547            # for nIF == 1 the following will set multiple restfrequency for
2548            # that IF
2549            scan.set_restfreqs(freqs=[1.4e9, 1.41e9, 1.42e9])
2550            # set multiple restfrequencies per IF. as a list of lists where
2551            # the outer list has nIF elements, the inner s arbitrary
2552            scan.set_restfreqs(freqs=[[1.4e9, 1.41e9], [1.67e9]])
2553
2554       *Note*:
2555
2556            To do more sophisticate Restfrequency setting, e.g. on a
2557            source and IF basis, use scantable.set_selection() before using
2558            this function::
2559
2560                # provided your scantable is called scan
2561                selection = selector()
2562                selection.set_name('ORION*')
2563                selection.set_ifs([1])
2564                scan.set_selection(selection)
2565                scan.set_restfreqs(freqs=86.6e9)
2566
2567        """
2568        varlist = vars()
2569        from asap import linecatalog
2570        # simple  value
2571        if isinstance(freqs, int) or isinstance(freqs, float):
2572            self._setrestfreqs([freqs], [""], unit)
2573        # list of values
2574        elif isinstance(freqs, list) or isinstance(freqs, tuple):
2575            # list values are scalars
2576            if isinstance(freqs[-1], int) or isinstance(freqs[-1], float):
2577                if len(freqs) == 1:
2578                    self._setrestfreqs(freqs, [""], unit)
2579                else:
2580                    # allow the 'old' mode of setting mulitple IFs
2581                    savesel = self._getselection()
2582                    sel = self.get_selection()
2583                    iflist = self.getifnos()
2584                    if len(freqs)>len(iflist):
2585                        raise ValueError("number of elements in list of list "
2586                                         "exeeds the current IF selections")
2587                    iflist = self.getifnos()
2588                    for i, fval in enumerate(freqs):
2589                        sel.set_ifs(iflist[i])
2590                        self._setselection(sel)
2591                        self._setrestfreqs([fval], [""], unit)
2592                    self._setselection(savesel)
2593
2594            # list values are dict, {'value'=, 'name'=)
2595            elif isinstance(freqs[-1], dict):
2596                values = []
2597                names = []
2598                for d in freqs:
2599                    values.append(d["value"])
2600                    names.append(d["name"])
2601                self._setrestfreqs(values, names, unit)
2602            elif isinstance(freqs[-1], list) or isinstance(freqs[-1], tuple):
2603                savesel = self._getselection()
2604                sel = self.get_selection()
2605                iflist = self.getifnos()
2606                if len(freqs)>len(iflist):
2607                    raise ValueError("number of elements in list of list exeeds"
2608                                     " the current IF selections")
2609                for i, fval in enumerate(freqs):
2610                    sel.set_ifs(iflist[i])
2611                    self._setselection(sel)
2612                    self._setrestfreqs(fval, [""], unit)
2613                self._setselection(savesel)
2614        # freqs are to be taken from a linecatalog
2615        elif isinstance(freqs, linecatalog):
2616            savesel = self._getselection()
2617            sel = self.get_selection()
2618            for i in xrange(freqs.nrow()):
2619                sel.set_ifs(iflist[i])
2620                self._setselection(sel)
2621                self._setrestfreqs([freqs.get_frequency(i)],
2622                                   [freqs.get_name(i)], "MHz")
2623                # ensure that we are not iterating past nIF
2624                if i == self.nif()-1: break
2625            self._setselection(savesel)
2626        else:
2627            return
2628        self._add_history("set_restfreqs", varlist)
2629
2630    @asaplog_post_dec
2631    def shift_refpix(self, delta):
2632        """\
2633        Shift the reference pixel of the Spectra Coordinate by an
2634        integer amount.
2635
2636        Parameters:
2637
2638            delta:   the amount to shift by
2639
2640        *Note*:
2641
2642            Be careful using this with broadband data.
2643
2644        """
2645        varlist = vars()
2646        Scantable.shift_refpix(self, delta)
2647        s._add_history("shift_refpix", varlist)
2648
2649    @asaplog_post_dec
2650    def history(self, filename=None, nrows=-1, start=0):
2651        """\
2652        Print the history. Optionally to a file.
2653
2654        Parameters:
2655
2656            filename:    The name of the file to save the history to.
2657
2658        """
2659        n = self._historylength()
2660        if nrows == -1:
2661            nrows = n
2662        if start+nrows > n:
2663            nrows = nrows-start
2664        if n > 1000 and nrows == n:
2665            nrows = 1000
2666            start = n-1000
2667            asaplog.push("Warning: History has {0} entries. Displaying last "
2668                         "1000".format(n))
2669        hist = list(self._gethistory(nrows, start))
2670        out = "-"*80
2671        for h in hist:
2672            if not h.strip():
2673                continue
2674            if h.find("---") >-1:
2675                continue
2676            else:
2677                items = h.split("##")
2678                date = items[0]
2679                func = items[1]
2680                items = items[2:]
2681                out += "\n"+date+"\n"
2682                out += "Function: %s\n  Parameters:" % (func)
2683                for i in items:
2684                    if i == '':
2685                        continue
2686                    s = i.split("=")
2687                    out += "\n   %s = %s" % (s[0], s[1])
2688                out = "\n".join([out, "*"*80])
2689        if filename is not None:
2690            if filename is "":
2691                filename = 'scantable_history.txt'
2692            filename = os.path.expandvars(os.path.expanduser(filename))
2693            if not os.path.isdir(filename):
2694                data = open(filename, 'w')
2695                data.write(out)
2696                data.close()
2697            else:
2698                msg = "Illegal file name '%s'." % (filename)
2699                raise IOError(msg)
2700        return page(out)
2701
2702    #
2703    # Maths business
2704    #
2705    @asaplog_post_dec
2706    def average_time(self, mask=None, scanav=False, weight='tint', align=False,
2707                     avmode="NONE"):
2708        """\
2709        Return the (time) weighted average of a scan. Scans will be averaged
2710        only if the source direction (RA/DEC) is within 1' otherwise
2711
2712        *Note*:
2713
2714            in channels only - align if necessary
2715
2716        Parameters:
2717
2718            mask:     an optional mask (only used for 'var' and 'tsys'
2719                      weighting)
2720
2721            scanav:   True averages each scan separately
2722                      False (default) averages all scans together,
2723
2724            weight:   Weighting scheme.
2725                      'none'     (mean no weight)
2726                      'var'      (1/var(spec) weighted)
2727                      'tsys'     (1/Tsys**2 weighted)
2728                      'tint'     (integration time weighted)
2729                      'tintsys'  (Tint/Tsys**2)
2730                      'median'   ( median averaging)
2731                      The default is 'tint'
2732
2733            align:    align the spectra in velocity before averaging. It takes
2734                      the time of the first spectrum as reference time.
2735            avmode:   'SOURCE' - also select by source name -  or
2736                      'NONE' (default). Not applicable for scanav=True or
2737                      weight=median
2738
2739        Example::
2740
2741            # time average the scantable without using a mask
2742            newscan = scan.average_time()
2743
2744        """
2745        varlist = vars()
2746        weight = weight or 'TINT'
2747        mask = mask or ()
2748        scanav = (scanav and 'SCAN') or avmode.upper()
2749        scan = (self, )
2750
2751        if align:
2752            scan = (self.freq_align(insitu=False), )
2753            asaplog.push("Note: Alignment is don on a source-by-source basis")
2754            asaplog.push("Note: Averaging (by default) is not")
2755            # we need to set it to SOURCE averaging here           
2756        s = None
2757        if weight.upper() == 'MEDIAN':
2758            s = scantable(self._math._averagechannel(scan[0], 'MEDIAN',
2759                                                     scanav))
2760        else:
2761            s = scantable(self._math._average(scan, mask, weight.upper(),
2762                          scanav))
2763        s._add_history("average_time", varlist)
2764        return s
2765
2766    @asaplog_post_dec
2767    def convert_flux(self, jyperk=None, eta=None, d=None, insitu=None):
2768        """\
2769        Return a scan where all spectra are converted to either
2770        Jansky or Kelvin depending upon the flux units of the scan table.
2771        By default the function tries to look the values up internally.
2772        If it can't find them (or if you want to over-ride), you must
2773        specify EITHER jyperk OR eta (and D which it will try to look up
2774        also if you don't set it). jyperk takes precedence if you set both.
2775
2776        Parameters:
2777
2778            jyperk:      the Jy / K conversion factor
2779
2780            eta:         the aperture efficiency
2781
2782            d:           the geometric diameter (metres)
2783
2784            insitu:      if False a new scantable is returned.
2785                         Otherwise, the scaling is done in-situ
2786                         The default is taken from .asaprc (False)
2787
2788        """
2789        if insitu is None: insitu = rcParams['insitu']
2790        self._math._setinsitu(insitu)
2791        varlist = vars()
2792        jyperk = jyperk or -1.0
2793        d = d or -1.0
2794        eta = eta or -1.0
2795        s = scantable(self._math._convertflux(self, d, eta, jyperk))
2796        s._add_history("convert_flux", varlist)
2797        if insitu: self._assign(s)
2798        else: return s
2799
2800    @asaplog_post_dec
2801    def gain_el(self, poly=None, filename="", method="linear", insitu=None):
2802        """\
2803        Return a scan after applying a gain-elevation correction.
2804        The correction can be made via either a polynomial or a
2805        table-based interpolation (and extrapolation if necessary).
2806        You specify polynomial coefficients, an ascii table or neither.
2807        If you specify neither, then a polynomial correction will be made
2808        with built in coefficients known for certain telescopes (an error
2809        will occur if the instrument is not known).
2810        The data and Tsys are *divided* by the scaling factors.
2811
2812        Parameters:
2813
2814            poly:        Polynomial coefficients (default None) to compute a
2815                         gain-elevation correction as a function of
2816                         elevation (in degrees).
2817
2818            filename:    The name of an ascii file holding correction factors.
2819                         The first row of the ascii file must give the column
2820                         names and these MUST include columns
2821                         'ELEVATION' (degrees) and 'FACTOR' (multiply data
2822                         by this) somewhere.
2823                         The second row must give the data type of the
2824                         column. Use 'R' for Real and 'I' for Integer.
2825                         An example file would be
2826                         (actual factors are arbitrary) :
2827
2828                         TIME ELEVATION FACTOR
2829                         R R R
2830                         0.1 0 0.8
2831                         0.2 20 0.85
2832                         0.3 40 0.9
2833                         0.4 60 0.85
2834                         0.5 80 0.8
2835                         0.6 90 0.75
2836
2837            method:      Interpolation method when correcting from a table.
2838                         Values are  'nearest', 'linear' (default), 'cubic'
2839                         and 'spline'
2840
2841            insitu:      if False a new scantable is returned.
2842                         Otherwise, the scaling is done in-situ
2843                         The default is taken from .asaprc (False)
2844
2845        """
2846
2847        if insitu is None: insitu = rcParams['insitu']
2848        self._math._setinsitu(insitu)
2849        varlist = vars()
2850        poly = poly or ()
2851        from os.path import expandvars
2852        filename = expandvars(filename)
2853        s = scantable(self._math._gainel(self, poly, filename, method))
2854        s._add_history("gain_el", varlist)
2855        if insitu:
2856            self._assign(s)
2857        else:
2858            return s
2859
2860    @asaplog_post_dec
2861    def freq_align(self, reftime=None, method='cubic', insitu=None):
2862        """\
2863        Return a scan where all rows have been aligned in frequency/velocity.
2864        The alignment frequency frame (e.g. LSRK) is that set by function
2865        set_freqframe.
2866
2867        Parameters:
2868
2869            reftime:     reference time to align at. By default, the time of
2870                         the first row of data is used.
2871
2872            method:      Interpolation method for regridding the spectra.
2873                         Choose from 'nearest', 'linear', 'cubic' (default)
2874                         and 'spline'
2875
2876            insitu:      if False a new scantable is returned.
2877                         Otherwise, the scaling is done in-situ
2878                         The default is taken from .asaprc (False)
2879
2880        """
2881        if insitu is None: insitu = rcParams["insitu"]
2882        oldInsitu = self._math._insitu()
2883        self._math._setinsitu(insitu)
2884        varlist = vars()
2885        reftime = reftime or ""
2886        s = scantable(self._math._freq_align(self, reftime, method))
2887        s._add_history("freq_align", varlist)
2888        self._math._setinsitu(oldInsitu)
2889        if insitu:
2890            self._assign(s)
2891        else:
2892            return s
2893
2894    @asaplog_post_dec
2895    def opacity(self, tau=None, insitu=None):
2896        """\
2897        Apply an opacity correction. The data
2898        and Tsys are multiplied by the correction factor.
2899
2900        Parameters:
2901
2902            tau:         (list of) opacity from which the correction factor is
2903                         exp(tau*ZD)
2904                         where ZD is the zenith-distance.
2905                         If a list is provided, it has to be of length nIF,
2906                         nIF*nPol or 1 and in order of IF/POL, e.g.
2907                         [opif0pol0, opif0pol1, opif1pol0 ...]
2908                         if tau is `None` the opacities are determined from a
2909                         model.
2910
2911            insitu:      if False a new scantable is returned.
2912                         Otherwise, the scaling is done in-situ
2913                         The default is taken from .asaprc (False)
2914
2915        """
2916        if insitu is None:
2917            insitu = rcParams['insitu']
2918        self._math._setinsitu(insitu)
2919        varlist = vars()
2920        if not hasattr(tau, "__len__"):
2921            tau = [tau]
2922        s = scantable(self._math._opacity(self, tau))
2923        s._add_history("opacity", varlist)
2924        if insitu:
2925            self._assign(s)
2926        else:
2927            return s
2928
2929    @asaplog_post_dec
2930    def bin(self, width=5, insitu=None):
2931        """\
2932        Return a scan where all spectra have been binned up.
2933
2934        Parameters:
2935
2936            width:       The bin width (default=5) in pixels
2937
2938            insitu:      if False a new scantable is returned.
2939                         Otherwise, the scaling is done in-situ
2940                         The default is taken from .asaprc (False)
2941
2942        """
2943        if insitu is None:
2944            insitu = rcParams['insitu']
2945        self._math._setinsitu(insitu)
2946        varlist = vars()
2947        s = scantable(self._math._bin(self, width))
2948        s._add_history("bin", varlist)
2949        if insitu:
2950            self._assign(s)
2951        else:
2952            return s
2953
2954    @asaplog_post_dec
2955    def reshape(self, first, last, insitu=None):
2956        """Resize the band by providing first and last channel.
2957        This will cut off all channels outside [first, last].
2958        """
2959        if insitu is None:
2960            insitu = rcParams['insitu']
2961        varlist = vars()
2962        if last < 0:
2963            last = self.nchan()-1 + last
2964        s = None
2965        if insitu:
2966            s = self
2967        else:
2968            s = self.copy()
2969        s._reshape(first,last)
2970        s._add_history("reshape", varlist)
2971        if not insitu:
2972            return s       
2973
2974    @asaplog_post_dec
2975    def resample(self, width=5, method='cubic', insitu=None):
2976        """\
2977        Return a scan where all spectra have been binned up.
2978
2979        Parameters:
2980
2981            width:       The bin width (default=5) in pixels
2982
2983            method:      Interpolation method when correcting from a table.
2984                         Values are  'nearest', 'linear', 'cubic' (default)
2985                         and 'spline'
2986
2987            insitu:      if False a new scantable is returned.
2988                         Otherwise, the scaling is done in-situ
2989                         The default is taken from .asaprc (False)
2990
2991        """
2992        if insitu is None:
2993            insitu = rcParams['insitu']
2994        self._math._setinsitu(insitu)
2995        varlist = vars()
2996        s = scantable(self._math._resample(self, method, width))
2997        s._add_history("resample", varlist)
2998        if insitu:
2999            self._assign(s)
3000        else:
3001            return s
3002
3003    @asaplog_post_dec
3004    def average_pol(self, mask=None, weight='none'):
3005        """\
3006        Average the Polarisations together.
3007
3008        Parameters:
3009
3010            mask:        An optional mask defining the region, where the
3011                         averaging will be applied. The output will have all
3012                         specified points masked.
3013
3014            weight:      Weighting scheme. 'none' (default), 'var' (1/var(spec)
3015                         weighted), or 'tsys' (1/Tsys**2 weighted)
3016
3017        """
3018        varlist = vars()
3019        mask = mask or ()
3020        s = scantable(self._math._averagepol(self, mask, weight.upper()))
3021        s._add_history("average_pol", varlist)
3022        return s
3023
3024    @asaplog_post_dec
3025    def average_beam(self, mask=None, weight='none'):
3026        """\
3027        Average the Beams together.
3028
3029        Parameters:
3030            mask:        An optional mask defining the region, where the
3031                         averaging will be applied. The output will have all
3032                         specified points masked.
3033
3034            weight:      Weighting scheme. 'none' (default), 'var' (1/var(spec)
3035                         weighted), or 'tsys' (1/Tsys**2 weighted)
3036
3037        """
3038        varlist = vars()
3039        mask = mask or ()
3040        s = scantable(self._math._averagebeams(self, mask, weight.upper()))
3041        s._add_history("average_beam", varlist)
3042        return s
3043
3044    def parallactify(self, pflag):
3045        """\
3046        Set a flag to indicate whether this data should be treated as having
3047        been 'parallactified' (total phase == 0.0)
3048
3049        Parameters:
3050
3051            pflag:  Bool indicating whether to turn this on (True) or
3052                    off (False)
3053
3054        """
3055        varlist = vars()
3056        self._parallactify(pflag)
3057        self._add_history("parallactify", varlist)
3058
3059    @asaplog_post_dec
3060    def convert_pol(self, poltype=None):
3061        """\
3062        Convert the data to a different polarisation type.
3063        Note that you will need cross-polarisation terms for most conversions.
3064
3065        Parameters:
3066
3067            poltype:    The new polarisation type. Valid types are:
3068                        'linear', 'circular', 'stokes' and 'linpol'
3069
3070        """
3071        varlist = vars()
3072        s = scantable(self._math._convertpol(self, poltype))
3073        s._add_history("convert_pol", varlist)
3074        return s
3075
3076    @asaplog_post_dec
3077    def smooth(self, kernel="hanning", width=5.0, order=2, plot=False,
3078               insitu=None):
3079        """\
3080        Smooth the spectrum by the specified kernel (conserving flux).
3081
3082        Parameters:
3083
3084            kernel:     The type of smoothing kernel. Select from
3085                        'hanning' (default), 'gaussian', 'boxcar', 'rmedian'
3086                        or 'poly'
3087
3088            width:      The width of the kernel in pixels. For hanning this is
3089                        ignored otherwise it defauls to 5 pixels.
3090                        For 'gaussian' it is the Full Width Half
3091                        Maximum. For 'boxcar' it is the full width.
3092                        For 'rmedian' and 'poly' it is the half width.
3093
3094            order:      Optional parameter for 'poly' kernel (default is 2), to
3095                        specify the order of the polnomial. Ignored by all other
3096                        kernels.
3097
3098            plot:       plot the original and the smoothed spectra.
3099                        In this each indivual fit has to be approved, by
3100                        typing 'y' or 'n'
3101
3102            insitu:     if False a new scantable is returned.
3103                        Otherwise, the scaling is done in-situ
3104                        The default is taken from .asaprc (False)
3105
3106        """
3107        if insitu is None: insitu = rcParams['insitu']
3108        self._math._setinsitu(insitu)
3109        varlist = vars()
3110
3111        if plot: orgscan = self.copy()
3112
3113        s = scantable(self._math._smooth(self, kernel.lower(), width, order))
3114        s._add_history("smooth", varlist)
3115
3116        action = 'H'
3117        if plot:
3118            from asap.asapplotter import new_asaplot
3119            theplot = new_asaplot(rcParams['plotter.gui'])
3120            from matplotlib import rc as rcp
3121            rcp('lines', linewidth=1)
3122            theplot.set_panels()
3123            ylab=s._get_ordinate_label()
3124            #theplot.palette(0,["#777777","red"])
3125            for r in xrange(s.nrow()):
3126                xsm=s._getabcissa(r)
3127                ysm=s._getspectrum(r)
3128                xorg=orgscan._getabcissa(r)
3129                yorg=orgscan._getspectrum(r)
3130                if action != "N": #skip plotting if rejecting all
3131                    theplot.clear()
3132                    theplot.hold()
3133                    theplot.set_axes('ylabel',ylab)
3134                    theplot.set_axes('xlabel',s._getabcissalabel(r))
3135                    theplot.set_axes('title',s._getsourcename(r))
3136                    theplot.set_line(label='Original',color="#777777")
3137                    theplot.plot(xorg,yorg)
3138                    theplot.set_line(label='Smoothed',color="red")
3139                    theplot.plot(xsm,ysm)
3140                    ### Ugly part for legend
3141                    for i in [0,1]:
3142                        theplot.subplots[0]['lines'].append(
3143                            [theplot.subplots[0]['axes'].lines[i]]
3144                            )
3145                    theplot.release()
3146                    ### Ugly part for legend
3147                    theplot.subplots[0]['lines']=[]
3148                res = self._get_verify_action("Accept smoothing?",action)
3149                #print "IF%d, POL%d: got result = %s" %(s.getif(r),s.getpol(r),res)
3150                if r == 0: action = None
3151                #res = raw_input("Accept smoothing ([y]/n): ")
3152                if res.upper() == 'N':
3153                    # reject for the current rows
3154                    s._setspectrum(yorg, r)
3155                elif res.upper() == 'R':
3156                    # reject all the following rows
3157                    action = "N"
3158                    s._setspectrum(yorg, r)
3159                elif res.upper() == 'A':
3160                    # accept all the following rows
3161                    break
3162            theplot.quit()
3163            del theplot
3164            del orgscan
3165
3166        if insitu: self._assign(s)
3167        else: return s
3168
3169    @asaplog_post_dec
3170    def regrid_channel(self, width=5, plot=False, insitu=None):
3171        """\
3172        Regrid the spectra by the specified channel width
3173
3174        Parameters:
3175
3176            width:      The channel width (float) of regridded spectra
3177                        in the current spectral unit.
3178
3179            plot:       [NOT IMPLEMENTED YET]
3180                        plot the original and the regridded spectra.
3181                        In this each indivual fit has to be approved, by
3182                        typing 'y' or 'n'
3183
3184            insitu:     if False a new scantable is returned.
3185                        Otherwise, the scaling is done in-situ
3186                        The default is taken from .asaprc (False)
3187
3188        """
3189        if insitu is None: insitu = rcParams['insitu']
3190        varlist = vars()
3191
3192        if plot:
3193           asaplog.post()
3194           asaplog.push("Verification plot is not implemtnetd yet.")
3195           asaplog.post("WARN")
3196
3197        s = self.copy()
3198        s._regrid_specchan(width)
3199
3200        s._add_history("regrid_channel", varlist)
3201
3202#         if plot:
3203#             from asap.asapplotter import new_asaplot
3204#             theplot = new_asaplot(rcParams['plotter.gui'])
3205#             from matplotlib import rc as rcp
3206#             rcp('lines', linewidth=1)
3207#             theplot.set_panels()
3208#             ylab=s._get_ordinate_label()
3209#             #theplot.palette(0,["#777777","red"])
3210#             for r in xrange(s.nrow()):
3211#                 xsm=s._getabcissa(r)
3212#                 ysm=s._getspectrum(r)
3213#                 xorg=orgscan._getabcissa(r)
3214#                 yorg=orgscan._getspectrum(r)
3215#                 theplot.clear()
3216#                 theplot.hold()
3217#                 theplot.set_axes('ylabel',ylab)
3218#                 theplot.set_axes('xlabel',s._getabcissalabel(r))
3219#                 theplot.set_axes('title',s._getsourcename(r))
3220#                 theplot.set_line(label='Original',color="#777777")
3221#                 theplot.plot(xorg,yorg)
3222#                 theplot.set_line(label='Smoothed',color="red")
3223#                 theplot.plot(xsm,ysm)
3224#                 ### Ugly part for legend
3225#                 for i in [0,1]:
3226#                     theplot.subplots[0]['lines'].append(
3227#                         [theplot.subplots[0]['axes'].lines[i]]
3228#                         )
3229#                 theplot.release()
3230#                 ### Ugly part for legend
3231#                 theplot.subplots[0]['lines']=[]
3232#                 res = raw_input("Accept smoothing ([y]/n): ")
3233#                 if res.upper() == 'N':
3234#                     s._setspectrum(yorg, r)
3235#             theplot.quit()
3236#             del theplot
3237#             del orgscan
3238
3239        if insitu: self._assign(s)
3240        else: return s
3241
3242    @asaplog_post_dec
3243    def _parse_wn(self, wn):
3244        if isinstance(wn, list) or isinstance(wn, tuple):
3245            return wn
3246        elif isinstance(wn, int):
3247            return [ wn ]
3248        elif isinstance(wn, str):
3249            if '-' in wn:                            # case 'a-b' : return [a,a+1,...,b-1,b]
3250                val = wn.split('-')
3251                val = [int(val[0]), int(val[1])]
3252                val.sort()
3253                res = [i for i in xrange(val[0], val[1]+1)]
3254            elif wn[:2] == '<=' or wn[:2] == '=<':   # cases '<=a','=<a' : return [0,1,...,a-1,a]
3255                val = int(wn[2:])+1
3256                res = [i for i in xrange(val)]
3257            elif wn[-2:] == '>=' or wn[-2:] == '=>': # cases 'a>=','a=>' : return [0,1,...,a-1,a]
3258                val = int(wn[:-2])+1
3259                res = [i for i in xrange(val)]
3260            elif wn[0] == '<':                       # case '<a' :         return [0,1,...,a-2,a-1]
3261                val = int(wn[1:])
3262                res = [i for i in xrange(val)]
3263            elif wn[-1] == '>':                      # case 'a>' :         return [0,1,...,a-2,a-1]
3264                val = int(wn[:-1])
3265                res = [i for i in xrange(val)]
3266            elif wn[:2] == '>=' or wn[:2] == '=>':   # cases '>=a','=>a' : return [a,-999], which is
3267                                                     #                     then interpreted in C++
3268                                                     #                     side as [a,a+1,...,a_nyq]
3269                                                     #                     (CAS-3759)
3270                val = int(wn[2:])
3271                res = [val, -999]
3272                #res = [i for i in xrange(val, self.nchan()/2+1)]
3273            elif wn[-2:] == '<=' or wn[-2:] == '=<': # cases 'a<=','a=<' : return [a,-999], which is
3274                                                     #                     then interpreted in C++
3275                                                     #                     side as [a,a+1,...,a_nyq]
3276                                                     #                     (CAS-3759)
3277                val = int(wn[:-2])
3278                res = [val, -999]
3279                #res = [i for i in xrange(val, self.nchan()/2+1)]
3280            elif wn[0] == '>':                       # case '>a' :         return [a+1,-999], which is
3281                                                     #                     then interpreted in C++
3282                                                     #                     side as [a+1,a+2,...,a_nyq]
3283                                                     #                     (CAS-3759)
3284                val = int(wn[1:])+1
3285                res = [val, -999]
3286                #res = [i for i in xrange(val, self.nchan()/2+1)]
3287            elif wn[-1] == '<':                      # case 'a<' :         return [a+1,-999], which is
3288                                                     #                     then interpreted in C++
3289                                                     #                     side as [a+1,a+2,...,a_nyq]
3290                                                     #                     (CAS-3759)
3291                val = int(wn[:-1])+1
3292                res = [val, -999]
3293                #res = [i for i in xrange(val, self.nchan()/2+1)]
3294
3295            return res
3296        else:
3297            msg = 'wrong value given for addwn/rejwn'
3298            raise RuntimeError(msg)
3299
3300    @asaplog_post_dec
3301    def apply_bltable(self, insitu=None, retfitres=None, inbltable=None, outbltable=None, overwrite=None):
3302        """\
3303        Subtract baseline based on parameters written in Baseline Table.
3304
3305        Parameters:
3306            insitu:        if True, baseline fitting/subtraction is done
3307                           in-situ. If False, a new scantable with
3308                           baseline subtracted is returned. Actually,
3309                           format of the returned value depends on both
3310                           insitu and retfitres (see below).
3311                           The default is taken from .asaprc (False)
3312            retfitres:     if True, the results of baseline fitting (i.e.,
3313                           coefficients and rms) are returned.
3314                           default is False.
3315                           The format of the returned value of this
3316                           function varies as follows:
3317                           (1) in case insitu=True and retfitres=True:
3318                               fitting result.
3319                           (2) in case insitu=True and retfitres=False:
3320                               None.
3321                           (3) in case insitu=False and retfitres=True:
3322                               a dictionary containing a new scantable
3323                               (with baseline subtracted) and the fitting
3324                               results.
3325                           (4) in case insitu=False and retfitres=False:
3326                               a new scantable (with baseline subtracted).
3327            inbltable:     name of input baseline table. The row number of
3328                           scantable and that of inbltable must be
3329                           identical.
3330            outbltable:    name of output baseline table where baseline
3331                           parameters and fitting results recorded.
3332                           default is ''(no output).
3333            overwrite:     if True when an existing baseline table is
3334                           specified for outbltable, overwrites it.
3335                           Otherwise there is no harm.
3336                           default is False.
3337        """
3338
3339        try:
3340            varlist = vars()
3341            if retfitres      is None: retfitres      = False
3342            if inbltable      is None: raise ValueError("bltable missing.")
3343            if outbltable     is None: outbltable     = ''
3344            if overwrite      is None: overwrite      = False
3345
3346            if insitu is None: insitu = rcParams['insitu']
3347            if insitu:
3348                workscan = self
3349            else:
3350                workscan = self.copy()
3351
3352            sres = workscan._apply_bltable(inbltable,
3353                                           retfitres,
3354                                           outbltable,
3355                                           os.path.exists(outbltable),
3356                                           overwrite)
3357            if retfitres: res = parse_fitresult(sres)
3358
3359            workscan._add_history('apply_bltable', varlist)
3360
3361            if insitu:
3362                self._assign(workscan)
3363                if retfitres:
3364                    return res
3365                else:
3366                    return None
3367            else:
3368                if retfitres:
3369                    return {'scantable': workscan, 'fitresults': res}
3370                else:
3371                    return workscan
3372       
3373        except RuntimeError, e:
3374            raise_fitting_failure_exception(e)
3375
3376    @asaplog_post_dec
3377    def sub_baseline(self, insitu=None, retfitres=None, blinfo=None, bltable=None, overwrite=None):
3378        """\
3379        Subtract baseline based on parameters written in the input list.
3380
3381        Parameters:
3382            insitu:        if True, baseline fitting/subtraction is done
3383                           in-situ. If False, a new scantable with
3384                           baseline subtracted is returned. Actually,
3385                           format of the returned value depends on both
3386                           insitu and retfitres (see below).
3387                           The default is taken from .asaprc (False)
3388            retfitres:     if True, the results of baseline fitting (i.e.,
3389                           coefficients and rms) are returned.
3390                           default is False.
3391                           The format of the returned value of this
3392                           function varies as follows:
3393                           (1) in case insitu=True and retfitres=True:
3394                               fitting result.
3395                           (2) in case insitu=True and retfitres=False:
3396                               None.
3397                           (3) in case insitu=False and retfitres=True:
3398                               a dictionary containing a new scantable
3399                               (with baseline subtracted) and the fitting
3400                               results.
3401                           (4) in case insitu=False and retfitres=False:
3402                               a new scantable (with baseline subtracted).
3403            blinfo:        baseline parameter set stored in a dictionary
3404                           or a list of dictionary. Each dictionary
3405                           corresponds to each spectrum and must contain
3406                           the following keys and values:
3407                             'row': row number,
3408                             'blfunc': function name. available ones include
3409                                       'poly', 'chebyshev', 'cspline' and
3410                                       'sinusoid',
3411                             'order': maximum order of polynomial. needed
3412                                      if blfunc='poly' or 'chebyshev',
3413                             'npiece': number or piecewise polynomial.
3414                                       needed if blfunc='cspline',
3415                             'nwave': a list of sinusoidal wave numbers.
3416                                      needed if blfunc='sinusoid', and
3417                             'masklist': min-max windows for channel mask.
3418                                         the specified ranges will be used
3419                                         for fitting.
3420            bltable:       name of output baseline table where baseline
3421                           parameters and fitting results recorded.
3422                           default is ''(no output).
3423            overwrite:     if True when an existing baseline table is
3424                           specified for bltable, overwrites it.
3425                           Otherwise there is no harm.
3426                           default is False.
3427                           
3428        Example:
3429            sub_baseline(blinfo=[{'row':0, 'blfunc':'poly', 'order':5,
3430                                  'masklist':[[10,350],[352,510]]},
3431                                 {'row':1, 'blfunc':'cspline', 'npiece':3,
3432                                  'masklist':[[3,16],[19,404],[407,511]]}
3433                                  ])
3434
3435                the first spectrum (row=0) will be fitted with polynomial
3436                of order=5 and the next one (row=1) will be fitted with cubic
3437                spline consisting of 3 pieces.
3438        """
3439
3440        try:
3441            varlist = vars()
3442            if retfitres      is None: retfitres      = False
3443            if blinfo         is None: blinfo         = []
3444            if bltable        is None: bltable        = ''
3445            if overwrite      is None: overwrite      = False
3446
3447            if insitu is None: insitu = rcParams['insitu']
3448            if insitu:
3449                workscan = self
3450            else:
3451                workscan = self.copy()
3452
3453            nrow = workscan.nrow()
3454
3455            in_blinfo = pack_blinfo(blinfo=blinfo, maxirow=nrow)
3456
3457            print "in_blinfo=< "+ str(in_blinfo)+" >"
3458
3459            sres = workscan._sub_baseline(in_blinfo,
3460                                          retfitres,
3461                                          bltable,
3462                                          os.path.exists(bltable),
3463                                          overwrite)
3464            if retfitres: res = parse_fitresult(sres)
3465           
3466            workscan._add_history('sub_baseline', varlist)
3467
3468            if insitu:
3469                self._assign(workscan)
3470                if retfitres:
3471                    return res
3472                else:
3473                    return None
3474            else:
3475                if retfitres:
3476                    return {'scantable': workscan, 'fitresults': res}
3477                else:
3478                    return workscan
3479
3480        except RuntimeError, e:
3481            raise_fitting_failure_exception(e)
3482
3483    @asaplog_post_dec
3484    def calc_aic(self, value=None, blfunc=None, order=None, mask=None,
3485                 whichrow=None, uselinefinder=None, edge=None,
3486                 threshold=None, chan_avg_limit=None):
3487        """\
3488        Calculates and returns model selection criteria for a specified
3489        baseline model and a given spectrum data.
3490        Available values include Akaike Information Criterion (AIC), the
3491        corrected Akaike Information Criterion (AICc) by Sugiura(1978),
3492        Bayesian Information Criterion (BIC) and the Generalised Cross
3493        Validation (GCV).
3494
3495        Parameters:
3496            value:         name of model selection criteria to calculate.
3497                           available ones include 'aic', 'aicc', 'bic' and
3498                           'gcv'. default is 'aicc'.
3499            blfunc:        baseline function name. available ones include
3500                           'chebyshev', 'cspline' and 'sinusoid'.
3501                           default is 'chebyshev'.
3502            order:         parameter for basline function. actually stands for
3503                           order of polynomial (order) for 'chebyshev',
3504                           number of spline pieces (npiece) for 'cspline' and
3505                           maximum wave number for 'sinusoid', respectively.
3506                           default is 5 (which is also the default order value
3507                           for [auto_]chebyshev_baseline()).
3508            mask:          an optional mask. default is [].
3509            whichrow:      row number. default is 0 (the first row)
3510            uselinefinder: use sd.linefinder() to flag out line regions
3511                           default is True.
3512            edge:           an optional number of channel to drop at
3513                            the edge of spectrum. If only one value is
3514                            specified, the same number will be dropped
3515                            from both sides of the spectrum. Default
3516                            is to keep all channels. Nested tuples
3517                            represent individual edge selection for
3518                            different IFs (a number of spectral channels
3519                            can be different)
3520                            default is (0, 0).
3521            threshold:      the threshold used by line finder. It is
3522                            better to keep it large as only strong lines
3523                            affect the baseline solution.
3524                            default is 3.
3525            chan_avg_limit: a maximum number of consequtive spectral
3526                            channels to average during the search of
3527                            weak and broad lines. The default is no
3528                            averaging (and no search for weak lines).
3529                            If such lines can affect the fitted baseline
3530                            (e.g. a high order polynomial is fitted),
3531                            increase this parameter (usually values up
3532                            to 8 are reasonable). Most users of this
3533                            method should find the default value sufficient.
3534                            default is 1.
3535
3536        Example:
3537            aic = scan.calc_aic(blfunc='chebyshev', order=5, whichrow=0)
3538        """
3539
3540        try:
3541            varlist = vars()
3542
3543            if value          is None: value          = 'aicc'
3544            if blfunc         is None: blfunc         = 'chebyshev'
3545            if order          is None: order          = 5
3546            if mask           is None: mask           = []
3547            if whichrow       is None: whichrow       = 0
3548            if uselinefinder  is None: uselinefinder  = True
3549            if edge           is None: edge           = (0, 0)
3550            if threshold      is None: threshold      = 3
3551            if chan_avg_limit is None: chan_avg_limit = 1
3552
3553            return self._calc_aic(value, blfunc, order, mask,
3554                                  whichrow, uselinefinder, edge,
3555                                  threshold, chan_avg_limit)
3556           
3557        except RuntimeError, e:
3558            raise_fitting_failure_exception(e)
3559
3560    @asaplog_post_dec
3561    def sinusoid_baseline(self, mask=None, applyfft=None,
3562                          fftmethod=None, fftthresh=None,
3563                          addwn=None, rejwn=None,
3564                          insitu=None,
3565                          clipthresh=None, clipniter=None,
3566                          plot=None,
3567                          getresidual=None,
3568                          showprogress=None, minnrow=None,
3569                          outlog=None,
3570                          blfile=None, csvformat=None,
3571                          bltable=None):
3572        """\
3573        Return a scan which has been baselined (all rows) with sinusoidal
3574        functions.
3575
3576        Parameters:
3577            mask:          an optional mask
3578            applyfft:      if True use some method, such as FFT, to find
3579                           strongest sinusoidal components in the wavenumber
3580                           domain to be used for baseline fitting.
3581                           default is True.
3582            fftmethod:     method to find the strong sinusoidal components.
3583                           now only 'fft' is available and it is the default.
3584            fftthresh:     the threshold to select wave numbers to be used for
3585                           fitting from the distribution of amplitudes in the
3586                           wavenumber domain.
3587                           both float and string values accepted.
3588                           given a float value, the unit is set to sigma.
3589                           for string values, allowed formats include:
3590                               'xsigma' or 'x' (= x-sigma level. e.g.,
3591                               '3sigma'), or
3592                               'topx' (= the x strongest ones, e.g. 'top5').
3593                           default is 3.0 (unit: sigma).
3594            addwn:         the additional wave numbers to be used for fitting.
3595                           list or integer value is accepted to specify every
3596                           wave numbers. also string value can be used in case
3597                           you need to specify wave numbers in a certain range,
3598                           e.g., 'a-b' (= a, a+1, a+2, ..., b-1, b),
3599                                 '<a'  (= 0,1,...,a-2,a-1),
3600                                 '>=a' (= a, a+1, ... up to the maximum wave
3601                                        number corresponding to the Nyquist
3602                                        frequency for the case of FFT).
3603                           default is [0].
3604            rejwn:         the wave numbers NOT to be used for fitting.
3605                           can be set just as addwn but has higher priority:
3606                           wave numbers which are specified both in addwn
3607                           and rejwn will NOT be used. default is [].
3608            insitu:        if False a new scantable is returned.
3609                           Otherwise, the scaling is done in-situ
3610                           The default is taken from .asaprc (False)
3611            clipthresh:    Clipping threshold. (default is 3.0, unit: sigma)
3612            clipniter:     maximum number of iteration of 'clipthresh'-sigma
3613                           clipping (default is 0)
3614            plot:      *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
3615                           plot the fit and the residual. In this each
3616                           indivual fit has to be approved, by typing 'y'
3617                           or 'n'
3618            getresidual:   if False, returns best-fit values instead of
3619                           residual. (default is True)
3620            showprogress:  show progress status for large data.
3621                           default is True.
3622            minnrow:       minimum number of input spectra to show.
3623                           default is 1000.
3624            outlog:        Output the coefficients of the best-fit
3625                           function to logger (default is False)
3626            blfile:        Name of a text file in which the best-fit
3627                           parameter values to be written
3628                           (default is '': no file/logger output)
3629            csvformat:     if True blfile is csv-formatted, default is False.
3630            bltable:       name of a baseline table where fitting results
3631                           (coefficients, rms, etc.) are to be written.
3632                           if given, fitting results will NOT be output to
3633                           scantable (insitu=True) or None will be
3634                           returned (insitu=False).
3635                           (default is "": no table output)
3636
3637        Example:
3638            # return a scan baselined by a combination of sinusoidal curves
3639            # having wave numbers in spectral window up to 10,
3640            # also with 3-sigma clipping, iteration up to 4 times
3641            bscan = scan.sinusoid_baseline(addwn='<=10',clipthresh=3.0,clipniter=4)
3642
3643        Note:
3644            The best-fit parameter values output in logger and/or blfile are now
3645            based on specunit of 'channel'.
3646        """
3647       
3648        try:
3649            varlist = vars()
3650       
3651            if insitu is None: insitu = rcParams['insitu']
3652            if insitu:
3653                workscan = self
3654            else:
3655                workscan = self.copy()
3656           
3657            if mask          is None: mask          = []
3658            if applyfft      is None: applyfft      = True
3659            if fftmethod     is None: fftmethod     = 'fft'
3660            if fftthresh     is None: fftthresh     = 3.0
3661            if addwn         is None: addwn         = [0]
3662            if rejwn         is None: rejwn         = []
3663            if clipthresh    is None: clipthresh    = 3.0
3664            if clipniter     is None: clipniter     = 0
3665            if plot          is None: plot          = False
3666            if getresidual   is None: getresidual   = True
3667            if showprogress  is None: showprogress  = True
3668            if minnrow       is None: minnrow       = 1000
3669            if outlog        is None: outlog        = False
3670            if blfile        is None: blfile        = ''
3671            if csvformat     is None: csvformat     = False
3672            if bltable       is None: bltable       = ''
3673
3674            sapplyfft = 'true' if applyfft else 'false'
3675            fftinfo = ','.join([sapplyfft, fftmethod.lower(), str(fftthresh).lower()])
3676
3677            scsvformat = 'T' if csvformat else 'F'
3678
3679            #CURRENTLY, PLOT=true is UNAVAILABLE UNTIL sinusoidal fitting is implemented as a fitter method.
3680            workscan._sinusoid_baseline(mask,
3681                                        fftinfo,
3682                                        #applyfft, fftmethod.lower(),
3683                                        #str(fftthresh).lower(),
3684                                        workscan._parse_wn(addwn),
3685                                        workscan._parse_wn(rejwn),
3686                                        clipthresh, clipniter,
3687                                        getresidual,
3688                                        pack_progress_params(showprogress,
3689                                                             minnrow),
3690                                        outlog, scsvformat+blfile,
3691                                        bltable)
3692            workscan._add_history('sinusoid_baseline', varlist)
3693
3694            if bltable == '':
3695                if insitu:
3696                    self._assign(workscan)
3697                else:
3698                    return workscan
3699            else:
3700                if not insitu:
3701                    return None
3702           
3703        except RuntimeError, e:
3704            raise_fitting_failure_exception(e)
3705
3706
3707    @asaplog_post_dec
3708    def auto_sinusoid_baseline(self, mask=None, applyfft=None,
3709                               fftmethod=None, fftthresh=None,
3710                               addwn=None, rejwn=None,
3711                               insitu=None,
3712                               clipthresh=None, clipniter=None,
3713                               edge=None, threshold=None, chan_avg_limit=None,
3714                               plot=None,
3715                               getresidual=None,
3716                               showprogress=None, minnrow=None,
3717                               outlog=None,
3718                               blfile=None, csvformat=None,
3719                               bltable=None):
3720        """\
3721        Return a scan which has been baselined (all rows) with sinusoidal
3722        functions.
3723        Spectral lines are detected first using linefinder and masked out
3724        to avoid them affecting the baseline solution.
3725
3726        Parameters:
3727            mask:           an optional mask retreived from scantable
3728            applyfft:       if True use some method, such as FFT, to find
3729                            strongest sinusoidal components in the wavenumber
3730                            domain to be used for baseline fitting.
3731                            default is True.
3732            fftmethod:      method to find the strong sinusoidal components.
3733                            now only 'fft' is available and it is the default.
3734            fftthresh:      the threshold to select wave numbers to be used for
3735                            fitting from the distribution of amplitudes in the
3736                            wavenumber domain.
3737                            both float and string values accepted.
3738                            given a float value, the unit is set to sigma.
3739                            for string values, allowed formats include:
3740                                'xsigma' or 'x' (= x-sigma level. e.g.,
3741                                '3sigma'), or
3742                                'topx' (= the x strongest ones, e.g. 'top5').
3743                            default is 3.0 (unit: sigma).
3744            addwn:          the additional wave numbers to be used for fitting.
3745                            list or integer value is accepted to specify every
3746                            wave numbers. also string value can be used in case
3747                            you need to specify wave numbers in a certain range,
3748                            e.g., 'a-b' (= a, a+1, a+2, ..., b-1, b),
3749                                  '<a'  (= 0,1,...,a-2,a-1),
3750                                  '>=a' (= a, a+1, ... up to the maximum wave
3751                                         number corresponding to the Nyquist
3752                                         frequency for the case of FFT).
3753                            default is [0].
3754            rejwn:          the wave numbers NOT to be used for fitting.
3755                            can be set just as addwn but has higher priority:
3756                            wave numbers which are specified both in addwn
3757                            and rejwn will NOT be used. default is [].
3758            insitu:         if False a new scantable is returned.
3759                            Otherwise, the scaling is done in-situ
3760                            The default is taken from .asaprc (False)
3761            clipthresh:     Clipping threshold. (default is 3.0, unit: sigma)
3762            clipniter:      maximum number of iteration of 'clipthresh'-sigma
3763                            clipping (default is 0)
3764            edge:           an optional number of channel to drop at
3765                            the edge of spectrum. If only one value is
3766                            specified, the same number will be dropped
3767                            from both sides of the spectrum. Default
3768                            is to keep all channels. Nested tuples
3769                            represent individual edge selection for
3770                            different IFs (a number of spectral channels
3771                            can be different)
3772            threshold:      the threshold used by line finder. It is
3773                            better to keep it large as only strong lines
3774                            affect the baseline solution.
3775            chan_avg_limit: a maximum number of consequtive spectral
3776                            channels to average during the search of
3777                            weak and broad lines. The default is no
3778                            averaging (and no search for weak lines).
3779                            If such lines can affect the fitted baseline
3780                            (e.g. a high order polynomial is fitted),
3781                            increase this parameter (usually values up
3782                            to 8 are reasonable). Most users of this
3783                            method should find the default value sufficient.
3784            plot:       *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
3785                            plot the fit and the residual. In this each
3786                            indivual fit has to be approved, by typing 'y'
3787                            or 'n'
3788            getresidual:    if False, returns best-fit values instead of
3789                            residual. (default is True)
3790            showprogress:   show progress status for large data.
3791                            default is True.
3792            minnrow:        minimum number of input spectra to show.
3793                            default is 1000.
3794            outlog:         Output the coefficients of the best-fit
3795                            function to logger (default is False)
3796            blfile:         Name of a text file in which the best-fit
3797                            parameter values to be written
3798                            (default is "": no file/logger output)
3799            csvformat:      if True blfile is csv-formatted, default is False.
3800            bltable:        name of a baseline table where fitting results
3801                            (coefficients, rms, etc.) are to be written.
3802                            if given, fitting results will NOT be output to
3803                            scantable (insitu=True) or None will be
3804                            returned (insitu=False).
3805                            (default is "": no table output)
3806
3807        Example:
3808            bscan = scan.auto_sinusoid_baseline(addwn='<=10', insitu=False)
3809       
3810        Note:
3811            The best-fit parameter values output in logger and/or blfile are now
3812            based on specunit of 'channel'.
3813        """
3814
3815        try:
3816            varlist = vars()
3817
3818            if insitu is None: insitu = rcParams['insitu']
3819            if insitu:
3820                workscan = self
3821            else:
3822                workscan = self.copy()
3823           
3824            if mask           is None: mask           = []
3825            if applyfft       is None: applyfft       = True
3826            if fftmethod      is None: fftmethod      = 'fft'
3827            if fftthresh      is None: fftthresh      = 3.0
3828            if addwn          is None: addwn          = [0]
3829            if rejwn          is None: rejwn          = []
3830            if clipthresh     is None: clipthresh     = 3.0
3831            if clipniter      is None: clipniter      = 0
3832            if edge           is None: edge           = (0,0)
3833            if threshold      is None: threshold      = 3
3834            if chan_avg_limit is None: chan_avg_limit = 1
3835            if plot           is None: plot           = False
3836            if getresidual    is None: getresidual    = True
3837            if showprogress   is None: showprogress   = True
3838            if minnrow        is None: minnrow        = 1000
3839            if outlog         is None: outlog         = False
3840            if blfile         is None: blfile         = ''
3841            if csvformat      is None: csvformat      = False
3842            if bltable        is None: bltable        = ''
3843
3844            sapplyfft = 'true' if applyfft else 'false'
3845            fftinfo = ','.join([sapplyfft, fftmethod.lower(), str(fftthresh).lower()])
3846
3847            scsvformat = 'T' if csvformat else 'F'
3848
3849            #CURRENTLY, PLOT=true is UNAVAILABLE UNTIL sinusoidal fitting is implemented as a fitter method.
3850            workscan._auto_sinusoid_baseline(mask,
3851                                             fftinfo,
3852                                             workscan._parse_wn(addwn),
3853                                             workscan._parse_wn(rejwn),
3854                                             clipthresh, clipniter,
3855                                             normalise_edge_param(edge),
3856                                             threshold, chan_avg_limit,
3857                                             getresidual,
3858                                             pack_progress_params(showprogress,
3859                                                                  minnrow),
3860                                             outlog, scsvformat+blfile, bltable)
3861            workscan._add_history("auto_sinusoid_baseline", varlist)
3862
3863            if bltable == '':
3864                if insitu:
3865                    self._assign(workscan)
3866                else:
3867                    return workscan
3868            else:
3869                if not insitu:
3870                    return None
3871           
3872        except RuntimeError, e:
3873            raise_fitting_failure_exception(e)
3874
3875    @asaplog_post_dec
3876    def cspline_baseline(self, mask=None, npiece=None, insitu=None,
3877                         clipthresh=None, clipniter=None, plot=None,
3878                         getresidual=None, showprogress=None, minnrow=None,
3879                         outlog=None, blfile=None, csvformat=None,
3880                         bltable=None):
3881        """\
3882        Return a scan which has been baselined (all rows) by cubic spline
3883        function (piecewise cubic polynomial).
3884
3885        Parameters:
3886            mask:         An optional mask
3887            npiece:       Number of pieces. (default is 2)
3888            insitu:       If False a new scantable is returned.
3889                          Otherwise, the scaling is done in-situ
3890                          The default is taken from .asaprc (False)
3891            clipthresh:   Clipping threshold. (default is 3.0, unit: sigma)
3892            clipniter:    maximum number of iteration of 'clipthresh'-sigma
3893                          clipping (default is 0)
3894            plot:     *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
3895                          plot the fit and the residual. In this each
3896                          indivual fit has to be approved, by typing 'y'
3897                          or 'n'
3898            getresidual:  if False, returns best-fit values instead of
3899                          residual. (default is True)
3900            showprogress: show progress status for large data.
3901                          default is True.
3902            minnrow:      minimum number of input spectra to show.
3903                          default is 1000.
3904            outlog:       Output the coefficients of the best-fit
3905                          function to logger (default is False)
3906            blfile:       Name of a text file in which the best-fit
3907                          parameter values to be written
3908                          (default is "": no file/logger output)
3909            csvformat:    if True blfile is csv-formatted, default is False.
3910            bltable:      name of a baseline table where fitting results
3911                          (coefficients, rms, etc.) are to be written.
3912                          if given, fitting results will NOT be output to
3913                          scantable (insitu=True) or None will be
3914                          returned (insitu=False).
3915                          (default is "": no table output)
3916
3917        Example:
3918            # return a scan baselined by a cubic spline consisting of 2 pieces
3919            # (i.e., 1 internal knot),
3920            # also with 3-sigma clipping, iteration up to 4 times
3921            bscan = scan.cspline_baseline(npiece=2,clipthresh=3.0,clipniter=4)
3922       
3923        Note:
3924            The best-fit parameter values output in logger and/or blfile are now
3925            based on specunit of 'channel'.
3926        """
3927       
3928        try:
3929            varlist = vars()
3930           
3931            if insitu is None: insitu = rcParams['insitu']
3932            if insitu:
3933                workscan = self
3934            else:
3935                workscan = self.copy()
3936
3937            if mask         is None: mask         = []
3938            if npiece       is None: npiece       = 2
3939            if clipthresh   is None: clipthresh   = 3.0
3940            if clipniter    is None: clipniter    = 0
3941            if plot         is None: plot         = False
3942            if getresidual  is None: getresidual  = True
3943            if showprogress is None: showprogress = True
3944            if minnrow      is None: minnrow      = 1000
3945            if outlog       is None: outlog       = False
3946            if blfile       is None: blfile       = ''
3947            if csvformat    is None: csvformat    = False
3948            if bltable      is None: bltable      = ''
3949
3950            scsvformat = 'T' if csvformat else 'F'
3951
3952            #CURRENTLY, PLOT=true UNAVAILABLE UNTIL cubic spline fitting is implemented as a fitter method.
3953            workscan._cspline_baseline(mask, npiece,
3954                                       clipthresh, clipniter,
3955                                       getresidual,
3956                                       pack_progress_params(showprogress,
3957                                                            minnrow),
3958                                       outlog, scsvformat+blfile,
3959                                       bltable)
3960            workscan._add_history("cspline_baseline", varlist)
3961
3962            if bltable == '':
3963                if insitu:
3964                    self._assign(workscan)
3965                else:
3966                    return workscan
3967            else:
3968                if not insitu:
3969                    return None
3970           
3971        except RuntimeError, e:
3972            raise_fitting_failure_exception(e)
3973
3974    @asaplog_post_dec
3975    def auto_cspline_baseline(self, mask=None, npiece=None, insitu=None,
3976                              clipthresh=None, clipniter=None,
3977                              edge=None, threshold=None, chan_avg_limit=None,
3978                              getresidual=None, plot=None,
3979                              showprogress=None, minnrow=None, outlog=None,
3980                              blfile=None, csvformat=None, bltable=None):
3981        """\
3982        Return a scan which has been baselined (all rows) by cubic spline
3983        function (piecewise cubic polynomial).
3984        Spectral lines are detected first using linefinder and masked out
3985        to avoid them affecting the baseline solution.
3986
3987        Parameters:
3988            mask:           an optional mask retreived from scantable
3989            npiece:         Number of pieces. (default is 2)
3990            insitu:         if False a new scantable is returned.
3991                            Otherwise, the scaling is done in-situ
3992                            The default is taken from .asaprc (False)
3993            clipthresh:     Clipping threshold. (default is 3.0, unit: sigma)
3994            clipniter:      maximum number of iteration of 'clipthresh'-sigma
3995                            clipping (default is 0)
3996            edge:           an optional number of channel to drop at
3997                            the edge of spectrum. If only one value is
3998                            specified, the same number will be dropped
3999                            from both sides of the spectrum. Default
4000                            is to keep all channels. Nested tuples
4001                            represent individual edge selection for
4002                            different IFs (a number of spectral channels
4003                            can be different)
4004            threshold:      the threshold used by line finder. It is
4005                            better to keep it large as only strong lines
4006                            affect the baseline solution.
4007            chan_avg_limit: a maximum number of consequtive spectral
4008                            channels to average during the search of
4009                            weak and broad lines. The default is no
4010                            averaging (and no search for weak lines).
4011                            If such lines can affect the fitted baseline
4012                            (e.g. a high order polynomial is fitted),
4013                            increase this parameter (usually values up
4014                            to 8 are reasonable). Most users of this
4015                            method should find the default value sufficient.
4016            plot:       *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
4017                            plot the fit and the residual. In this each
4018                            indivual fit has to be approved, by typing 'y'
4019                            or 'n'
4020            getresidual:    if False, returns best-fit values instead of
4021                            residual. (default is True)
4022            showprogress:   show progress status for large data.
4023                            default is True.
4024            minnrow:        minimum number of input spectra to show.
4025                            default is 1000.
4026            outlog:         Output the coefficients of the best-fit
4027                            function to logger (default is False)
4028            blfile:         Name of a text file in which the best-fit
4029                            parameter values to be written
4030                            (default is "": no file/logger output)
4031            csvformat:      if True blfile is csv-formatted, default is False.
4032            bltable:        name of a baseline table where fitting results
4033                            (coefficients, rms, etc.) are to be written.
4034                            if given, fitting results will NOT be output to
4035                            scantable (insitu=True) or None will be
4036                            returned (insitu=False).
4037                            (default is "": no table output)
4038
4039        Example:
4040            bscan = scan.auto_cspline_baseline(npiece=3, insitu=False)
4041       
4042        Note:
4043            The best-fit parameter values output in logger and/or blfile are now
4044            based on specunit of 'channel'.
4045        """
4046
4047        try:
4048            varlist = vars()
4049
4050            if insitu is None: insitu = rcParams['insitu']
4051            if insitu:
4052                workscan = self
4053            else:
4054                workscan = self.copy()
4055           
4056            #if mask           is None: mask           = [True for i in xrange(workscan.nchan())]
4057            if mask           is None: mask           = []
4058            if npiece         is None: npiece         = 2
4059            if clipthresh     is None: clipthresh     = 3.0
4060            if clipniter      is None: clipniter      = 0
4061            if edge           is None: edge           = (0, 0)
4062            if threshold      is None: threshold      = 3
4063            if chan_avg_limit is None: chan_avg_limit = 1
4064            if plot           is None: plot           = False
4065            if getresidual    is None: getresidual    = True
4066            if showprogress   is None: showprogress   = True
4067            if minnrow        is None: minnrow        = 1000
4068            if outlog         is None: outlog         = False
4069            if blfile         is None: blfile         = ''
4070            if csvformat      is None: csvformat      = False
4071            if bltable        is None: bltable        = ''
4072
4073            scsvformat = 'T' if csvformat else 'F'
4074
4075            #CURRENTLY, PLOT=true UNAVAILABLE UNTIL cubic spline fitting is implemented as a fitter method.
4076            workscan._auto_cspline_baseline(mask, npiece,
4077                                            clipthresh, clipniter,
4078                                            normalise_edge_param(edge),
4079                                            threshold,
4080                                            chan_avg_limit, getresidual,
4081                                            pack_progress_params(showprogress,
4082                                                                 minnrow),
4083                                            outlog,
4084                                            scsvformat+blfile,
4085                                            bltable)
4086            workscan._add_history("auto_cspline_baseline", varlist)
4087
4088            if bltable == '':
4089                if insitu:
4090                    self._assign(workscan)
4091                else:
4092                    return workscan
4093            else:
4094                if not insitu:
4095                    return None
4096           
4097        except RuntimeError, e:
4098            raise_fitting_failure_exception(e)
4099
4100    @asaplog_post_dec
4101    def chebyshev_baseline(self, mask=None, order=None, insitu=None,
4102                           clipthresh=None, clipniter=None, plot=None,
4103                           getresidual=None, showprogress=None, minnrow=None,
4104                           outlog=None, blfile=None, csvformat=None,
4105                           bltable=None):
4106        """\
4107        Return a scan which has been baselined (all rows) by Chebyshev polynomials.
4108
4109        Parameters:
4110            mask:         An optional mask
4111            order:        the maximum order of Chebyshev polynomial (default is 5)
4112            insitu:       If False a new scantable is returned.
4113                          Otherwise, the scaling is done in-situ
4114                          The default is taken from .asaprc (False)
4115            clipthresh:   Clipping threshold. (default is 3.0, unit: sigma)
4116            clipniter:    maximum number of iteration of 'clipthresh'-sigma
4117                          clipping (default is 0)
4118            plot:     *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
4119                          plot the fit and the residual. In this each
4120                          indivual fit has to be approved, by typing 'y'
4121                          or 'n'
4122            getresidual:  if False, returns best-fit values instead of
4123                          residual. (default is True)
4124            showprogress: show progress status for large data.
4125                          default is True.
4126            minnrow:      minimum number of input spectra to show.
4127                          default is 1000.
4128            outlog:       Output the coefficients of the best-fit
4129                          function to logger (default is False)
4130            blfile:       Name of a text file in which the best-fit
4131                          parameter values to be written
4132                          (default is "": no file/logger output)
4133            csvformat:    if True blfile is csv-formatted, default is False.
4134            bltable:      name of a baseline table where fitting results
4135                          (coefficients, rms, etc.) are to be written.
4136                          if given, fitting results will NOT be output to
4137                          scantable (insitu=True) or None will be
4138                          returned (insitu=False).
4139                          (default is "": no table output)
4140
4141        Example:
4142            # return a scan baselined by a cubic spline consisting of 2 pieces
4143            # (i.e., 1 internal knot),
4144            # also with 3-sigma clipping, iteration up to 4 times
4145            bscan = scan.cspline_baseline(npiece=2,clipthresh=3.0,clipniter=4)
4146       
4147        Note:
4148            The best-fit parameter values output in logger and/or blfile are now
4149            based on specunit of 'channel'.
4150        """
4151       
4152        try:
4153            varlist = vars()
4154           
4155            if insitu is None: insitu = rcParams['insitu']
4156            if insitu:
4157                workscan = self
4158            else:
4159                workscan = self.copy()
4160
4161            if mask         is None: mask         = []
4162            if order        is None: order        = 5
4163            if clipthresh   is None: clipthresh   = 3.0
4164            if clipniter    is None: clipniter    = 0
4165            if plot         is None: plot         = False
4166            if getresidual  is None: getresidual  = True
4167            if showprogress is None: showprogress = True
4168            if minnrow      is None: minnrow      = 1000
4169            if outlog       is None: outlog       = False
4170            if blfile       is None: blfile       = ''
4171            if csvformat    is None: csvformat    = False
4172            if bltable      is None: bltable      = ''
4173
4174            scsvformat = 'T' if csvformat else 'F'
4175
4176            #CURRENTLY, PLOT=true UNAVAILABLE UNTIL cubic spline fitting is implemented as a fitter method.
4177            workscan._chebyshev_baseline(mask, order,
4178                                         clipthresh, clipniter,
4179                                         getresidual,
4180                                         pack_progress_params(showprogress,
4181                                                              minnrow),
4182                                         outlog, scsvformat+blfile,
4183                                         bltable)
4184            workscan._add_history("chebyshev_baseline", varlist)
4185
4186            if bltable == '':
4187                if insitu:
4188                    self._assign(workscan)
4189                else:
4190                    return workscan
4191            else:
4192                if not insitu:
4193                    return None
4194           
4195        except RuntimeError, e:
4196            raise_fitting_failure_exception(e)
4197
4198    @asaplog_post_dec
4199    def auto_chebyshev_baseline(self, mask=None, order=None, insitu=None,
4200                              clipthresh=None, clipniter=None,
4201                              edge=None, threshold=None, chan_avg_limit=None,
4202                              getresidual=None, plot=None,
4203                              showprogress=None, minnrow=None, outlog=None,
4204                              blfile=None, csvformat=None, bltable=None):
4205        """\
4206        Return a scan which has been baselined (all rows) by Chebyshev polynomials.
4207        Spectral lines are detected first using linefinder and masked out
4208        to avoid them affecting the baseline solution.
4209
4210        Parameters:
4211            mask:           an optional mask retreived from scantable
4212            order:          the maximum order of Chebyshev polynomial (default is 5)
4213            insitu:         if False a new scantable is returned.
4214                            Otherwise, the scaling is done in-situ
4215                            The default is taken from .asaprc (False)
4216            clipthresh:     Clipping threshold. (default is 3.0, unit: sigma)
4217            clipniter:      maximum number of iteration of 'clipthresh'-sigma
4218                            clipping (default is 0)
4219            edge:           an optional number of channel to drop at
4220                            the edge of spectrum. If only one value is
4221                            specified, the same number will be dropped
4222                            from both sides of the spectrum. Default
4223                            is to keep all channels. Nested tuples
4224                            represent individual edge selection for
4225                            different IFs (a number of spectral channels
4226                            can be different)
4227            threshold:      the threshold used by line finder. It is
4228                            better to keep it large as only strong lines
4229                            affect the baseline solution.
4230            chan_avg_limit: a maximum number of consequtive spectral
4231                            channels to average during the search of
4232                            weak and broad lines. The default is no
4233                            averaging (and no search for weak lines).
4234                            If such lines can affect the fitted baseline
4235                            (e.g. a high order polynomial is fitted),
4236                            increase this parameter (usually values up
4237                            to 8 are reasonable). Most users of this
4238                            method should find the default value sufficient.
4239            plot:       *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
4240                            plot the fit and the residual. In this each
4241                            indivual fit has to be approved, by typing 'y'
4242                            or 'n'
4243            getresidual:    if False, returns best-fit values instead of
4244                            residual. (default is True)
4245            showprogress:   show progress status for large data.
4246                            default is True.
4247            minnrow:        minimum number of input spectra to show.
4248                            default is 1000.
4249            outlog:         Output the coefficients of the best-fit
4250                            function to logger (default is False)
4251            blfile:         Name of a text file in which the best-fit
4252                            parameter values to be written
4253                            (default is "": no file/logger output)
4254            csvformat:      if True blfile is csv-formatted, default is False.
4255            bltable:        name of a baseline table where fitting results
4256                            (coefficients, rms, etc.) are to be written.
4257                            if given, fitting results will NOT be output to
4258                            scantable (insitu=True) or None will be
4259                            returned (insitu=False).
4260                            (default is "": no table output)
4261
4262        Example:
4263            bscan = scan.auto_cspline_baseline(npiece=3, insitu=False)
4264       
4265        Note:
4266            The best-fit parameter values output in logger and/or blfile are now
4267            based on specunit of 'channel'.
4268        """
4269
4270        try:
4271            varlist = vars()
4272
4273            if insitu is None: insitu = rcParams['insitu']
4274            if insitu:
4275                workscan = self
4276            else:
4277                workscan = self.copy()
4278           
4279            if mask           is None: mask           = []
4280            if order          is None: order          = 5
4281            if clipthresh     is None: clipthresh     = 3.0
4282            if clipniter      is None: clipniter      = 0
4283            if edge           is None: edge           = (0, 0)
4284            if threshold      is None: threshold      = 3
4285            if chan_avg_limit is None: chan_avg_limit = 1
4286            if plot           is None: plot           = False
4287            if getresidual    is None: getresidual    = True
4288            if showprogress   is None: showprogress   = True
4289            if minnrow        is None: minnrow        = 1000
4290            if outlog         is None: outlog         = False
4291            if blfile         is None: blfile         = ''
4292            if csvformat      is None: csvformat      = False
4293            if bltable        is None: bltable        = ''
4294
4295            scsvformat = 'T' if csvformat else 'F'
4296
4297            #CURRENTLY, PLOT=true UNAVAILABLE UNTIL cubic spline fitting is implemented as a fitter method.
4298            workscan._auto_chebyshev_baseline(mask, order,
4299                                              clipthresh, clipniter,
4300                                              normalise_edge_param(edge),
4301                                              threshold,
4302                                              chan_avg_limit, getresidual,
4303                                              pack_progress_params(showprogress,
4304                                                                   minnrow),
4305                                              outlog, scsvformat+blfile,
4306                                              bltable)
4307            workscan._add_history("auto_chebyshev_baseline", varlist)
4308
4309            if bltable == '':
4310                if insitu:
4311                    self._assign(workscan)
4312                else:
4313                    return workscan
4314            else:
4315                if not insitu:
4316                    return None
4317           
4318        except RuntimeError, e:
4319            raise_fitting_failure_exception(e)
4320
4321    @asaplog_post_dec
4322    def poly_baseline(self, mask=None, order=None, insitu=None,
4323                      clipthresh=None, clipniter=None, plot=None,
4324                      getresidual=None, showprogress=None, minnrow=None,
4325                      outlog=None, blfile=None, csvformat=None,
4326                      bltable=None):
4327        """\
4328        Return a scan which has been baselined (all rows) by a polynomial.
4329        Parameters:
4330            mask:         an optional mask
4331            order:        the order of the polynomial (default is 0)
4332            insitu:       if False a new scantable is returned.
4333                          Otherwise, the scaling is done in-situ
4334                          The default is taken from .asaprc (False)
4335            clipthresh:   Clipping threshold. (default is 3.0, unit: sigma)
4336            clipniter:    maximum number of iteration of 'clipthresh'-sigma
4337                          clipping (default is 0)
4338            plot:         plot the fit and the residual. In this each
4339                          indivual fit has to be approved, by typing 'y'
4340                          or 'n'
4341            getresidual:  if False, returns best-fit values instead of
4342                          residual. (default is True)
4343            showprogress: show progress status for large data.
4344                          default is True.
4345            minnrow:      minimum number of input spectra to show.
4346                          default is 1000.
4347            outlog:       Output the coefficients of the best-fit
4348                          function to logger (default is False)
4349            blfile:       Name of a text file in which the best-fit
4350                          parameter values to be written
4351                          (default is "": no file/logger output)
4352            csvformat:    if True blfile is csv-formatted, default is False.
4353            bltable:      name of a baseline table where fitting results
4354                          (coefficients, rms, etc.) are to be written.
4355                          if given, fitting results will NOT be output to
4356                          scantable (insitu=True) or None will be
4357                          returned (insitu=False).
4358                          (default is "": no table output)
4359
4360        Example:
4361            # return a scan baselined by a third order polynomial,
4362            # not using a mask
4363            bscan = scan.poly_baseline(order=3)
4364        """
4365       
4366        try:
4367            varlist = vars()
4368       
4369            if insitu is None:
4370                insitu = rcParams["insitu"]
4371            if insitu:
4372                workscan = self
4373            else:
4374                workscan = self.copy()
4375
4376            if mask         is None: mask         = []
4377            if order        is None: order        = 0
4378            if clipthresh   is None: clipthresh   = 3.0
4379            if clipniter    is None: clipniter    = 0
4380            if plot         is None: plot         = False
4381            if getresidual  is None: getresidual  = True
4382            if showprogress is None: showprogress = True
4383            if minnrow      is None: minnrow      = 1000
4384            if outlog       is None: outlog       = False
4385            if blfile       is None: blfile       = ''
4386            if csvformat    is None: csvformat    = False
4387            if bltable      is None: bltable      = ''
4388
4389            scsvformat = 'T' if csvformat else 'F'
4390
4391            if plot:
4392                outblfile = (blfile != "") and \
4393                    os.path.exists(os.path.expanduser(
4394                                    os.path.expandvars(blfile))
4395                                   )
4396                if outblfile:
4397                    blf = open(blfile, "a")
4398               
4399                f = fitter()
4400                f.set_function(lpoly=order)
4401               
4402                rows = xrange(workscan.nrow())
4403                #if len(rows) > 0: workscan._init_blinfo()
4404
4405                action = "H"
4406                for r in rows:
4407                    f.x = workscan._getabcissa(r)
4408                    f.y = workscan._getspectrum(r)
4409                    if mask:
4410                        f.mask = mask_and(mask, workscan._getmask(r))    # (CAS-1434)
4411                    else: # mask=None
4412                        f.mask = workscan._getmask(r)
4413                   
4414                    f.data = None
4415                    f.fit()
4416
4417                    if action != "Y": # skip plotting when accepting all
4418                        f.plot(residual=True)
4419                    #accept_fit = raw_input("Accept fit ( [y]/n ): ")
4420                    #if accept_fit.upper() == "N":
4421                    #    #workscan._append_blinfo(None, None, None)
4422                    #    continue
4423                    accept_fit = self._get_verify_action("Accept fit?",action)
4424                    if r == 0: action = None
4425                    if accept_fit.upper() == "N":
4426                        continue
4427                    elif accept_fit.upper() == "R":
4428                        break
4429                    elif accept_fit.upper() == "A":
4430                        action = "Y"
4431                   
4432                    blpars = f.get_parameters()
4433                    masklist = workscan.get_masklist(f.mask, row=r, silent=True)
4434                    #workscan._append_blinfo(blpars, masklist, f.mask)
4435                    workscan._setspectrum((f.fitter.getresidual()
4436                                           if getresidual else f.fitter.getfit()), r)
4437                   
4438                    if outblfile:
4439                        rms = workscan.get_rms(f.mask, r)
4440                        dataout = \
4441                            workscan.format_blparams_row(blpars["params"],
4442                                                         blpars["fixed"],
4443                                                         rms, str(masklist),
4444                                                         r, True, csvformat)
4445                        blf.write(dataout)
4446
4447                f._p.unmap()
4448                f._p = None
4449
4450                if outblfile:
4451                    blf.close()
4452            else:
4453                workscan._poly_baseline(mask, order,
4454                                        clipthresh, clipniter, #
4455                                        getresidual,
4456                                        pack_progress_params(showprogress,
4457                                                             minnrow),
4458                                        outlog, scsvformat+blfile,
4459                                        bltable)  #
4460           
4461            workscan._add_history("poly_baseline", varlist)
4462           
4463            if insitu:
4464                self._assign(workscan)
4465            else:
4466                return workscan
4467           
4468        except RuntimeError, e:
4469            raise_fitting_failure_exception(e)
4470
4471    @asaplog_post_dec
4472    def auto_poly_baseline(self, mask=None, order=None, insitu=None,
4473                           clipthresh=None, clipniter=None,
4474                           edge=None, threshold=None, chan_avg_limit=None,
4475                           getresidual=None, plot=None,
4476                           showprogress=None, minnrow=None, outlog=None,
4477                           blfile=None, csvformat=None, bltable=None):
4478        """\
4479        Return a scan which has been baselined (all rows) by a polynomial.
4480        Spectral lines are detected first using linefinder and masked out
4481        to avoid them affecting the baseline solution.
4482
4483        Parameters:
4484            mask:           an optional mask retreived from scantable
4485            order:          the order of the polynomial (default is 0)
4486            insitu:         if False a new scantable is returned.
4487                            Otherwise, the scaling is done in-situ
4488                            The default is taken from .asaprc (False)
4489            clipthresh:     Clipping threshold. (default is 3.0, unit: sigma)
4490            clipniter:      maximum number of iteration of 'clipthresh'-sigma
4491                            clipping (default is 0)
4492            edge:           an optional number of channel to drop at
4493                            the edge of spectrum. If only one value is
4494                            specified, the same number will be dropped
4495                            from both sides of the spectrum. Default
4496                            is to keep all channels. Nested tuples
4497                            represent individual edge selection for
4498                            different IFs (a number of spectral channels
4499                            can be different)
4500            threshold:      the threshold used by line finder. It is
4501                            better to keep it large as only strong lines
4502                            affect the baseline solution.
4503            chan_avg_limit: a maximum number of consequtive spectral
4504                            channels to average during the search of
4505                            weak and broad lines. The default is no
4506                            averaging (and no search for weak lines).
4507                            If such lines can affect the fitted baseline
4508                            (e.g. a high order polynomial is fitted),
4509                            increase this parameter (usually values up
4510                            to 8 are reasonable). Most users of this
4511                            method should find the default value sufficient.
4512            plot:           plot the fit and the residual. In this each
4513                            indivual fit has to be approved, by typing 'y'
4514                            or 'n'
4515            getresidual:    if False, returns best-fit values instead of
4516                            residual. (default is True)
4517            showprogress:   show progress status for large data.
4518                            default is True.
4519            minnrow:        minimum number of input spectra to show.
4520                            default is 1000.
4521            outlog:         Output the coefficients of the best-fit
4522                            function to logger (default is False)
4523            blfile:         Name of a text file in which the best-fit
4524                            parameter values to be written
4525                            (default is "": no file/logger output)
4526            csvformat:      if True blfile is csv-formatted, default is False.
4527            bltable:        name of a baseline table where fitting results
4528                            (coefficients, rms, etc.) are to be written.
4529                            if given, fitting results will NOT be output to
4530                            scantable (insitu=True) or None will be
4531                            returned (insitu=False).
4532                            (default is "": no table output)
4533
4534        Example:
4535            bscan = scan.auto_poly_baseline(order=7, insitu=False)
4536        """
4537
4538        try:
4539            varlist = vars()
4540
4541            if insitu is None:
4542                insitu = rcParams['insitu']
4543            if insitu:
4544                workscan = self
4545            else:
4546                workscan = self.copy()
4547
4548            if mask           is None: mask           = []
4549            if order          is None: order          = 0
4550            if clipthresh     is None: clipthresh     = 3.0
4551            if clipniter      is None: clipniter      = 0
4552            if edge           is None: edge           = (0, 0)
4553            if threshold      is None: threshold      = 3
4554            if chan_avg_limit is None: chan_avg_limit = 1
4555            if plot           is None: plot           = False
4556            if getresidual    is None: getresidual    = True
4557            if showprogress   is None: showprogress   = True
4558            if minnrow        is None: minnrow        = 1000
4559            if outlog         is None: outlog         = False
4560            if blfile         is None: blfile         = ''
4561            if csvformat      is None: csvformat      = False
4562            if bltable        is None: bltable        = ''
4563
4564            scsvformat = 'T' if csvformat else 'F'
4565
4566            edge = normalise_edge_param(edge)
4567
4568            if plot:
4569                outblfile = (blfile != "") and \
4570                    os.path.exists(os.path.expanduser(os.path.expandvars(blfile)))
4571                if outblfile: blf = open(blfile, "a")
4572               
4573                from asap.asaplinefind import linefinder
4574                fl = linefinder()
4575                fl.set_options(threshold=threshold, avg_limit=chan_avg_limit)
4576                fl.set_scan(workscan)
4577               
4578                f = fitter()
4579                f.set_function(lpoly=order)
4580
4581                rows = xrange(workscan.nrow())
4582                #if len(rows) > 0: workscan._init_blinfo()
4583
4584                action = "H"
4585                for r in rows:
4586                    idx = 2*workscan.getif(r)
4587                    if mask:
4588                        msk = mask_and(mask, workscan._getmask(r)) # (CAS-1434)
4589                    else: # mask=None
4590                        msk = workscan._getmask(r)
4591                    fl.find_lines(r, msk, edge[idx:idx+2]) 
4592
4593                    f.x = workscan._getabcissa(r)
4594                    f.y = workscan._getspectrum(r)
4595                    f.mask = fl.get_mask()
4596                    f.data = None
4597                    f.fit()
4598
4599                    if action != "Y": # skip plotting when accepting all
4600                        f.plot(residual=True)
4601                    #accept_fit = raw_input("Accept fit ( [y]/n ): ")
4602                    accept_fit = self._get_verify_action("Accept fit?",action)
4603                    if r == 0: action = None
4604                    if accept_fit.upper() == "N":
4605                        #workscan._append_blinfo(None, None, None)
4606                        continue
4607                    elif accept_fit.upper() == "R":
4608                        break
4609                    elif accept_fit.upper() == "A":
4610                        action = "Y"
4611
4612                    blpars = f.get_parameters()
4613                    masklist = workscan.get_masklist(f.mask, row=r, silent=True)
4614                    #workscan._append_blinfo(blpars, masklist, f.mask)
4615                    workscan._setspectrum(
4616                        (f.fitter.getresidual() if getresidual
4617                                                else f.fitter.getfit()), r
4618                        )
4619
4620                    if outblfile:
4621                        rms = workscan.get_rms(f.mask, r)
4622                        dataout = \
4623                            workscan.format_blparams_row(blpars["params"],
4624                                                         blpars["fixed"],
4625                                                         rms, str(masklist),
4626                                                         r, True, csvformat)
4627                        blf.write(dataout)
4628                   
4629                f._p.unmap()
4630                f._p = None
4631
4632                if outblfile: blf.close()
4633            else:
4634                workscan._auto_poly_baseline(mask, order,
4635                                             clipthresh, clipniter,
4636                                             edge, threshold,
4637                                             chan_avg_limit, getresidual,
4638                                             pack_progress_params(showprogress,
4639                                                                  minnrow),
4640                                             outlog, scsvformat+blfile,
4641                                             bltable)
4642            workscan._add_history("auto_poly_baseline", varlist)
4643
4644            if bltable == '':
4645                if insitu:
4646                    self._assign(workscan)
4647                else:
4648                    return workscan
4649            else:
4650                if not insitu:
4651                    return None
4652           
4653        except RuntimeError, e:
4654            raise_fitting_failure_exception(e)
4655
4656    def _init_blinfo(self):
4657        """\
4658        Initialise the following three auxiliary members:
4659           blpars : parameters of the best-fit baseline,
4660           masklists : mask data (edge positions of masked channels) and
4661           actualmask : mask data (in boolean list),
4662        to keep for use later (including output to logger/text files).
4663        Used by poly_baseline() and auto_poly_baseline() in case of
4664        'plot=True'.
4665        """
4666        self.blpars = []
4667        self.masklists = []
4668        self.actualmask = []
4669        return
4670
4671    def _append_blinfo(self, data_blpars, data_masklists, data_actualmask):
4672        """\
4673        Append baseline-fitting related info to blpars, masklist and
4674        actualmask.
4675        """
4676        self.blpars.append(data_blpars)
4677        self.masklists.append(data_masklists)
4678        self.actualmask.append(data_actualmask)
4679        return
4680       
4681    @asaplog_post_dec
4682    def rotate_linpolphase(self, angle):
4683        """\
4684        Rotate the phase of the complex polarization O=Q+iU correlation.
4685        This is always done in situ in the raw data.  So if you call this
4686        function more than once then each call rotates the phase further.
4687
4688        Parameters:
4689
4690            angle:   The angle (degrees) to rotate (add) by.
4691
4692        Example::
4693
4694            scan.rotate_linpolphase(2.3)
4695
4696        """
4697        varlist = vars()
4698        self._math._rotate_linpolphase(self, angle)
4699        self._add_history("rotate_linpolphase", varlist)
4700        return
4701
4702    @asaplog_post_dec
4703    def rotate_xyphase(self, angle):
4704        """\
4705        Rotate the phase of the XY correlation.  This is always done in situ
4706        in the data.  So if you call this function more than once
4707        then each call rotates the phase further.
4708
4709        Parameters:
4710
4711            angle:   The angle (degrees) to rotate (add) by.
4712
4713        Example::
4714
4715            scan.rotate_xyphase(2.3)
4716
4717        """
4718        varlist = vars()
4719        self._math._rotate_xyphase(self, angle)
4720        self._add_history("rotate_xyphase", varlist)
4721        return
4722
4723    @asaplog_post_dec
4724    def swap_linears(self):
4725        """\
4726        Swap the linear polarisations XX and YY, or better the first two
4727        polarisations as this also works for ciculars.
4728        """
4729        varlist = vars()
4730        self._math._swap_linears(self)
4731        self._add_history("swap_linears", varlist)
4732        return
4733
4734    @asaplog_post_dec
4735    def invert_phase(self):
4736        """\
4737        Invert the phase of the complex polarisation
4738        """
4739        varlist = vars()
4740        self._math._invert_phase(self)
4741        self._add_history("invert_phase", varlist)
4742        return
4743
4744    @asaplog_post_dec
4745    def add(self, offset, insitu=None):
4746        """\
4747        Return a scan where all spectra have the offset added
4748
4749        Parameters:
4750
4751            offset:      the offset
4752
4753            insitu:      if False a new scantable is returned.
4754                         Otherwise, the scaling is done in-situ
4755                         The default is taken from .asaprc (False)
4756
4757        """
4758        if insitu is None: insitu = rcParams['insitu']
4759        self._math._setinsitu(insitu)
4760        varlist = vars()
4761        s = scantable(self._math._unaryop(self, offset, "ADD", False))
4762        s._add_history("add", varlist)
4763        if insitu:
4764            self._assign(s)
4765        else:
4766            return s
4767
4768    @asaplog_post_dec
4769    def scale(self, factor, tsys=True, insitu=None):
4770        """\
4771
4772        Return a scan where all spectra are scaled by the given 'factor'
4773
4774        Parameters:
4775
4776            factor:      the scaling factor (float or 1D float list)
4777
4778            insitu:      if False a new scantable is returned.
4779                         Otherwise, the scaling is done in-situ
4780                         The default is taken from .asaprc (False)
4781
4782            tsys:        if True (default) then apply the operation to Tsys
4783                         as well as the data
4784
4785        """
4786        if insitu is None: insitu = rcParams['insitu']
4787        self._math._setinsitu(insitu)
4788        varlist = vars()
4789        s = None
4790        import numpy
4791        if isinstance(factor, list) or isinstance(factor, numpy.ndarray):
4792            if isinstance(factor[0], list) or isinstance(factor[0],
4793                                                         numpy.ndarray):
4794                from asapmath import _array2dOp
4795                s = _array2dOp( self, factor, "MUL", tsys, insitu )
4796            else:
4797                s = scantable( self._math._arrayop( self, factor,
4798                                                    "MUL", tsys ) )
4799        else:
4800            s = scantable(self._math._unaryop(self, factor, "MUL", tsys))
4801        s._add_history("scale", varlist)
4802        if insitu:
4803            self._assign(s)
4804        else:
4805            return s
4806
4807    @preserve_selection
4808    def set_sourcetype(self, match, matchtype="pattern",
4809                       sourcetype="reference"):
4810        """\
4811        Set the type of the source to be an source or reference scan
4812        using the provided pattern.
4813
4814        Parameters:
4815
4816            match:          a Unix style pattern, regular expression or selector
4817
4818            matchtype:      'pattern' (default) UNIX style pattern or
4819                            'regex' regular expression
4820
4821            sourcetype:     the type of the source to use (source/reference)
4822
4823        """
4824        varlist = vars()
4825        stype = -1
4826        if sourcetype.lower().startswith("r") or sourcetype.lower() == "off":
4827            stype = 1
4828        elif sourcetype.lower().startswith("s") or sourcetype.lower() == "on":
4829            stype = 0
4830        else:
4831            raise ValueError("Illegal sourcetype use s(ource)/on or r(eference)/off")
4832        if matchtype.lower().startswith("p"):
4833            matchtype = "pattern"
4834        elif matchtype.lower().startswith("r"):
4835            matchtype = "regex"
4836        else:
4837            raise ValueError("Illegal matchtype, use p(attern) or r(egex)")
4838        sel = selector()
4839        if isinstance(match, selector):
4840            sel = match
4841        else:
4842            sel.set_query("SRCNAME=%s('%s')" % (matchtype, match))
4843        self.set_selection(sel)
4844        self._setsourcetype(stype)
4845        self._add_history("set_sourcetype", varlist)
4846
4847
4848    def set_sourcename(self, name):
4849        varlist = vars()
4850        self._setsourcename(name)
4851        self._add_history("set_sourcename", varlist)
4852
4853    @asaplog_post_dec
4854    @preserve_selection
4855    def auto_quotient(self, preserve=True, mode='paired', verify=False):
4856        """\
4857        This function allows to build quotients automatically.
4858        It assumes the observation to have the same number of
4859        "ons" and "offs"
4860
4861        Parameters:
4862
4863            preserve:       you can preserve (default) the continuum or
4864                            remove it.  The equations used are
4865
4866                            preserve: Output = Toff * (on/off) - Toff
4867
4868                            remove:   Output = Toff * (on/off) - Ton
4869
4870            mode:           the on/off detection mode
4871                            'paired' (default)
4872                            identifies 'off' scans by the
4873                            trailing '_R' (Mopra/Parkes) or
4874                            '_e'/'_w' (Tid) and matches
4875                            on/off pairs from the observing pattern
4876                            'time'
4877                            finds the closest off in time
4878
4879        .. todo:: verify argument is not implemented
4880
4881        """
4882        varlist = vars()
4883        modes = ["time", "paired"]
4884        if not mode in modes:
4885            msg = "please provide valid mode. Valid modes are %s" % (modes)
4886            raise ValueError(msg)
4887        s = None
4888        if mode.lower() == "paired":
4889            from asap._asap import srctype
4890            sel = self.get_selection()
4891            #sel.set_query("SRCTYPE==psoff")
4892            sel.set_types(srctype.psoff)
4893            self.set_selection(sel)
4894            offs = self.copy()
4895            #sel.set_query("SRCTYPE==pson")
4896            sel.set_types(srctype.pson)
4897            self.set_selection(sel)
4898            ons = self.copy()
4899            s = scantable(self._math._quotient(ons, offs, preserve))
4900        elif mode.lower() == "time":
4901            s = scantable(self._math._auto_quotient(self, mode, preserve))
4902        s._add_history("auto_quotient", varlist)
4903        return s
4904
4905    @asaplog_post_dec
4906    def mx_quotient(self, mask = None, weight='median', preserve=True):
4907        """\
4908        Form a quotient using "off" beams when observing in "MX" mode.
4909
4910        Parameters:
4911
4912            mask:           an optional mask to be used when weight == 'stddev'
4913
4914            weight:         How to average the off beams.  Default is 'median'.
4915
4916            preserve:       you can preserve (default) the continuum or
4917                            remove it.  The equations used are:
4918
4919                                preserve: Output = Toff * (on/off) - Toff
4920
4921                                remove:   Output = Toff * (on/off) - Ton
4922
4923        """
4924        mask = mask or ()
4925        varlist = vars()
4926        on = scantable(self._math._mx_extract(self, 'on'))
4927        preoff = scantable(self._math._mx_extract(self, 'off'))
4928        off = preoff.average_time(mask=mask, weight=weight, scanav=False)
4929        from asapmath  import quotient
4930        q = quotient(on, off, preserve)
4931        q._add_history("mx_quotient", varlist)
4932        return q
4933
4934    @asaplog_post_dec
4935    def freq_switch(self, insitu=None):
4936        """\
4937        Apply frequency switching to the data.
4938
4939        Parameters:
4940
4941            insitu:      if False a new scantable is returned.
4942                         Otherwise, the swictching is done in-situ
4943                         The default is taken from .asaprc (False)
4944
4945        """
4946        if insitu is None: insitu = rcParams['insitu']
4947        self._math._setinsitu(insitu)
4948        varlist = vars()
4949        s = scantable(self._math._freqswitch(self))
4950        s._add_history("freq_switch", varlist)
4951        if insitu:
4952            self._assign(s)
4953        else:
4954            return s
4955
4956    @asaplog_post_dec
4957    def recalc_azel(self):
4958        """Recalculate the azimuth and elevation for each position."""
4959        varlist = vars()
4960        self._recalcazel()
4961        self._add_history("recalc_azel", varlist)
4962        return
4963
4964    @asaplog_post_dec
4965    def __add__(self, other):
4966        """
4967        implicit on all axes and on Tsys
4968        """
4969        varlist = vars()
4970        s = self.__op( other, "ADD" )
4971        s._add_history("operator +", varlist)
4972        return s
4973
4974    @asaplog_post_dec
4975    def __sub__(self, other):
4976        """
4977        implicit on all axes and on Tsys
4978        """
4979        varlist = vars()
4980        s = self.__op( other, "SUB" )
4981        s._add_history("operator -", varlist)
4982        return s
4983
4984    @asaplog_post_dec
4985    def __mul__(self, other):
4986        """
4987        implicit on all axes and on Tsys
4988        """
4989        varlist = vars()
4990        s = self.__op( other, "MUL" ) ;
4991        s._add_history("operator *", varlist)
4992        return s
4993
4994
4995    @asaplog_post_dec
4996    def __div__(self, other):
4997        """
4998        implicit on all axes and on Tsys
4999        """
5000        varlist = vars()
5001        s = self.__op( other, "DIV" )
5002        s._add_history("operator /", varlist)
5003        return s
5004
5005    @asaplog_post_dec
5006    def __op( self, other, mode ):
5007        s = None
5008        if isinstance(other, scantable):
5009            s = scantable(self._math._binaryop(self, other, mode))
5010        elif isinstance(other, float):
5011            if other == 0.0:
5012                raise ZeroDivisionError("Dividing by zero is not recommended")
5013            s = scantable(self._math._unaryop(self, other, mode, False))
5014        elif isinstance(other, list) or isinstance(other, numpy.ndarray):
5015            if isinstance(other[0], list) \
5016                    or isinstance(other[0], numpy.ndarray):
5017                from asapmath import _array2dOp
5018                s = _array2dOp( self, other, mode, False )
5019            else:
5020                s = scantable( self._math._arrayop( self, other,
5021                                                    mode, False ) )
5022        else:
5023            raise TypeError("Other input is not a scantable or float value")
5024        return s
5025
5026    @asaplog_post_dec
5027    def get_fit(self, row=0):
5028        """\
5029        Print or return the stored fits for a row in the scantable
5030
5031        Parameters:
5032
5033            row:    the row which the fit has been applied to.
5034
5035        """
5036        if row > self.nrow():
5037            return
5038        from asap.asapfit import asapfit
5039        fit = asapfit(self._getfit(row))
5040        asaplog.push( '%s' %(fit) )
5041        return fit.as_dict()
5042
5043    @preserve_selection
5044    def flag_nans(self):
5045        """\
5046        Utility function to flag NaN values in the scantable.
5047        """
5048        import numpy
5049        basesel = self.get_selection()
5050        for i in range(self.nrow()):
5051            sel = self.get_row_selector(i)
5052            self.set_selection(basesel+sel)
5053            nans = numpy.isnan(self._getspectrum(0))
5054            if numpy.any(nans):
5055                bnans = [ bool(v) for v in nans]
5056                self.flag(bnans)
5057       
5058        self.set_selection(basesel)
5059
5060    def get_row_selector(self, rowno):
5061        return selector(rows=[rowno])
5062
5063    def _add_history(self, funcname, parameters):
5064        if not rcParams['scantable.history']:
5065            return
5066        # create date
5067        sep = "##"
5068        from datetime import datetime
5069        dstr = datetime.now().strftime('%Y/%m/%d %H:%M:%S')
5070        hist = dstr+sep
5071        hist += funcname+sep#cdate+sep
5072        if parameters.has_key('self'):
5073            del parameters['self']
5074        for k, v in parameters.iteritems():
5075            if type(v) is dict:
5076                for k2, v2 in v.iteritems():
5077                    hist += k2
5078                    hist += "="
5079                    if isinstance(v2, scantable):
5080                        hist += 'scantable'
5081                    elif k2 == 'mask':
5082                        if isinstance(v2, list) or isinstance(v2, tuple):
5083                            hist += str(self._zip_mask(v2))
5084                        else:
5085                            hist += str(v2)
5086                    else:
5087                        hist += str(v2)
5088            else:
5089                hist += k
5090                hist += "="
5091                if isinstance(v, scantable):
5092                    hist += 'scantable'
5093                elif k == 'mask':
5094                    if isinstance(v, list) or isinstance(v, tuple):
5095                        hist += str(self._zip_mask(v))
5096                    else:
5097                        hist += str(v)
5098                else:
5099                    hist += str(v)
5100            hist += sep
5101        hist = hist[:-2] # remove trailing '##'
5102        self._addhistory(hist)
5103
5104
5105    def _zip_mask(self, mask):
5106        mask = list(mask)
5107        i = 0
5108        segments = []
5109        while mask[i:].count(1):
5110            i += mask[i:].index(1)
5111            if mask[i:].count(0):
5112                j = i + mask[i:].index(0)
5113            else:
5114                j = len(mask)
5115            segments.append([i, j])
5116            i = j
5117        return segments
5118
5119    def _get_ordinate_label(self):
5120        fu = "("+self.get_fluxunit()+")"
5121        import re
5122        lbl = "Intensity"
5123        if re.match(".K.", fu):
5124            lbl = "Brightness Temperature "+ fu
5125        elif re.match(".Jy.", fu):
5126            lbl = "Flux density "+ fu
5127        return lbl
5128
5129    def _check_ifs(self):
5130#        return len(set([self.nchan(i) for i in self.getifnos()])) == 1
5131        nchans = [self.nchan(i) for i in self.getifnos()]
5132        nchans = filter(lambda t: t > 0, nchans)
5133        return (sum(nchans)/len(nchans) == nchans[0])
5134
5135    @asaplog_post_dec
5136    def _fill(self, names, unit, average, opts={}):
5137        first = True
5138        fullnames = []
5139        for name in names:
5140            name = os.path.expandvars(name)
5141            name = os.path.expanduser(name)
5142            if not os.path.exists(name):
5143                msg = "File '%s' does not exists" % (name)
5144                raise IOError(msg)
5145            fullnames.append(name)
5146        if average:
5147            asaplog.push('Auto averaging integrations')
5148        stype = int(rcParams['scantable.storage'].lower() == 'disk')
5149        for name in fullnames:
5150            tbl = Scantable(stype)
5151            if is_ms( name ):
5152                r = msfiller( tbl )
5153            else:
5154                r = filler( tbl )
5155            msg = "Importing %s..." % (name)
5156            asaplog.push(msg, False)
5157            r.open(name, opts)
5158            rx = rcParams['scantable.reference']
5159            r.setreferenceexpr(rx)
5160            r.fill()
5161            if average:
5162                tbl = self._math._average((tbl, ), (), 'NONE', 'SCAN')
5163            if not first:
5164                tbl = self._math._merge([self, tbl])
5165            Scantable.__init__(self, tbl)
5166            r.close()
5167            del r, tbl
5168            first = False
5169            #flush log
5170        asaplog.post()
5171        if unit is not None:
5172            self.set_fluxunit(unit)
5173        if not is_casapy():
5174            self.set_freqframe(rcParams['scantable.freqframe'])
5175
5176    def _get_verify_action( self, msg, action=None ):
5177        valid_act = ['Y', 'N', 'A', 'R']
5178        if not action or not isinstance(action, str):
5179            action = raw_input("%s [Y/n/a/r] (h for help): " % msg)
5180        if action == '':
5181            return "Y"
5182        elif (action.upper()[0] in valid_act):
5183            return action.upper()[0]
5184        elif (action.upper()[0] in ['H','?']):
5185            print "Available actions of verification [Y|n|a|r]"
5186            print " Y : Yes for current data (default)"
5187            print " N : No for current data"
5188            print " A : Accept all in the following and exit from verification"
5189            print " R : Reject all in the following and exit from verification"
5190            print " H or ?: help (show this message)"
5191            return self._get_verify_action(msg)
5192        else:
5193            return 'Y'
5194
5195    def __getitem__(self, key):
5196        if key < 0:
5197            key += self.nrow()
5198        if key >= self.nrow():
5199            raise IndexError("Row index out of range.")
5200        return self._getspectrum(key)
5201
5202    def __setitem__(self, key, value):
5203        if key < 0:
5204            key += self.nrow()
5205        if key >= self.nrow():
5206            raise IndexError("Row index out of range.")
5207        if not hasattr(value, "__len__") or \
5208                len(value) > self.nchan(self.getif(key)):
5209            raise ValueError("Spectrum length doesn't match.")
5210        return self._setspectrum(value, key)
5211
5212    def __len__(self):
5213        return self.nrow()
5214
5215    def __iter__(self):
5216        for i in range(len(self)):
5217            yield self[i]
Note: See TracBrowser for help on using the repository browser.