source: trunk/python/scantable.py @ 2791

Last change on this file since 2791 was 2791, checked in by Malte Marquarding, 11 years ago

Ticket #289: added anility to set Tsys, both for scalar and vector values

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