source: trunk/python/scantable.py @ 2290

Last change on this file since 2290 was 2290, checked in by Kana Sugimoto, 13 years ago

New Development: Yes

JIRA Issue: Yes (CAS-3219/ASAP-247)

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs: sdlist unit test (reference data to be updated)

Put in Release Notes: Yes

Module(s): scantable.summary, sdlist

Description:

Output format of scantable summary changed.
Less use of TableIterator? for speed up scantable.summary/sdlist now lists a scantable
with 348,000 records (NRO 45m w/ 25beams x 13,920scans) in ~30sec (was ~7 min previously).


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