source: trunk/python/scantable.py @ 2610

Last change on this file since 2610 was 2610, checked in by Kana Sugimoto, 12 years ago

New Development: No (an enhancement)

JIRA Issue: Yes (CAS-4146/Trac-276)

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs: Interactive tests

Put in Release Notes: Yes

Module(s): scantable, sdbaseline (blfunc='poly'), sdsmooth (kernel!='regrid')

Description:

Implemented a way to exit from verification in the middle of scantable.
Added choises 'a', 'r', and 'h' in actions. Below is the full list of actions.
Available actions of verification [Y|n|a|r]

Y : Yes for current data (default)
N : No for current data
A : Accept all in the following and exit from verification
R : Reject all in the following and exit from verification
H or ?: help (show this message)


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