source: trunk/python/scantable.py @ 2937

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

New Development: No

JIRA Issue: Yes CAS-6485

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs:

Put in Release Notes: No

Module(s): sd

Description: modified scantable.parse_spw_selection() so that velocity corresponding to the central frequency rather than the centeral value of velocity range is checked in selecting spw via velocity ranges.


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