source: trunk/python/scantable.py @ 484

Last change on this file since 484 was 484, checked in by mar637, 19 years ago
  • added history
  • added contructor argument for (auto) averaging
  • tidying
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.2 KB
Line 
1from asap._asap import sdtable
2from asap import rcParams
3from numarray import ones,zeros
4import sys
5
6class scantable(sdtable):
7    """
8        The ASAP container for scans
9    """
10   
11    def __init__(self, filename, average=None, unit=None):
12        """
13        Create a scantable from a saved one or make a reference
14        Parameters:
15            filename:    the name of an asap table on disk
16                         or
17                         the name of a rpfits/sdfits/ms file
18                         (integrations within scans are auto averaged
19                         and the whole file is read)
20                         or
21                         [advanced] a reference to an existing
22                         scantable
23            average:     average all integrations withinb a scan on read.
24                         The default (True) is taken from .asaprc.
25            unit:         brightness unit; must be consistent with K or Jy.
26                         Over-rides the default selected by the reader
27                         (input rpfits/sdfits/ms) or replaces the value
28                         in existing scantables
29        """
30        varlist = vars()
31        self._vb = rcParams['verbose']
32        self._p = None
33       
34        if average is None or type(average) is not bool:
35            autoav = rcParams['scantable.autoaverage']           
36
37        if isinstance(filename,sdtable):
38            sdtable.__init__(self, filename)           
39            if unit is not None:
40                self.set_fluxunit(unit)                       
41        else:
42            import os.path
43            if not os.path.exists(filename):
44                print "File '%s' not found." % (filename)
45                return
46            filename = os.path.expandvars(filename)
47            if os.path.isdir(filename):
48                # crude check if asap table
49                if os.path.exists(filename+'/table.info'):
50                    sdtable.__init__(self, filename)
51                    if unit is not None:
52                        self.set_fluxunit(unit)                       
53                else:
54                    print "The given file '%s'is not a valid asap table." % (filename)
55                    return
56            else:           
57                from asap._asap import sdreader
58                ifSel = -1
59                beamSel = -1
60                r = sdreader(filename,ifSel,beamSel)
61                print 'Importing data...'
62                r._read([-1])
63                tbl = r._getdata()
64                if unit is not None:
65                    tbl.set_fluxunit(unit)
66                if autoav:
67                    from asap._asap import average
68                    tmp = tuple([tbl])
69                    print 'Auto averaging integrations...'
70                    tbl2 = average(tmp,(),True,'none')
71                    sdtable.__init__(self,tbl2)
72                    del r,tbl
73                else:
74                    sdtable.__init__(self,tbl)
75                self._add_history("scantable", varlist)
76
77    def save(self, name=None, format=None, stokes=False, overwrite=False):
78        """
79        Store the scantable on disk. This can be an asap (aips++) Table, SDFITS,
80        Image FITS or MS2 format.
81        Parameters:
82            name:        the name of the outputfile. For format="FITS" this
83                         is the directory file name into which all the files will
84                         be written (default is 'asap_FITS')
85            format:      an optional file format. Default is ASAP.
86                         Allowed are - 'ASAP' (save as ASAP [aips++] Table),
87                                       'SDFITS' (save as SDFITS file)
88                                       'FITS' (saves each row as a FITS Image)
89                                       'ASCII' (saves as ascii text file)
90                                       'MS2' (saves as an aips++
91                                              MeasurementSet V2)
92            stokes:      Convert to Stokes parameters.  Default is False.
93            overwrite:   If the file should be overwritten if it exists.
94                         The default False is to return with warning
95                         without writing the output. USE WITH CARE.
96        Example:
97            scan.save('myscan.asap')
98            scan.save('myscan.sdfits','SDFITS')
99        """
100        from os import path
101        if format is None: format = rcParams['scantable.save']
102        suffix = '.'+format.lower()
103        if name is None or name =="":
104            name = 'scantable'+suffix
105            print "No filename given. Using default name %s..." % name
106        name = path.expandvars(name)
107        if path.isfile(name) or path.isdir(name):
108            if not overwrite:
109                print "File %s already exists." % name
110                return
111        format2 = format.upper()
112        if format2 == 'ASAP':
113            self._save(name)
114        else:
115            from asap._asap import sdwriter as _sw
116            w = _sw(format2)
117            w.write(self, name, stokes)
118        return
119
120    def copy(self):
121        """
122        Return a copy of this scantable.
123        Parameters:
124            none
125        Example:
126            copiedscan = scan.copy()
127        """
128        sd = scantable(sdtable._copy(self))
129        return sd
130
131    def get_scan(self, scanid=None):
132        """
133        Return a specific scan (by scanno) or collection of scans (by
134        source name) in a new scantable.
135        Parameters:
136            scanid:    a scanno or a source name
137        Example:
138            scan.get_scan('323p459')
139            # gets all scans containing the source '323p459'
140        """
141        if scanid is None:
142            print "Please specify a scan no or name to retrieve from the scantable"
143        try:
144            if type(scanid) is str:
145                s = sdtable._getsource(self,scanid)
146                return scantable(s)
147            elif type(scanid) is int:
148                s = sdtable._getscan(self,[scanid])
149                return scantable(s)
150            elif type(scanid) is list:
151                s = sdtable._getscan(self,scanid)
152                return scantable(s)
153            else:
154                print "Illegal scanid type, use 'int' or 'list' if ints."
155        except RuntimeError:
156            print "Couldn't find any match."
157
158    def __str__(self):
159        return sdtable._summary(self,True)
160
161    def summary(self,filename=None, verbose=None):
162        """
163        Print a summary of the contents of this scantable.
164        Parameters:
165            filename:    the name of a file to write the putput to
166                         Default - no file output
167            verbose:     print extra info such as the frequency table
168                         The default (False) is taken from .asaprc
169        """
170        info = sdtable._summary(self, verbose)
171        if verbose is None: verbose = rcParams['scantable.verbosesummary']
172        if filename is not None:
173            if filename is "":
174                filename = 'scantable_summary.txt'
175            from os.path import expandvars, isdir
176            filename = expandvars(filename)
177            if not isdir(filename):
178                data = open(filename, 'w')
179                data.write(info)
180                data.close()
181            else:
182                print "Illegal file name '%s'." % (filename)
183        print info
184           
185    def set_cursor(self, thebeam=0,theif=0,thepol=0):
186        """
187        Set the spectrum for individual operations.
188        Parameters:
189            thebeam,theif,thepol:    a number
190        Example:
191            scan.set_cursor(0,0,1)
192            pol1sig = scan.stats(all=False) # returns std dev for beam=0
193                                            # if=0, pol=1
194        """
195        varlist = vars()
196        self.setbeam(thebeam)
197        self.setpol(thepol)
198        self.setif(theif)
199        self._add_history("set_cursor",varlist)
200        return
201
202    def get_cursor(self):
203        """
204        Return/print a the current 'cursor' into the Beam/IF/Pol cube.
205        Parameters:
206            none
207        Returns:
208            a list of values (currentBeam,currentIF,currentPol)
209        Example:
210            none
211        """
212        i = self.getbeam()
213        j = self.getif()
214        k = self.getpol()
215        if self._vb:
216            print "--------------------------------------------------"
217            print " Cursor position"
218            print "--------------------------------------------------"
219            out = 'Beam=%d IF=%d Pol=%d ' % (i,j,k)
220            print out
221        return i,j,k
222
223    def stats(self, stat='stddev', mask=None, allaxes=None):
224        """
225        Determine the specified statistic of the current beam/if/pol
226        Takes a 'mask' as an optional parameter to specify which
227        channels should be excluded.
228        Parameters:
229            stat:    'min', 'max', 'sumsq', 'sum', 'mean'
230                     'var', 'stddev', 'avdev', 'rms', 'median'
231            mask:    an optional mask specifying where the statistic
232                     should be determined.
233            allaxes: if True apply to all spectra. Otherwise
234                     apply only to the selected (beam/pol/if)spectra only.
235                     The default is taken from .asaprc (True if none)
236        Example:
237            scan.set_unit('channel')
238            msk = scan.create_mask([100,200],[500,600])
239            scan.stats(stat='mean', mask=m)
240        """
241        if allaxes is None: allaxes = rcParams['scantable.allaxes']
242        from asap._asap import stats as _stats
243        from numarray import array,zeros,Float
244        if mask == None:
245            mask = ones(self.nchan())
246        axes = ['Beam','IF','Pol','Time']
247
248        beamSel,IFSel,polSel = (self.getbeam(),self.getif(),self.getpol())
249        if allaxes:
250            n = self.nbeam()*self.nif()*self.npol()*self.nrow()
251            shp = [self.nbeam(),self.nif(),self.npol(),self.nrow()]
252            arr = array(zeros(n),shape=shp,type=Float)
253
254            for i in range(self.nbeam()):
255                self.setbeam(i)
256                for j in range(self.nif()):
257                    self.setif(j)
258                    for k in range(self.npol()):
259                        self.setpol(k)
260                        arr[i,j,k,:] = _stats(self,mask,stat,-1)
261            retval = {'axes': axes, 'data': arr, 'cursor':None}
262            tm = [self._gettime(val) for val in range(self.nrow())]
263            if self._vb:
264                self._print_values(retval,stat,tm)
265            self.setbeam(beamSel)
266            self.setif(IFSel)
267            self.setpol(polSel)
268            return retval
269
270        else:
271            statval = _stats(self,mask,stat,-1)
272            out = ''
273            for l in range(self.nrow()):
274                tm = self._gettime(l)
275                out += 'Time[%s]:\n' % (tm)
276                if self.nbeam() > 1: out +=  ' Beam[%d] ' % (beamSel)
277                if self.nif() > 1: out +=  ' IF[%d] ' % (IFSel)
278                if self.npol() > 1: out +=  ' Pol[%d] ' % (polSel)
279                out += '= %3.3f\n' % (statval[l])
280                out +=  "--------------------------------------------------\n"
281
282            if self._vb:
283                print "--------------------------------------------------"
284                print " ",stat
285                print "--------------------------------------------------"
286                print out
287            retval = {'axes': axes, 'data': array(statval), 'cursor':(i,j,k)}
288            return retval
289
290    def stddev(self,mask=None, allaxes=None):
291        """
292        Determine the standard deviation of the current beam/if/pol
293        Takes a 'mask' as an optional parameter to specify which
294        channels should be excluded.
295        Parameters:
296            mask:    an optional mask specifying where the standard
297                     deviation should be determined.
298            allaxes: optional flag to show all or a cursor selected
299                     spectrum of Beam/IF/Pol. Default is all or taken
300                     from .asaprc
301
302        Example:
303            scan.set_unit('channel')
304            msk = scan.create_mask([100,200],[500,600])
305            scan.stddev(mask=m)
306        """
307        if allaxes is None: allaxes = rcParams['scantable.allaxes']
308        return self.stats(stat='stddev',mask=mask, allaxes=allaxes);
309
310    def get_tsys(self, allaxes=None):
311        """
312        Return the System temperatures.
313        Parameters:
314           allaxes:     if True apply to all spectra. Otherwise
315                        apply only to the selected (beam/pol/if)spectra only.
316                        The default is taken from .asaprc (True if none)
317        Returns:
318            a list of Tsys values.
319        """
320        if allaxes is None: allaxes = rcParams['scantable.allaxes']
321        from numarray import array,zeros,Float
322        axes = ['Beam','IF','Pol','Time']
323
324        if allaxes:
325            n = self.nbeam()*self.nif()*self.npol()*self.nrow()
326            shp = [self.nbeam(),self.nif(),self.npol(),self.nrow()]
327            arr = array(zeros(n),shape=shp,type=Float)
328
329            for i in range(self.nbeam()):
330                self.setbeam(i)
331                for j in range(self.nif()):
332                    self.setif(j)
333                    for k in range(self.npol()):
334                        self.setpol(k)
335                        arr[i,j,k,:] = self._gettsys()
336            retval = {'axes': axes, 'data': arr, 'cursor':None}
337            tm = [self._gettime(val) for val in range(self.nrow())]
338            if self._vb:
339                self._print_values(retval,'Tsys',tm)
340            return retval
341
342        else:
343            i,j,k = (self.getbeam(),self.getif(),self.getpol())
344            statval = self._gettsys()
345            out = ''
346            for l in range(self.nrow()):
347                tm = self._gettime(l)
348                out += 'Time[%s]:\n' % (tm)
349                if self.nbeam() > 1: out +=  ' Beam[%d] ' % (i)
350                if self.nif() > 1: out +=  ' IF[%d] ' % (j)
351                if self.npol() > 1: out +=  ' Pol[%d] ' % (k)
352                out += '= %3.3f\n' % (statval[l])
353                out +=  "--------------------------------------------------\n"
354
355            if self._vb:
356                print "--------------------------------------------------"
357                print " TSys"
358                print "--------------------------------------------------"
359                print out
360            retval = {'axes': axes, 'data': array(statval), 'cursor':(i,j,k)}
361            return retval
362       
363    def get_time(self, row=-1):
364        """
365        Get a list of time stamps for the observations.
366        Return a string for each integration in the scantable.
367        Parameters:
368            row:    row no of integration. Default -1 return all rows
369        Example:
370            none
371        """
372        out = []
373        if row == -1:
374            for i in range(self.nrow()):
375                out.append(self._gettime(i))
376            return out
377        else:
378            if row < self.nrow():
379                return self._gettime(row)
380
381    def set_unit(self, unit='channel'):
382        """
383        Set the unit for all following operations on this scantable
384        Parameters:
385            unit:    optional unit, default is 'channel'
386                     one of '*Hz','km/s','channel', ''
387        """
388        varlist = vars()
389        if unit in ['','pixel', 'channel']:
390            unit = ''
391        inf = list(self._getcoordinfo())
392        inf[0] = unit
393        self._setcoordinfo(inf)
394        if self._p: self.plot()
395        self._add_history("set_unit",varlist)
396
397    def set_instrument(self, instr):
398        """
399        Set the instrument for subsequent processing
400        Parameters:
401            instr:    Select from 'ATPKSMB', 'ATPKSHOH', 'ATMOPRA',
402                      'DSS-43' (Tid), 'CEDUNA', and 'HOBART'
403        """
404        self._setInstrument(instr)
405        self._add_history("set_instument",vars())
406
407    def set_doppler(self, doppler='RADIO'):
408        """
409        Set the doppler for all following operations on this scantable.
410        Parameters:
411            doppler:    One of 'RADIO', 'OPTICAL', 'Z', 'BETA', 'GAMMA'
412        """
413        varlist = vars()
414        inf = list(self._getcoordinfo())
415        inf[2] = doppler
416        self._setcoordinfo(inf)
417        if self._p: self.plot()
418        self._add_history("set_doppler",vars())
419 
420    def set_freqframe(self, frame=None):
421        """
422        Set the frame type of the Spectral Axis.
423        Parameters:
424            frame:   an optional frame type, default 'LSRK'.
425        Examples:
426            scan.set_freqframe('BARY')
427        """
428        if frame is None: frame = rcParams['scantable.freqframe']
429        varlist = vars()
430        valid = ['REST','TOPO','LSRD','LSRK','BARY', \
431                   'GEO','GALACTO','LGROUP','CMB']
432        if frame in valid:
433            inf = list(self._getcoordinfo())
434            inf[1] = frame
435            self._setcoordinfo(inf)
436            self._add_history("set_freqframe",varlist)
437        else:
438            print "Please specify a valid freq type. Valid types are:\n",valid
439           
440    def get_unit(self):
441        """
442        Get the default unit set in this scantable
443        Parameters:
444        Returns:
445            A unit string
446        """
447        inf = self._getcoordinfo()
448        unit = inf[0]
449        if unit == '': unit = 'channel'
450        return unit
451
452    def get_abcissa(self, rowno=0):
453        """
454        Get the abcissa in the current coordinate setup for the currently
455        selected Beam/IF/Pol
456        Parameters:
457            rowno:    an optional row number in the scantable. Default is the
458                      first row, i.e. rowno=0
459        Returns:
460            The abcissa values and it's format string (as a dictionary)
461        """
462        abc = self._getabcissa(rowno)
463        lbl = self._getabcissalabel(rowno)       
464        return abc, lbl
465        #return {'abcissa':abc,'label':lbl}
466
467    def create_mask(self, *args, **kwargs):
468        """
469        Compute and return a mask based on [min,max] windows.
470        The specified windows are to be INCLUDED, when the mask is
471        applied.
472        Parameters:
473            [min,max],[min2,max2],...
474                Pairs of start/end points specifying the regions
475                to be masked
476            invert:     optional argument. If specified as True,
477                        return an inverted mask, i.e. the regions
478                        specified are EXCLUDED
479        Example:
480            scan.set_unit('channel')
481
482            a)
483            msk = scan.set_mask([400,500],[800,900])
484            # masks everything outside 400 and 500
485            # and 800 and 900 in the unit 'channel'
486
487            b)
488            msk = scan.set_mask([400,500],[800,900], invert=True)
489            # masks the regions between 400 and 500
490            # and 800 and 900 in the unit 'channel'
491           
492        """
493        varlist = vars()
494        u = self._getcoordinfo()[0]
495        if self._vb:
496            if u == "": u = "channel"
497            print "The current mask window unit is", u
498        n = self.nchan()
499        data = self._getabcissa()
500        msk = zeros(n)
501        for window in args:
502            if (len(window) != 2 or window[0] > window[1] ):
503                print "A window needs to be defined as [min,max]"
504                return
505            for i in range(n):
506                if data[i] >= window[0] and data[i] < window[1]:
507                    msk[i] = 1
508        if kwargs.has_key('invert'):
509            if kwargs.get('invert'):
510                from numarray import logical_not
511                msk = logical_not(msk)
512        self._add_history("create_mask", varlist)
513        return msk
514   
515    def get_restfreqs(self):
516        """
517        Get the restfrequency(s) stored in this scantable.
518        The return value(s) are always of unit 'Hz'
519        Parameters:
520            none
521        Returns:
522            a list of doubles
523        """
524        return list(self._getrestfreqs())
525
526    def lines(self):
527        """
528        Print the list of known spectral lines
529        """
530        sdtable._lines(self)
531
532    def set_restfreqs(self, freqs=None, unit='Hz', lines=None, source=None,
533                      theif=None):
534        """
535        Select the restfrequency for the specified source and IF OR
536        replace for all IFs.  If the 'freqs' argument holds a scalar,
537        then that rest frequency will be applied to the selected
538        data (and added to the list of available rest frequencies).
539        In this way, you can set a rest frequency for each
540        source and IF combination.   If the 'freqs' argument holds
541        a vector, then it MUST be of length the number of IFs
542        (and the available restfrequencies will be replaced by
543        this vector).  In this case, *all* data ('source' and
544        'theif' are ignored) have the restfrequency set per IF according
545        to the corresponding value you give in the 'freqs' vector. 
546        E.g. 'freqs=[1e9,2e9]'  would mean IF 0 gets restfreq 1e9 and
547        IF 1 gets restfreq 2e9.
548
549        You can also specify the frequencies via known line names
550        in the argument 'lines'.  Use 'freqs' or 'lines'.  'freqs'
551        takes precedence. See the list of known names via function
552        scantable.lines()
553        Parameters:
554            freqs:   list of rest frequencies
555            unit:    unit for rest frequency (default 'Hz')
556            lines:   list of known spectral lines names (alternative to freqs).
557                     See possible list via scantable.lines()
558            source:  Source name (blank means all)
559            theif:   IF (-1 means all)
560        Example:
561            scan.set_restfreqs(freqs=1.4e9, source='NGC253', theif=2)
562            scan.set_restfreqs(freqs=[1.4e9,1.67e9])
563        """
564        varlist = vars()
565        if source is None:
566            source = ""
567        if theif is None:
568            theif = -1
569        t = type(freqs)
570        if t is int or t is float:
571           freqs = [freqs]
572        if freqs is None:
573           freqs = []
574        t = type(lines)
575        if t is str:
576           lines = [lines]
577        if lines is None:
578           lines = []
579        sdtable._setrestfreqs(self, freqs, unit, lines, source, theif)
580        self._add_history("set_restfreqs", varlist)
581       
582
583
584    def flag_spectrum(self, thebeam, theif, thepol):
585        """
586        This flags a selected spectrum in the scan 'for good'.
587        USE WITH CARE - not reversible.
588        Use masks for non-permanent exclusion of channels.
589        Parameters:
590            thebeam,theif,thepol:    all have to be explicitly
591                                     specified
592        Example:
593            scan.flag_spectrum(0,0,1)
594            flags the spectrum for Beam=0, IF=0, Pol=1
595        """
596        if (thebeam < self.nbeam() and
597            theif < self.nif() and
598            thepol < self.npol()):
599            sdtable.setbeam(self, thebeam)
600            sdtable.setif(self, theif)
601            sdtable.setpol(self, thepol)
602            sdtable._flag(self)
603            self._add_history("flag_spectrum", vars())
604        else:
605            print "Please specify a valid (Beam/IF/Pol)"
606        return
607
608    def plot(self, what='spectrum',col='Pol', panel=None):
609        """
610        Plot the spectra contained in the scan. Alternatively you can also
611        Plot Tsys vs Time
612        Parameters:
613            what:     a choice of 'spectrum' (default) or 'tsys'
614            col:      which out of Beams/IFs/Pols should be colour stacked
615            panel:    set up multiple panels, currently not working.
616        """
617        print "Warning! Not fully functional. Use plotter.plot() instead"
618       
619        validcol = {'Beam':self.nbeam(),'IF':self.nif(),'Pol':self.npol()}
620
621        validyax = ['spectrum','tsys']
622        from asap.asaplot import ASAPlot
623        if not self._p:
624            self._p = ASAPlot()
625            #print "Plotting not enabled"
626            #return
627        if self._p.is_dead:
628            del self._p
629            self._p = ASAPlot()
630        npan = 1
631        x = None
632        if what == 'tsys':
633            n = self.nrow()
634            if n < 2:
635                print "Only one integration. Can't plot."
636                return
637        self._p.hold()
638        self._p.clear()
639        if panel == 'Time':
640            npan = self.nrow()
641            self._p.set_panels(rows=npan)
642        xlab,ylab,tlab = None,None,None
643        self._vb = False
644        sel = self.get_cursor()       
645        for i in range(npan):
646            if npan > 1:
647                self._p.subplot(i)
648            for j in range(validcol[col]):
649                x = None
650                y = None
651                m = None
652                tlab = self._getsourcename(i)
653                import re
654                tlab = re.sub('_S','',tlab)
655                if col == 'Beam':
656                    self.setbeam(j)
657                elif col == 'IF':
658                    self.setif(j)
659                elif col == 'Pol':
660                    self.setpol(j)
661                if what == 'tsys':
662                    x = range(self.nrow())
663                    xlab = 'Time [pixel]'
664                    m = list(ones(len(x)))
665                    y = []
666                    ylab = r'$T_{sys}$'
667                    for k in range(len(x)):
668                        y.append(self._gettsys(k))
669                else:
670                    x,xlab = self.get_abcissa(i)
671                    y = self._getspectrum(i)
672                    ylab = r'Flux'
673                    m = self._getmask(i)
674                llab = col+' '+str(j)
675                self._p.set_line(label=llab)
676                self._p.plot(x,y,m)
677            self._p.set_axes('xlabel',xlab)
678            self._p.set_axes('ylabel',ylab)
679            self._p.set_axes('title',tlab)
680        self._p.release()
681        self.set_cursor(sel[0],sel[1],sel[2])
682        self._vb = rcParams['verbose']
683        return
684
685        print out
686
687    def _print_values(self, dat, label='', timestamps=[]):
688        d = dat['data']
689        a = dat['axes']
690        shp = d.getshape()
691        out = ''
692        for i in range(shp[3]):
693            out += '%s [%s]:\n' % (a[3],timestamps[i])
694            t = d[:,:,:,i]
695            for j in range(shp[0]):
696                if shp[0] > 1: out +=  ' %s[%d] ' % (a[0],j)
697                for k in range(shp[1]):
698                    if shp[1] > 1: out +=  ' %s[%d] ' % (a[1],k)
699                    for l in range(shp[2]):
700                        if shp[2] > 1: out +=  ' %s[%d] ' % (a[2],l)
701                        out += '= %3.3f\n' % (t[j,k,l])
702            out += "-"*80
703            out += "\n"
704        print "-"*80
705        print " ", label
706        print "-"*80
707        print out
708
709    def history(self):
710        hist = list(self._gethistory())
711        print "-"*80
712        for h in hist:
713            items = h.split("##")
714            date = items[0]
715            func = items[1]
716            items = items[2:]
717            print date           
718            print "Function: %s\n  Parameters:" % (func)
719            for i in items:
720                s = i.split("=")
721                print "   %s: %s" % (s[0],s[1])
722            print "-"*80
723        return
724
725    def _add_history(self, funcname, parameters):
726        # create date
727        sep = "##"
728        from datetime import datetime
729        dstr = datetime.now().strftime('%Y/%m/%d %H:%M:%S')
730        hist = dstr+sep
731        hist += funcname+sep#cdate+sep
732        if parameters.has_key('self'): del parameters['self']
733        for k,v in parameters.iteritems():
734            if type(v) is dict:
735                for k2,v2 in v.iteritems():
736                    hist += k2
737                    hist += "="
738                    if isinstance(v2,scantable):
739                        hist += 'scantable'
740                    elif k2 == 'mask':
741                        hist += str(self._zip_mask(v2))
742                    else:
743                        hist += str(v2)                   
744            else:
745                hist += k
746                hist += "="
747                if isinstance(v,scantable):
748                    hist += 'scantable'
749                elif k == 'mask':
750                    hist += str(self._zip_mask(v))
751                else:
752                    hist += str(v)
753            hist += sep
754        hist = hist[:-2] # remove trailing '##'
755        self._addhistory(hist)
756       
757
758    def _zip_mask(self, mask):
759        mask = list(mask)
760        i = 0
761        segments = []
762        while mask[i:].count(1):
763            i += mask[i:].index(1)
764            if mask[i:].count(0):
765                j = i + mask[i:].index(0)
766            else:
767                j = len(mask)               
768            segments.append([i,j])
769            i = j
770        return segments
Note: See TracBrowser for help on using the repository browser.