source: trunk/python/scantable.py @ 2987

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

New Development: No

JIRA Issue: No

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs:

Put in Release Notes:

Module(s): sd

Description: fixed parse_idx_selection for mode='row' so that it returns a list of existing row numbers only.


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