source: trunk/python/scantable.py @ 1579

Last change on this file since 1579 was 1579, checked in by Malte Marquarding, 15 years ago

Ticket #46; changed scnatable.lag_flag to handle a start/end value rather than a width. lag channels can also be specified directly by seeting the unit to

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