source: trunk/python/scantable.py @ 2973

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

New Development: No

JIRA Issue: Yes CAS-6599

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs: test_sdstat

Put in Release Notes:

Module(s): sd

Description: modified scantable.stats() so that the output of sdstat have None value for flagged rows.


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