source: trunk/python/scantable.py @ 2820

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

Issue #293: added scantbale.drop_history and added extra parameters to scantable.history to allow selection of rows

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 182.5 KB
Line 
1"""This module defines the scantable class."""
2
3import os
4import re
5import tempfile
6import numpy
7try:
8    from functools import wraps as wraps_dec
9except ImportError:
10    from asap.compatibility import wraps as wraps_dec
11
12from asap.env import is_casapy
13from asap._asap import Scantable
14from asap._asap import filler, msfiller
15from asap.parameters import rcParams
16from asap.logging import asaplog, asaplog_post_dec
17from asap.selector import selector
18from asap.linecatalog import linecatalog
19from asap.coordinate import coordinate
20from asap.utils import _n_bools, mask_not, mask_and, mask_or, page
21from asap.asapfitter import fitter
22
23def preserve_selection(func):
24    @wraps_dec(func)
25    def wrap(obj, *args, **kw):
26        basesel = obj.get_selection()
27        try:
28            val = func(obj, *args, **kw)
29        finally:
30            obj.set_selection(basesel)
31        return val
32    return wrap
33
34def is_scantable(filename):
35    """Is the given file a scantable?
36
37    Parameters:
38
39        filename: the name of the file/directory to test
40
41    """
42    if ( os.path.isdir(filename)
43         and os.path.exists(filename+'/table.info')
44         and os.path.exists(filename+'/table.dat') ):
45        f=open(filename+'/table.info')
46        l=f.readline()
47        f.close()
48        match_pattern = '^Type = (Scantable)? *$'
49        if re.match(match_pattern,l):
50            return True
51        else:
52            return False
53    else:
54        return False
55##     return (os.path.isdir(filename)
56##             and not os.path.exists(filename+'/table.f1')
57##             and os.path.exists(filename+'/table.info'))
58
59def is_ms(filename):
60    """Is the given file a MeasurementSet?
61
62    Parameters:
63
64        filename: the name of the file/directory to test
65
66    """
67    if ( os.path.isdir(filename)
68         and os.path.exists(filename+'/table.info')
69         and os.path.exists(filename+'/table.dat') ):
70        f=open(filename+'/table.info')
71        l=f.readline()
72        f.close()
73        if ( l.find('Measurement Set') != -1 ):
74            return True
75        else:
76            return False
77    else:
78        return False
79
80def normalise_edge_param(edge):
81    """\
82    Convert a given edge value to a one-dimensional array that can be
83    given to baseline-fitting/subtraction functions.
84    The length of the output value will be an even because values for
85    the both sides of spectra are to be contained for each IF. When
86    the length is 2, the values will be applied to all IFs. If the length
87    is larger than 2, it will be 2*ifnos().
88    Accepted format of edge include:
89            * an integer - will be used for both sides of spectra of all IFs.
90                  e.g. 10 is converted to [10,10]
91            * an empty list/tuple [] - converted to [0, 0] and used for all IFs.
92            * a list/tuple containing an integer - same as the above case.
93                  e.g. [10] is converted to [10,10]
94            * a list/tuple containing two integers - will be used for all IFs.
95                  e.g. [5,10] is output as it is. no need to convert.
96            * a list/tuple of lists/tuples containing TWO integers -
97                  each element of edge will be used for each IF.
98                  e.g. [[5,10],[15,20]] - [5,10] for IF[0] and [15,20] for IF[1].
99                 
100                  If an element contains the same integer values, the input 'edge'
101                  parameter can be given in a simpler shape in the following cases:
102                      ** when len(edge)!=2
103                          any elements containing the same values can be replaced
104                          to single integers.
105                          e.g. [[15,15]] can be simplified to [15] (or [15,15] or 15 also).
106                          e.g. [[1,1],[2,2],[3,3]] can be simplified to [1,2,3].
107                      ** when len(edge)=2
108                          care is needed for this case: ONLY ONE of the
109                          elements can be a single integer,
110                          e.g. [[5,5],[10,10]] can be simplified to [5,[10,10]]
111                               or [[5,5],10], but can NOT be simplified to [5,10].
112                               when [5,10] given, it is interpreted as
113                               [[5,10],[5,10],[5,10],...] instead, as shown before.
114    """
115    from asap import _is_sequence_or_number as _is_valid
116    if isinstance(edge, list) or isinstance(edge, tuple):
117        for edgepar in edge:
118            if not _is_valid(edgepar, int):
119                raise ValueError, "Each element of the 'edge' tuple has \
120                                   to be a pair of integers or an integer."
121            if isinstance(edgepar, list) or isinstance(edgepar, tuple):
122                if len(edgepar) != 2:
123                    raise ValueError, "Each element of the 'edge' tuple has \
124                                       to be a pair of integers or an integer."
125    else:
126        if not _is_valid(edge, int):
127            raise ValueError, "Parameter 'edge' has to be an integer or a \
128                               pair of integers specified as a tuple. \
129                               Nested tuples are allowed \
130                               to make individual selection for different IFs."
131       
132
133    if isinstance(edge, int):
134        edge = [ edge, edge ]                 # e.g. 3   => [3,3]
135    elif isinstance(edge, list) or isinstance(edge, tuple):
136        if len(edge) == 0:
137            edge = [0, 0]                     # e.g. []  => [0,0]
138        elif len(edge) == 1:
139            if isinstance(edge[0], int):
140                edge = [ edge[0], edge[0] ]   # e.g. [1] => [1,1]
141
142    commonedge = True
143    if len(edge) > 2: commonedge = False
144    else:
145        for edgepar in edge:
146            if isinstance(edgepar, list) or isinstance(edgepar, tuple):
147                commonedge = False
148                break
149
150    if commonedge:
151        if len(edge) > 1:
152            norm_edge = edge
153        else:
154            norm_edge = edge + edge
155    else:
156        norm_edge = []
157        for edgepar in edge:
158            if isinstance(edgepar, int):
159                norm_edge += [edgepar, edgepar]
160            else:
161                norm_edge += edgepar
162
163    return norm_edge
164
165def raise_fitting_failure_exception(e):
166    msg = "The fit failed, possibly because it didn't converge."
167    if rcParams["verbose"]:
168        asaplog.push(str(e))
169        asaplog.push(str(msg))
170    else:
171        raise RuntimeError(str(e)+'\n'+msg)
172
173def pack_progress_params(showprogress, minnrow):
174    return str(showprogress).lower() + ',' + str(minnrow)
175
176def pack_blinfo(blinfo, maxirow):
177    """\
178    convert a dictionary or a list of dictionaries of baseline info
179    into a list of comma-separated strings.
180    """
181    if isinstance(blinfo, dict):
182        res = do_pack_blinfo(blinfo, maxirow)
183        return [res] if res != '' else []
184    elif isinstance(blinfo, list) or isinstance(blinfo, tuple):
185        res = []
186        for i in xrange(len(blinfo)):
187            resi = do_pack_blinfo(blinfo[i], maxirow)
188            if resi != '':
189                res.append(resi)
190    return res
191
192def do_pack_blinfo(blinfo, maxirow):
193    """\
194    convert a dictionary of baseline info for a spectrum into
195    a comma-separated string.
196    """
197    dinfo = {}
198    for key in ['row', 'blfunc', 'masklist']:
199        if blinfo.has_key(key):
200            val = blinfo[key]
201            if key == 'row':
202                irow = val
203            if isinstance(val, list) or isinstance(val, tuple):
204                slval = []
205                for i in xrange(len(val)):
206                    if isinstance(val[i], list) or isinstance(val[i], tuple):
207                        for j in xrange(len(val[i])):
208                            slval.append(str(val[i][j]))
209                    else:
210                        slval.append(str(val[i]))
211                sval = ",".join(slval)
212            else:
213                sval = str(val)
214
215            dinfo[key] = sval
216        else:
217            raise ValueError("'"+key+"' is missing in blinfo.")
218
219    if irow >= maxirow: return ''
220   
221    for key in ['order', 'npiece', 'nwave']:
222        if blinfo.has_key(key):
223            val = blinfo[key]
224            if isinstance(val, list) or isinstance(val, tuple):
225                slval = []
226                for i in xrange(len(val)):
227                    slval.append(str(val[i]))
228                sval = ",".join(slval)
229            else:
230                sval = str(val)
231            dinfo[key] = sval
232
233    blfunc = dinfo['blfunc']
234    fspec_keys = {'poly': 'order', 'chebyshev': 'order', 'cspline': 'npiece', 'sinusoid': 'nwave'}
235
236    fspec_key = fspec_keys[blfunc]
237    if not blinfo.has_key(fspec_key):
238        raise ValueError("'"+fspec_key+"' is missing in blinfo.")
239
240    clip_params_n = 0
241    for key in ['clipthresh', 'clipniter']:
242        if blinfo.has_key(key):
243            clip_params_n += 1
244            dinfo[key] = str(blinfo[key])
245
246    if clip_params_n == 0:
247        dinfo['clipthresh'] = '0.0'
248        dinfo['clipniter']  = '0'
249    elif clip_params_n != 2:
250        raise ValueError("both 'clipthresh' and 'clipniter' must be given for n-sigma clipping.")
251
252    lf_params_n = 0
253    for key in ['thresh', 'edge', 'chan_avg_limit']:
254        if blinfo.has_key(key):
255            lf_params_n += 1
256            val = blinfo[key]
257            if isinstance(val, list) or isinstance(val, tuple):
258                slval = []
259                for i in xrange(len(val)):
260                    slval.append(str(val[i]))
261                sval = ",".join(slval)
262            else:
263                sval = str(val)
264            dinfo[key] = sval
265
266    if lf_params_n == 3:
267        dinfo['use_linefinder'] = 'true'
268    elif lf_params_n == 0:
269        dinfo['use_linefinder'] = 'false'
270        dinfo['thresh']         = ''
271        dinfo['edge']           = ''
272        dinfo['chan_avg_limit'] = ''
273    else:
274        raise ValueError("all of 'thresh', 'edge' and 'chan_avg_limit' must be given to use linefinder.")
275   
276    slblinfo = [dinfo['row'], blfunc, dinfo[fspec_key], dinfo['masklist'], \
277                dinfo['clipthresh'], dinfo['clipniter'], \
278                dinfo['use_linefinder'], dinfo['thresh'], dinfo['edge'], dinfo['chan_avg_limit']]
279   
280    return ":".join(slblinfo)
281
282def parse_fitresult(sres):
283    """\
284    Parse the returned value of apply_bltable() or sub_baseline() and
285    extract row number, the best-fit coefficients and rms, then pack
286    them into a dictionary.
287    The input value is generated by Scantable::packFittingResults() and
288    formatted as 'row:coeff[0],coeff[1],..,coeff[n-1]:rms'.
289    """
290    res = []
291    for i in xrange(len(sres)):
292        (srow, scoeff, srms) = sres[i].split(":")
293        row = int(srow)
294        rms = float(srms)
295        lscoeff = scoeff.split(",")
296        coeff = []
297        for j in xrange(len(lscoeff)):
298            coeff.append(float(lscoeff[j]))
299        res.append({'row': row, 'coeff': coeff, 'rms': rms})
300
301    return res
302
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, nrows=-1, start=0):
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        n = self._historylength()
2079        if nrows == -1:
2080            nrows = n
2081        if start+nrows > n:
2082            nrows = nrows-start
2083        if n > 1000 and nrows == n:
2084            nrows = 1000
2085            start = n-1000
2086            asaplog.push("Warning: History has {0} entries. Displaying last "
2087                         "1000".format(n))
2088        hist = list(self._gethistory(nrows, start))
2089        out = "-"*80
2090        for h in hist:
2091            if not h.strip():
2092                continue
2093            if h.find("---") >-1:
2094                continue
2095            else:
2096                items = h.split("##")
2097                date = items[0]
2098                func = items[1]
2099                items = items[2:]
2100                out += "\n"+date+"\n"
2101                out += "Function: %s\n  Parameters:" % (func)
2102                for i in items:
2103                    if i == '':
2104                        continue
2105                    s = i.split("=")
2106                    out += "\n   %s = %s" % (s[0], s[1])
2107                out = "\n".join([out, "*"*80])
2108        if filename is not None:
2109            if filename is "":
2110                filename = 'scantable_history.txt'
2111            filename = os.path.expandvars(os.path.expanduser(filename))
2112            if not os.path.isdir(filename):
2113                data = open(filename, 'w')
2114                data.write(out)
2115                data.close()
2116            else:
2117                msg = "Illegal file name '%s'." % (filename)
2118                raise IOError(msg)
2119        return page(out)
2120
2121    #
2122    # Maths business
2123    #
2124    @asaplog_post_dec
2125    def average_time(self, mask=None, scanav=False, weight='tint', align=False,
2126                     avmode="NONE"):
2127        """\
2128        Return the (time) weighted average of a scan. Scans will be averaged
2129        only if the source direction (RA/DEC) is within 1' otherwise
2130
2131        *Note*:
2132
2133            in channels only - align if necessary
2134
2135        Parameters:
2136
2137            mask:     an optional mask (only used for 'var' and 'tsys'
2138                      weighting)
2139
2140            scanav:   True averages each scan separately
2141                      False (default) averages all scans together,
2142
2143            weight:   Weighting scheme.
2144                      'none'     (mean no weight)
2145                      'var'      (1/var(spec) weighted)
2146                      'tsys'     (1/Tsys**2 weighted)
2147                      'tint'     (integration time weighted)
2148                      'tintsys'  (Tint/Tsys**2)
2149                      'median'   ( median averaging)
2150                      The default is 'tint'
2151
2152            align:    align the spectra in velocity before averaging. It takes
2153                      the time of the first spectrum as reference time.
2154            avmode:   'SOURCE' - also select by source name -  or
2155                      'NONE' (default). Not applicable for scanav=True or
2156                      weight=median
2157
2158        Example::
2159
2160            # time average the scantable without using a mask
2161            newscan = scan.average_time()
2162
2163        """
2164        varlist = vars()
2165        weight = weight or 'TINT'
2166        mask = mask or ()
2167        scanav = (scanav and 'SCAN') or avmode.upper()
2168        scan = (self, )
2169
2170        if align:
2171            scan = (self.freq_align(insitu=False), )
2172            asaplog.push("Note: Alignment is don on a source-by-source basis")
2173            asaplog.push("Note: Averaging (by default) is not")
2174            # we need to set it to SOURCE averaging here           
2175        s = None
2176        if weight.upper() == 'MEDIAN':
2177            s = scantable(self._math._averagechannel(scan[0], 'MEDIAN',
2178                                                     scanav))
2179        else:
2180            s = scantable(self._math._average(scan, mask, weight.upper(),
2181                          scanav))
2182        s._add_history("average_time", varlist)
2183        return s
2184
2185    @asaplog_post_dec
2186    def convert_flux(self, jyperk=None, eta=None, d=None, insitu=None):
2187        """\
2188        Return a scan where all spectra are converted to either
2189        Jansky or Kelvin depending upon the flux units of the scan table.
2190        By default the function tries to look the values up internally.
2191        If it can't find them (or if you want to over-ride), you must
2192        specify EITHER jyperk OR eta (and D which it will try to look up
2193        also if you don't set it). jyperk takes precedence if you set both.
2194
2195        Parameters:
2196
2197            jyperk:      the Jy / K conversion factor
2198
2199            eta:         the aperture efficiency
2200
2201            d:           the geometric diameter (metres)
2202
2203            insitu:      if False a new scantable is returned.
2204                         Otherwise, the scaling is done in-situ
2205                         The default is taken from .asaprc (False)
2206
2207        """
2208        if insitu is None: insitu = rcParams['insitu']
2209        self._math._setinsitu(insitu)
2210        varlist = vars()
2211        jyperk = jyperk or -1.0
2212        d = d or -1.0
2213        eta = eta or -1.0
2214        s = scantable(self._math._convertflux(self, d, eta, jyperk))
2215        s._add_history("convert_flux", varlist)
2216        if insitu: self._assign(s)
2217        else: return s
2218
2219    @asaplog_post_dec
2220    def gain_el(self, poly=None, filename="", method="linear", insitu=None):
2221        """\
2222        Return a scan after applying a gain-elevation correction.
2223        The correction can be made via either a polynomial or a
2224        table-based interpolation (and extrapolation if necessary).
2225        You specify polynomial coefficients, an ascii table or neither.
2226        If you specify neither, then a polynomial correction will be made
2227        with built in coefficients known for certain telescopes (an error
2228        will occur if the instrument is not known).
2229        The data and Tsys are *divided* by the scaling factors.
2230
2231        Parameters:
2232
2233            poly:        Polynomial coefficients (default None) to compute a
2234                         gain-elevation correction as a function of
2235                         elevation (in degrees).
2236
2237            filename:    The name of an ascii file holding correction factors.
2238                         The first row of the ascii file must give the column
2239                         names and these MUST include columns
2240                         'ELEVATION' (degrees) and 'FACTOR' (multiply data
2241                         by this) somewhere.
2242                         The second row must give the data type of the
2243                         column. Use 'R' for Real and 'I' for Integer.
2244                         An example file would be
2245                         (actual factors are arbitrary) :
2246
2247                         TIME ELEVATION FACTOR
2248                         R R R
2249                         0.1 0 0.8
2250                         0.2 20 0.85
2251                         0.3 40 0.9
2252                         0.4 60 0.85
2253                         0.5 80 0.8
2254                         0.6 90 0.75
2255
2256            method:      Interpolation method when correcting from a table.
2257                         Values are  'nearest', 'linear' (default), 'cubic'
2258                         and 'spline'
2259
2260            insitu:      if False a new scantable is returned.
2261                         Otherwise, the scaling is done in-situ
2262                         The default is taken from .asaprc (False)
2263
2264        """
2265
2266        if insitu is None: insitu = rcParams['insitu']
2267        self._math._setinsitu(insitu)
2268        varlist = vars()
2269        poly = poly or ()
2270        from os.path import expandvars
2271        filename = expandvars(filename)
2272        s = scantable(self._math._gainel(self, poly, filename, method))
2273        s._add_history("gain_el", varlist)
2274        if insitu:
2275            self._assign(s)
2276        else:
2277            return s
2278
2279    @asaplog_post_dec
2280    def freq_align(self, reftime=None, method='cubic', insitu=None):
2281        """\
2282        Return a scan where all rows have been aligned in frequency/velocity.
2283        The alignment frequency frame (e.g. LSRK) is that set by function
2284        set_freqframe.
2285
2286        Parameters:
2287
2288            reftime:     reference time to align at. By default, the time of
2289                         the first row of data is used.
2290
2291            method:      Interpolation method for regridding the spectra.
2292                         Choose from 'nearest', 'linear', 'cubic' (default)
2293                         and 'spline'
2294
2295            insitu:      if False a new scantable is returned.
2296                         Otherwise, the scaling is done in-situ
2297                         The default is taken from .asaprc (False)
2298
2299        """
2300        if insitu is None: insitu = rcParams["insitu"]
2301        oldInsitu = self._math._insitu()
2302        self._math._setinsitu(insitu)
2303        varlist = vars()
2304        reftime = reftime or ""
2305        s = scantable(self._math._freq_align(self, reftime, method))
2306        s._add_history("freq_align", varlist)
2307        self._math._setinsitu(oldInsitu)
2308        if insitu:
2309            self._assign(s)
2310        else:
2311            return s
2312
2313    @asaplog_post_dec
2314    def opacity(self, tau=None, insitu=None):
2315        """\
2316        Apply an opacity correction. The data
2317        and Tsys are multiplied by the correction factor.
2318
2319        Parameters:
2320
2321            tau:         (list of) opacity from which the correction factor is
2322                         exp(tau*ZD)
2323                         where ZD is the zenith-distance.
2324                         If a list is provided, it has to be of length nIF,
2325                         nIF*nPol or 1 and in order of IF/POL, e.g.
2326                         [opif0pol0, opif0pol1, opif1pol0 ...]
2327                         if tau is `None` the opacities are determined from a
2328                         model.
2329
2330            insitu:      if False a new scantable is returned.
2331                         Otherwise, the scaling is done in-situ
2332                         The default is taken from .asaprc (False)
2333
2334        """
2335        if insitu is None:
2336            insitu = rcParams['insitu']
2337        self._math._setinsitu(insitu)
2338        varlist = vars()
2339        if not hasattr(tau, "__len__"):
2340            tau = [tau]
2341        s = scantable(self._math._opacity(self, tau))
2342        s._add_history("opacity", varlist)
2343        if insitu:
2344            self._assign(s)
2345        else:
2346            return s
2347
2348    @asaplog_post_dec
2349    def bin(self, width=5, insitu=None):
2350        """\
2351        Return a scan where all spectra have been binned up.
2352
2353        Parameters:
2354
2355            width:       The bin width (default=5) in pixels
2356
2357            insitu:      if False a new scantable is returned.
2358                         Otherwise, the scaling is done in-situ
2359                         The default is taken from .asaprc (False)
2360
2361        """
2362        if insitu is None:
2363            insitu = rcParams['insitu']
2364        self._math._setinsitu(insitu)
2365        varlist = vars()
2366        s = scantable(self._math._bin(self, width))
2367        s._add_history("bin", varlist)
2368        if insitu:
2369            self._assign(s)
2370        else:
2371            return s
2372
2373    @asaplog_post_dec
2374    def reshape(self, first, last, insitu=None):
2375        """Resize the band by providing first and last channel.
2376        This will cut off all channels outside [first, last].
2377        """
2378        if insitu is None:
2379            insitu = rcParams['insitu']
2380        varlist = vars()
2381        if last < 0:
2382            last = self.nchan()-1 + last
2383        s = None
2384        if insitu:
2385            s = self
2386        else:
2387            s = self.copy()
2388        s._reshape(first,last)
2389        s._add_history("reshape", varlist)
2390        if not insitu:
2391            return s       
2392
2393    @asaplog_post_dec
2394    def resample(self, width=5, method='cubic', insitu=None):
2395        """\
2396        Return a scan where all spectra have been binned up.
2397
2398        Parameters:
2399
2400            width:       The bin width (default=5) in pixels
2401
2402            method:      Interpolation method when correcting from a table.
2403                         Values are  'nearest', 'linear', 'cubic' (default)
2404                         and 'spline'
2405
2406            insitu:      if False a new scantable is returned.
2407                         Otherwise, the scaling is done in-situ
2408                         The default is taken from .asaprc (False)
2409
2410        """
2411        if insitu is None:
2412            insitu = rcParams['insitu']
2413        self._math._setinsitu(insitu)
2414        varlist = vars()
2415        s = scantable(self._math._resample(self, method, width))
2416        s._add_history("resample", varlist)
2417        if insitu:
2418            self._assign(s)
2419        else:
2420            return s
2421
2422    @asaplog_post_dec
2423    def average_pol(self, mask=None, weight='none'):
2424        """\
2425        Average the Polarisations together.
2426
2427        Parameters:
2428
2429            mask:        An optional mask defining the region, where the
2430                         averaging will be applied. The output will have all
2431                         specified points masked.
2432
2433            weight:      Weighting scheme. 'none' (default), 'var' (1/var(spec)
2434                         weighted), or 'tsys' (1/Tsys**2 weighted)
2435
2436        """
2437        varlist = vars()
2438        mask = mask or ()
2439        s = scantable(self._math._averagepol(self, mask, weight.upper()))
2440        s._add_history("average_pol", varlist)
2441        return s
2442
2443    @asaplog_post_dec
2444    def average_beam(self, mask=None, weight='none'):
2445        """\
2446        Average the Beams together.
2447
2448        Parameters:
2449            mask:        An optional mask defining the region, where the
2450                         averaging will be applied. The output will have all
2451                         specified points masked.
2452
2453            weight:      Weighting scheme. 'none' (default), 'var' (1/var(spec)
2454                         weighted), or 'tsys' (1/Tsys**2 weighted)
2455
2456        """
2457        varlist = vars()
2458        mask = mask or ()
2459        s = scantable(self._math._averagebeams(self, mask, weight.upper()))
2460        s._add_history("average_beam", varlist)
2461        return s
2462
2463    def parallactify(self, pflag):
2464        """\
2465        Set a flag to indicate whether this data should be treated as having
2466        been 'parallactified' (total phase == 0.0)
2467
2468        Parameters:
2469
2470            pflag:  Bool indicating whether to turn this on (True) or
2471                    off (False)
2472
2473        """
2474        varlist = vars()
2475        self._parallactify(pflag)
2476        self._add_history("parallactify", varlist)
2477
2478    @asaplog_post_dec
2479    def convert_pol(self, poltype=None):
2480        """\
2481        Convert the data to a different polarisation type.
2482        Note that you will need cross-polarisation terms for most conversions.
2483
2484        Parameters:
2485
2486            poltype:    The new polarisation type. Valid types are:
2487                        'linear', 'circular', 'stokes' and 'linpol'
2488
2489        """
2490        varlist = vars()
2491        s = scantable(self._math._convertpol(self, poltype))
2492        s._add_history("convert_pol", varlist)
2493        return s
2494
2495    @asaplog_post_dec
2496    def smooth(self, kernel="hanning", width=5.0, order=2, plot=False,
2497               insitu=None):
2498        """\
2499        Smooth the spectrum by the specified kernel (conserving flux).
2500
2501        Parameters:
2502
2503            kernel:     The type of smoothing kernel. Select from
2504                        'hanning' (default), 'gaussian', 'boxcar', 'rmedian'
2505                        or 'poly'
2506
2507            width:      The width of the kernel in pixels. For hanning this is
2508                        ignored otherwise it defauls to 5 pixels.
2509                        For 'gaussian' it is the Full Width Half
2510                        Maximum. For 'boxcar' it is the full width.
2511                        For 'rmedian' and 'poly' it is the half width.
2512
2513            order:      Optional parameter for 'poly' kernel (default is 2), to
2514                        specify the order of the polnomial. Ignored by all other
2515                        kernels.
2516
2517            plot:       plot the original and the smoothed spectra.
2518                        In this each indivual fit has to be approved, by
2519                        typing 'y' or 'n'
2520
2521            insitu:     if False a new scantable is returned.
2522                        Otherwise, the scaling is done in-situ
2523                        The default is taken from .asaprc (False)
2524
2525        """
2526        if insitu is None: insitu = rcParams['insitu']
2527        self._math._setinsitu(insitu)
2528        varlist = vars()
2529
2530        if plot: orgscan = self.copy()
2531
2532        s = scantable(self._math._smooth(self, kernel.lower(), width, order))
2533        s._add_history("smooth", varlist)
2534
2535        action = 'H'
2536        if plot:
2537            from asap.asapplotter import new_asaplot
2538            theplot = new_asaplot(rcParams['plotter.gui'])
2539            from matplotlib import rc as rcp
2540            rcp('lines', linewidth=1)
2541            theplot.set_panels()
2542            ylab=s._get_ordinate_label()
2543            #theplot.palette(0,["#777777","red"])
2544            for r in xrange(s.nrow()):
2545                xsm=s._getabcissa(r)
2546                ysm=s._getspectrum(r)
2547                xorg=orgscan._getabcissa(r)
2548                yorg=orgscan._getspectrum(r)
2549                if action != "N": #skip plotting if rejecting all
2550                    theplot.clear()
2551                    theplot.hold()
2552                    theplot.set_axes('ylabel',ylab)
2553                    theplot.set_axes('xlabel',s._getabcissalabel(r))
2554                    theplot.set_axes('title',s._getsourcename(r))
2555                    theplot.set_line(label='Original',color="#777777")
2556                    theplot.plot(xorg,yorg)
2557                    theplot.set_line(label='Smoothed',color="red")
2558                    theplot.plot(xsm,ysm)
2559                    ### Ugly part for legend
2560                    for i in [0,1]:
2561                        theplot.subplots[0]['lines'].append(
2562                            [theplot.subplots[0]['axes'].lines[i]]
2563                            )
2564                    theplot.release()
2565                    ### Ugly part for legend
2566                    theplot.subplots[0]['lines']=[]
2567                res = self._get_verify_action("Accept smoothing?",action)
2568                #print "IF%d, POL%d: got result = %s" %(s.getif(r),s.getpol(r),res)
2569                if r == 0: action = None
2570                #res = raw_input("Accept smoothing ([y]/n): ")
2571                if res.upper() == 'N':
2572                    # reject for the current rows
2573                    s._setspectrum(yorg, r)
2574                elif res.upper() == 'R':
2575                    # reject all the following rows
2576                    action = "N"
2577                    s._setspectrum(yorg, r)
2578                elif res.upper() == 'A':
2579                    # accept all the following rows
2580                    break
2581            theplot.quit()
2582            del theplot
2583            del orgscan
2584
2585        if insitu: self._assign(s)
2586        else: return s
2587
2588    @asaplog_post_dec
2589    def regrid_channel(self, width=5, plot=False, insitu=None):
2590        """\
2591        Regrid the spectra by the specified channel width
2592
2593        Parameters:
2594
2595            width:      The channel width (float) of regridded spectra
2596                        in the current spectral unit.
2597
2598            plot:       [NOT IMPLEMENTED YET]
2599                        plot the original and the regridded spectra.
2600                        In this each indivual fit has to be approved, by
2601                        typing 'y' or 'n'
2602
2603            insitu:     if False a new scantable is returned.
2604                        Otherwise, the scaling is done in-situ
2605                        The default is taken from .asaprc (False)
2606
2607        """
2608        if insitu is None: insitu = rcParams['insitu']
2609        varlist = vars()
2610
2611        if plot:
2612           asaplog.post()
2613           asaplog.push("Verification plot is not implemtnetd yet.")
2614           asaplog.post("WARN")
2615
2616        s = self.copy()
2617        s._regrid_specchan(width)
2618
2619        s._add_history("regrid_channel", varlist)
2620
2621#         if plot:
2622#             from asap.asapplotter import new_asaplot
2623#             theplot = new_asaplot(rcParams['plotter.gui'])
2624#             from matplotlib import rc as rcp
2625#             rcp('lines', linewidth=1)
2626#             theplot.set_panels()
2627#             ylab=s._get_ordinate_label()
2628#             #theplot.palette(0,["#777777","red"])
2629#             for r in xrange(s.nrow()):
2630#                 xsm=s._getabcissa(r)
2631#                 ysm=s._getspectrum(r)
2632#                 xorg=orgscan._getabcissa(r)
2633#                 yorg=orgscan._getspectrum(r)
2634#                 theplot.clear()
2635#                 theplot.hold()
2636#                 theplot.set_axes('ylabel',ylab)
2637#                 theplot.set_axes('xlabel',s._getabcissalabel(r))
2638#                 theplot.set_axes('title',s._getsourcename(r))
2639#                 theplot.set_line(label='Original',color="#777777")
2640#                 theplot.plot(xorg,yorg)
2641#                 theplot.set_line(label='Smoothed',color="red")
2642#                 theplot.plot(xsm,ysm)
2643#                 ### Ugly part for legend
2644#                 for i in [0,1]:
2645#                     theplot.subplots[0]['lines'].append(
2646#                         [theplot.subplots[0]['axes'].lines[i]]
2647#                         )
2648#                 theplot.release()
2649#                 ### Ugly part for legend
2650#                 theplot.subplots[0]['lines']=[]
2651#                 res = raw_input("Accept smoothing ([y]/n): ")
2652#                 if res.upper() == 'N':
2653#                     s._setspectrum(yorg, r)
2654#             theplot.quit()
2655#             del theplot
2656#             del orgscan
2657
2658        if insitu: self._assign(s)
2659        else: return s
2660
2661    @asaplog_post_dec
2662    def _parse_wn(self, wn):
2663        if isinstance(wn, list) or isinstance(wn, tuple):
2664            return wn
2665        elif isinstance(wn, int):
2666            return [ wn ]
2667        elif isinstance(wn, str):
2668            if '-' in wn:                            # case 'a-b' : return [a,a+1,...,b-1,b]
2669                val = wn.split('-')
2670                val = [int(val[0]), int(val[1])]
2671                val.sort()
2672                res = [i for i in xrange(val[0], val[1]+1)]
2673            elif wn[:2] == '<=' or wn[:2] == '=<':   # cases '<=a','=<a' : return [0,1,...,a-1,a]
2674                val = int(wn[2:])+1
2675                res = [i for i in xrange(val)]
2676            elif wn[-2:] == '>=' or wn[-2:] == '=>': # cases 'a>=','a=>' : return [0,1,...,a-1,a]
2677                val = int(wn[:-2])+1
2678                res = [i for i in xrange(val)]
2679            elif wn[0] == '<':                       # case '<a' :         return [0,1,...,a-2,a-1]
2680                val = int(wn[1:])
2681                res = [i for i in xrange(val)]
2682            elif wn[-1] == '>':                      # case 'a>' :         return [0,1,...,a-2,a-1]
2683                val = int(wn[:-1])
2684                res = [i for i in xrange(val)]
2685            elif wn[:2] == '>=' or wn[:2] == '=>':   # cases '>=a','=>a' : return [a,-999], which is
2686                                                     #                     then interpreted in C++
2687                                                     #                     side as [a,a+1,...,a_nyq]
2688                                                     #                     (CAS-3759)
2689                val = int(wn[2:])
2690                res = [val, -999]
2691                #res = [i for i in xrange(val, self.nchan()/2+1)]
2692            elif wn[-2:] == '<=' or wn[-2:] == '=<': # cases 'a<=','a=<' : return [a,-999], which is
2693                                                     #                     then interpreted in C++
2694                                                     #                     side as [a,a+1,...,a_nyq]
2695                                                     #                     (CAS-3759)
2696                val = int(wn[:-2])
2697                res = [val, -999]
2698                #res = [i for i in xrange(val, self.nchan()/2+1)]
2699            elif wn[0] == '>':                       # case '>a' :         return [a+1,-999], which is
2700                                                     #                     then interpreted in C++
2701                                                     #                     side as [a+1,a+2,...,a_nyq]
2702                                                     #                     (CAS-3759)
2703                val = int(wn[1:])+1
2704                res = [val, -999]
2705                #res = [i for i in xrange(val, self.nchan()/2+1)]
2706            elif wn[-1] == '<':                      # case 'a<' :         return [a+1,-999], which is
2707                                                     #                     then interpreted in C++
2708                                                     #                     side as [a+1,a+2,...,a_nyq]
2709                                                     #                     (CAS-3759)
2710                val = int(wn[:-1])+1
2711                res = [val, -999]
2712                #res = [i for i in xrange(val, self.nchan()/2+1)]
2713
2714            return res
2715        else:
2716            msg = 'wrong value given for addwn/rejwn'
2717            raise RuntimeError(msg)
2718
2719    @asaplog_post_dec
2720    def apply_bltable(self, insitu=None, retfitres=None, inbltable=None, outbltable=None, overwrite=None):
2721        """\
2722        Subtract baseline based on parameters written in Baseline Table.
2723
2724        Parameters:
2725            insitu:        if True, baseline fitting/subtraction is done
2726                           in-situ. If False, a new scantable with
2727                           baseline subtracted is returned. Actually,
2728                           format of the returned value depends on both
2729                           insitu and retfitres (see below).
2730                           The default is taken from .asaprc (False)
2731            retfitres:     if True, the results of baseline fitting (i.e.,
2732                           coefficients and rms) are returned.
2733                           default is False.
2734                           The format of the returned value of this
2735                           function varies as follows:
2736                           (1) in case insitu=True and retfitres=True:
2737                               fitting result.
2738                           (2) in case insitu=True and retfitres=False:
2739                               None.
2740                           (3) in case insitu=False and retfitres=True:
2741                               a dictionary containing a new scantable
2742                               (with baseline subtracted) and the fitting
2743                               results.
2744                           (4) in case insitu=False and retfitres=False:
2745                               a new scantable (with baseline subtracted).
2746            inbltable:     name of input baseline table. The row number of
2747                           scantable and that of inbltable must be
2748                           identical.
2749            outbltable:    name of output baseline table where baseline
2750                           parameters and fitting results recorded.
2751                           default is ''(no output).
2752            overwrite:     if True when an existing baseline table is
2753                           specified for outbltable, overwrites it.
2754                           Otherwise there is no harm.
2755                           default is False.
2756        """
2757
2758        try:
2759            varlist = vars()
2760            if retfitres      is None: retfitres      = False
2761            if inbltable      is None: raise ValueError("bltable missing.")
2762            if outbltable     is None: outbltable     = ''
2763            if overwrite      is None: overwrite      = False
2764
2765            if insitu is None: insitu = rcParams['insitu']
2766            if insitu:
2767                workscan = self
2768            else:
2769                workscan = self.copy()
2770
2771            sres = workscan._apply_bltable(inbltable,
2772                                           retfitres,
2773                                           outbltable,
2774                                           os.path.exists(outbltable),
2775                                           overwrite)
2776            if retfitres: res = parse_fitresult(sres)
2777
2778            workscan._add_history('apply_bltable', varlist)
2779
2780            if insitu:
2781                self._assign(workscan)
2782                if retfitres:
2783                    return res
2784                else:
2785                    return None
2786            else:
2787                if retfitres:
2788                    return {'scantable': workscan, 'fitresults': res}
2789                else:
2790                    return workscan
2791       
2792        except RuntimeError, e:
2793            raise_fitting_failure_exception(e)
2794
2795    @asaplog_post_dec
2796    def sub_baseline(self, insitu=None, retfitres=None, blinfo=None, bltable=None, overwrite=None):
2797        """\
2798        Subtract baseline based on parameters written in the input list.
2799
2800        Parameters:
2801            insitu:        if True, baseline fitting/subtraction is done
2802                           in-situ. If False, a new scantable with
2803                           baseline subtracted is returned. Actually,
2804                           format of the returned value depends on both
2805                           insitu and retfitres (see below).
2806                           The default is taken from .asaprc (False)
2807            retfitres:     if True, the results of baseline fitting (i.e.,
2808                           coefficients and rms) are returned.
2809                           default is False.
2810                           The format of the returned value of this
2811                           function varies as follows:
2812                           (1) in case insitu=True and retfitres=True:
2813                               fitting result.
2814                           (2) in case insitu=True and retfitres=False:
2815                               None.
2816                           (3) in case insitu=False and retfitres=True:
2817                               a dictionary containing a new scantable
2818                               (with baseline subtracted) and the fitting
2819                               results.
2820                           (4) in case insitu=False and retfitres=False:
2821                               a new scantable (with baseline subtracted).
2822            blinfo:        baseline parameter set stored in a dictionary
2823                           or a list of dictionary. Each dictionary
2824                           corresponds to each spectrum and must contain
2825                           the following keys and values:
2826                             'row': row number,
2827                             'blfunc': function name. available ones include
2828                                       'poly', 'chebyshev', 'cspline' and
2829                                       'sinusoid',
2830                             'order': maximum order of polynomial. needed
2831                                      if blfunc='poly' or 'chebyshev',
2832                             'npiece': number or piecewise polynomial.
2833                                       needed if blfunc='cspline',
2834                             'nwave': a list of sinusoidal wave numbers.
2835                                      needed if blfunc='sinusoid', and
2836                             'masklist': min-max windows for channel mask.
2837                                         the specified ranges will be used
2838                                         for fitting.
2839            bltable:       name of output baseline table where baseline
2840                           parameters and fitting results recorded.
2841                           default is ''(no output).
2842            overwrite:     if True when an existing baseline table is
2843                           specified for bltable, overwrites it.
2844                           Otherwise there is no harm.
2845                           default is False.
2846                           
2847        Example:
2848            sub_baseline(blinfo=[{'row':0, 'blfunc':'poly', 'order':5,
2849                                  'masklist':[[10,350],[352,510]]},
2850                                 {'row':1, 'blfunc':'cspline', 'npiece':3,
2851                                  'masklist':[[3,16],[19,404],[407,511]]}
2852                                  ])
2853
2854                the first spectrum (row=0) will be fitted with polynomial
2855                of order=5 and the next one (row=1) will be fitted with cubic
2856                spline consisting of 3 pieces.
2857        """
2858
2859        try:
2860            varlist = vars()
2861            if retfitres      is None: retfitres      = False
2862            if blinfo         is None: blinfo         = []
2863            if bltable        is None: bltable        = ''
2864            if overwrite      is None: overwrite      = False
2865
2866            if insitu is None: insitu = rcParams['insitu']
2867            if insitu:
2868                workscan = self
2869            else:
2870                workscan = self.copy()
2871
2872            nrow = workscan.nrow()
2873
2874            in_blinfo = pack_blinfo(blinfo=blinfo, maxirow=nrow)
2875
2876            print "in_blinfo=< "+ str(in_blinfo)+" >"
2877
2878            sres = workscan._sub_baseline(in_blinfo,
2879                                          retfitres,
2880                                          bltable,
2881                                          os.path.exists(bltable),
2882                                          overwrite)
2883            if retfitres: res = parse_fitresult(sres)
2884           
2885            workscan._add_history('sub_baseline', varlist)
2886
2887            if insitu:
2888                self._assign(workscan)
2889                if retfitres:
2890                    return res
2891                else:
2892                    return None
2893            else:
2894                if retfitres:
2895                    return {'scantable': workscan, 'fitresults': res}
2896                else:
2897                    return workscan
2898
2899        except RuntimeError, e:
2900            raise_fitting_failure_exception(e)
2901
2902    @asaplog_post_dec
2903    def calc_aic(self, value=None, blfunc=None, order=None, mask=None,
2904                 whichrow=None, uselinefinder=None, edge=None,
2905                 threshold=None, chan_avg_limit=None):
2906        """\
2907        Calculates and returns model selection criteria for a specified
2908        baseline model and a given spectrum data.
2909        Available values include Akaike Information Criterion (AIC), the
2910        corrected Akaike Information Criterion (AICc) by Sugiura(1978),
2911        Bayesian Information Criterion (BIC) and the Generalised Cross
2912        Validation (GCV).
2913
2914        Parameters:
2915            value:         name of model selection criteria to calculate.
2916                           available ones include 'aic', 'aicc', 'bic' and
2917                           'gcv'. default is 'aicc'.
2918            blfunc:        baseline function name. available ones include
2919                           'chebyshev', 'cspline' and 'sinusoid'.
2920                           default is 'chebyshev'.
2921            order:         parameter for basline function. actually stands for
2922                           order of polynomial (order) for 'chebyshev',
2923                           number of spline pieces (npiece) for 'cspline' and
2924                           maximum wave number for 'sinusoid', respectively.
2925                           default is 5 (which is also the default order value
2926                           for [auto_]chebyshev_baseline()).
2927            mask:          an optional mask. default is [].
2928            whichrow:      row number. default is 0 (the first row)
2929            uselinefinder: use sd.linefinder() to flag out line regions
2930                           default is True.
2931            edge:           an optional number of channel to drop at
2932                            the edge of spectrum. If only one value is
2933                            specified, the same number will be dropped
2934                            from both sides of the spectrum. Default
2935                            is to keep all channels. Nested tuples
2936                            represent individual edge selection for
2937                            different IFs (a number of spectral channels
2938                            can be different)
2939                            default is (0, 0).
2940            threshold:      the threshold used by line finder. It is
2941                            better to keep it large as only strong lines
2942                            affect the baseline solution.
2943                            default is 3.
2944            chan_avg_limit: a maximum number of consequtive spectral
2945                            channels to average during the search of
2946                            weak and broad lines. The default is no
2947                            averaging (and no search for weak lines).
2948                            If such lines can affect the fitted baseline
2949                            (e.g. a high order polynomial is fitted),
2950                            increase this parameter (usually values up
2951                            to 8 are reasonable). Most users of this
2952                            method should find the default value sufficient.
2953                            default is 1.
2954
2955        Example:
2956            aic = scan.calc_aic(blfunc='chebyshev', order=5, whichrow=0)
2957        """
2958
2959        try:
2960            varlist = vars()
2961
2962            if value          is None: value          = 'aicc'
2963            if blfunc         is None: blfunc         = 'chebyshev'
2964            if order          is None: order          = 5
2965            if mask           is None: mask           = []
2966            if whichrow       is None: whichrow       = 0
2967            if uselinefinder  is None: uselinefinder  = True
2968            if edge           is None: edge           = (0, 0)
2969            if threshold      is None: threshold      = 3
2970            if chan_avg_limit is None: chan_avg_limit = 1
2971
2972            return self._calc_aic(value, blfunc, order, mask,
2973                                  whichrow, uselinefinder, edge,
2974                                  threshold, chan_avg_limit)
2975           
2976        except RuntimeError, e:
2977            raise_fitting_failure_exception(e)
2978
2979    @asaplog_post_dec
2980    def sinusoid_baseline(self, mask=None, applyfft=None,
2981                          fftmethod=None, fftthresh=None,
2982                          addwn=None, rejwn=None,
2983                          insitu=None,
2984                          clipthresh=None, clipniter=None,
2985                          plot=None,
2986                          getresidual=None,
2987                          showprogress=None, minnrow=None,
2988                          outlog=None,
2989                          blfile=None, csvformat=None,
2990                          bltable=None):
2991        """\
2992        Return a scan which has been baselined (all rows) with sinusoidal
2993        functions.
2994
2995        Parameters:
2996            mask:          an optional mask
2997            applyfft:      if True use some method, such as FFT, to find
2998                           strongest sinusoidal components in the wavenumber
2999                           domain to be used for baseline fitting.
3000                           default is True.
3001            fftmethod:     method to find the strong sinusoidal components.
3002                           now only 'fft' is available and it is the default.
3003            fftthresh:     the threshold to select wave numbers to be used for
3004                           fitting from the distribution of amplitudes in the
3005                           wavenumber domain.
3006                           both float and string values accepted.
3007                           given a float value, the unit is set to sigma.
3008                           for string values, allowed formats include:
3009                               'xsigma' or 'x' (= x-sigma level. e.g.,
3010                               '3sigma'), or
3011                               'topx' (= the x strongest ones, e.g. 'top5').
3012                           default is 3.0 (unit: sigma).
3013            addwn:         the additional wave numbers to be used for fitting.
3014                           list or integer value is accepted to specify every
3015                           wave numbers. also string value can be used in case
3016                           you need to specify wave numbers in a certain range,
3017                           e.g., 'a-b' (= a, a+1, a+2, ..., b-1, b),
3018                                 '<a'  (= 0,1,...,a-2,a-1),
3019                                 '>=a' (= a, a+1, ... up to the maximum wave
3020                                        number corresponding to the Nyquist
3021                                        frequency for the case of FFT).
3022                           default is [0].
3023            rejwn:         the wave numbers NOT to be used for fitting.
3024                           can be set just as addwn but has higher priority:
3025                           wave numbers which are specified both in addwn
3026                           and rejwn will NOT be used. default is [].
3027            insitu:        if False a new scantable is returned.
3028                           Otherwise, the scaling is done in-situ
3029                           The default is taken from .asaprc (False)
3030            clipthresh:    Clipping threshold. (default is 3.0, unit: sigma)
3031            clipniter:     maximum number of iteration of 'clipthresh'-sigma
3032                           clipping (default is 0)
3033            plot:      *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
3034                           plot the fit and the residual. In this each
3035                           indivual fit has to be approved, by typing 'y'
3036                           or 'n'
3037            getresidual:   if False, returns best-fit values instead of
3038                           residual. (default is True)
3039            showprogress:  show progress status for large data.
3040                           default is True.
3041            minnrow:       minimum number of input spectra to show.
3042                           default is 1000.
3043            outlog:        Output the coefficients of the best-fit
3044                           function to logger (default is False)
3045            blfile:        Name of a text file in which the best-fit
3046                           parameter values to be written
3047                           (default is '': no file/logger output)
3048            csvformat:     if True blfile is csv-formatted, default is False.
3049            bltable:       name of a baseline table where fitting results
3050                           (coefficients, rms, etc.) are to be written.
3051                           if given, fitting results will NOT be output to
3052                           scantable (insitu=True) or None will be
3053                           returned (insitu=False).
3054                           (default is "": no table output)
3055
3056        Example:
3057            # return a scan baselined by a combination of sinusoidal curves
3058            # having wave numbers in spectral window up to 10,
3059            # also with 3-sigma clipping, iteration up to 4 times
3060            bscan = scan.sinusoid_baseline(addwn='<=10',clipthresh=3.0,clipniter=4)
3061
3062        Note:
3063            The best-fit parameter values output in logger and/or blfile are now
3064            based on specunit of 'channel'.
3065        """
3066       
3067        try:
3068            varlist = vars()
3069       
3070            if insitu is None: insitu = rcParams['insitu']
3071            if insitu:
3072                workscan = self
3073            else:
3074                workscan = self.copy()
3075           
3076            if mask          is None: mask          = []
3077            if applyfft      is None: applyfft      = True
3078            if fftmethod     is None: fftmethod     = 'fft'
3079            if fftthresh     is None: fftthresh     = 3.0
3080            if addwn         is None: addwn         = [0]
3081            if rejwn         is None: rejwn         = []
3082            if clipthresh    is None: clipthresh    = 3.0
3083            if clipniter     is None: clipniter     = 0
3084            if plot          is None: plot          = False
3085            if getresidual   is None: getresidual   = True
3086            if showprogress  is None: showprogress  = True
3087            if minnrow       is None: minnrow       = 1000
3088            if outlog        is None: outlog        = False
3089            if blfile        is None: blfile        = ''
3090            if csvformat     is None: csvformat     = False
3091            if bltable       is None: bltable       = ''
3092
3093            sapplyfft = 'true' if applyfft else 'false'
3094            fftinfo = ','.join([sapplyfft, fftmethod.lower(), str(fftthresh).lower()])
3095
3096            scsvformat = 'T' if csvformat else 'F'
3097
3098            #CURRENTLY, PLOT=true is UNAVAILABLE UNTIL sinusoidal fitting is implemented as a fitter method.
3099            workscan._sinusoid_baseline(mask,
3100                                        fftinfo,
3101                                        #applyfft, fftmethod.lower(),
3102                                        #str(fftthresh).lower(),
3103                                        workscan._parse_wn(addwn),
3104                                        workscan._parse_wn(rejwn),
3105                                        clipthresh, clipniter,
3106                                        getresidual,
3107                                        pack_progress_params(showprogress,
3108                                                             minnrow),
3109                                        outlog, scsvformat+blfile,
3110                                        bltable)
3111            workscan._add_history('sinusoid_baseline', varlist)
3112
3113            if bltable == '':
3114                if insitu:
3115                    self._assign(workscan)
3116                else:
3117                    return workscan
3118            else:
3119                if not insitu:
3120                    return None
3121           
3122        except RuntimeError, e:
3123            raise_fitting_failure_exception(e)
3124
3125
3126    @asaplog_post_dec
3127    def auto_sinusoid_baseline(self, mask=None, applyfft=None,
3128                               fftmethod=None, fftthresh=None,
3129                               addwn=None, rejwn=None,
3130                               insitu=None,
3131                               clipthresh=None, clipniter=None,
3132                               edge=None, threshold=None, chan_avg_limit=None,
3133                               plot=None,
3134                               getresidual=None,
3135                               showprogress=None, minnrow=None,
3136                               outlog=None,
3137                               blfile=None, csvformat=None,
3138                               bltable=None):
3139        """\
3140        Return a scan which has been baselined (all rows) with sinusoidal
3141        functions.
3142        Spectral lines are detected first using linefinder and masked out
3143        to avoid them affecting the baseline solution.
3144
3145        Parameters:
3146            mask:           an optional mask retreived from scantable
3147            applyfft:       if True use some method, such as FFT, to find
3148                            strongest sinusoidal components in the wavenumber
3149                            domain to be used for baseline fitting.
3150                            default is True.
3151            fftmethod:      method to find the strong sinusoidal components.
3152                            now only 'fft' is available and it is the default.
3153            fftthresh:      the threshold to select wave numbers to be used for
3154                            fitting from the distribution of amplitudes in the
3155                            wavenumber domain.
3156                            both float and string values accepted.
3157                            given a float value, the unit is set to sigma.
3158                            for string values, allowed formats include:
3159                                'xsigma' or 'x' (= x-sigma level. e.g.,
3160                                '3sigma'), or
3161                                'topx' (= the x strongest ones, e.g. 'top5').
3162                            default is 3.0 (unit: sigma).
3163            addwn:          the additional wave numbers to be used for fitting.
3164                            list or integer value is accepted to specify every
3165                            wave numbers. also string value can be used in case
3166                            you need to specify wave numbers in a certain range,
3167                            e.g., 'a-b' (= a, a+1, a+2, ..., b-1, b),
3168                                  '<a'  (= 0,1,...,a-2,a-1),
3169                                  '>=a' (= a, a+1, ... up to the maximum wave
3170                                         number corresponding to the Nyquist
3171                                         frequency for the case of FFT).
3172                            default is [0].
3173            rejwn:          the wave numbers NOT to be used for fitting.
3174                            can be set just as addwn but has higher priority:
3175                            wave numbers which are specified both in addwn
3176                            and rejwn will NOT be used. default is [].
3177            insitu:         if False a new scantable is returned.
3178                            Otherwise, the scaling is done in-situ
3179                            The default is taken from .asaprc (False)
3180            clipthresh:     Clipping threshold. (default is 3.0, unit: sigma)
3181            clipniter:      maximum number of iteration of 'clipthresh'-sigma
3182                            clipping (default is 0)
3183            edge:           an optional number of channel to drop at
3184                            the edge of spectrum. If only one value is
3185                            specified, the same number will be dropped
3186                            from both sides of the spectrum. Default
3187                            is to keep all channels. Nested tuples
3188                            represent individual edge selection for
3189                            different IFs (a number of spectral channels
3190                            can be different)
3191            threshold:      the threshold used by line finder. It is
3192                            better to keep it large as only strong lines
3193                            affect the baseline solution.
3194            chan_avg_limit: a maximum number of consequtive spectral
3195                            channels to average during the search of
3196                            weak and broad lines. The default is no
3197                            averaging (and no search for weak lines).
3198                            If such lines can affect the fitted baseline
3199                            (e.g. a high order polynomial is fitted),
3200                            increase this parameter (usually values up
3201                            to 8 are reasonable). Most users of this
3202                            method should find the default value sufficient.
3203            plot:       *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
3204                            plot the fit and the residual. In this each
3205                            indivual fit has to be approved, by typing 'y'
3206                            or 'n'
3207            getresidual:    if False, returns best-fit values instead of
3208                            residual. (default is True)
3209            showprogress:   show progress status for large data.
3210                            default is True.
3211            minnrow:        minimum number of input spectra to show.
3212                            default is 1000.
3213            outlog:         Output the coefficients of the best-fit
3214                            function to logger (default is False)
3215            blfile:         Name of a text file in which the best-fit
3216                            parameter values to be written
3217                            (default is "": no file/logger output)
3218            csvformat:      if True blfile is csv-formatted, default is False.
3219            bltable:        name of a baseline table where fitting results
3220                            (coefficients, rms, etc.) are to be written.
3221                            if given, fitting results will NOT be output to
3222                            scantable (insitu=True) or None will be
3223                            returned (insitu=False).
3224                            (default is "": no table output)
3225
3226        Example:
3227            bscan = scan.auto_sinusoid_baseline(addwn='<=10', insitu=False)
3228       
3229        Note:
3230            The best-fit parameter values output in logger and/or blfile are now
3231            based on specunit of 'channel'.
3232        """
3233
3234        try:
3235            varlist = vars()
3236
3237            if insitu is None: insitu = rcParams['insitu']
3238            if insitu:
3239                workscan = self
3240            else:
3241                workscan = self.copy()
3242           
3243            if mask           is None: mask           = []
3244            if applyfft       is None: applyfft       = True
3245            if fftmethod      is None: fftmethod      = 'fft'
3246            if fftthresh      is None: fftthresh      = 3.0
3247            if addwn          is None: addwn          = [0]
3248            if rejwn          is None: rejwn          = []
3249            if clipthresh     is None: clipthresh     = 3.0
3250            if clipniter      is None: clipniter      = 0
3251            if edge           is None: edge           = (0,0)
3252            if threshold      is None: threshold      = 3
3253            if chan_avg_limit is None: chan_avg_limit = 1
3254            if plot           is None: plot           = False
3255            if getresidual    is None: getresidual    = True
3256            if showprogress   is None: showprogress   = True
3257            if minnrow        is None: minnrow        = 1000
3258            if outlog         is None: outlog         = False
3259            if blfile         is None: blfile         = ''
3260            if csvformat      is None: csvformat      = False
3261            if bltable        is None: bltable        = ''
3262
3263            sapplyfft = 'true' if applyfft else 'false'
3264            fftinfo = ','.join([sapplyfft, fftmethod.lower(), str(fftthresh).lower()])
3265
3266            scsvformat = 'T' if csvformat else 'F'
3267
3268            #CURRENTLY, PLOT=true is UNAVAILABLE UNTIL sinusoidal fitting is implemented as a fitter method.
3269            workscan._auto_sinusoid_baseline(mask,
3270                                             fftinfo,
3271                                             workscan._parse_wn(addwn),
3272                                             workscan._parse_wn(rejwn),
3273                                             clipthresh, clipniter,
3274                                             normalise_edge_param(edge),
3275                                             threshold, chan_avg_limit,
3276                                             getresidual,
3277                                             pack_progress_params(showprogress,
3278                                                                  minnrow),
3279                                             outlog, scsvformat+blfile, bltable)
3280            workscan._add_history("auto_sinusoid_baseline", varlist)
3281
3282            if bltable == '':
3283                if insitu:
3284                    self._assign(workscan)
3285                else:
3286                    return workscan
3287            else:
3288                if not insitu:
3289                    return None
3290           
3291        except RuntimeError, e:
3292            raise_fitting_failure_exception(e)
3293
3294    @asaplog_post_dec
3295    def cspline_baseline(self, mask=None, npiece=None, insitu=None,
3296                         clipthresh=None, clipniter=None, plot=None,
3297                         getresidual=None, showprogress=None, minnrow=None,
3298                         outlog=None, blfile=None, csvformat=None,
3299                         bltable=None):
3300        """\
3301        Return a scan which has been baselined (all rows) by cubic spline
3302        function (piecewise cubic polynomial).
3303
3304        Parameters:
3305            mask:         An optional mask
3306            npiece:       Number of pieces. (default is 2)
3307            insitu:       If False a new scantable is returned.
3308                          Otherwise, the scaling is done in-situ
3309                          The default is taken from .asaprc (False)
3310            clipthresh:   Clipping threshold. (default is 3.0, unit: sigma)
3311            clipniter:    maximum number of iteration of 'clipthresh'-sigma
3312                          clipping (default is 0)
3313            plot:     *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
3314                          plot the fit and the residual. In this each
3315                          indivual fit has to be approved, by typing 'y'
3316                          or 'n'
3317            getresidual:  if False, returns best-fit values instead of
3318                          residual. (default is True)
3319            showprogress: show progress status for large data.
3320                          default is True.
3321            minnrow:      minimum number of input spectra to show.
3322                          default is 1000.
3323            outlog:       Output the coefficients of the best-fit
3324                          function to logger (default is False)
3325            blfile:       Name of a text file in which the best-fit
3326                          parameter values to be written
3327                          (default is "": no file/logger output)
3328            csvformat:    if True blfile is csv-formatted, default is False.
3329            bltable:      name of a baseline table where fitting results
3330                          (coefficients, rms, etc.) are to be written.
3331                          if given, fitting results will NOT be output to
3332                          scantable (insitu=True) or None will be
3333                          returned (insitu=False).
3334                          (default is "": no table output)
3335
3336        Example:
3337            # return a scan baselined by a cubic spline consisting of 2 pieces
3338            # (i.e., 1 internal knot),
3339            # also with 3-sigma clipping, iteration up to 4 times
3340            bscan = scan.cspline_baseline(npiece=2,clipthresh=3.0,clipniter=4)
3341       
3342        Note:
3343            The best-fit parameter values output in logger and/or blfile are now
3344            based on specunit of 'channel'.
3345        """
3346       
3347        try:
3348            varlist = vars()
3349           
3350            if insitu is None: insitu = rcParams['insitu']
3351            if insitu:
3352                workscan = self
3353            else:
3354                workscan = self.copy()
3355
3356            if mask         is None: mask         = []
3357            if npiece       is None: npiece       = 2
3358            if clipthresh   is None: clipthresh   = 3.0
3359            if clipniter    is None: clipniter    = 0
3360            if plot         is None: plot         = False
3361            if getresidual  is None: getresidual  = True
3362            if showprogress is None: showprogress = True
3363            if minnrow      is None: minnrow      = 1000
3364            if outlog       is None: outlog       = False
3365            if blfile       is None: blfile       = ''
3366            if csvformat    is None: csvformat    = False
3367            if bltable      is None: bltable      = ''
3368
3369            scsvformat = 'T' if csvformat else 'F'
3370
3371            #CURRENTLY, PLOT=true UNAVAILABLE UNTIL cubic spline fitting is implemented as a fitter method.
3372            workscan._cspline_baseline(mask, npiece,
3373                                       clipthresh, clipniter,
3374                                       getresidual,
3375                                       pack_progress_params(showprogress,
3376                                                            minnrow),
3377                                       outlog, scsvformat+blfile,
3378                                       bltable)
3379            workscan._add_history("cspline_baseline", varlist)
3380
3381            if bltable == '':
3382                if insitu:
3383                    self._assign(workscan)
3384                else:
3385                    return workscan
3386            else:
3387                if not insitu:
3388                    return None
3389           
3390        except RuntimeError, e:
3391            raise_fitting_failure_exception(e)
3392
3393    @asaplog_post_dec
3394    def auto_cspline_baseline(self, mask=None, npiece=None, insitu=None,
3395                              clipthresh=None, clipniter=None,
3396                              edge=None, threshold=None, chan_avg_limit=None,
3397                              getresidual=None, plot=None,
3398                              showprogress=None, minnrow=None, outlog=None,
3399                              blfile=None, csvformat=None, bltable=None):
3400        """\
3401        Return a scan which has been baselined (all rows) by cubic spline
3402        function (piecewise cubic polynomial).
3403        Spectral lines are detected first using linefinder and masked out
3404        to avoid them affecting the baseline solution.
3405
3406        Parameters:
3407            mask:           an optional mask retreived from scantable
3408            npiece:         Number of pieces. (default is 2)
3409            insitu:         if False a new scantable is returned.
3410                            Otherwise, the scaling is done in-situ
3411                            The default is taken from .asaprc (False)
3412            clipthresh:     Clipping threshold. (default is 3.0, unit: sigma)
3413            clipniter:      maximum number of iteration of 'clipthresh'-sigma
3414                            clipping (default is 0)
3415            edge:           an optional number of channel to drop at
3416                            the edge of spectrum. If only one value is
3417                            specified, the same number will be dropped
3418                            from both sides of the spectrum. Default
3419                            is to keep all channels. Nested tuples
3420                            represent individual edge selection for
3421                            different IFs (a number of spectral channels
3422                            can be different)
3423            threshold:      the threshold used by line finder. It is
3424                            better to keep it large as only strong lines
3425                            affect the baseline solution.
3426            chan_avg_limit: a maximum number of consequtive spectral
3427                            channels to average during the search of
3428                            weak and broad lines. The default is no
3429                            averaging (and no search for weak lines).
3430                            If such lines can affect the fitted baseline
3431                            (e.g. a high order polynomial is fitted),
3432                            increase this parameter (usually values up
3433                            to 8 are reasonable). Most users of this
3434                            method should find the default value sufficient.
3435            plot:       *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
3436                            plot the fit and the residual. In this each
3437                            indivual fit has to be approved, by typing 'y'
3438                            or 'n'
3439            getresidual:    if False, returns best-fit values instead of
3440                            residual. (default is True)
3441            showprogress:   show progress status for large data.
3442                            default is True.
3443            minnrow:        minimum number of input spectra to show.
3444                            default is 1000.
3445            outlog:         Output the coefficients of the best-fit
3446                            function to logger (default is False)
3447            blfile:         Name of a text file in which the best-fit
3448                            parameter values to be written
3449                            (default is "": no file/logger output)
3450            csvformat:      if True blfile is csv-formatted, default is False.
3451            bltable:        name of a baseline table where fitting results
3452                            (coefficients, rms, etc.) are to be written.
3453                            if given, fitting results will NOT be output to
3454                            scantable (insitu=True) or None will be
3455                            returned (insitu=False).
3456                            (default is "": no table output)
3457
3458        Example:
3459            bscan = scan.auto_cspline_baseline(npiece=3, insitu=False)
3460       
3461        Note:
3462            The best-fit parameter values output in logger and/or blfile are now
3463            based on specunit of 'channel'.
3464        """
3465
3466        try:
3467            varlist = vars()
3468
3469            if insitu is None: insitu = rcParams['insitu']
3470            if insitu:
3471                workscan = self
3472            else:
3473                workscan = self.copy()
3474           
3475            #if mask           is None: mask           = [True for i in xrange(workscan.nchan())]
3476            if mask           is None: mask           = []
3477            if npiece         is None: npiece         = 2
3478            if clipthresh     is None: clipthresh     = 3.0
3479            if clipniter      is None: clipniter      = 0
3480            if edge           is None: edge           = (0, 0)
3481            if threshold      is None: threshold      = 3
3482            if chan_avg_limit is None: chan_avg_limit = 1
3483            if plot           is None: plot           = False
3484            if getresidual    is None: getresidual    = True
3485            if showprogress   is None: showprogress   = True
3486            if minnrow        is None: minnrow        = 1000
3487            if outlog         is None: outlog         = False
3488            if blfile         is None: blfile         = ''
3489            if csvformat      is None: csvformat      = False
3490            if bltable        is None: bltable        = ''
3491
3492            scsvformat = 'T' if csvformat else 'F'
3493
3494            #CURRENTLY, PLOT=true UNAVAILABLE UNTIL cubic spline fitting is implemented as a fitter method.
3495            workscan._auto_cspline_baseline(mask, npiece,
3496                                            clipthresh, clipniter,
3497                                            normalise_edge_param(edge),
3498                                            threshold,
3499                                            chan_avg_limit, getresidual,
3500                                            pack_progress_params(showprogress,
3501                                                                 minnrow),
3502                                            outlog,
3503                                            scsvformat+blfile,
3504                                            bltable)
3505            workscan._add_history("auto_cspline_baseline", varlist)
3506
3507            if bltable == '':
3508                if insitu:
3509                    self._assign(workscan)
3510                else:
3511                    return workscan
3512            else:
3513                if not insitu:
3514                    return None
3515           
3516        except RuntimeError, e:
3517            raise_fitting_failure_exception(e)
3518
3519    @asaplog_post_dec
3520    def chebyshev_baseline(self, mask=None, order=None, insitu=None,
3521                           clipthresh=None, clipniter=None, plot=None,
3522                           getresidual=None, showprogress=None, minnrow=None,
3523                           outlog=None, blfile=None, csvformat=None,
3524                           bltable=None):
3525        """\
3526        Return a scan which has been baselined (all rows) by Chebyshev polynomials.
3527
3528        Parameters:
3529            mask:         An optional mask
3530            order:        the maximum order of Chebyshev polynomial (default is 5)
3531            insitu:       If False a new scantable is returned.
3532                          Otherwise, the scaling is done in-situ
3533                          The default is taken from .asaprc (False)
3534            clipthresh:   Clipping threshold. (default is 3.0, unit: sigma)
3535            clipniter:    maximum number of iteration of 'clipthresh'-sigma
3536                          clipping (default is 0)
3537            plot:     *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
3538                          plot the fit and the residual. In this each
3539                          indivual fit has to be approved, by typing 'y'
3540                          or 'n'
3541            getresidual:  if False, returns best-fit values instead of
3542                          residual. (default is True)
3543            showprogress: show progress status for large data.
3544                          default is True.
3545            minnrow:      minimum number of input spectra to show.
3546                          default is 1000.
3547            outlog:       Output the coefficients of the best-fit
3548                          function to logger (default is False)
3549            blfile:       Name of a text file in which the best-fit
3550                          parameter values to be written
3551                          (default is "": no file/logger output)
3552            csvformat:    if True blfile is csv-formatted, default is False.
3553            bltable:      name of a baseline table where fitting results
3554                          (coefficients, rms, etc.) are to be written.
3555                          if given, fitting results will NOT be output to
3556                          scantable (insitu=True) or None will be
3557                          returned (insitu=False).
3558                          (default is "": no table output)
3559
3560        Example:
3561            # return a scan baselined by a cubic spline consisting of 2 pieces
3562            # (i.e., 1 internal knot),
3563            # also with 3-sigma clipping, iteration up to 4 times
3564            bscan = scan.cspline_baseline(npiece=2,clipthresh=3.0,clipniter=4)
3565       
3566        Note:
3567            The best-fit parameter values output in logger and/or blfile are now
3568            based on specunit of 'channel'.
3569        """
3570       
3571        try:
3572            varlist = vars()
3573           
3574            if insitu is None: insitu = rcParams['insitu']
3575            if insitu:
3576                workscan = self
3577            else:
3578                workscan = self.copy()
3579
3580            if mask         is None: mask         = []
3581            if order        is None: order        = 5
3582            if clipthresh   is None: clipthresh   = 3.0
3583            if clipniter    is None: clipniter    = 0
3584            if plot         is None: plot         = False
3585            if getresidual  is None: getresidual  = True
3586            if showprogress is None: showprogress = True
3587            if minnrow      is None: minnrow      = 1000
3588            if outlog       is None: outlog       = False
3589            if blfile       is None: blfile       = ''
3590            if csvformat    is None: csvformat    = False
3591            if bltable      is None: bltable      = ''
3592
3593            scsvformat = 'T' if csvformat else 'F'
3594
3595            #CURRENTLY, PLOT=true UNAVAILABLE UNTIL cubic spline fitting is implemented as a fitter method.
3596            workscan._chebyshev_baseline(mask, order,
3597                                         clipthresh, clipniter,
3598                                         getresidual,
3599                                         pack_progress_params(showprogress,
3600                                                              minnrow),
3601                                         outlog, scsvformat+blfile,
3602                                         bltable)
3603            workscan._add_history("chebyshev_baseline", varlist)
3604
3605            if bltable == '':
3606                if insitu:
3607                    self._assign(workscan)
3608                else:
3609                    return workscan
3610            else:
3611                if not insitu:
3612                    return None
3613           
3614        except RuntimeError, e:
3615            raise_fitting_failure_exception(e)
3616
3617    @asaplog_post_dec
3618    def auto_chebyshev_baseline(self, mask=None, order=None, insitu=None,
3619                              clipthresh=None, clipniter=None,
3620                              edge=None, threshold=None, chan_avg_limit=None,
3621                              getresidual=None, plot=None,
3622                              showprogress=None, minnrow=None, outlog=None,
3623                              blfile=None, csvformat=None, bltable=None):
3624        """\
3625        Return a scan which has been baselined (all rows) by Chebyshev polynomials.
3626        Spectral lines are detected first using linefinder and masked out
3627        to avoid them affecting the baseline solution.
3628
3629        Parameters:
3630            mask:           an optional mask retreived from scantable
3631            order:          the maximum order of Chebyshev polynomial (default is 5)
3632            insitu:         if False a new scantable is returned.
3633                            Otherwise, the scaling is done in-situ
3634                            The default is taken from .asaprc (False)
3635            clipthresh:     Clipping threshold. (default is 3.0, unit: sigma)
3636            clipniter:      maximum number of iteration of 'clipthresh'-sigma
3637                            clipping (default is 0)
3638            edge:           an optional number of channel to drop at
3639                            the edge of spectrum. If only one value is
3640                            specified, the same number will be dropped
3641                            from both sides of the spectrum. Default
3642                            is to keep all channels. Nested tuples
3643                            represent individual edge selection for
3644                            different IFs (a number of spectral channels
3645                            can be different)
3646            threshold:      the threshold used by line finder. It is
3647                            better to keep it large as only strong lines
3648                            affect the baseline solution.
3649            chan_avg_limit: a maximum number of consequtive spectral
3650                            channels to average during the search of
3651                            weak and broad lines. The default is no
3652                            averaging (and no search for weak lines).
3653                            If such lines can affect the fitted baseline
3654                            (e.g. a high order polynomial is fitted),
3655                            increase this parameter (usually values up
3656                            to 8 are reasonable). Most users of this
3657                            method should find the default value sufficient.
3658            plot:       *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
3659                            plot the fit and the residual. In this each
3660                            indivual fit has to be approved, by typing 'y'
3661                            or 'n'
3662            getresidual:    if False, returns best-fit values instead of
3663                            residual. (default is True)
3664            showprogress:   show progress status for large data.
3665                            default is True.
3666            minnrow:        minimum number of input spectra to show.
3667                            default is 1000.
3668            outlog:         Output the coefficients of the best-fit
3669                            function to logger (default is False)
3670            blfile:         Name of a text file in which the best-fit
3671                            parameter values to be written
3672                            (default is "": no file/logger output)
3673            csvformat:      if True blfile is csv-formatted, default is False.
3674            bltable:        name of a baseline table where fitting results
3675                            (coefficients, rms, etc.) are to be written.
3676                            if given, fitting results will NOT be output to
3677                            scantable (insitu=True) or None will be
3678                            returned (insitu=False).
3679                            (default is "": no table output)
3680
3681        Example:
3682            bscan = scan.auto_cspline_baseline(npiece=3, insitu=False)
3683       
3684        Note:
3685            The best-fit parameter values output in logger and/or blfile are now
3686            based on specunit of 'channel'.
3687        """
3688
3689        try:
3690            varlist = vars()
3691
3692            if insitu is None: insitu = rcParams['insitu']
3693            if insitu:
3694                workscan = self
3695            else:
3696                workscan = self.copy()
3697           
3698            if mask           is None: mask           = []
3699            if order          is None: order          = 5
3700            if clipthresh     is None: clipthresh     = 3.0
3701            if clipniter      is None: clipniter      = 0
3702            if edge           is None: edge           = (0, 0)
3703            if threshold      is None: threshold      = 3
3704            if chan_avg_limit is None: chan_avg_limit = 1
3705            if plot           is None: plot           = False
3706            if getresidual    is None: getresidual    = True
3707            if showprogress   is None: showprogress   = True
3708            if minnrow        is None: minnrow        = 1000
3709            if outlog         is None: outlog         = False
3710            if blfile         is None: blfile         = ''
3711            if csvformat      is None: csvformat      = False
3712            if bltable        is None: bltable        = ''
3713
3714            scsvformat = 'T' if csvformat else 'F'
3715
3716            #CURRENTLY, PLOT=true UNAVAILABLE UNTIL cubic spline fitting is implemented as a fitter method.
3717            workscan._auto_chebyshev_baseline(mask, order,
3718                                              clipthresh, clipniter,
3719                                              normalise_edge_param(edge),
3720                                              threshold,
3721                                              chan_avg_limit, getresidual,
3722                                              pack_progress_params(showprogress,
3723                                                                   minnrow),
3724                                              outlog, scsvformat+blfile,
3725                                              bltable)
3726            workscan._add_history("auto_chebyshev_baseline", varlist)
3727
3728            if bltable == '':
3729                if insitu:
3730                    self._assign(workscan)
3731                else:
3732                    return workscan
3733            else:
3734                if not insitu:
3735                    return None
3736           
3737        except RuntimeError, e:
3738            raise_fitting_failure_exception(e)
3739
3740    @asaplog_post_dec
3741    def poly_baseline(self, mask=None, order=None, insitu=None,
3742                      clipthresh=None, clipniter=None, plot=None,
3743                      getresidual=None, showprogress=None, minnrow=None,
3744                      outlog=None, blfile=None, csvformat=None,
3745                      bltable=None):
3746        """\
3747        Return a scan which has been baselined (all rows) by a polynomial.
3748        Parameters:
3749            mask:         an optional mask
3750            order:        the order of the polynomial (default is 0)
3751            insitu:       if False a new scantable is returned.
3752                          Otherwise, the scaling is done in-situ
3753                          The default is taken from .asaprc (False)
3754            clipthresh:   Clipping threshold. (default is 3.0, unit: sigma)
3755            clipniter:    maximum number of iteration of 'clipthresh'-sigma
3756                          clipping (default is 0)
3757            plot:         plot the fit and the residual. In this each
3758                          indivual fit has to be approved, by typing 'y'
3759                          or 'n'
3760            getresidual:  if False, returns best-fit values instead of
3761                          residual. (default is True)
3762            showprogress: show progress status for large data.
3763                          default is True.
3764            minnrow:      minimum number of input spectra to show.
3765                          default is 1000.
3766            outlog:       Output the coefficients of the best-fit
3767                          function to logger (default is False)
3768            blfile:       Name of a text file in which the best-fit
3769                          parameter values to be written
3770                          (default is "": no file/logger output)
3771            csvformat:    if True blfile is csv-formatted, default is False.
3772            bltable:      name of a baseline table where fitting results
3773                          (coefficients, rms, etc.) are to be written.
3774                          if given, fitting results will NOT be output to
3775                          scantable (insitu=True) or None will be
3776                          returned (insitu=False).
3777                          (default is "": no table output)
3778
3779        Example:
3780            # return a scan baselined by a third order polynomial,
3781            # not using a mask
3782            bscan = scan.poly_baseline(order=3)
3783        """
3784       
3785        try:
3786            varlist = vars()
3787       
3788            if insitu is None:
3789                insitu = rcParams["insitu"]
3790            if insitu:
3791                workscan = self
3792            else:
3793                workscan = self.copy()
3794
3795            if mask         is None: mask         = []
3796            if order        is None: order        = 0
3797            if clipthresh   is None: clipthresh   = 3.0
3798            if clipniter    is None: clipniter    = 0
3799            if plot         is None: plot         = False
3800            if getresidual  is None: getresidual  = True
3801            if showprogress is None: showprogress = True
3802            if minnrow      is None: minnrow      = 1000
3803            if outlog       is None: outlog       = False
3804            if blfile       is None: blfile       = ''
3805            if csvformat    is None: csvformat    = False
3806            if bltable      is None: bltable      = ''
3807
3808            scsvformat = 'T' if csvformat else 'F'
3809
3810            if plot:
3811                outblfile = (blfile != "") and \
3812                    os.path.exists(os.path.expanduser(
3813                                    os.path.expandvars(blfile))
3814                                   )
3815                if outblfile:
3816                    blf = open(blfile, "a")
3817               
3818                f = fitter()
3819                f.set_function(lpoly=order)
3820               
3821                rows = xrange(workscan.nrow())
3822                #if len(rows) > 0: workscan._init_blinfo()
3823
3824                action = "H"
3825                for r in rows:
3826                    f.x = workscan._getabcissa(r)
3827                    f.y = workscan._getspectrum(r)
3828                    if mask:
3829                        f.mask = mask_and(mask, workscan._getmask(r))    # (CAS-1434)
3830                    else: # mask=None
3831                        f.mask = workscan._getmask(r)
3832                   
3833                    f.data = None
3834                    f.fit()
3835
3836                    if action != "Y": # skip plotting when accepting all
3837                        f.plot(residual=True)
3838                    #accept_fit = raw_input("Accept fit ( [y]/n ): ")
3839                    #if accept_fit.upper() == "N":
3840                    #    #workscan._append_blinfo(None, None, None)
3841                    #    continue
3842                    accept_fit = self._get_verify_action("Accept fit?",action)
3843                    if r == 0: action = None
3844                    if accept_fit.upper() == "N":
3845                        continue
3846                    elif accept_fit.upper() == "R":
3847                        break
3848                    elif accept_fit.upper() == "A":
3849                        action = "Y"
3850                   
3851                    blpars = f.get_parameters()
3852                    masklist = workscan.get_masklist(f.mask, row=r, silent=True)
3853                    #workscan._append_blinfo(blpars, masklist, f.mask)
3854                    workscan._setspectrum((f.fitter.getresidual()
3855                                           if getresidual else f.fitter.getfit()), r)
3856                   
3857                    if outblfile:
3858                        rms = workscan.get_rms(f.mask, r)
3859                        dataout = \
3860                            workscan.format_blparams_row(blpars["params"],
3861                                                         blpars["fixed"],
3862                                                         rms, str(masklist),
3863                                                         r, True, csvformat)
3864                        blf.write(dataout)
3865
3866                f._p.unmap()
3867                f._p = None
3868
3869                if outblfile:
3870                    blf.close()
3871            else:
3872                workscan._poly_baseline(mask, order,
3873                                        clipthresh, clipniter, #
3874                                        getresidual,
3875                                        pack_progress_params(showprogress,
3876                                                             minnrow),
3877                                        outlog, scsvformat+blfile,
3878                                        bltable)  #
3879           
3880            workscan._add_history("poly_baseline", varlist)
3881           
3882            if insitu:
3883                self._assign(workscan)
3884            else:
3885                return workscan
3886           
3887        except RuntimeError, e:
3888            raise_fitting_failure_exception(e)
3889
3890    @asaplog_post_dec
3891    def auto_poly_baseline(self, mask=None, order=None, insitu=None,
3892                           clipthresh=None, clipniter=None,
3893                           edge=None, threshold=None, chan_avg_limit=None,
3894                           getresidual=None, plot=None,
3895                           showprogress=None, minnrow=None, outlog=None,
3896                           blfile=None, csvformat=None, bltable=None):
3897        """\
3898        Return a scan which has been baselined (all rows) by a polynomial.
3899        Spectral lines are detected first using linefinder and masked out
3900        to avoid them affecting the baseline solution.
3901
3902        Parameters:
3903            mask:           an optional mask retreived from scantable
3904            order:          the order of the polynomial (default is 0)
3905            insitu:         if False a new scantable is returned.
3906                            Otherwise, the scaling is done in-situ
3907                            The default is taken from .asaprc (False)
3908            clipthresh:     Clipping threshold. (default is 3.0, unit: sigma)
3909            clipniter:      maximum number of iteration of 'clipthresh'-sigma
3910                            clipping (default is 0)
3911            edge:           an optional number of channel to drop at
3912                            the edge of spectrum. If only one value is
3913                            specified, the same number will be dropped
3914                            from both sides of the spectrum. Default
3915                            is to keep all channels. Nested tuples
3916                            represent individual edge selection for
3917                            different IFs (a number of spectral channels
3918                            can be different)
3919            threshold:      the threshold used by line finder. It is
3920                            better to keep it large as only strong lines
3921                            affect the baseline solution.
3922            chan_avg_limit: a maximum number of consequtive spectral
3923                            channels to average during the search of
3924                            weak and broad lines. The default is no
3925                            averaging (and no search for weak lines).
3926                            If such lines can affect the fitted baseline
3927                            (e.g. a high order polynomial is fitted),
3928                            increase this parameter (usually values up
3929                            to 8 are reasonable). Most users of this
3930                            method should find the default value sufficient.
3931            plot:           plot the fit and the residual. In this each
3932                            indivual fit has to be approved, by typing 'y'
3933                            or 'n'
3934            getresidual:    if False, returns best-fit values instead of
3935                            residual. (default is True)
3936            showprogress:   show progress status for large data.
3937                            default is True.
3938            minnrow:        minimum number of input spectra to show.
3939                            default is 1000.
3940            outlog:         Output the coefficients of the best-fit
3941                            function to logger (default is False)
3942            blfile:         Name of a text file in which the best-fit
3943                            parameter values to be written
3944                            (default is "": no file/logger output)
3945            csvformat:      if True blfile is csv-formatted, default is False.
3946            bltable:        name of a baseline table where fitting results
3947                            (coefficients, rms, etc.) are to be written.
3948                            if given, fitting results will NOT be output to
3949                            scantable (insitu=True) or None will be
3950                            returned (insitu=False).
3951                            (default is "": no table output)
3952
3953        Example:
3954            bscan = scan.auto_poly_baseline(order=7, insitu=False)
3955        """
3956
3957        try:
3958            varlist = vars()
3959
3960            if insitu is None:
3961                insitu = rcParams['insitu']
3962            if insitu:
3963                workscan = self
3964            else:
3965                workscan = self.copy()
3966
3967            if mask           is None: mask           = []
3968            if order          is None: order          = 0
3969            if clipthresh     is None: clipthresh     = 3.0
3970            if clipniter      is None: clipniter      = 0
3971            if edge           is None: edge           = (0, 0)
3972            if threshold      is None: threshold      = 3
3973            if chan_avg_limit is None: chan_avg_limit = 1
3974            if plot           is None: plot           = False
3975            if getresidual    is None: getresidual    = True
3976            if showprogress   is None: showprogress   = True
3977            if minnrow        is None: minnrow        = 1000
3978            if outlog         is None: outlog         = False
3979            if blfile         is None: blfile         = ''
3980            if csvformat      is None: csvformat      = False
3981            if bltable        is None: bltable        = ''
3982
3983            scsvformat = 'T' if csvformat else 'F'
3984
3985            edge = normalise_edge_param(edge)
3986
3987            if plot:
3988                outblfile = (blfile != "") and \
3989                    os.path.exists(os.path.expanduser(os.path.expandvars(blfile)))
3990                if outblfile: blf = open(blfile, "a")
3991               
3992                from asap.asaplinefind import linefinder
3993                fl = linefinder()
3994                fl.set_options(threshold=threshold, avg_limit=chan_avg_limit)
3995                fl.set_scan(workscan)
3996               
3997                f = fitter()
3998                f.set_function(lpoly=order)
3999
4000                rows = xrange(workscan.nrow())
4001                #if len(rows) > 0: workscan._init_blinfo()
4002
4003                action = "H"
4004                for r in rows:
4005                    idx = 2*workscan.getif(r)
4006                    if mask:
4007                        msk = mask_and(mask, workscan._getmask(r)) # (CAS-1434)
4008                    else: # mask=None
4009                        msk = workscan._getmask(r)
4010                    fl.find_lines(r, msk, edge[idx:idx+2]) 
4011
4012                    f.x = workscan._getabcissa(r)
4013                    f.y = workscan._getspectrum(r)
4014                    f.mask = fl.get_mask()
4015                    f.data = None
4016                    f.fit()
4017
4018                    if action != "Y": # skip plotting when accepting all
4019                        f.plot(residual=True)
4020                    #accept_fit = raw_input("Accept fit ( [y]/n ): ")
4021                    accept_fit = self._get_verify_action("Accept fit?",action)
4022                    if r == 0: action = None
4023                    if accept_fit.upper() == "N":
4024                        #workscan._append_blinfo(None, None, None)
4025                        continue
4026                    elif accept_fit.upper() == "R":
4027                        break
4028                    elif accept_fit.upper() == "A":
4029                        action = "Y"
4030
4031                    blpars = f.get_parameters()
4032                    masklist = workscan.get_masklist(f.mask, row=r, silent=True)
4033                    #workscan._append_blinfo(blpars, masklist, f.mask)
4034                    workscan._setspectrum(
4035                        (f.fitter.getresidual() if getresidual
4036                                                else f.fitter.getfit()), r
4037                        )
4038
4039                    if outblfile:
4040                        rms = workscan.get_rms(f.mask, r)
4041                        dataout = \
4042                            workscan.format_blparams_row(blpars["params"],
4043                                                         blpars["fixed"],
4044                                                         rms, str(masklist),
4045                                                         r, True, csvformat)
4046                        blf.write(dataout)
4047                   
4048                f._p.unmap()
4049                f._p = None
4050
4051                if outblfile: blf.close()
4052            else:
4053                workscan._auto_poly_baseline(mask, order,
4054                                             clipthresh, clipniter,
4055                                             edge, threshold,
4056                                             chan_avg_limit, getresidual,
4057                                             pack_progress_params(showprogress,
4058                                                                  minnrow),
4059                                             outlog, scsvformat+blfile,
4060                                             bltable)
4061            workscan._add_history("auto_poly_baseline", varlist)
4062
4063            if bltable == '':
4064                if insitu:
4065                    self._assign(workscan)
4066                else:
4067                    return workscan
4068            else:
4069                if not insitu:
4070                    return None
4071           
4072        except RuntimeError, e:
4073            raise_fitting_failure_exception(e)
4074
4075    def _init_blinfo(self):
4076        """\
4077        Initialise the following three auxiliary members:
4078           blpars : parameters of the best-fit baseline,
4079           masklists : mask data (edge positions of masked channels) and
4080           actualmask : mask data (in boolean list),
4081        to keep for use later (including output to logger/text files).
4082        Used by poly_baseline() and auto_poly_baseline() in case of
4083        'plot=True'.
4084        """
4085        self.blpars = []
4086        self.masklists = []
4087        self.actualmask = []
4088        return
4089
4090    def _append_blinfo(self, data_blpars, data_masklists, data_actualmask):
4091        """\
4092        Append baseline-fitting related info to blpars, masklist and
4093        actualmask.
4094        """
4095        self.blpars.append(data_blpars)
4096        self.masklists.append(data_masklists)
4097        self.actualmask.append(data_actualmask)
4098        return
4099       
4100    @asaplog_post_dec
4101    def rotate_linpolphase(self, angle):
4102        """\
4103        Rotate the phase of the complex polarization O=Q+iU correlation.
4104        This is always done in situ in the raw data.  So if you call this
4105        function more than once then each call rotates the phase further.
4106
4107        Parameters:
4108
4109            angle:   The angle (degrees) to rotate (add) by.
4110
4111        Example::
4112
4113            scan.rotate_linpolphase(2.3)
4114
4115        """
4116        varlist = vars()
4117        self._math._rotate_linpolphase(self, angle)
4118        self._add_history("rotate_linpolphase", varlist)
4119        return
4120
4121    @asaplog_post_dec
4122    def rotate_xyphase(self, angle):
4123        """\
4124        Rotate the phase of the XY correlation.  This is always done in situ
4125        in the data.  So if you call this function more than once
4126        then each call rotates the phase further.
4127
4128        Parameters:
4129
4130            angle:   The angle (degrees) to rotate (add) by.
4131
4132        Example::
4133
4134            scan.rotate_xyphase(2.3)
4135
4136        """
4137        varlist = vars()
4138        self._math._rotate_xyphase(self, angle)
4139        self._add_history("rotate_xyphase", varlist)
4140        return
4141
4142    @asaplog_post_dec
4143    def swap_linears(self):
4144        """\
4145        Swap the linear polarisations XX and YY, or better the first two
4146        polarisations as this also works for ciculars.
4147        """
4148        varlist = vars()
4149        self._math._swap_linears(self)
4150        self._add_history("swap_linears", varlist)
4151        return
4152
4153    @asaplog_post_dec
4154    def invert_phase(self):
4155        """\
4156        Invert the phase of the complex polarisation
4157        """
4158        varlist = vars()
4159        self._math._invert_phase(self)
4160        self._add_history("invert_phase", varlist)
4161        return
4162
4163    @asaplog_post_dec
4164    def add(self, offset, insitu=None):
4165        """\
4166        Return a scan where all spectra have the offset added
4167
4168        Parameters:
4169
4170            offset:      the offset
4171
4172            insitu:      if False a new scantable is returned.
4173                         Otherwise, the scaling is done in-situ
4174                         The default is taken from .asaprc (False)
4175
4176        """
4177        if insitu is None: insitu = rcParams['insitu']
4178        self._math._setinsitu(insitu)
4179        varlist = vars()
4180        s = scantable(self._math._unaryop(self, offset, "ADD", False))
4181        s._add_history("add", varlist)
4182        if insitu:
4183            self._assign(s)
4184        else:
4185            return s
4186
4187    @asaplog_post_dec
4188    def scale(self, factor, tsys=True, insitu=None):
4189        """\
4190
4191        Return a scan where all spectra are scaled by the given 'factor'
4192
4193        Parameters:
4194
4195            factor:      the scaling factor (float or 1D float list)
4196
4197            insitu:      if False a new scantable is returned.
4198                         Otherwise, the scaling is done in-situ
4199                         The default is taken from .asaprc (False)
4200
4201            tsys:        if True (default) then apply the operation to Tsys
4202                         as well as the data
4203
4204        """
4205        if insitu is None: insitu = rcParams['insitu']
4206        self._math._setinsitu(insitu)
4207        varlist = vars()
4208        s = None
4209        import numpy
4210        if isinstance(factor, list) or isinstance(factor, numpy.ndarray):
4211            if isinstance(factor[0], list) or isinstance(factor[0],
4212                                                         numpy.ndarray):
4213                from asapmath import _array2dOp
4214                s = _array2dOp( self, factor, "MUL", tsys, insitu )
4215            else:
4216                s = scantable( self._math._arrayop( self, factor,
4217                                                    "MUL", tsys ) )
4218        else:
4219            s = scantable(self._math._unaryop(self, factor, "MUL", tsys))
4220        s._add_history("scale", varlist)
4221        if insitu:
4222            self._assign(s)
4223        else:
4224            return s
4225
4226    @preserve_selection
4227    def set_sourcetype(self, match, matchtype="pattern",
4228                       sourcetype="reference"):
4229        """\
4230        Set the type of the source to be an source or reference scan
4231        using the provided pattern.
4232
4233        Parameters:
4234
4235            match:          a Unix style pattern, regular expression or selector
4236
4237            matchtype:      'pattern' (default) UNIX style pattern or
4238                            'regex' regular expression
4239
4240            sourcetype:     the type of the source to use (source/reference)
4241
4242        """
4243        varlist = vars()
4244        stype = -1
4245        if sourcetype.lower().startswith("r") or sourcetype.lower() == "off":
4246            stype = 1
4247        elif sourcetype.lower().startswith("s") or sourcetype.lower() == "on":
4248            stype = 0
4249        else:
4250            raise ValueError("Illegal sourcetype use s(ource)/on or r(eference)/off")
4251        if matchtype.lower().startswith("p"):
4252            matchtype = "pattern"
4253        elif matchtype.lower().startswith("r"):
4254            matchtype = "regex"
4255        else:
4256            raise ValueError("Illegal matchtype, use p(attern) or r(egex)")
4257        sel = selector()
4258        if isinstance(match, selector):
4259            sel = match
4260        else:
4261            sel.set_query("SRCNAME=%s('%s')" % (matchtype, match))
4262        self.set_selection(sel)
4263        self._setsourcetype(stype)
4264        self._add_history("set_sourcetype", varlist)
4265
4266
4267    def set_sourcename(self, name):
4268        varlist = vars()
4269        self._setsourcename(name)
4270        self._add_history("set_sourcename", varlist)
4271
4272    @asaplog_post_dec
4273    @preserve_selection
4274    def auto_quotient(self, preserve=True, mode='paired', verify=False):
4275        """\
4276        This function allows to build quotients automatically.
4277        It assumes the observation to have the same number of
4278        "ons" and "offs"
4279
4280        Parameters:
4281
4282            preserve:       you can preserve (default) the continuum or
4283                            remove it.  The equations used are
4284
4285                            preserve: Output = Toff * (on/off) - Toff
4286
4287                            remove:   Output = Toff * (on/off) - Ton
4288
4289            mode:           the on/off detection mode
4290                            'paired' (default)
4291                            identifies 'off' scans by the
4292                            trailing '_R' (Mopra/Parkes) or
4293                            '_e'/'_w' (Tid) and matches
4294                            on/off pairs from the observing pattern
4295                            'time'
4296                            finds the closest off in time
4297
4298        .. todo:: verify argument is not implemented
4299
4300        """
4301        varlist = vars()
4302        modes = ["time", "paired"]
4303        if not mode in modes:
4304            msg = "please provide valid mode. Valid modes are %s" % (modes)
4305            raise ValueError(msg)
4306        s = None
4307        if mode.lower() == "paired":
4308            sel = self.get_selection()
4309            sel.set_query("SRCTYPE==psoff")
4310            self.set_selection(sel)
4311            offs = self.copy()
4312            sel.set_query("SRCTYPE==pson")
4313            self.set_selection(sel)
4314            ons = self.copy()
4315            s = scantable(self._math._quotient(ons, offs, preserve))
4316        elif mode.lower() == "time":
4317            s = scantable(self._math._auto_quotient(self, mode, preserve))
4318        s._add_history("auto_quotient", varlist)
4319        return s
4320
4321    @asaplog_post_dec
4322    def mx_quotient(self, mask = None, weight='median', preserve=True):
4323        """\
4324        Form a quotient using "off" beams when observing in "MX" mode.
4325
4326        Parameters:
4327
4328            mask:           an optional mask to be used when weight == 'stddev'
4329
4330            weight:         How to average the off beams.  Default is 'median'.
4331
4332            preserve:       you can preserve (default) the continuum or
4333                            remove it.  The equations used are:
4334
4335                                preserve: Output = Toff * (on/off) - Toff
4336
4337                                remove:   Output = Toff * (on/off) - Ton
4338
4339        """
4340        mask = mask or ()
4341        varlist = vars()
4342        on = scantable(self._math._mx_extract(self, 'on'))
4343        preoff = scantable(self._math._mx_extract(self, 'off'))
4344        off = preoff.average_time(mask=mask, weight=weight, scanav=False)
4345        from asapmath  import quotient
4346        q = quotient(on, off, preserve)
4347        q._add_history("mx_quotient", varlist)
4348        return q
4349
4350    @asaplog_post_dec
4351    def freq_switch(self, insitu=None):
4352        """\
4353        Apply frequency switching to the data.
4354
4355        Parameters:
4356
4357            insitu:      if False a new scantable is returned.
4358                         Otherwise, the swictching is done in-situ
4359                         The default is taken from .asaprc (False)
4360
4361        """
4362        if insitu is None: insitu = rcParams['insitu']
4363        self._math._setinsitu(insitu)
4364        varlist = vars()
4365        s = scantable(self._math._freqswitch(self))
4366        s._add_history("freq_switch", varlist)
4367        if insitu:
4368            self._assign(s)
4369        else:
4370            return s
4371
4372    @asaplog_post_dec
4373    def recalc_azel(self):
4374        """Recalculate the azimuth and elevation for each position."""
4375        varlist = vars()
4376        self._recalcazel()
4377        self._add_history("recalc_azel", varlist)
4378        return
4379
4380    @asaplog_post_dec
4381    def __add__(self, other):
4382        """
4383        implicit on all axes and on Tsys
4384        """
4385        varlist = vars()
4386        s = self.__op( other, "ADD" )
4387        s._add_history("operator +", varlist)
4388        return s
4389
4390    @asaplog_post_dec
4391    def __sub__(self, other):
4392        """
4393        implicit on all axes and on Tsys
4394        """
4395        varlist = vars()
4396        s = self.__op( other, "SUB" )
4397        s._add_history("operator -", varlist)
4398        return s
4399
4400    @asaplog_post_dec
4401    def __mul__(self, other):
4402        """
4403        implicit on all axes and on Tsys
4404        """
4405        varlist = vars()
4406        s = self.__op( other, "MUL" ) ;
4407        s._add_history("operator *", varlist)
4408        return s
4409
4410
4411    @asaplog_post_dec
4412    def __div__(self, other):
4413        """
4414        implicit on all axes and on Tsys
4415        """
4416        varlist = vars()
4417        s = self.__op( other, "DIV" )
4418        s._add_history("operator /", varlist)
4419        return s
4420
4421    @asaplog_post_dec
4422    def __op( self, other, mode ):
4423        s = None
4424        if isinstance(other, scantable):
4425            s = scantable(self._math._binaryop(self, other, mode))
4426        elif isinstance(other, float):
4427            if other == 0.0:
4428                raise ZeroDivisionError("Dividing by zero is not recommended")
4429            s = scantable(self._math._unaryop(self, other, mode, False))
4430        elif isinstance(other, list) or isinstance(other, numpy.ndarray):
4431            if isinstance(other[0], list) \
4432                    or isinstance(other[0], numpy.ndarray):
4433                from asapmath import _array2dOp
4434                s = _array2dOp( self, other, mode, False )
4435            else:
4436                s = scantable( self._math._arrayop( self, other,
4437                                                    mode, False ) )
4438        else:
4439            raise TypeError("Other input is not a scantable or float value")
4440        return s
4441
4442    @asaplog_post_dec
4443    def get_fit(self, row=0):
4444        """\
4445        Print or return the stored fits for a row in the scantable
4446
4447        Parameters:
4448
4449            row:    the row which the fit has been applied to.
4450
4451        """
4452        if row > self.nrow():
4453            return
4454        from asap.asapfit import asapfit
4455        fit = asapfit(self._getfit(row))
4456        asaplog.push( '%s' %(fit) )
4457        return fit.as_dict()
4458
4459    @preserve_selection
4460    def flag_nans(self):
4461        """\
4462        Utility function to flag NaN values in the scantable.
4463        """
4464        import numpy
4465        basesel = self.get_selection()
4466        for i in range(self.nrow()):
4467            sel = self.get_row_selector(i)
4468            self.set_selection(basesel+sel)
4469            nans = numpy.isnan(self._getspectrum(0))
4470        if numpy.any(nans):
4471            bnans = [ bool(v) for v in nans]
4472            self.flag(bnans)
4473
4474    def get_row_selector(self, rowno):
4475        return selector(rows=[rowno])
4476
4477    def _add_history(self, funcname, parameters):
4478        if not rcParams['scantable.history']:
4479            return
4480        # create date
4481        sep = "##"
4482        from datetime import datetime
4483        dstr = datetime.now().strftime('%Y/%m/%d %H:%M:%S')
4484        hist = dstr+sep
4485        hist += funcname+sep#cdate+sep
4486        if parameters.has_key('self'):
4487            del parameters['self']
4488        for k, v in parameters.iteritems():
4489            if type(v) is dict:
4490                for k2, v2 in v.iteritems():
4491                    hist += k2
4492                    hist += "="
4493                    if isinstance(v2, scantable):
4494                        hist += 'scantable'
4495                    elif k2 == 'mask':
4496                        if isinstance(v2, list) or isinstance(v2, tuple):
4497                            hist += str(self._zip_mask(v2))
4498                        else:
4499                            hist += str(v2)
4500                    else:
4501                        hist += str(v2)
4502            else:
4503                hist += k
4504                hist += "="
4505                if isinstance(v, scantable):
4506                    hist += 'scantable'
4507                elif k == 'mask':
4508                    if isinstance(v, list) or isinstance(v, tuple):
4509                        hist += str(self._zip_mask(v))
4510                    else:
4511                        hist += str(v)
4512                else:
4513                    hist += str(v)
4514            hist += sep
4515        hist = hist[:-2] # remove trailing '##'
4516        self._addhistory(hist)
4517
4518
4519    def _zip_mask(self, mask):
4520        mask = list(mask)
4521        i = 0
4522        segments = []
4523        while mask[i:].count(1):
4524            i += mask[i:].index(1)
4525            if mask[i:].count(0):
4526                j = i + mask[i:].index(0)
4527            else:
4528                j = len(mask)
4529            segments.append([i, j])
4530            i = j
4531        return segments
4532
4533    def _get_ordinate_label(self):
4534        fu = "("+self.get_fluxunit()+")"
4535        import re
4536        lbl = "Intensity"
4537        if re.match(".K.", fu):
4538            lbl = "Brightness Temperature "+ fu
4539        elif re.match(".Jy.", fu):
4540            lbl = "Flux density "+ fu
4541        return lbl
4542
4543    def _check_ifs(self):
4544#        return len(set([self.nchan(i) for i in self.getifnos()])) == 1
4545        nchans = [self.nchan(i) for i in self.getifnos()]
4546        nchans = filter(lambda t: t > 0, nchans)
4547        return (sum(nchans)/len(nchans) == nchans[0])
4548
4549    @asaplog_post_dec
4550    def _fill(self, names, unit, average, opts={}):
4551        first = True
4552        fullnames = []
4553        for name in names:
4554            name = os.path.expandvars(name)
4555            name = os.path.expanduser(name)
4556            if not os.path.exists(name):
4557                msg = "File '%s' does not exists" % (name)
4558                raise IOError(msg)
4559            fullnames.append(name)
4560        if average:
4561            asaplog.push('Auto averaging integrations')
4562        stype = int(rcParams['scantable.storage'].lower() == 'disk')
4563        for name in fullnames:
4564            tbl = Scantable(stype)
4565            if is_ms( name ):
4566                r = msfiller( tbl )
4567            else:
4568                r = filler( tbl )
4569            msg = "Importing %s..." % (name)
4570            asaplog.push(msg, False)
4571            r.open(name, opts)
4572            rx = rcParams['scantable.reference']
4573            r.setreferenceexpr(rx)
4574            r.fill()
4575            if average:
4576                tbl = self._math._average((tbl, ), (), 'NONE', 'SCAN')
4577            if not first:
4578                tbl = self._math._merge([self, tbl])
4579            Scantable.__init__(self, tbl)
4580            r.close()
4581            del r, tbl
4582            first = False
4583            #flush log
4584        asaplog.post()
4585        if unit is not None:
4586            self.set_fluxunit(unit)
4587        if not is_casapy():
4588            self.set_freqframe(rcParams['scantable.freqframe'])
4589
4590    def _get_verify_action( self, msg, action=None ):
4591        valid_act = ['Y', 'N', 'A', 'R']
4592        if not action or not isinstance(action, str):
4593            action = raw_input("%s [Y/n/a/r] (h for help): " % msg)
4594        if action == '':
4595            return "Y"
4596        elif (action.upper()[0] in valid_act):
4597            return action.upper()[0]
4598        elif (action.upper()[0] in ['H','?']):
4599            print "Available actions of verification [Y|n|a|r]"
4600            print " Y : Yes for current data (default)"
4601            print " N : No for current data"
4602            print " A : Accept all in the following and exit from verification"
4603            print " R : Reject all in the following and exit from verification"
4604            print " H or ?: help (show this message)"
4605            return self._get_verify_action(msg)
4606        else:
4607            return 'Y'
4608
4609    def __getitem__(self, key):
4610        if key < 0:
4611            key += self.nrow()
4612        if key >= self.nrow():
4613            raise IndexError("Row index out of range.")
4614        return self._getspectrum(key)
4615
4616    def __setitem__(self, key, value):
4617        if key < 0:
4618            key += self.nrow()
4619        if key >= self.nrow():
4620            raise IndexError("Row index out of range.")
4621        if not hasattr(value, "__len__") or \
4622                len(value) > self.nchan(self.getif(key)):
4623            raise ValueError("Spectrum length doesn't match.")
4624        return self._setspectrum(value, key)
4625
4626    def __len__(self):
4627        return self.nrow()
4628
4629    def __iter__(self):
4630        for i in range(len(self)):
4631            yield self[i]
Note: See TracBrowser for help on using the repository browser.