source: branches/alma/python/scantable.py @ 1496

Last change on this file since 1496 was 1496, checked in by TakTsutsumi, 15 years ago

New Development: No

JIRA Issue: No

Ready to Release: Yes

Interface Changes: Yes

What Interface Changed: a new boolean, getpt

Test Programs: List test programs

Put in Release Notes: No

Module(s): Module Names change impacts.

Description: Added a boolean, getpt to init()

and _fill() to allow control over
pointing data filling.
Also commented out a line forces
the default frequency
frame setting when scantble is read.


  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 72.6 KB
Line 
1from asap._asap import Scantable
2from asap import rcParams
3from asap import print_log
4from asap import asaplog
5from asap import selector
6from asap import linecatalog
7from asap import _n_bools, mask_not, mask_and, mask_or
8
9class scantable(Scantable):
10    """
11        The ASAP container for scans
12    """
13
14    def __init__(self, filename, average=None, unit=None, getpt=None):
15        """
16        Create a scantable from a saved one or make a reference
17        Parameters:
18            filename:    the name of an asap table on disk
19                         or
20                         the name of a rpfits/sdfits/ms file
21                         (integrations within scans are auto averaged
22                         and the whole file is read)
23                         or
24                         [advanced] a reference to an existing
25                         scantable
26            average:     average all integrations withinb a scan on read.
27                         The default (True) is taken from .asaprc.
28            unit:         brightness unit; must be consistent with K or Jy.
29                         Over-rides the default selected by the reader
30                         (input rpfits/sdfits/ms) or replaces the value
31                         in existing scantables
32        """
33        if average is None:
34            average = rcParams['scantable.autoaverage']
35        if getpt is None:
36            getpt = False
37        varlist = vars()
38        from asap._asap import stmath
39        self._math = stmath()
40        if isinstance(filename, Scantable):
41            Scantable.__init__(self, filename)
42        else:
43            if isinstance(filename, str):
44                import os.path
45                filename = os.path.expandvars(filename)
46                filename = os.path.expanduser(filename)
47                if not os.path.exists(filename):
48                    s = "File '%s' not found." % (filename)
49                    if rcParams['verbose']:
50                        asaplog.push(s)
51                        print asaplog.pop().strip()
52                        return
53                    raise IOError(s)
54                if os.path.isdir(filename) \
55                    and not os.path.exists(filename+'/table.f1'):
56                    # crude check if asap table
57                    if os.path.exists(filename+'/table.info'):
58                        ondisk = rcParams['scantable.storage'] == 'disk'
59                        Scantable.__init__(self, filename, ondisk)
60                        if unit is not None:
61                            self.set_fluxunit(unit)
62                        # do not reset to the default freqframe
63                        #self.set_freqframe(rcParams['scantable.freqframe'])
64                    else:
65                        msg = "The given file '%s'is not a valid " \
66                              "asap table." % (filename)
67                        if rcParams['verbose']:
68                            print msg
69                            return
70                        else:
71                            raise IOError(msg)
72                else:
73                    self._fill([filename], unit, average, getpt)
74            elif (isinstance(filename, list) or isinstance(filename, tuple)) \
75                  and isinstance(filename[-1], str):
76                self._fill(filename, unit, average, getpt)
77        self._add_history("scantable", varlist)
78        print_log()
79
80    def save(self, name=None, format=None, overwrite=False):
81        """
82        Store the scantable on disk. This can be an asap (aips++) Table,
83        SDFITS or MS2 format.
84        Parameters:
85            name:        the name of the outputfile. For format "ASCII"
86                         this is the root file name (data in 'name'.txt
87                         and header in 'name'_header.txt)
88            format:      an optional file format. Default is ASAP.
89                         Allowed are - 'ASAP' (save as ASAP [aips++] Table),
90                                       'SDFITS' (save as SDFITS file)
91                                       'ASCII' (saves as ascii text file)
92                                       'MS2' (saves as an aips++
93                                              MeasurementSet V2)
94            overwrite:   If the file should be overwritten if it exists.
95                         The default False is to return with warning
96                         without writing the output. USE WITH CARE.
97        Example:
98            scan.save('myscan.asap')
99            scan.save('myscan.sdfits', 'SDFITS')
100        """
101        from os import path
102        if format is None: format = rcParams['scantable.save']
103        suffix = '.'+format.lower()
104        if name is None or name == "":
105            name = 'scantable'+suffix
106            msg = "No filename given. Using default name %s..." % name
107            asaplog.push(msg)
108        name = path.expandvars(name)
109        if path.isfile(name) or path.isdir(name):
110            if not overwrite:
111                msg = "File %s exists." % name
112                if rcParams['verbose']:
113                    print msg
114                    return
115                else:
116                    raise IOError(msg)
117        format2 = format.upper()
118        if format2 == 'ASAP':
119            self._save(name)
120        else:
121            from asap._asap import stwriter as stw
122            writer = stw(format2)
123            writer.write(self, name)
124        print_log()
125        return
126
127    def copy(self):
128        """
129        Return a copy of this scantable.
130        Note:
131            This makes a full (deep) copy. scan2 = scan1 makes a reference.
132        Parameters:
133            none
134        Example:
135            copiedscan = scan.copy()
136        """
137        sd = scantable(Scantable._copy(self))
138        return sd
139
140    def drop_scan(self, scanid=None):
141        """
142        Return a new scantable where the specified scan number(s) has(have)
143        been dropped.
144        Parameters:
145            scanid:    a (list of) scan number(s)
146        """
147        from asap import _is_sequence_or_number as _is_valid
148        from asap import _to_list
149        from asap import unique
150        if not _is_valid(scanid):
151            if rcParams['verbose']:
152                print "Please specify a scanno to drop from the scantable"
153                return
154            else:
155                raise RuntimeError("No scan given")
156        try:
157            scanid = _to_list(scanid)
158            allscans = unique([ self.getscan(i) for i in range(self.nrow())])
159            for sid in scanid: allscans.remove(sid)
160            if len(allscans) == 0:
161                raise ValueError("Can't remove all scans")
162        except ValueError:
163            if rcParams['verbose']:
164                print "Couldn't find any match."
165                return
166            else: raise
167        try:
168            bsel = self.get_selection()
169            sel = selector()
170            sel.set_scans(allscans)
171            self.set_selection(bsel+sel)
172            scopy = self._copy()
173            self.set_selection(bsel)
174            return scantable(scopy)
175        except RuntimeError:
176            if rcParams['verbose']:
177                print "Couldn't find any match."
178            else:
179                raise
180
181
182    def get_scan(self, scanid=None):
183        """
184        Return a specific scan (by scanno) or collection of scans (by
185        source name) in a new scantable.
186        Note:
187            See scantable.drop_scan() for the inverse operation.
188        Parameters:
189            scanid:    a (list of) scanno or a source name, unix-style
190                       patterns are accepted for source name matching, e.g.
191                       '*_R' gets all 'ref scans
192        Example:
193            # get all scans containing the source '323p459'
194            newscan = scan.get_scan('323p459')
195            # get all 'off' scans
196            refscans = scan.get_scan('*_R')
197            # get a susbset of scans by scanno (as listed in scan.summary())
198            newscan = scan.get_scan([0, 2, 7, 10])
199        """
200        if scanid is None:
201            if rcParams['verbose']:
202                print "Please specify a scan no or name to " \
203                      "retrieve from the scantable"
204                return
205            else:
206                raise RuntimeError("No scan given")
207
208        try:
209            bsel = self.get_selection()
210            sel = selector()
211            if type(scanid) is str:
212                sel.set_name(scanid)
213                self.set_selection(bsel+sel)
214                scopy = self._copy()
215                self.set_selection(bsel)
216                return scantable(scopy)
217            elif type(scanid) is int:
218                sel.set_scans([scanid])
219                self.set_selection(bsel+sel)
220                scopy = self._copy()
221                self.set_selection(bsel)
222                return scantable(scopy)
223            elif type(scanid) is list:
224                sel.set_scans(scanid)
225                self.set_selection(sel)
226                scopy = self._copy()
227                self.set_selection(bsel)
228                return scantable(scopy)
229            else:
230                msg = "Illegal scanid type, use 'int' or 'list' if ints."
231                if rcParams['verbose']:
232                    print msg
233                else:
234                    raise TypeError(msg)
235        except RuntimeError:
236            if rcParams['verbose']: print "Couldn't find any match."
237            else: raise
238
239    def __str__(self):
240        return Scantable._summary(self, True)
241
242    def summary(self, filename=None):
243        """
244        Print a summary of the contents of this scantable.
245        Parameters:
246            filename:    the name of a file to write the putput to
247                         Default - no file output
248            verbose:     print extra info such as the frequency table
249                         The default (False) is taken from .asaprc
250        """
251        info = Scantable._summary(self, True)
252        #if verbose is None: verbose = rcParams['scantable.verbosesummary']
253        if filename is not None:
254            if filename is "":
255                filename = 'scantable_summary.txt'
256            from os.path import expandvars, isdir
257            filename = expandvars(filename)
258            if not isdir(filename):
259                data = open(filename, 'w')
260                data.write(info)
261                data.close()
262            else:
263                msg = "Illegal file name '%s'." % (filename)
264                if rcParams['verbose']:
265                    print msg
266                else:
267                    raise IOError(msg)
268        if rcParams['verbose']:
269            try:
270                from IPython.genutils import page as pager
271            except ImportError:
272                from pydoc import pager
273            pager(info)
274        else:
275            return info
276
277
278    def get_selection(self):
279        """
280        Get the selection object currently set on this scantable.
281        Parameters:
282            none
283        Example:
284            sel = scan.get_selection()
285            sel.set_ifs(0)              # select IF 0
286            scan.set_selection(sel)     # apply modified selection
287        """
288        return selector(self._getselection())
289
290    def set_selection(self, selection=selector()):
291        """
292        Select a subset of the data. All following operations on this scantable
293        are only applied to thi selection.
294        Parameters:
295            selection:    a selector object (default unset the selection)
296        Examples:
297            sel = selector()         # create a selection object
298            self.set_scans([0, 3])    # select SCANNO 0 and 3
299            scan.set_selection(sel)  # set the selection
300            scan.summary()           # will only print summary of scanno 0 an 3
301            scan.set_selection()     # unset the selection
302        """
303        self._setselection(selection)
304
305    def get_row(self, row=0, insitu=None):
306        """
307        Select a row in the scantable.
308        Return a scantable with single row.
309        Parameters:
310            row: row no of integration, default is 0.
311            insitu: if False a new scantable is returned.
312                    Otherwise, the scaling is done in-situ
313                    The default is taken from .asaprc (False)
314        """
315        if insitu is None: insitu = rcParams['insitu']
316        if not insitu:
317            workscan = self.copy()
318        else:
319            workscan = self
320        # Select a row
321        sel=selector()
322        sel.set_scans([workscan.getscan(row)])
323        sel.set_cycles([workscan.getcycle(row)])
324        sel.set_beams([workscan.getbeam(row)])
325        sel.set_ifs([workscan.getif(row)])
326        sel.set_polarisations([workscan.getpol(row)])
327        sel.set_name(workscan._getsourcename(row))
328        workscan.set_selection(sel)
329        if not workscan.nrow() == 1:
330            msg = "Cloud not identify single row. %d rows selected."%(workscan.nrow())
331            raise RuntimeError(msg)
332        del sel
333        if insitu:
334            self._assign(workscan)
335        else:
336            return workscan
337
338    def stats(self, stat='stddev', mask=None):
339        """
340        Determine the specified statistic of the current beam/if/pol
341        Takes a 'mask' as an optional parameter to specify which
342        channels should be excluded.
343        Parameters:
344            stat:    'min', 'max', 'sumsq', 'sum', 'mean'
345                     'var', 'stddev', 'avdev', 'rms', 'median'
346            mask:    an optional mask specifying where the statistic
347                     should be determined.
348        Example:
349            scan.set_unit('channel')
350            msk = scan.create_mask([100, 200], [500, 600])
351            scan.stats(stat='mean', mask=m)
352        """
353        if mask == None:
354            mask = []
355        axes = ['Beam', 'IF', 'Pol', 'Time']
356        if not self._check_ifs():
357            raise ValueError("Cannot apply mask as the IFs have different "
358                             "number of channels. Please use setselection() "
359                             "to select individual IFs")
360
361        statvals = self._math._stats(self, mask, stat)
362        out = ''
363        axes = []
364        for i in range(self.nrow()):
365            axis = []
366            axis.append(self.getscan(i))
367            axis.append(self.getbeam(i))
368            axis.append(self.getif(i))
369            axis.append(self.getpol(i))
370            axis.append(self.getcycle(i))
371            axes.append(axis)
372            tm = self._gettime(i)
373            src = self._getsourcename(i)
374            out += 'Scan[%d] (%s) ' % (axis[0], src)
375            out += 'Time[%s]:\n' % (tm)
376            if self.nbeam(-1) > 1: out +=  ' Beam[%d] ' % (axis[1])
377            if self.nif(-1) > 1: out +=  ' IF[%d] ' % (axis[2])
378            if self.npol(-1) > 1: out +=  ' Pol[%d] ' % (axis[3])
379            out += '= %3.3f\n' % (statvals[i])
380            out +=  "--------------------------------------------------\n"
381
382        if rcParams['verbose']:
383            print "--------------------------------------------------"
384            print " ", stat
385            print "--------------------------------------------------"
386            print out
387        #else:
388            #retval = { 'axesnames': ['scanno', 'beamno', 'ifno', 'polno', 'cycleno'],
389            #           'axes' : axes,
390            #           'data': statvals}
391        return statvals
392
393    def stddev(self, mask=None):
394        """
395        Determine the standard deviation of the current beam/if/pol
396        Takes a 'mask' as an optional parameter to specify which
397        channels should be excluded.
398        Parameters:
399            mask:    an optional mask specifying where the standard
400                     deviation should be determined.
401
402        Example:
403            scan.set_unit('channel')
404            msk = scan.create_mask([100, 200], [500, 600])
405            scan.stddev(mask=m)
406        """
407        return self.stats(stat='stddev', mask=mask);
408
409
410    def get_column_names(self):
411        """
412        Return a  list of column names, which can be used for selection.
413        """
414        return list(Scantable.get_column_names(self))
415
416    def get_tsys(self):
417        """
418        Return the System temperatures.
419        Returns:
420            a list of Tsys values for the current selection
421        """
422
423        return self._row_callback(self._gettsys, "Tsys")
424
425    def _row_callback(self, callback, label):
426        axes = []
427        axesnames = ['scanno', 'beamno', 'ifno', 'polno', 'cycleno']
428        out = ""
429        outvec = []
430        for i in range(self.nrow()):
431            axis = []
432            axis.append(self.getscan(i))
433            axis.append(self.getbeam(i))
434            axis.append(self.getif(i))
435            axis.append(self.getpol(i))
436            axis.append(self.getcycle(i))
437            axes.append(axis)
438            tm = self._gettime(i)
439            src = self._getsourcename(i)
440            out += 'Scan[%d] (%s) ' % (axis[0], src)
441            out += 'Time[%s]:\n' % (tm)
442            if self.nbeam(-1) > 1: out +=  ' Beam[%d] ' % (axis[1])
443            if self.nif(-1) > 1: out +=  ' IF[%d] ' % (axis[2])
444            if self.npol(-1) > 1: out +=  ' Pol[%d] ' % (axis[3])
445            outvec.append(callback(i))
446            out += '= %3.3f\n' % (outvec[i])
447            out +=  "--------------------------------------------------\n"
448        if rcParams['verbose']:
449            print "--------------------------------------------------"
450            print " %s" % (label)
451            print "--------------------------------------------------"
452            print out
453        # disabled because the vector seems more useful
454        #retval = {'axesnames': axesnames, 'axes': axes, 'data': outvec}
455        return outvec
456
457    def _get_column(self, callback, row=-1):
458        """
459        """
460        if row == -1:
461            return [callback(i) for i in range(self.nrow())]
462        else:
463            if  0 <= row < self.nrow():
464                return callback(row)
465
466
467    def get_time(self, row=-1, asdatetime=False):
468        """
469        Get a list of time stamps for the observations.
470        Return a datetime object for each integration time stamp in the scantable.
471        Parameters:
472            row:          row no of integration. Default -1 return all rows
473            asdatetime:   return values as datetime objects rather than strings
474        Example:
475            none
476        """
477        from time import strptime
478        from datetime import datetime
479        times = self._get_column(self._gettime, row)
480        if not asdatetime:
481            return times
482        format = "%Y/%m/%d/%H:%M:%S"
483        if isinstance(times, list):
484            return [datetime(*strptime(i, format)[:6]) for i in times]
485        else:
486            return datetime(*strptime(times, format)[:6])
487
488
489    def get_inttime(self, row=-1):
490        """
491        Get a list of integration times for the observations.
492        Return a time in seconds for each integration in the scantable.
493        Parameters:
494            row:    row no of integration. Default -1 return all rows.
495        Example:
496            none
497        """
498        return self._get_column(self._getinttime, row)       
499       
500
501    def get_sourcename(self, row=-1):
502        """
503        Get a list source names for the observations.
504        Return a string for each integration in the scantable.
505        Parameters:
506            row:    row no of integration. Default -1 return all rows.
507        Example:
508            none
509        """
510        return self._get_column(self._getsourcename, row)
511
512    def get_elevation(self, row=-1):
513        """
514        Get a list of elevations for the observations.
515        Return a float for each integration in the scantable.
516        Parameters:
517            row:    row no of integration. Default -1 return all rows.
518        Example:
519            none
520        """
521        return self._get_column(self._getelevation, row)
522
523    def get_azimuth(self, row=-1):
524        """
525        Get a list of azimuths for the observations.
526        Return a float for each integration in the scantable.
527        Parameters:
528            row:    row no of integration. Default -1 return all rows.
529        Example:
530            none
531        """
532        return self._get_column(self._getazimuth, row)
533
534    def get_parangle(self, row=-1):
535        """
536        Get a list of parallactic angles for the observations.
537        Return a float for each integration in the scantable.
538        Parameters:
539            row:    row no of integration. Default -1 return all rows.
540        Example:
541            none
542        """
543        return self._get_column(self._getparangle, row)
544
545    def get_direction(self, row=-1):
546        """
547        Get a list of Positions on the sky (direction) for the observations.
548        Return a float for each integration in the scantable.
549        Parameters:
550            row:    row no of integration. Default -1 return all rows
551        Example:
552            none
553        """
554        return self._get_column(self._getdirection, row)
555
556    def get_directionval(self, row=-1):
557        """
558        Get a list of Positions on the sky (direction) for the observations.
559        Return a float for each integration in the scantable.
560        Parameters:
561            row:    row no of integration. Default -1 return all rows
562        Example:
563            none
564        """
565        return self._get_column(self._getdirectionvec, row)
566
567    def set_unit(self, unit='channel'):
568        """
569        Set the unit for all following operations on this scantable
570        Parameters:
571            unit:    optional unit, default is 'channel'
572                     one of '*Hz', 'km/s', 'channel', ''
573        """
574        varlist = vars()
575        if unit in ['', 'pixel', 'channel']:
576            unit = ''
577        inf = list(self._getcoordinfo())
578        inf[0] = unit
579        self._setcoordinfo(inf)
580        self._add_history("set_unit", varlist)
581
582    def set_instrument(self, instr):
583        """
584        Set the instrument for subsequent processing.
585        Parameters:
586            instr:    Select from 'ATPKSMB', 'ATPKSHOH', 'ATMOPRA',
587                      'DSS-43' (Tid), 'CEDUNA', and 'HOBART'
588        """
589        self._setInstrument(instr)
590        self._add_history("set_instument", vars())
591        print_log()
592
593    def set_feedtype(self, feedtype):
594        """
595        Overwrite the feed type, which might not be set correctly.
596        Parameters:
597            feedtype:     'linear' or 'circular'
598        """
599        self._setfeedtype(feedtype)
600        self._add_history("set_feedtype", vars())
601        print_log()
602
603    def set_doppler(self, doppler='RADIO'):
604        """
605        Set the doppler for all following operations on this scantable.
606        Parameters:
607            doppler:    One of 'RADIO', 'OPTICAL', 'Z', 'BETA', 'GAMMA'
608        """
609        varlist = vars()
610        inf = list(self._getcoordinfo())
611        inf[2] = doppler
612        self._setcoordinfo(inf)
613        self._add_history("set_doppler", vars())
614        print_log()
615
616    def set_freqframe(self, frame=None):
617        """
618        Set the frame type of the Spectral Axis.
619        Parameters:
620            frame:   an optional frame type, default 'LSRK'. Valid frames are:
621                     'REST', 'TOPO', 'LSRD', 'LSRK', 'BARY',
622                     'GEO', 'GALACTO', 'LGROUP', 'CMB'
623        Examples:
624            scan.set_freqframe('BARY')
625        """
626        if frame is None: frame = rcParams['scantable.freqframe']
627        varlist = vars()
628        valid = ['REST', 'TOPO', 'LSRD', 'LSRK', 'BARY', \
629                   'GEO', 'GALACTO', 'LGROUP', 'CMB']
630
631        if frame in valid:
632            inf = list(self._getcoordinfo())
633            inf[1] = frame
634            self._setcoordinfo(inf)
635            self._add_history("set_freqframe", varlist)
636        else:
637            msg  = "Please specify a valid freq type. Valid types are:\n", valid
638            if rcParams['verbose']:
639                print msg
640            else:
641                raise TypeError(msg)
642        print_log()
643
644    def set_dirframe(self, frame=""):
645        """
646        Set the frame type of the Direction on the sky.
647        Parameters:
648            frame:   an optional frame type, default ''. Valid frames are:
649                     'J2000', 'B1950', 'GALACTIC'
650        Examples:
651            scan.set_dirframe('GALACTIC')
652        """
653        varlist = vars()
654        try:
655            Scantable.set_dirframe(self, frame)
656        except RuntimeError, msg:
657            if rcParams['verbose']:
658                print msg
659            else:
660                raise
661        self._add_history("set_dirframe", varlist)
662
663    def get_unit(self):
664        """
665        Get the default unit set in this scantable
666        Returns:
667            A unit string
668        """
669        inf = self._getcoordinfo()
670        unit = inf[0]
671        if unit == '': unit = 'channel'
672        return unit
673
674    def get_abcissa(self, rowno=0):
675        """
676        Get the abcissa in the current coordinate setup for the currently
677        selected Beam/IF/Pol
678        Parameters:
679            rowno:    an optional row number in the scantable. Default is the
680                      first row, i.e. rowno=0
681        Returns:
682            The abcissa values and the format string (as a dictionary)
683        """
684        abc = self._getabcissa(rowno)
685        lbl = self._getabcissalabel(rowno)
686        print_log()
687        return abc, lbl
688
689    def flag(self, mask=None, unflag=False):
690        """
691        Flag the selected data using an optional channel mask.
692        Parameters:
693            mask:   an optional channel mask, created with create_mask. Default
694                    (no mask) is all channels.
695            unflag:    if True, unflag the data
696        """
697        varlist = vars()
698        if mask is None:
699            mask = []
700        try:
701            self._flag(mask, unflag)
702        except RuntimeError, msg:
703            if rcParams['verbose']:
704                print msg
705                return
706            else: raise
707        self._add_history("flag", varlist)
708
709    def lag_flag(self, frequency, width=0.0, unit="GHz", insitu=None):
710        """
711        Flag the data in 'lag' space by providing a frequency to remove.
712        Flagged data in the scantable gets set to 0.0 before the fft.
713        No taper is applied.
714        Parameters:
715            frequency:    the frequency (really a period within the bandwidth)
716                          to remove
717            width:        the width of the frequency to remove, to remove a
718                          range of frequencies aroung the centre.
719            unit:         the frequency unit (default "GHz")
720        Notes:
721            It is recommended to flag edges of the band or strong
722            signals beforehand.
723        """
724        if insitu is None: insitu = rcParams['insitu']
725        self._math._setinsitu(insitu)
726        varlist = vars()
727        base = { "GHz": 1000000000., "MHz": 1000000., "kHz": 1000., "Hz": 1. }
728        if not base.has_key(unit):
729            raise ValueError("%s is not a valid unit." % unit)
730        try:
731            s = scantable(self._math._lag_flag(self, frequency*base[unit],
732                                               width*base[unit]))
733        except RuntimeError, msg:
734            if rcParams['verbose']:
735                print msg
736                return
737            else: raise
738        s._add_history("lag_flag", varlist)
739        print_log()
740        if insitu:
741            self._assign(s)
742        else:
743            return s
744
745
746    def create_mask(self, *args, **kwargs):
747        """
748        Compute and return a mask based on [min, max] windows.
749        The specified windows are to be INCLUDED, when the mask is
750        applied.
751        Parameters:
752            [min, max], [min2, max2], ...
753                Pairs of start/end points (inclusive)specifying the regions
754                to be masked
755            invert:     optional argument. If specified as True,
756                        return an inverted mask, i.e. the regions
757                        specified are EXCLUDED
758            row:        create the mask using the specified row for
759                        unit conversions, default is row=0
760                        only necessary if frequency varies over rows.
761        Example:
762            scan.set_unit('channel')
763            a)
764            msk = scan.create_mask([400, 500], [800, 900])
765            # masks everything outside 400 and 500
766            # and 800 and 900 in the unit 'channel'
767
768            b)
769            msk = scan.create_mask([400, 500], [800, 900], invert=True)
770            # masks the regions between 400 and 500
771            # and 800 and 900 in the unit 'channel'
772            c)
773            mask only channel 400
774            msk =  scan.create_mask([400, 400])
775        """
776        row = 0
777        if kwargs.has_key("row"):
778            row = kwargs.get("row")
779        data = self._getabcissa(row)
780        u = self._getcoordinfo()[0]
781        if rcParams['verbose']:
782            if u == "": u = "channel"
783            msg = "The current mask window unit is %s" % u
784            i = self._check_ifs()
785            if not i:
786                msg += "\nThis mask is only valid for IF=%d" % (self.getif(i))
787            asaplog.push(msg)
788        n = self.nchan()
789        msk = _n_bools(n, False)
790        # test if args is a 'list' or a 'normal *args - UGLY!!!
791
792        ws = (isinstance(args[-1][-1], int) or isinstance(args[-1][-1], float)) \
793             and args or args[0]
794        for window in ws:
795            if (len(window) != 2 or window[0] > window[1] ):
796                raise TypeError("A window needs to be defined as [min, max]")
797            for i in range(n):
798                if data[i] >= window[0] and data[i] <= window[1]:
799                    msk[i] = True
800        if kwargs.has_key('invert'):
801            if kwargs.get('invert'):
802                msk = mask_not(msk)
803        print_log()
804        return msk
805
806    def get_masklist(self, mask=None, row=0):
807        """
808        Compute and return a list of mask windows, [min, max].
809        Parameters:
810            mask:       channel mask, created with create_mask.
811            row:        calcutate the masklist using the specified row
812                        for unit conversions, default is row=0
813                        only necessary if frequency varies over rows.
814        Returns:
815            [min, max], [min2, max2], ...
816                Pairs of start/end points (inclusive)specifying
817                the masked regions
818        """
819        if not (isinstance(mask,list) or isinstance(mask, tuple)):
820            raise TypeError("The mask should be list or tuple.")
821        if len(mask) < 2:
822            raise TypeError("The mask elements should be > 1")
823        if self.nchan() != len(mask):
824            msg = "Number of channels in scantable != number of mask elements"
825            raise TypeError(msg)
826        data = self._getabcissa(row)
827        u = self._getcoordinfo()[0]
828        if rcParams['verbose']:
829            if u == "": u = "channel"
830            msg = "The current mask window unit is %s" % u
831            i = self._check_ifs()
832            if not i:
833                msg += "\nThis mask is only valid for IF=%d" % (self.getif(i))
834            asaplog.push(msg)
835        masklist=[]
836        ist, ien = None, None
837        ist, ien=self.get_mask_indices(mask)
838        if ist is not None and ien is not None:
839            for i in xrange(len(ist)):
840                range=[data[ist[i]],data[ien[i]]]
841                range.sort()
842                masklist.append([range[0],range[1]])
843        return masklist
844
845    def get_mask_indices(self, mask=None):
846        """
847        Compute and Return lists of mask start indices and mask end indices.
848         Parameters:
849            mask:       channel mask, created with create_mask.
850        Returns:
851            List of mask start indices and that of mask end indices,
852            i.e., [istart1,istart2,....], [iend1,iend2,....].
853        """
854        if not (isinstance(mask,list) or isinstance(mask, tuple)):
855            raise TypeError("The mask should be list or tuple.")
856        if len(mask) < 2:
857            raise TypeError("The mask elements should be > 1")
858        istart=[]
859        iend=[]
860        if mask[0]: istart.append(0)
861        for i in range(len(mask)-1):
862            if not mask[i] and mask[i+1]:
863                istart.append(i+1)
864            elif mask[i] and not mask[i+1]:
865                iend.append(i)
866        if mask[len(mask)-1]: iend.append(len(mask)-1)
867        if len(istart) != len(iend):
868            raise RuntimeError("Numbers of mask start != mask end.")
869        for i in range(len(istart)):
870            if istart[i] > iend[i]:
871                raise RuntimeError("Mask start index > mask end index")
872                break
873        return istart,iend
874
875#    def get_restfreqs(self):
876#        """
877#        Get the restfrequency(s) stored in this scantable.
878#        The return value(s) are always of unit 'Hz'
879#        Parameters:
880#            none
881#        Returns:
882#            a list of doubles
883#        """
884#        return list(self._getrestfreqs())
885
886    def get_restfreqs(self, ids=None):
887        """
888        Get the restfrequency(s) stored in this scantable.
889        The return value(s) are always of unit 'Hz'
890        Parameters:
891            ids: (optional) a list of MOLECULE_ID for that restfrequency(s) to
892                 be retrieved
893        Returns:
894            dictionary containing ids and a list of doubles for each id
895        """
896        if ids is None:
897            rfreqs={}
898            idlist = self.getmolnos()
899            for i in idlist:
900                rfreqs[i]=list(self._getrestfreqs(i))
901            return rfreqs
902        else:
903            if type(ids)==list or type(ids)==tuple:
904                rfreqs={}
905                for i in ids:
906                    rfreqs[i]=list(self._getrestfreqs(i))
907                return rfreqs
908            else:
909                return list(self._getrestfreqs(ids))
910            #return list(self._getrestfreqs(ids))
911
912    def set_restfreqs(self, freqs=None, unit='Hz'):
913        """
914        ********NEED TO BE UPDATED begin************
915        Set or replace the restfrequency specified and
916        If the 'freqs' argument holds a scalar,
917        then that rest frequency will be applied to all the selected
918        data.  If the 'freqs' argument holds
919        a vector, then it MUST be of equal or smaller length than
920        the number of IFs (and the available restfrequencies will be
921        replaced by this vector).  In this case, *all* data have
922        the restfrequency set per IF according
923        to the corresponding value you give in the 'freqs' vector.
924        E.g. 'freqs=[1e9, 2e9]'  would mean IF 0 gets restfreq 1e9 and
925        IF 1 gets restfreq 2e9.
926        ********NEED TO BE UPDATED end************
927        You can also specify the frequencies via a linecatalog/
928
929        Parameters:
930            freqs:   list of rest frequency values or string idenitfiers
931            unit:    unit for rest frequency (default 'Hz')
932
933        Example:
934            # set the given restfrequency for the all currently selected IFs
935            scan.set_restfreqs(freqs=1.4e9)
936            # set multiple restfrequencies to all the selected data
937            scan.set_restfreqs(freqs=[1.4e9, 1.41e9, 1.42e9])
938            # If the number of IFs in the data is >= 2 the IF0 gets the first
939            # value IF1 the second... NOTE that freqs needs to be
940            # specified in list of list (e.g. [[],[],...] ).
941            scan.set_restfreqs(freqs=[[1.4e9],[1.67e9]])
942            #set the given restfrequency for the whole table (by name)
943            scan.set_restfreqs(freqs="OH1667")
944
945        Note:
946            To do more sophisticate Restfrequency setting, e.g. on a
947            source and IF basis, use scantable.set_selection() before using
948            this function.
949            # provide your scantable is call scan
950            selection = selector()
951            selection.set_name("ORION*")
952            selection.set_ifs([1])
953            scan.set_selection(selection)
954            scan.set_restfreqs(freqs=86.6e9)
955
956        """
957        varlist = vars()
958        from asap import linecatalog
959        # simple  value
960        if isinstance(freqs, int) or isinstance(freqs, float):
961            # TT mod
962            #self._setrestfreqs(freqs, "",unit)
963            self._setrestfreqs([freqs], [""],unit)
964        # list of values
965        elif isinstance(freqs, list) or isinstance(freqs, tuple):
966            # list values are scalars
967            if isinstance(freqs[-1], int) or isinstance(freqs[-1], float):
968                self._setrestfreqs(freqs, [""],unit)
969            # list values are tuples, (value, name)
970            elif isinstance(freqs[-1], dict):
971                #sel = selector()
972                #savesel = self._getselection()
973                #iflist = self.getifnos()
974                #for i in xrange(len(freqs)):
975                #    sel.set_ifs(iflist[i])
976                #    self._setselection(sel)
977                #    self._setrestfreqs(freqs[i], "",unit)
978                #self._setselection(savesel)
979                self._setrestfreqs(freqs["value"],
980                                   freqs["name"], "MHz")
981            elif isinstance(freqs[-1], list) or isinstance(freqs[-1], tuple):
982                sel = selector()
983                savesel = self._getselection()
984                iflist = self.getifnos()
985                if len(freqs)>len(iflist):
986                    raise ValueError("number of elements in list of list exeeds the current IF selections")
987                for i in xrange(len(freqs)):
988                    sel.set_ifs(iflist[i])
989                    self._setselection(sel)
990                    self._setrestfreqs(freqs[i]["value"],
991                                       freqs[i]["name"], "MHz")
992                self._setselection(savesel)
993        # freqs are to be taken from a linecatalog
994        elif isinstance(freqs, linecatalog):
995            sel = selector()
996            savesel = self._getselection()
997            for i in xrange(freqs.nrow()):
998                sel.set_ifs(iflist[i])
999                self._setselection(sel)
1000                self._setrestfreqs(freqs.get_frequency(i),
1001                                   freqs.get_name(i), "MHz")
1002                # ensure that we are not iterating past nIF
1003                if i == self.nif()-1: break
1004            self._setselection(savesel)
1005        else:
1006            return
1007        self._add_history("set_restfreqs", varlist)
1008
1009    def shift_refpix(self, delta):
1010        """
1011        Shift the reference pixel of the Spectra Coordinate by an
1012        integer amount.
1013        Parameters:
1014            delta:   the amount to shift by
1015        Note:
1016            Be careful using this with broadband data.
1017        """
1018        Scantable.shift(self, delta)
1019
1020    def history(self, filename=None):
1021        """
1022        Print the history. Optionally to a file.
1023        Parameters:
1024            filename:    The name  of the file to save the history to.
1025        """
1026        hist = list(self._gethistory())
1027        out = "-"*80
1028        for h in hist:
1029            if h.startswith("---"):
1030                out += "\n"+h
1031            else:
1032                items = h.split("##")
1033                date = items[0]
1034                func = items[1]
1035                items = items[2:]
1036                out += "\n"+date+"\n"
1037                out += "Function: %s\n  Parameters:" % (func)
1038                for i in items:
1039                    s = i.split("=")
1040                    out += "\n   %s = %s" % (s[0], s[1])
1041                out += "\n"+"-"*80
1042        if filename is not None:
1043            if filename is "":
1044                filename = 'scantable_history.txt'
1045            import os
1046            filename = os.path.expandvars(os.path.expanduser(filename))
1047            if not os.path.isdir(filename):
1048                data = open(filename, 'w')
1049                data.write(out)
1050                data.close()
1051            else:
1052                msg = "Illegal file name '%s'." % (filename)
1053                if rcParams['verbose']:
1054                    print msg
1055                else:
1056                    raise IOError(msg)
1057        if rcParams['verbose']:
1058            try:
1059                from IPython.genutils import page as pager
1060            except ImportError:
1061                from pydoc import pager
1062            pager(out)
1063        else:
1064            return out
1065        return
1066    #
1067    # Maths business
1068    #
1069
1070    def average_time(self, mask=None, scanav=False, weight='tint', align=False):
1071        """
1072        Return the (time) weighted average of a scan.
1073        Note:
1074            in channels only - align if necessary
1075        Parameters:
1076            mask:     an optional mask (only used for 'var' and 'tsys'
1077                      weighting)
1078            scanav:   True averages each scan separately
1079                      False (default) averages all scans together,
1080            weight:   Weighting scheme.
1081                      'none'     (mean no weight)
1082                      'var'      (1/var(spec) weighted)
1083                      'tsys'     (1/Tsys**2 weighted)
1084                      'tint'     (integration time weighted)
1085                      'tintsys'  (Tint/Tsys**2)
1086                      'median'   ( median averaging)
1087                      The default is 'tint'
1088            align:    align the spectra in velocity before averaging. It takes
1089                      the time of the first spectrum as reference time.
1090        Example:
1091            # time average the scantable without using a mask
1092            newscan = scan.average_time()
1093        """
1094        varlist = vars()
1095        if weight is None: weight = 'TINT'
1096        if mask is None: mask = ()
1097        if scanav: scanav = "SCAN"
1098        else: scanav = "NONE"
1099        scan = (self, )
1100        try:
1101            if align:
1102                scan = (self.freq_align(insitu=False), )
1103            s = None
1104            if weight.upper() == 'MEDIAN':
1105                s = scantable(self._math._averagechannel(scan[0], 'MEDIAN',
1106                                                         scanav))
1107            else:
1108                s = scantable(self._math._average(scan, mask, weight.upper(),
1109                              scanav))
1110        except RuntimeError, msg:
1111            if rcParams['verbose']:
1112                print msg
1113                return
1114            else: raise
1115        s._add_history("average_time", varlist)
1116        print_log()
1117        return s
1118
1119    def convert_flux(self, jyperk=None, eta=None, d=None, insitu=None):
1120        """
1121        Return a scan where all spectra are converted to either
1122        Jansky or Kelvin depending upon the flux units of the scan table.
1123        By default the function tries to look the values up internally.
1124        If it can't find them (or if you want to over-ride), you must
1125        specify EITHER jyperk OR eta (and D which it will try to look up
1126        also if you don't set it). jyperk takes precedence if you set both.
1127        Parameters:
1128            jyperk:      the Jy / K conversion factor
1129            eta:         the aperture efficiency
1130            d:           the geomtric diameter (metres)
1131            insitu:      if False a new scantable is returned.
1132                         Otherwise, the scaling is done in-situ
1133                         The default is taken from .asaprc (False)
1134        """
1135        if insitu is None: insitu = rcParams['insitu']
1136        self._math._setinsitu(insitu)
1137        varlist = vars()
1138        if jyperk is None: jyperk = -1.0
1139        if d is None: d = -1.0
1140        if eta is None: eta = -1.0
1141        s = scantable(self._math._convertflux(self, d, eta, jyperk))
1142        s._add_history("convert_flux", varlist)
1143        print_log()
1144        if insitu: self._assign(s)
1145        else: return s
1146
1147    def gain_el(self, poly=None, filename="", method="linear", insitu=None):
1148        """
1149        Return a scan after applying a gain-elevation correction.
1150        The correction can be made via either a polynomial or a
1151        table-based interpolation (and extrapolation if necessary).
1152        You specify polynomial coefficients, an ascii table or neither.
1153        If you specify neither, then a polynomial correction will be made
1154        with built in coefficients known for certain telescopes (an error
1155        will occur if the instrument is not known).
1156        The data and Tsys are *divided* by the scaling factors.
1157        Parameters:
1158            poly:        Polynomial coefficients (default None) to compute a
1159                         gain-elevation correction as a function of
1160                         elevation (in degrees).
1161            filename:    The name of an ascii file holding correction factors.
1162                         The first row of the ascii file must give the column
1163                         names and these MUST include columns
1164                         "ELEVATION" (degrees) and "FACTOR" (multiply data
1165                         by this) somewhere.
1166                         The second row must give the data type of the
1167                         column. Use 'R' for Real and 'I' for Integer.
1168                         An example file would be
1169                         (actual factors are arbitrary) :
1170
1171                         TIME ELEVATION FACTOR
1172                         R R R
1173                         0.1 0 0.8
1174                         0.2 20 0.85
1175                         0.3 40 0.9
1176                         0.4 60 0.85
1177                         0.5 80 0.8
1178                         0.6 90 0.75
1179            method:      Interpolation method when correcting from a table.
1180                         Values are  "nearest", "linear" (default), "cubic"
1181                         and "spline"
1182            insitu:      if False a new scantable is returned.
1183                         Otherwise, the scaling is done in-situ
1184                         The default is taken from .asaprc (False)
1185        """
1186
1187        if insitu is None: insitu = rcParams['insitu']
1188        self._math._setinsitu(insitu)
1189        varlist = vars()
1190        if poly is None:
1191            poly = ()
1192        from os.path import expandvars
1193        filename = expandvars(filename)
1194        s = scantable(self._math._gainel(self, poly, filename, method))
1195        s._add_history("gain_el", varlist)
1196        print_log()
1197        if insitu: self._assign(s)
1198        else: return s
1199
1200    def freq_align(self, reftime=None, method='cubic', insitu=None):
1201        """
1202        Return a scan where all rows have been aligned in frequency/velocity.
1203        The alignment frequency frame (e.g. LSRK) is that set by function
1204        set_freqframe.
1205        Parameters:
1206            reftime:     reference time to align at. By default, the time of
1207                         the first row of data is used.
1208            method:      Interpolation method for regridding the spectra.
1209                         Choose from "nearest", "linear", "cubic" (default)
1210                         and "spline"
1211            insitu:      if False a new scantable is returned.
1212                         Otherwise, the scaling is done in-situ
1213                         The default is taken from .asaprc (False)
1214        """
1215        if insitu is None: insitu = rcParams["insitu"]
1216        self._math._setinsitu(insitu)
1217        varlist = vars()
1218        if reftime is None: reftime = ""
1219        s = scantable(self._math._freq_align(self, reftime, method))
1220        s._add_history("freq_align", varlist)
1221        print_log()
1222        if insitu: self._assign(s)
1223        else: return s
1224
1225    def opacity(self, tau, insitu=None):
1226        """
1227        Apply an opacity correction. The data
1228        and Tsys are multiplied by the correction factor.
1229        Parameters:
1230            tau:         Opacity from which the correction factor is
1231                         exp(tau*ZD)
1232                         where ZD is the zenith-distance
1233            insitu:      if False a new scantable is returned.
1234                         Otherwise, the scaling is done in-situ
1235                         The default is taken from .asaprc (False)
1236        """
1237        if insitu is None: insitu = rcParams['insitu']
1238        self._math._setinsitu(insitu)
1239        varlist = vars()
1240        s = scantable(self._math._opacity(self, tau))
1241        s._add_history("opacity", varlist)
1242        print_log()
1243        if insitu: self._assign(s)
1244        else: return s
1245
1246    def bin(self, width=5, insitu=None):
1247        """
1248        Return a scan where all spectra have been binned up.
1249        Parameters:
1250            width:       The bin width (default=5) in pixels
1251            insitu:      if False a new scantable is returned.
1252                         Otherwise, the scaling is done in-situ
1253                         The default is taken from .asaprc (False)
1254        """
1255        if insitu is None: insitu = rcParams['insitu']
1256        self._math._setinsitu(insitu)
1257        varlist = vars()
1258        s = scantable(self._math._bin(self, width))
1259        s._add_history("bin", varlist)
1260        print_log()
1261        if insitu: self._assign(s)
1262        else: return s
1263
1264
1265    def resample(self, width=5, method='cubic', insitu=None):
1266        """
1267        Return a scan where all spectra have been binned up.
1268       
1269        Parameters:
1270            width:       The bin width (default=5) in pixels
1271            method:      Interpolation method when correcting from a table.
1272                         Values are  "nearest", "linear", "cubic" (default)
1273                         and "spline"
1274            insitu:      if False a new scantable is returned.
1275                         Otherwise, the scaling is done in-situ
1276                         The default is taken from .asaprc (False)
1277        """
1278        if insitu is None: insitu = rcParams['insitu']
1279        self._math._setinsitu(insitu)
1280        varlist = vars()
1281        s = scantable(self._math._resample(self, method, width))
1282        s._add_history("resample", varlist)
1283        print_log()
1284        if insitu: self._assign(s)
1285        else: return s
1286
1287
1288    def average_pol(self, mask=None, weight='none'):
1289        """
1290        Average the Polarisations together.
1291        Parameters:
1292            mask:        An optional mask defining the region, where the
1293                         averaging will be applied. The output will have all
1294                         specified points masked.
1295            weight:      Weighting scheme. 'none' (default), 'var' (1/var(spec)
1296                         weighted), or 'tsys' (1/Tsys**2 weighted)
1297        """
1298        varlist = vars()
1299        if mask is None:
1300            mask = ()
1301        s = scantable(self._math._averagepol(self, mask, weight.upper()))
1302        s._add_history("average_pol", varlist)
1303        print_log()
1304        return s
1305
1306    def average_beam(self, mask=None, weight='none'):
1307        """
1308        Average the Beams together.
1309        Parameters:
1310            mask:        An optional mask defining the region, where the
1311                         averaging will be applied. The output will have all
1312                         specified points masked.
1313            weight:      Weighting scheme. 'none' (default), 'var' (1/var(spec)
1314                         weighted), or 'tsys' (1/Tsys**2 weighted)
1315        """
1316        varlist = vars()
1317        if mask is None:
1318            mask = ()
1319        s = scantable(self._math._averagebeams(self, mask, weight.upper()))
1320        s._add_history("average_beam", varlist)
1321        print_log()
1322        return s
1323
1324    def convert_pol(self, poltype=None):
1325        """
1326        Convert the data to a different polarisation type.
1327        Parameters:
1328            poltype:    The new polarisation type. Valid types are:
1329                        "linear", "stokes" and "circular"
1330        """
1331        varlist = vars()
1332        try:
1333            s = scantable(self._math._convertpol(self, poltype))
1334        except RuntimeError, msg:
1335            if rcParams['verbose']:
1336                print msg
1337                return
1338            else:
1339                raise
1340        s._add_history("convert_pol", varlist)
1341        print_log()
1342        return s
1343
1344    def smooth(self, kernel="hanning", width=5.0, insitu=None):
1345        """
1346        Smooth the spectrum by the specified kernel (conserving flux).
1347        Parameters:
1348            kernel:     The type of smoothing kernel. Select from
1349                        'hanning' (default), 'gaussian', 'boxcar' and
1350                        'rmedian'
1351            width:      The width of the kernel in pixels. For hanning this is
1352                        ignored otherwise it defauls to 5 pixels.
1353                        For 'gaussian' it is the Full Width Half
1354                        Maximum. For 'boxcar' it is the full width.
1355                        For 'rmedian' it is the half width.
1356            insitu:     if False a new scantable is returned.
1357                        Otherwise, the scaling is done in-situ
1358                        The default is taken from .asaprc (False)
1359        Example:
1360             none
1361        """
1362        if insitu is None: insitu = rcParams['insitu']
1363        self._math._setinsitu(insitu)
1364        varlist = vars()
1365        s = scantable(self._math._smooth(self, kernel.lower(), width))
1366        s._add_history("smooth", varlist)
1367        print_log()
1368        if insitu: self._assign(s)
1369        else: return s
1370
1371
1372    def poly_baseline(self, mask=None, order=0, plot=False, uselin=False, insitu=None):
1373        """
1374        Return a scan which has been baselined (all rows) by a polynomial.
1375        Parameters:
1376            mask:       an optional mask
1377            order:      the order of the polynomial (default is 0)
1378            plot:       plot the fit and the residual. In this each
1379                        indivual fit has to be approved, by typing 'y'
1380                        or 'n'
1381            uselin:     use linear polynomial fit
1382            insitu:     if False a new scantable is returned.
1383                        Otherwise, the scaling is done in-situ
1384                        The default is taken from .asaprc (False)
1385        Example:
1386            # return a scan baselined by a third order polynomial,
1387            # not using a mask
1388            bscan = scan.poly_baseline(order=3)
1389        """
1390        if insitu is None: insitu = rcParams['insitu']
1391        varlist = vars()
1392        if mask is None:
1393            mask = [True for i in xrange(self.nchan(-1))]
1394        from asap.asapfitter import fitter
1395        try:
1396            f = fitter()
1397            f.set_scan(self, mask)
1398            if uselin:
1399                f.set_function(lpoly=order)
1400            else:
1401                f.set_function(poly=order)
1402            s = f.auto_fit(insitu, plot=plot)
1403            # Save parameters of baseline fits as a class attribute.
1404            # NOTICE: It does not reflect changes in scantable!
1405            self.blpars = f.blpars
1406            s._add_history("poly_baseline", varlist)
1407            print_log()
1408            if insitu: self._assign(s)
1409            else: return s
1410        except RuntimeError:
1411            msg = "The fit failed, possibly because it didn't converge."
1412            if rcParams['verbose']:
1413                print msg
1414                return
1415            else:
1416                raise RuntimeError(msg)
1417
1418
1419    def auto_poly_baseline(self, mask=[], edge=(0, 0), order=0,
1420                           threshold=3, chan_avg_limit=1, plot=False,
1421                           insitu=None):
1422        """
1423        Return a scan which has been baselined (all rows) by a polynomial.
1424        Spectral lines are detected first using linefinder and masked out
1425        to avoid them affecting the baseline solution.
1426
1427        Parameters:
1428            mask:       an optional mask retreived from scantable
1429            edge:       an optional number of channel to drop at
1430                        the edge of spectrum. If only one value is
1431                        specified, the same number will be dropped from
1432                        both sides of the spectrum. Default is to keep
1433                        all channels. Nested tuples represent individual
1434                        edge selection for different IFs (a number of spectral
1435                        channels can be different)
1436            order:      the order of the polynomial (default is 0)
1437            threshold:  the threshold used by line finder. It is better to
1438                        keep it large as only strong lines affect the
1439                        baseline solution.
1440            chan_avg_limit:
1441                        a maximum number of consequtive spectral channels to
1442                        average during the search of weak and broad lines.
1443                        The default is no averaging (and no search for weak
1444                        lines). If such lines can affect the fitted baseline
1445                        (e.g. a high order polynomial is fitted), increase this
1446                        parameter (usually values up to 8 are reasonable). Most
1447                        users of this method should find the default value
1448                        sufficient.
1449            plot:       plot the fit and the residual. In this each
1450                        indivual fit has to be approved, by typing 'y'
1451                        or 'n'
1452            insitu:     if False a new scantable is returned.
1453                        Otherwise, the scaling is done in-situ
1454                        The default is taken from .asaprc (False)
1455
1456        Example:
1457            scan2=scan.auto_poly_baseline(order=7)
1458        """
1459        if insitu is None: insitu = rcParams['insitu']
1460        varlist = vars()
1461        from asap.asapfitter import fitter
1462        from asap.asaplinefind import linefinder
1463        from asap import _is_sequence_or_number as _is_valid
1464
1465        # check whether edge is set up for each IF individually
1466        individualedge = False;
1467        if len(edge) > 1:
1468            if isinstance(edge[0], list) or isinstance(edge[0], tuple):
1469                individualedge = True;
1470
1471        if not _is_valid(edge, int) and not individualedge:
1472            raise ValueError, "Parameter 'edge' has to be an integer or a \
1473            pair of integers specified as a tuple. Nested tuples are allowed \
1474            to make individual selection for different IFs."
1475
1476        curedge = (0, 0)
1477        if individualedge:
1478            for edgepar in edge:
1479                if not _is_valid(edgepar, int):
1480                    raise ValueError, "Each element of the 'edge' tuple has \
1481                                       to be a pair of integers or an integer."
1482        else:
1483            curedge = edge;
1484
1485        # setup fitter
1486        f = fitter()
1487        f.set_function(poly=order)
1488
1489        # setup line finder
1490        fl = linefinder()
1491        fl.set_options(threshold=threshold,avg_limit=chan_avg_limit)
1492
1493        if not insitu:
1494            workscan = self.copy()
1495        else:
1496            workscan = self
1497
1498        fl.set_scan(workscan)
1499
1500        rows = range(workscan.nrow())
1501        # Save parameters of baseline fits & masklists as a class attribute.
1502        # NOTICE: It does not reflect changes in scantable!
1503        if len(rows) > 0:
1504            self.blpars=[]
1505            self.masklists=[]
1506        asaplog.push("Processing:")
1507        for r in rows:
1508            msg = " Scan[%d] Beam[%d] IF[%d] Pol[%d] Cycle[%d]" % \
1509                (workscan.getscan(r), workscan.getbeam(r), workscan.getif(r), \
1510                 workscan.getpol(r), workscan.getcycle(r))
1511            asaplog.push(msg, False)
1512
1513            # figure out edge parameter
1514            if individualedge:
1515                if len(edge) >= workscan.getif(r):
1516                    raise RuntimeError, "Number of edge elements appear to " \
1517                                        "be less than the number of IFs"
1518                    curedge = edge[workscan.getif(r)]
1519
1520            # setup line finder
1521            fl.find_lines(r, mask, curedge)
1522            outmask=fl.get_mask()
1523            f.set_scan(workscan, fl.get_mask())
1524            f.x = workscan._getabcissa(r)
1525            f.y = workscan._getspectrum(r)
1526            f.data = None
1527            f.fit()
1528           
1529            # Show mask list
1530            masklist=workscan.get_masklist(fl.get_mask(),row=r)
1531            msg = "mask range: "+str(masklist)
1532            asaplog.push(msg, False)
1533
1534            fpar = f.get_parameters()
1535            if plot:
1536                f.plot(residual=True)
1537                x = raw_input("Accept fit ( [y]/n ): ")
1538                if x.upper() == 'N':
1539                    self.blpars.append(None)
1540                    self.masklists.append(None)
1541                    continue
1542            workscan._setspectrum(f.fitter.getresidual(), r)
1543            self.blpars.append(fpar)
1544            self.masklists.append(masklist)
1545        if plot:
1546            f._p.unmap()
1547            f._p = None
1548        workscan._add_history("auto_poly_baseline", varlist)
1549        if insitu:
1550            self._assign(workscan)
1551        else:
1552            return workscan
1553
1554    def rotate_linpolphase(self, angle):
1555        """
1556        Rotate the phase of the complex polarization O=Q+iU correlation.
1557        This is always done in situ in the raw data.  So if you call this
1558        function more than once then each call rotates the phase further.
1559        Parameters:
1560            angle:   The angle (degrees) to rotate (add) by.
1561        Examples:
1562            scan.rotate_linpolphase(2.3)
1563        """
1564        varlist = vars()
1565        self._math._rotate_linpolphase(self, angle)
1566        self._add_history("rotate_linpolphase", varlist)
1567        print_log()
1568        return
1569
1570
1571    def rotate_xyphase(self, angle):
1572        """
1573        Rotate the phase of the XY correlation.  This is always done in situ
1574        in the data.  So if you call this function more than once
1575        then each call rotates the phase further.
1576        Parameters:
1577            angle:   The angle (degrees) to rotate (add) by.
1578        Examples:
1579            scan.rotate_xyphase(2.3)
1580        """
1581        varlist = vars()
1582        self._math._rotate_xyphase(self, angle)
1583        self._add_history("rotate_xyphase", varlist)
1584        print_log()
1585        return
1586
1587    def swap_linears(self):
1588        """
1589        Swap the linear polarisations XX and YY, or better the first two
1590        polarisations as this also works for ciculars.
1591        """
1592        varlist = vars()
1593        self._math._swap_linears(self)
1594        self._add_history("swap_linears", varlist)
1595        print_log()
1596        return
1597
1598    def invert_phase(self):
1599        """
1600        Invert the phase of the complex polarisation
1601        """
1602        varlist = vars()
1603        self._math._invert_phase(self)
1604        self._add_history("invert_phase", varlist)
1605        print_log()
1606        return
1607
1608    def add(self, offset, insitu=None):
1609        """
1610        Return a scan where all spectra have the offset added
1611        Parameters:
1612            offset:      the offset
1613            insitu:      if False a new scantable is returned.
1614                         Otherwise, the scaling is done in-situ
1615                         The default is taken from .asaprc (False)
1616        """
1617        if insitu is None: insitu = rcParams['insitu']
1618        self._math._setinsitu(insitu)
1619        varlist = vars()
1620        s = scantable(self._math._unaryop(self, offset, "ADD", False))
1621        s._add_history("add", varlist)
1622        print_log()
1623        if insitu:
1624            self._assign(s)
1625        else:
1626            return s
1627
1628    def scale(self, factor, tsys=True, insitu=None):
1629        """
1630        Return a scan where all spectra are scaled by the give 'factor'
1631        Parameters:
1632            factor:      the scaling factor
1633            insitu:      if False a new scantable is returned.
1634                         Otherwise, the scaling is done in-situ
1635                         The default is taken from .asaprc (False)
1636            tsys:        if True (default) then apply the operation to Tsys
1637                         as well as the data
1638        """
1639        if insitu is None: insitu = rcParams['insitu']
1640        self._math._setinsitu(insitu)
1641        varlist = vars()
1642        s = scantable(self._math._unaryop(self, factor, "MUL", tsys))
1643        s._add_history("scale", varlist)
1644        print_log()
1645        if insitu:
1646            self._assign(s)
1647        else:
1648            return s
1649
1650    def auto_quotient(self, preserve=True, mode='paired'):
1651        """
1652        This function allows to build quotients automatically.
1653        It assumes the observation to have the same numer of
1654        "ons" and "offs"
1655        Parameters:
1656            preserve:       you can preserve (default) the continuum or
1657                            remove it.  The equations used are
1658                            preserve: Output = Toff * (on/off) - Toff
1659                            remove:   Output = Toff * (on/off) - Ton
1660            mode:           the on/off detection mode
1661                            'paired' (default)
1662                            identifies 'off' scans by the
1663                            trailing '_R' (Mopra/Parkes) or
1664                            '_e'/'_w' (Tid) and matches
1665                            on/off pairs from the observing pattern
1666                'time'
1667                   finds the closest off in time
1668
1669        """
1670        modes = ["time", "paired"]
1671        if not mode in modes:
1672            msg = "please provide valid mode. Valid modes are %s" % (modes)
1673            raise ValueError(msg)
1674        varlist = vars()
1675        s = None
1676        if mode.lower() == "paired":
1677            basesel = self.get_selection()
1678            sel = selector()+basesel
1679            sel.set_query("SRCTYPE==1")
1680            self.set_selection(sel)
1681            offs = self.copy()
1682            sel.set_query("SRCTYPE==0")
1683            self.set_selection(sel)
1684            ons = self.copy()
1685            s = scantable(self._math._quotient(ons, offs, preserve))
1686            self.set_selection(basesel)
1687        elif mode.lower() == "time":
1688            s = scantable(self._math._auto_quotient(self, mode, preserve))
1689        s._add_history("auto_quotient", varlist)
1690        print_log()
1691        return s
1692
1693    def mx_quotient(self, mask = None, weight='median', preserve=True):
1694        """
1695        Form a quotient using "off" beams when observing in "MX" mode.
1696        Parameters:
1697            mask:           an optional mask to be used when weight == 'stddev'
1698            weight:         How to average the off beams.  Default is 'median'.
1699            preserve:       you can preserve (default) the continuum or
1700                            remove it.  The equations used are
1701                            preserve: Output = Toff * (on/off) - Toff
1702                            remove:   Output = Toff * (on/off) - Ton
1703        """
1704        if mask is None: mask = ()
1705        varlist = vars()
1706        on = scantable(self._math._mx_extract(self, 'on'))
1707        preoff = scantable(self._math._mx_extract(self, 'off'))
1708        off = preoff.average_time(mask=mask, weight=weight, scanav=False)
1709        from asapmath  import quotient
1710        q = quotient(on, off, preserve)
1711        q._add_history("mx_quotient", varlist)
1712        print_log()
1713        return q
1714
1715    def freq_switch(self, insitu=None):
1716        """
1717        Apply frequency switching to the data.
1718        Parameters:
1719            insitu:      if False a new scantable is returned.
1720                         Otherwise, the swictching is done in-situ
1721                         The default is taken from .asaprc (False)
1722        Example:
1723            none
1724        """
1725        if insitu is None: insitu = rcParams['insitu']
1726        self._math._setinsitu(insitu)
1727        varlist = vars()
1728        s = scantable(self._math._freqswitch(self))
1729        s._add_history("freq_switch", varlist)
1730        print_log()
1731        if insitu: self._assign(s)
1732        else: return s
1733
1734    def recalc_azel(self):
1735        """
1736        Recalculate the azimuth and elevation for each position.
1737        Parameters:
1738            none
1739        Example:
1740        """
1741        varlist = vars()
1742        self._recalcazel()
1743        self._add_history("recalc_azel", varlist)
1744        print_log()
1745        return
1746
1747    def __add__(self, other):
1748        varlist = vars()
1749        s = None
1750        if isinstance(other, scantable):
1751            s = scantable(self._math._binaryop(self, other, "ADD"))
1752        elif isinstance(other, float):
1753            s = scantable(self._math._unaryop(self, other, "ADD", False))
1754        else:
1755            raise TypeError("Other input is not a scantable or float value")
1756        s._add_history("operator +", varlist)
1757        print_log()
1758        return s
1759
1760    def __sub__(self, other):
1761        """
1762        implicit on all axes and on Tsys
1763        """
1764        varlist = vars()
1765        s = None
1766        if isinstance(other, scantable):
1767            s = scantable(self._math._binaryop(self, other, "SUB"))
1768        elif isinstance(other, float):
1769            s = scantable(self._math._unaryop(self, other, "SUB", False))
1770        else:
1771            raise TypeError("Other input is not a scantable or float value")
1772        s._add_history("operator -", varlist)
1773        print_log()
1774        return s
1775
1776    def __mul__(self, other):
1777        """
1778        implicit on all axes and on Tsys
1779        """
1780        varlist = vars()
1781        s = None
1782        if isinstance(other, scantable):
1783            s = scantable(self._math._binaryop(self, other, "MUL"))
1784        elif isinstance(other, float):
1785            s = scantable(self._math._unaryop(self, other, "MUL", False))
1786        else:
1787            raise TypeError("Other input is not a scantable or float value")
1788        s._add_history("operator *", varlist)
1789        print_log()
1790        return s
1791
1792
1793    def __div__(self, other):
1794        """
1795        implicit on all axes and on Tsys
1796        """
1797        varlist = vars()
1798        s = None
1799        if isinstance(other, scantable):
1800            s = scantable(self._math._binaryop(self, other, "DIV"))
1801        elif isinstance(other, float):
1802            if other == 0.0:
1803                raise ZeroDivisionError("Dividing by zero is not recommended")
1804            s = scantable(self._math._unaryop(self, other, "DIV", False))
1805        else:
1806            raise TypeError("Other input is not a scantable or float value")
1807        s._add_history("operator /", varlist)
1808        print_log()
1809        return s
1810
1811    def get_fit(self, row=0):
1812        """
1813        Print or return the stored fits for a row in the scantable
1814        Parameters:
1815            row:    the row which the fit has been applied to.
1816        """
1817        if row > self.nrow():
1818            return
1819        from asap.asapfit import asapfit
1820        fit = asapfit(self._getfit(row))
1821        if rcParams['verbose']:
1822            print fit
1823            return
1824        else:
1825            return fit.as_dict()
1826
1827    def _add_history(self, funcname, parameters):
1828        # create date
1829        sep = "##"
1830        from datetime import datetime
1831        dstr = datetime.now().strftime('%Y/%m/%d %H:%M:%S')
1832        hist = dstr+sep
1833        hist += funcname+sep#cdate+sep
1834        if parameters.has_key('self'): del parameters['self']
1835        for k, v in parameters.iteritems():
1836            if type(v) is dict:
1837                for k2, v2 in v.iteritems():
1838                    hist += k2
1839                    hist += "="
1840                    if isinstance(v2, scantable):
1841                        hist += 'scantable'
1842                    elif k2 == 'mask':
1843                        if isinstance(v2, list) or isinstance(v2, tuple):
1844                            hist += str(self._zip_mask(v2))
1845                        else:
1846                            hist += str(v2)
1847                    else:
1848                        hist += str(v2)
1849            else:
1850                hist += k
1851                hist += "="
1852                if isinstance(v, scantable):
1853                    hist += 'scantable'
1854                elif k == 'mask':
1855                    if isinstance(v, list) or isinstance(v, tuple):
1856                        hist += str(self._zip_mask(v))
1857                    else:
1858                        hist += str(v)
1859                else:
1860                    hist += str(v)
1861            hist += sep
1862        hist = hist[:-2] # remove trailing '##'
1863        self._addhistory(hist)
1864
1865
1866    def _zip_mask(self, mask):
1867        mask = list(mask)
1868        i = 0
1869        segments = []
1870        while mask[i:].count(1):
1871            i += mask[i:].index(1)
1872            if mask[i:].count(0):
1873                j = i + mask[i:].index(0)
1874            else:
1875                j = len(mask)
1876            segments.append([i, j])
1877            i = j
1878        return segments
1879
1880    def _get_ordinate_label(self):
1881        fu = "("+self.get_fluxunit()+")"
1882        import re
1883        lbl = "Intensity"
1884        if re.match(".K.", fu):
1885            lbl = "Brightness Temperature "+ fu
1886        elif re.match(".Jy.", fu):
1887            lbl = "Flux density "+ fu
1888        return lbl
1889
1890    def _check_ifs(self):
1891        nchans = [self.nchan(i) for i in range(self.nif(-1))]
1892        nchans = filter(lambda t: t > 0, nchans)
1893        return (sum(nchans)/len(nchans) == nchans[0])
1894
1895    def _fill(self, names, unit, average, getpt):
1896        import os
1897        from asap._asap import stfiller
1898        first = True
1899        fullnames = []
1900        for name in names:
1901            name = os.path.expandvars(name)
1902            name = os.path.expanduser(name)
1903            if not os.path.exists(name):
1904                msg = "File '%s' does not exists" % (name)
1905                if rcParams['verbose']:
1906                    asaplog.push(msg)
1907                    print asaplog.pop().strip()
1908                    return
1909                raise IOError(msg)
1910            fullnames.append(name)
1911        if average:
1912            asaplog.push('Auto averaging integrations')
1913        stype = int(rcParams['scantable.storage'].lower() == 'disk')
1914        for name in fullnames:
1915            tbl = Scantable(stype)
1916            r = stfiller(tbl)
1917            msg = "Importing %s..." % (name)
1918            asaplog.push(msg, False)
1919            print_log()
1920            r._open(name, -1, -1, getpt)
1921            r._read()
1922            #tbl = r._getdata()
1923            if average:
1924                tbl = self._math._average((tbl, ), (), 'NONE', 'SCAN')
1925                #tbl = tbl2
1926            if not first:
1927                tbl = self._math._merge([self, tbl])
1928                #tbl = tbl2
1929            Scantable.__init__(self, tbl)
1930            r._close()
1931            del r, tbl
1932            first = False
1933        if unit is not None:
1934            self.set_fluxunit(unit)
1935        #self.set_freqframe(rcParams['scantable.freqframe'])
1936
Note: See TracBrowser for help on using the repository browser.