source: trunk/python/scantable.py @ 2818

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

Issue #291: added scantable.set_sourcename to overwrite sourcename to allow freq_align to work. Exposed 'SOURCE' averaging to python api. Added some logging to freq_align refering to the sources it aligns to

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