source: trunk/python/scantable.py @ 3101

Last change on this file since 3101 was 3010, 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:

Put in Release Notes: No

Module(s): sd

Description: modified so that sd.scantable.stats() returns None for flagged rows and rows with all channels flagged.


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