source: trunk/python/scantable.py @ 2891

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

New Development: No

JIRA Issue: Yes CAS-5870

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs:

Put in Release Notes:

Module(s): sd

Description: added functions for scantable to correctly treat the given restfreq.


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