source: trunk/python/scantable.py @ 2480

Last change on this file since 2480 was 2480, checked in by Malte Marquarding, 12 years ago

Ticket #269: fix for custom reference scan expressions not being honoured.

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