source: trunk/python/scantable.py@ 1027

Last change on this file since 1027 was 1024, checked in by mar637, 19 years ago

Fixed create_mask to use start and end point. Added example

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 55.3 KB
Line 
1from asap._asap import Scantable
2from asap import rcParams
3from asap import print_log, asaplog
4from asap import selector
5from numarray import ones,zeros
6import sys
7
8class scantable(Scantable):
9 """
10 The ASAP container for scans
11 """
12
13 def __init__(self, filename, average=None, unit=None):
14 """
15 Create a scantable from a saved one or make a reference
16 Parameters:
17 filename: the name of an asap table on disk
18 or
19 the name of a rpfits/sdfits/ms file
20 (integrations within scans are auto averaged
21 and the whole file is read)
22 or
23 [advanced] a reference to an existing
24 scantable
25 average: average all integrations withinb a scan on read.
26 The default (True) is taken from .asaprc.
27 unit: brightness unit; must be consistent with K or Jy.
28 Over-rides the default selected by the reader
29 (input rpfits/sdfits/ms) or replaces the value
30 in existing scantables
31 """
32 if average is None:
33 average = rcParams['scantable.autoaverage']
34 varlist = vars()
35 from asap._asap import stmath
36 self._math = stmath()
37 if isinstance(filename, Scantable):
38 Scantable.__init__(self, filename)
39 else:
40 if isinstance(filename,str):
41 import os.path
42 filename = os.path.expandvars(filename)
43 filename = os.path.expanduser(filename)
44 if not os.path.exists(filename):
45 s = "File '%s' not found." % (filename)
46 if rcParams['verbose']:
47 asaplog.push(s)
48 print asaplog.pop().strip()
49 return
50 raise IOError(s)
51 if os.path.isdir(filename):
52 # crude check if asap table
53 if os.path.exists(filename+'/table.info'):
54 Scantable.__init__(self, filename, "memory")
55 if unit is not None:
56 self.set_fluxunit(unit)
57 self.set_freqframe(rcParams['scantable.freqframe'])
58 else:
59 msg = "The given file '%s'is not a valid asap table." % (filename)
60 if rcParams['verbose']:
61 print msg
62 return
63 else:
64 raise IOError(msg)
65 else:
66 self._fill([filename],unit, average)
67 elif (isinstance(filename,list) or isinstance(filename,tuple)) \
68 and isinstance(filename[-1], str):
69 self._fill(filename, unit, average)
70 print_log()
71
72 def save(self, name=None, format=None, overwrite=False):
73 """
74 Store the scantable on disk. This can be an asap (aips++) Table, SDFITS,
75 Image FITS or MS2 format.
76 Parameters:
77 name: the name of the outputfile. For format="FITS" this
78 is the directory file name into which all the files
79 will be written (default is 'asap_FITS'). For format
80 "ASCII" this is the root file name (data in 'name'.txt
81 and header in 'name'_header.txt)
82 format: an optional file format. Default is ASAP.
83 Allowed are - 'ASAP' (save as ASAP [aips++] Table),
84 'SDFITS' (save as SDFITS file)
85 'FITS' (saves each row as a FITS Image)
86 'ASCII' (saves as ascii text file)
87 'MS2' (saves as an aips++
88 MeasurementSet V2)
89 overwrite: If the file should be overwritten if it exists.
90 The default False is to return with warning
91 without writing the output. USE WITH CARE.
92 Example:
93 scan.save('myscan.asap')
94 scan.save('myscan.sdfits','SDFITS')
95 """
96 from os import path
97 if format is None: format = rcParams['scantable.save']
98 suffix = '.'+format.lower()
99 if name is None or name =="":
100 name = 'scantable'+suffix
101 from asap import asaplog
102 msg = "No filename given. Using default name %s..." % name
103 asaplog.push(msg)
104 name = path.expandvars(name)
105 if path.isfile(name) or path.isdir(name):
106 if not overwrite:
107 msg = "File %s exists." % name
108 if rcParams['verbose']:
109 print msg
110 return
111 else:
112 raise IOError(msg)
113 format2 = format.upper()
114 if format2 == 'ASAP':
115 self._save(name)
116 else:
117 from asap._asap import stwriter as stw
118 w = stw(format2)
119 w.write(self, name)
120 print_log()
121 return
122
123 def copy(self):
124 """
125 Return a copy of this scantable.
126 Parameters:
127 none
128 Example:
129 copiedscan = scan.copy()
130 """
131 sd = scantable(Scantable._copy(self))
132 return sd
133
134 def get_scan(self, scanid=None):
135 """
136 Return a specific scan (by scanno) or collection of scans (by
137 source name) in a new scantable.
138 Parameters:
139 scanid: a (list of) scanno or a source name, unix-style
140 patterns are accepted for source name matching, e.g.
141 '*_R' gets all 'ref scans
142 Example:
143 # get all scans containing the source '323p459'
144 newscan = scan.get_scan('323p459')
145 # get all 'off' scans
146 refscans = scan.get_scan('*_R')
147 # get a susbset of scans by scanno (as listed in scan.summary())
148 newscan = scan.get_scan([0,2,7,10])
149 """
150 if scanid is None:
151 if rcParams['verbose']:
152 print "Please specify a scan no or name to retrieve from the scantable"
153 return
154 else:
155 raise RuntimeError("No scan given")
156
157 try:
158 bsel = self.get_selection()
159 sel = selector()
160 if type(scanid) is str:
161 sel.set_name(scanid)
162 self.set_selection(bsel+sel)
163 scopy = self._copy()
164 self.set_selection(bsel)
165 return scantable(scopy)
166 elif type(scanid) is int:
167 sel.set_scans([scanid])
168 self.set_selection(bsel+sel)
169 scopy = self._copy()
170 self.set_selection(bsel)
171 return scantable(scopy)
172 elif type(scanid) is list:
173 sel.set_scans(scanid)
174 self.set_selection(sel)
175 scopy = self._copy()
176 self.set_selection(bsel)
177 return scantable(scopy)
178 else:
179 msg = "Illegal scanid type, use 'int' or 'list' if ints."
180 if rcParams['verbose']:
181 print msg
182 else:
183 raise TypeError(msg)
184 except RuntimeError:
185 if rcParams['verbose']: print "Couldn't find any match."
186 else: raise
187
188 def __str__(self):
189 return Scantable._summary(self,True)
190
191 def summary(self, filename=None):
192 """
193 Print a summary of the contents of this scantable.
194 Parameters:
195 filename: the name of a file to write the putput to
196 Default - no file output
197 verbose: print extra info such as the frequency table
198 The default (False) is taken from .asaprc
199 """
200 info = Scantable._summary(self, True)
201 #if verbose is None: verbose = rcParams['scantable.verbosesummary']
202 if filename is not None:
203 if filename is "":
204 filename = 'scantable_summary.txt'
205 from os.path import expandvars, isdir
206 filename = expandvars(filename)
207 if not isdir(filename):
208 data = open(filename, 'w')
209 data.write(info)
210 data.close()
211 else:
212 msg = "Illegal file name '%s'." % (filename)
213 if rcParams['verbose']:
214 print msg
215 else:
216 raise IOError(msg)
217 if rcParams['verbose']:
218 try:
219 from IPython.genutils import page as pager
220 except ImportError:
221 from pydoc import pager
222 pager(info)
223 else:
224 return info
225
226
227 def get_selection(self):
228 """
229 Get the selection object currently set on this scantable.
230 Parameters:
231 none
232 Example:
233 sel = scan.get_selection()
234 sel.set_ifs(0) # select IF 0
235 scan.set_selection(sel) # apply modified selection
236 """
237 return selector(self._getselection())
238
239 def set_selection(self, selection=selector()):
240 """
241 Select a subset of the data. All following operations on this scantable
242 are only applied to thi selection.
243 Parameters:
244 selection: a selector object (default unset the selection)
245 Examples:
246 sel = selector() # create a selection object
247 self.set_scans([0,3]) # select SCANNO 0 and 3
248 scan.set_selection(sel) # set the selection
249 scan.summary() # will only print summary of scanno 0 an 3
250 scan.set_selection() # unset the selection
251 """
252 self._setselection(selection)
253
254 def set_cursor(self, beam=0, IF=0, pol=0):
255 print "DEPRECATED - use set_selection"
256
257 def get_cursor(self):
258 print "DEPRECATED - use get_selection"
259
260 def stats(self, stat='stddev', mask=None):
261 """
262 Determine the specified statistic of the current beam/if/pol
263 Takes a 'mask' as an optional parameter to specify which
264 channels should be excluded.
265 Parameters:
266 stat: 'min', 'max', 'sumsq', 'sum', 'mean'
267 'var', 'stddev', 'avdev', 'rms', 'median'
268 mask: an optional mask specifying where the statistic
269 should be determined.
270 Example:
271 scan.set_unit('channel')
272 msk = scan.create_mask([100,200],[500,600])
273 scan.stats(stat='mean', mask=m)
274 """
275 from numarray import array,zeros,Float
276 if mask == None:
277 mask = []
278 axes = ['Beam','IF','Pol','Time']
279 if not self._check_ifs():
280 raise ValueError("Cannot apply mask as the IFs have different number of channels"
281 "Please use setselection() to select individual IFs")
282
283 statvals = self._math._stats(self, mask, stat)
284 out = ''
285 axes = []
286 for i in range(self.nrow()):
287 axis = []
288 axis.append(self.getscan(i))
289 axis.append(self.getbeam(i))
290 axis.append(self.getif(i))
291 axis.append(self.getpol(i))
292 axis.append(self.getcycle(i))
293 axes.append(axis)
294 tm = self._gettime(i)
295 src = self._getsourcename(i)
296 out += 'Scan[%d] (%s) ' % (axis[0], src)
297 out += 'Time[%s]:\n' % (tm)
298 if self.nbeam(-1) > 1: out += ' Beam[%d] ' % (axis[1])
299 if self.nif(-1) > 1: out += ' IF[%d] ' % (axis[2])
300 if self.npol(-1) > 1: out += ' Pol[%d] ' % (axis[3])
301 out += '= %3.3f\n' % (statvals[i])
302 out += "--------------------------------------------------\n"
303
304 if rcParams['verbose']:
305 print "--------------------------------------------------"
306 print " ",stat
307 print "--------------------------------------------------"
308 print out
309 retval = { 'axesnames': ['scanno','beamno','ifno','polno','cycleno'],
310 'axes' : axes,
311 'data': statvals}
312 return retval
313
314 def stddev(self,mask=None):
315 """
316 Determine the standard deviation of the current beam/if/pol
317 Takes a 'mask' as an optional parameter to specify which
318 channels should be excluded.
319 Parameters:
320 mask: an optional mask specifying where the standard
321 deviation should be determined.
322
323 Example:
324 scan.set_unit('channel')
325 msk = scan.create_mask([100,200],[500,600])
326 scan.stddev(mask=m)
327 """
328 return self.stats(stat='stddev',mask=mask);
329
330
331 def column_names(self):
332 """
333 Return a list of column names, which can be used for selection.
334 """
335 return list(Scantable.column_names(self))
336
337 def get_tsys(self):
338 """
339 Return the System temperatures.
340 Parameters:
341
342 Returns:
343 a list of Tsys values for the current selection
344 """
345
346 return self._row_callback(self._gettsys, "Tsys")
347
348 def _row_callback(self, callback, label):
349 axes = []
350 axesnames = ['scanno','beamno','ifno','polno','cycleno']
351 out = ""
352 outvec =[]
353 for i in range(self.nrow()):
354 axis = []
355 axis.append(self.getscan(i))
356 axis.append(self.getbeam(i))
357 axis.append(self.getif(i))
358 axis.append(self.getpol(i))
359 axis.append(self.getcycle(i))
360 axes.append(axis)
361 tm = self._gettime(i)
362 src = self._getsourcename(i)
363 out += 'Scan[%d] (%s) ' % (axis[0], src)
364 out += 'Time[%s]:\n' % (tm)
365 if self.nbeam(-1) > 1: out += ' Beam[%d] ' % (axis[1])
366 if self.nif(-1) > 1: out += ' IF[%d] ' % (axis[2])
367 if self.npol(-1) > 1: out += ' Pol[%d] ' % (axis[3])
368 outvec.append(callback(i))
369 out += '= %3.3f\n' % (outvec[i])
370 out += "--------------------------------------------------\n"
371 if rcParams['verbose']:
372 print "--------------------------------------------------"
373 print " %s" % (label)
374 print "--------------------------------------------------"
375 print out
376 retval = {'axesnames': axesnames, 'axes': axes, 'data': outvec}
377 return retval
378
379
380 def get_time(self, row=-1):
381 """
382 Get a list of time stamps for the observations.
383 Return a string for each integration in the scantable.
384 Parameters:
385 row: row no of integration. Default -1 return all rows
386 Example:
387 none
388 """
389 out = []
390 if row == -1:
391 for i in range(self.nrow()):
392 out.append(self._gettime(i))
393 return out
394 else:
395 if row < self.nrow():
396 return self._gettime(row)
397
398 def get_sourcename(self, row=-1):
399 """
400 Get a list source names for the observations.
401 Return a string for each integration in the scantable.
402 Parameters:
403 row: row no of integration. Default -1 return all rows
404 Example:
405 none
406 """
407 out = []
408 if row == -1:
409 return [self._getsourcename(i) for i in range(self.nrow())]
410 else:
411 if 0 <= row < self.nrow():
412 return self._getsourcename(row)
413
414 def get_elevation(self, row=-1):
415 """
416 Get a list of elevations for the observations.
417 Return a float for each integration in the scantable.
418 Parameters:
419 row: row no of integration. Default -1 return all rows
420 Example:
421 none
422 """
423 out = []
424 if row == -1:
425 return [self._getelevation(i) for i in range(self.nrow())]
426 else:
427 if 0 <= row < self.nrow():
428 return self._getelevation(row)
429
430 def get_azimuth(self, row=-1):
431 """
432 Get a list of azimuths for the observations.
433 Return a float for each integration in the scantable.
434 Parameters:
435 row: row no of integration. Default -1 return all rows
436 Example:
437 none
438 """
439 out = []
440 if row == -1:
441 return [self._getazimuth(i) for i in range(self.nrow())]
442 else:
443 if 0 <= row < self.nrow():
444 return self._getazimuth(row)
445
446 def get_parangle(self, row=-1):
447 """
448 Get a list of parallactic angles for the observations.
449 Return a float for each integration in the scantable.
450 Parameters:
451 row: row no of integration. Default -1 return all rows
452 Example:
453 none
454 """
455 out = []
456 if row == -1:
457 return [self._getparangle(i) for i in range(self.nrow())]
458 else:
459 if 0 <= row < self.nrow():
460 return self._getparangle(row)
461
462 def set_unit(self, unit='channel'):
463 """
464 Set the unit for all following operations on this scantable
465 Parameters:
466 unit: optional unit, default is 'channel'
467 one of '*Hz','km/s','channel', ''
468 """
469 varlist = vars()
470 if unit in ['','pixel', 'channel']:
471 unit = ''
472 inf = list(self._getcoordinfo())
473 inf[0] = unit
474 self._setcoordinfo(inf)
475 self._add_history("set_unit",varlist)
476
477 def set_instrument(self, instr):
478 """
479 Set the instrument for subsequent processing
480 Parameters:
481 instr: Select from 'ATPKSMB', 'ATPKSHOH', 'ATMOPRA',
482 'DSS-43' (Tid), 'CEDUNA', and 'HOBART'
483 """
484 self._setInstrument(instr)
485 self._add_history("set_instument",vars())
486 print_log()
487
488 def set_doppler(self, doppler='RADIO'):
489 """
490 Set the doppler for all following operations on this scantable.
491 Parameters:
492 doppler: One of 'RADIO', 'OPTICAL', 'Z', 'BETA', 'GAMMA'
493 """
494 varlist = vars()
495 inf = list(self._getcoordinfo())
496 inf[2] = doppler
497 self._setcoordinfo(inf)
498 self._add_history("set_doppler",vars())
499 print_log()
500
501 def set_freqframe(self, frame=None):
502 """
503 Set the frame type of the Spectral Axis.
504 Parameters:
505 frame: an optional frame type, default 'LSRK'. Valid frames are:
506 'REST','TOPO','LSRD','LSRK','BARY',
507 'GEO','GALACTO','LGROUP','CMB'
508 Examples:
509 scan.set_freqframe('BARY')
510 """
511 if frame is None: frame = rcParams['scantable.freqframe']
512 varlist = vars()
513 valid = ['REST','TOPO','LSRD','LSRK','BARY', \
514 'GEO','GALACTO','LGROUP','CMB']
515
516 if frame in valid:
517 inf = list(self._getcoordinfo())
518 inf[1] = frame
519 self._setcoordinfo(inf)
520 self._add_history("set_freqframe",varlist)
521 else:
522 msg = "Please specify a valid freq type. Valid types are:\n",valid
523 if rcParams['verbose']:
524 print msg
525 else:
526 raise TypeError(msg)
527 print_log()
528
529 def set_dirframe(self, frame=""):
530 """
531 Set the frame type of the Direction on the sky.
532 Parameters:
533 frame: an optional frame type, default ''. Valid frames are:
534 'J2000', 'B1950', 'GALACTIC'
535 Examples:
536 scan.set_dirframe('GALACTIC')
537 """
538 varlist = vars()
539 try:
540 Scantable.set_dirframe(self, frame)
541 except RuntimeError,msg:
542 if rcParams['verbose']:
543 print msg
544 else:
545 raise
546 self._add_history("set_dirframe",varlist)
547
548 def get_unit(self):
549 """
550 Get the default unit set in this scantable
551 Parameters:
552 Returns:
553 A unit string
554 """
555 inf = self._getcoordinfo()
556 unit = inf[0]
557 if unit == '': unit = 'channel'
558 return unit
559
560 def get_abcissa(self, rowno=0):
561 """
562 Get the abcissa in the current coordinate setup for the currently
563 selected Beam/IF/Pol
564 Parameters:
565 rowno: an optional row number in the scantable. Default is the
566 first row, i.e. rowno=0
567 Returns:
568 The abcissa values and it's format string (as a dictionary)
569 """
570 abc = self._getabcissa(rowno)
571 lbl = self._getabcissalabel(rowno)
572 print_log()
573 return abc, lbl
574
575 def flag(self, mask=[]):
576 """
577 Flag the selected data using an optional channel mask.
578 Parameters:
579 mask: an optional channel mask, created with create_mask. Default
580 (no mask) is all channels.
581 """
582 varlist = vars()
583 try:
584 self._flag(mask)
585 except RuntimeError,msg:
586 if rcParams['verbose']:
587 print msg
588 return
589 else: raise
590 self._add_history("flag", varlist)
591
592
593 def create_mask(self, *args, **kwargs):
594 """
595 Compute and return a mask based on [min,max] windows.
596 The specified windows are to be INCLUDED, when the mask is
597 applied.
598 Parameters:
599 [min,max],[min2,max2],...
600 Pairs of start/end points (inclusive)specifying the regions
601 to be masked
602 invert: optional argument. If specified as True,
603 return an inverted mask, i.e. the regions
604 specified are EXCLUDED
605 row: create the mask using the specified row for
606 unit conversions, default is row=0
607 only necessary if frequency varies over rows.
608 Example:
609 scan.set_unit('channel')
610 a)
611 msk = scan.create_mask([400,500],[800,900])
612 # masks everything outside 400 and 500
613 # and 800 and 900 in the unit 'channel'
614
615 b)
616 msk = scan.create_mask([400,500],[800,900], invert=True)
617 # masks the regions between 400 and 500
618 # and 800 and 900 in the unit 'channel'
619 c)
620 mask only channel 400
621 msk = scan.create_mask([400,400])
622 """
623 row = 0
624 if kwargs.has_key("row"):
625 row = kwargs.get("row")
626 data = self._getabcissa(row)
627 u = self._getcoordinfo()[0]
628 if rcParams['verbose']:
629 if u == "": u = "channel"
630 from asap import asaplog
631 msg = "The current mask window unit is %s" % u
632 if not self._check_ifs():
633 msg += "\nThis mask is only valid for IF=%d" % (self.getif(i))
634 asaplog.push(msg)
635 n = self.nchan()
636 msk = zeros(n)
637 # test if args is a 'list' or a 'normal *args - UGLY!!!
638
639 ws = (isinstance(args[-1][-1],int) or isinstance(args[-1][-1],float)) and args or args[0]
640 for window in ws:
641 if (len(window) != 2 or window[0] > window[1] ):
642 raise TypeError("A window needs to be defined as [min,max]")
643 for i in range(n):
644 if data[i] >= window[0] and data[i] <= window[1]:
645 msk[i] = 1
646 if kwargs.has_key('invert'):
647 if kwargs.get('invert'):
648 from numarray import logical_not
649 msk = logical_not(msk)
650 print_log()
651 return msk
652
653 def get_restfreqs(self):
654 """
655 Get the restfrequency(s) stored in this scantable.
656 The return value(s) are always of unit 'Hz'
657 Parameters:
658 none
659 Returns:
660 a list of doubles
661 """
662 return list(self._getrestfreqs())
663
664
665 def set_restfreqs(self, freqs=None, unit='Hz'):
666 """
667 Set or replace the restfrequency specified and
668 If the 'freqs' argument holds a scalar,
669 then that rest frequency will be applied to all the selected
670 data. If the 'freqs' argument holds
671 a vector, then it MUST be of equal or smaller length than
672 the number of IFs (and the available restfrequencies will be
673 replaced by this vector). In this case, *all* data have
674 the restfrequency set per IF according
675 to the corresponding value you give in the 'freqs' vector.
676 E.g. 'freqs=[1e9,2e9]' would mean IF 0 gets restfreq 1e9 and
677 IF 1 gets restfreq 2e9.
678 You can also specify the frequencies via known line names
679 from the built-in Lovas table.
680 Parameters:
681 freqs: list of rest frequency values or string idenitfiers
682 unit: unit for rest frequency (default 'Hz')
683
684 Example:
685 # set the given restfrequency for the whole table
686 scan.set_restfreqs(freqs=1.4e9)
687 # If thee number of IFs in the data is >= 2 the IF0 gets the first
688 # value IF1 the second...
689 scan.set_restfreqs(freqs=[1.4e9,1.67e9])
690 #set the given restfrequency for the whole table (by name)
691 scan.set_restfreqs(freqs="OH1667")
692
693 Note:
694 To do more sophisticate Restfrequency setting, e.g. on a
695 source and IF basis, use scantable.set_selection() before using
696 this function.
697 # provide your scantable is call scan
698 selection = selector()
699 selection.set_name("ORION*")
700 selection.set_ifs([1])
701 scan.set_selection(selection)
702 scan.set_restfreqs(freqs=86.6e9)
703
704 """
705 varlist = vars()
706
707 t = type(freqs)
708 if isinstance(freqs, int) or isinstance(freqs,float):
709 self._setrestfreqs(freqs, unit)
710 elif isinstance(freqs, list) or isinstance(freqs,tuple):
711 if isinstance(freqs[-1], int) or isinstance(freqs[-1],float):
712 sel = selector()
713 savesel = self._getselection()
714 for i in xrange(len(freqs)):
715 sel.set_ifs([i])
716 self._setselection(sel)
717 self._setrestfreqs(freqs[i], unit)
718 self._setselection(savesel)
719 elif isinstance(freqs[-1], str):
720 # not yet implemented
721 pass
722 else:
723 return
724 self._add_history("set_restfreqs", varlist)
725
726
727
728 def history(self):
729 hist = list(self._gethistory())
730 out = "-"*80
731 for h in hist:
732 if h.startswith("---"):
733 out += "\n"+h
734 else:
735 items = h.split("##")
736 date = items[0]
737 func = items[1]
738 items = items[2:]
739 out += "\n"+date+"\n"
740 out += "Function: %s\n Parameters:" % (func)
741 for i in items:
742 s = i.split("=")
743 out += "\n %s = %s" % (s[0],s[1])
744 out += "\n"+"-"*80
745 try:
746 from IPython.genutils import page as pager
747 except ImportError:
748 from pydoc import pager
749 pager(out)
750 return
751
752 #
753 # Maths business
754 #
755
756 def average_time(self, mask=None, scanav=False, weight='tint', align=False):
757 """
758 Return the (time) average of a scan, or apply it 'insitu'.
759 Note:
760 in channels only
761 The cursor of the output scan is set to 0.
762 Parameters:
763 one scan or comma separated scans
764 mask: an optional mask (only used for 'var' and 'tsys'
765 weighting)
766 scanav: True averages each scan separately
767 False (default) averages all scans together,
768 weight: Weighting scheme. 'none', 'var' (1/var(spec)
769 weighted), 'tsys' (1/Tsys**2 weighted), 'tint'
770 (integration time weighted) or 'tintsys' (Tint/Tsys**2).
771 The default is 'tint'
772 align: align the spectra in velocity before averaging. It takes
773 the time of the first spectrum as reference time.
774 Example:
775 # time average the scantable without using a mask
776 newscan = scan.average_time()
777 """
778 varlist = vars()
779 if weight is None: weight = 'TINT'
780 if mask is None: mask = ()
781 if scanav:
782 scanav = "SCAN"
783 else:
784 scanav = "NONE"
785 scan = (self,)
786 try:
787 if align:
788 scan = (self.freq_align(insitu=False),)
789 s = scantable(self._math._average(scan, mask, weight.upper(),
790 scanav))
791 except RuntimeError,msg:
792 if rcParams['verbose']:
793 print msg
794 return
795 else: raise
796 s._add_history("average_time",varlist)
797 print_log()
798 return s
799
800 def convert_flux(self, jyperk=None, eta=None, d=None, insitu=None):
801 """
802 Return a scan where all spectra are converted to either
803 Jansky or Kelvin depending upon the flux units of the scan table.
804 By default the function tries to look the values up internally.
805 If it can't find them (or if you want to over-ride), you must
806 specify EITHER jyperk OR eta (and D which it will try to look up
807 also if you don't set it). jyperk takes precedence if you set both.
808 Parameters:
809 jyperk: the Jy / K conversion factor
810 eta: the aperture efficiency
811 d: the geomtric diameter (metres)
812 insitu: if False a new scantable is returned.
813 Otherwise, the scaling is done in-situ
814 The default is taken from .asaprc (False)
815 allaxes: if True apply to all spectra. Otherwise
816 apply only to the selected (beam/pol/if)spectra only
817 The default is taken from .asaprc (True if none)
818 """
819 if insitu is None: insitu = rcParams['insitu']
820 self._math._setinsitu(insitu)
821 varlist = vars()
822 if jyperk is None: jyperk = -1.0
823 if d is None: d = -1.0
824 if eta is None: eta = -1.0
825 s = scantable(self._math._convertflux(self, d, eta, jyperk))
826 s._add_history("convert_flux", varlist)
827 print_log()
828 if insitu: self._assign(s)
829 else: return s
830
831 def gain_el(self, poly=None, filename="", method="linear", insitu=None):
832 """
833 Return a scan after applying a gain-elevation correction.
834 The correction can be made via either a polynomial or a
835 table-based interpolation (and extrapolation if necessary).
836 You specify polynomial coefficients, an ascii table or neither.
837 If you specify neither, then a polynomial correction will be made
838 with built in coefficients known for certain telescopes (an error
839 will occur if the instrument is not known).
840 The data and Tsys are *divided* by the scaling factors.
841 Parameters:
842 poly: Polynomial coefficients (default None) to compute a
843 gain-elevation correction as a function of
844 elevation (in degrees).
845 filename: The name of an ascii file holding correction factors.
846 The first row of the ascii file must give the column
847 names and these MUST include columns
848 "ELEVATION" (degrees) and "FACTOR" (multiply data
849 by this) somewhere.
850 The second row must give the data type of the
851 column. Use 'R' for Real and 'I' for Integer.
852 An example file would be
853 (actual factors are arbitrary) :
854
855 TIME ELEVATION FACTOR
856 R R R
857 0.1 0 0.8
858 0.2 20 0.85
859 0.3 40 0.9
860 0.4 60 0.85
861 0.5 80 0.8
862 0.6 90 0.75
863 method: Interpolation method when correcting from a table.
864 Values are "nearest", "linear" (default), "cubic"
865 and "spline"
866 insitu: if False a new scantable is returned.
867 Otherwise, the scaling is done in-situ
868 The default is taken from .asaprc (False)
869 """
870
871 if insitu is None: insitu = rcParams['insitu']
872 self._math._setinsitu(insitu)
873 varlist = vars()
874 if poly is None:
875 poly = ()
876 from os.path import expandvars
877 filename = expandvars(filename)
878 s = scantable(self._math._gainel(self, poly, filename, method))
879 s._add_history("gain_el", varlist)
880 print_log()
881 if insitu: self._assign(s)
882 else: return s
883
884 def freq_align(self, reftime=None, method='cubic', insitu=None):
885 """
886 Return a scan where all rows have been aligned in frequency/velocity.
887 The alignment frequency frame (e.g. LSRK) is that set by function
888 set_freqframe.
889 Parameters:
890 reftime: reference time to align at. By default, the time of
891 the first row of data is used.
892 method: Interpolation method for regridding the spectra.
893 Choose from "nearest", "linear", "cubic" (default)
894 and "spline"
895 insitu: if False a new scantable is returned.
896 Otherwise, the scaling is done in-situ
897 The default is taken from .asaprc (False)
898 """
899 if insitu is None: insitu = rcParams["insitu"]
900 self._math._setinsitu(insitu)
901 varlist = vars()
902 if reftime is None: reftime = ""
903 s = scantable(self._math._freq_align(self, reftime, method))
904 s._add_history("freq_align", varlist)
905 print_log()
906 if insitu: self._assign(s)
907 else: return s
908
909 def opacity(self, tau, insitu=None):
910 """
911 Apply an opacity correction. The data
912 and Tsys are multiplied by the correction factor.
913 Parameters:
914 tau: Opacity from which the correction factor is
915 exp(tau*ZD)
916 where ZD is the zenith-distance
917 insitu: if False a new scantable is returned.
918 Otherwise, the scaling is done in-situ
919 The default is taken from .asaprc (False)
920 """
921 if insitu is None: insitu = rcParams['insitu']
922 self._math._setinsitu(insitu)
923 varlist = vars()
924 s = scantable(self._math._opacity(self, tau))
925 s._add_history("opacity", varlist)
926 print_log()
927 if insitu: self._assign(s)
928 else: return s
929
930 def bin(self, width=5, insitu=None):
931 """
932 Return a scan where all spectra have been binned up.
933 width: The bin width (default=5) in pixels
934 insitu: if False a new scantable is returned.
935 Otherwise, the scaling is done in-situ
936 The default is taken from .asaprc (False)
937 """
938 if insitu is None: insitu = rcParams['insitu']
939 self._math._setinsitu(insitu)
940 varlist = vars()
941 s = scantable(self._math._bin(self, width))
942 s._add_history("bin",varlist)
943 print_log()
944 if insitu: self._assign(s)
945 else: return s
946
947
948 def resample(self, width=5, method='cubic', insitu=None):
949 """
950 Return a scan where all spectra have been binned up
951 width: The bin width (default=5) in pixels
952 method: Interpolation method when correcting from a table.
953 Values are "nearest", "linear", "cubic" (default)
954 and "spline"
955 insitu: if False a new scantable is returned.
956 Otherwise, the scaling is done in-situ
957 The default is taken from .asaprc (False)
958 """
959 if insitu is None: insitu = rcParams['insitu']
960 self._math._setinsitu(insitu)
961 varlist = vars()
962 s = scantable(self._math._resample(self, method, width))
963 s._add_history("resample",varlist)
964 print_log()
965 if insitu: self._assign(s)
966 else: return s
967
968
969 def average_pol(self, mask=None, weight='none'):
970 """
971 Average the Polarisations together.
972 Parameters:
973 mask: An optional mask defining the region, where the
974 averaging will be applied. The output will have all
975 specified points masked.
976 weight: Weighting scheme. 'none' (default), 'var' (1/var(spec)
977 weighted), or 'tsys' (1/Tsys**2 weighted)
978 """
979 varlist = vars()
980 if mask is None:
981 mask = ()
982 s = scantable(self._math._averagepol(self, mask, weight.upper()))
983 s._add_history("average_pol",varlist)
984 print_log()
985 return s
986
987 def convert_pol(self, poltype=None):
988 """
989 Convert the data to a different polarisation type.
990 Parameters:
991 poltype: The new polarisation type. Valid types are:
992 "linear", "stokes" and "circular"
993 """
994 varlist = vars()
995 try:
996 s = scantable(self._math._convertpol(self, poltype))
997 except RuntimeError,msg:
998 if rcParams['verbose']:
999 print msg
1000 return
1001 else:
1002 raise
1003 s._add_history("convert_pol",varlist)
1004 print_log()
1005 return s
1006
1007 def smooth(self, kernel="hanning", width=5.0, insitu=None):
1008 """
1009 Smooth the spectrum by the specified kernel (conserving flux).
1010 Parameters:
1011 scan: The input scan
1012 kernel: The type of smoothing kernel. Select from
1013 'hanning' (default), 'gaussian' and 'boxcar'.
1014 The first three characters are sufficient.
1015 width: The width of the kernel in pixels. For hanning this is
1016 ignored otherwise it defauls to 5 pixels.
1017 For 'gaussian' it is the Full Width Half
1018 Maximum. For 'boxcar' it is the full width.
1019 insitu: if False a new scantable is returned.
1020 Otherwise, the scaling is done in-situ
1021 The default is taken from .asaprc (False)
1022 Example:
1023 none
1024 """
1025 if insitu is None: insitu = rcParams['insitu']
1026 self._math._setinsitu(insitu)
1027 varlist = vars()
1028 s = scantable(self._math._smooth(self,kernel,width))
1029 s._add_history("smooth", varlist)
1030 print_log()
1031 if insitu: self._assign(s)
1032 else: return s
1033
1034
1035 def poly_baseline(self, mask=None, order=0, insitu=None):
1036 """
1037 Return a scan which has been baselined (all rows) by a polynomial.
1038 Parameters:
1039 scan: a scantable
1040 mask: an optional mask
1041 order: the order of the polynomial (default is 0)
1042 insitu: if False a new scantable is returned.
1043 Otherwise, the scaling is done in-situ
1044 The default is taken from .asaprc (False)
1045 allaxes: If True (default) apply to all spectra. Otherwise
1046 apply only to the selected (beam/pol/if)spectra only
1047 The default is taken from .asaprc (True if none)
1048 Example:
1049 # return a scan baselined by a third order polynomial,
1050 # not using a mask
1051 bscan = scan.poly_baseline(order=3)
1052 """
1053 if insitu is None: insitu = rcParams['insitu']
1054 varlist = vars()
1055 if mask is None:
1056 from numarray import ones
1057 mask = list(ones(self.nchan(-1)))
1058 from asap.asapfitter import fitter
1059 f = fitter()
1060 f.set_scan(self, mask)
1061 f.set_function(poly=order)
1062 s = f.auto_fit(insitu)
1063 s._add_history("poly_baseline", varlist)
1064 print_log()
1065 if insitu: self._assign(s)
1066 else: return s
1067
1068 def auto_poly_baseline(self, mask=[], edge=(0,0), order=0,
1069 threshold=3, insitu=None):
1070 """
1071 Return a scan which has been baselined (all rows) by a polynomial.
1072 Spectral lines are detected first using linefinder and masked out
1073 to avoid them affecting the baseline solution.
1074
1075 Parameters:
1076 mask: an optional mask retreived from scantable
1077 edge: an optional number of channel to drop at
1078 the edge of spectrum. If only one value is
1079 specified, the same number will be dropped from
1080 both sides of the spectrum. Default is to keep
1081 all channels. Nested tuples represent individual
1082 edge selection for different IFs (a number of spectral
1083 channels can be different)
1084 order: the order of the polynomial (default is 0)
1085 threshold: the threshold used by line finder. It is better to
1086 keep it large as only strong lines affect the
1087 baseline solution.
1088 insitu: if False a new scantable is returned.
1089 Otherwise, the scaling is done in-situ
1090 The default is taken from .asaprc (False)
1091
1092 Example:
1093 scan2=scan.auto_poly_baseline(order=7)
1094 """
1095 if insitu is None: insitu = rcParams['insitu']
1096 varlist = vars()
1097 from asap.asapfitter import fitter
1098 from asap.asaplinefind import linefinder
1099 from asap import _is_sequence_or_number as _is_valid
1100
1101 # check whether edge is set up for each IF individually
1102 individualEdge = False;
1103 if len(edge)>1:
1104 if isinstance(edge[0],list) or isinstance(edge[0],tuple):
1105 individualEdge = True;
1106
1107 if not _is_valid(edge, int) and not individualEdge:
1108 raise ValueError, "Parameter 'edge' has to be an integer or a \
1109 pair of integers specified as a tuple. Nested tuples are allowed \
1110 to make individual selection for different IFs."
1111
1112 curedge = (0,0)
1113 if individualEdge:
1114 for edge_par in edge:
1115 if not _is_valid(edge,int):
1116 raise ValueError, "Each element of the 'edge' tuple has \
1117 to be a pair of integers or an integer."
1118 else:
1119 curedge = edge;
1120
1121 # setup fitter
1122 f = fitter()
1123 f.set_function(poly=order)
1124
1125 # setup line finder
1126 fl=linefinder()
1127 fl.set_options(threshold=threshold)
1128
1129 if not insitu:
1130 workscan=self.copy()
1131 else:
1132 workscan=self
1133
1134 fl.set_scan(workscan)
1135
1136 rows=range(workscan.nrow())
1137 from asap import asaplog
1138 asaplog.push("Processing:")
1139 for r in rows:
1140 msg = " Scan[%d] Beam[%d] IF[%d] Pol[%d] Cycle[%d]" % (workscan.getscan(r),workscan.getbeam(r),workscan.getif(r),workscan.getpol(r), workscan.getcycle(r))
1141 asaplog.push(msg, False)
1142
1143 # figure out edge parameter
1144 if individualEdge:
1145 if len(edge)>=workscan.getif(r):
1146 raise RuntimeError, "Number of edge elements appear to be less than the number of IFs"
1147 curedge = edge[workscan.getif(r)]
1148
1149 # setup line finder
1150 fl.find_lines(r,mask,curedge)
1151 f.set_scan(workscan, fl.get_mask())
1152 f.x = workscan._getabcissa(r)
1153 f.y = workscan._getspectrum(r)
1154 f.data = None
1155 f.fit()
1156 x = f.get_parameters()
1157 workscan._setspectrum(f.fitter.getresidual(), r)
1158 workscan._add_history("poly_baseline", varlist)
1159 if insitu:
1160 self._assign(workscan)
1161 else:
1162 return workscan
1163
1164 def rotate_linpolphase(self, angle):
1165 """
1166 Rotate the phase of the complex polarization O=Q+iU correlation.
1167 This is always done in situ in the raw data. So if you call this
1168 function more than once then each call rotates the phase further.
1169 Parameters:
1170 angle: The angle (degrees) to rotate (add) by.
1171 Examples:
1172 scan.rotate_linpolphase(2.3)
1173 """
1174 varlist = vars()
1175 self._math._rotate_linpolphase(self, angle)
1176 self._add_history("rotate_linpolphase", varlist)
1177 print_log()
1178 return
1179
1180
1181 def rotate_xyphase(self, angle):
1182 """
1183 Rotate the phase of the XY correlation. This is always done in situ
1184 in the data. So if you call this function more than once
1185 then each call rotates the phase further.
1186 Parameters:
1187 angle: The angle (degrees) to rotate (add) by.
1188 Examples:
1189 scan.rotate_xyphase(2.3)
1190 """
1191 varlist = vars()
1192 self._math._rotate_xyphase(self, angle)
1193 self._add_history("rotate_xyphase", varlist)
1194 print_log()
1195 return
1196
1197 def swap_linears(self):
1198 """
1199 Swap the linear polarisations XX and YY
1200 """
1201 varlist = vars()
1202 self._math._swap_linears(self)
1203 self._add_history("swap_linears", varlist)
1204 print_log()
1205 return
1206
1207 def invert_phase(self):
1208 """
1209 Invert the phase of the complex polarisation
1210 """
1211 varlist = vars()
1212 self._math._invert_phase(self)
1213 self._add_history("invert_phase", varlist)
1214 print_log()
1215 return
1216
1217 def add(self, offset, insitu=None):
1218 """
1219 Return a scan where all spectra have the offset added
1220 Parameters:
1221 offset: the offset
1222 insitu: if False a new scantable is returned.
1223 Otherwise, the scaling is done in-situ
1224 The default is taken from .asaprc (False)
1225 """
1226 if insitu is None: insitu = rcParams['insitu']
1227 self._math._setinsitu(insitu)
1228 varlist = vars()
1229 s = scantable(self._math._unaryop(self, offset, "ADD", False))
1230 s._add_history("add",varlist)
1231 print_log()
1232 if insitu:
1233 self._assign(s)
1234 else:
1235 return s
1236
1237 def scale(self, factor, tsys=True, insitu=None,):
1238 """
1239 Return a scan where all spectra are scaled by the give 'factor'
1240 Parameters:
1241 factor: the scaling factor
1242 insitu: if False a new scantable is returned.
1243 Otherwise, the scaling is done in-situ
1244 The default is taken from .asaprc (False)
1245 tsys: if True (default) then apply the operation to Tsys
1246 as well as the data
1247 """
1248 if insitu is None: insitu = rcParams['insitu']
1249 self._math._setinsitu(insitu)
1250 varlist = vars()
1251 s = scantable(self._math._unaryop(self, factor, "MUL", tsys))
1252 s._add_history("scale",varlist)
1253 print_log()
1254 if insitu:
1255 self._assign(s)
1256 else:
1257 return s
1258
1259 def auto_quotient(self, mode='time', preserve=True):
1260 """
1261 This function allows to build quotients automatically.
1262 It assumes the observation to have the same numer of
1263 "ons" and "offs"
1264 It will support "closest off in time" in the future
1265 Parameters:
1266 mode: the on/off detection mode; 'suffix' (default)
1267 'suffix' identifies 'off' scans by the
1268 trailing '_R' (Mopra/Parkes) or
1269 '_e'/'_w' (Tid)
1270 preserve: you can preserve (default) the continuum or
1271 remove it. The equations used are
1272 preserve: Output = Toff * (on/off) - Toff
1273 remove: Output = Tref * (on/off) - Ton
1274 """
1275 modes = ["time"]
1276 if not mode in modes:
1277 msg = "please provide valid mode. Valid modes are %s" % (modes)
1278 raise ValueError(msg)
1279 varlist = vars()
1280 s = scantable(self._math._quotient(self, mode, preserve))
1281 s._add_history("auto_quotient",varlist)
1282 print_log()
1283 return s
1284
1285
1286
1287
1288 def freq_switch(self, insitu=None):
1289 """
1290 Apply frequency switching to the data.
1291 Parameters:
1292 insitu: if False a new scantable is returned.
1293 Otherwise, the swictching is done in-situ
1294 The default is taken from .asaprc (False)
1295 Example:
1296 none
1297 """
1298 if insitu is None: insitu = rcParams['insitu']
1299 self._math._setinsitu(insitu)
1300 varlist = vars()
1301 s = scantable(self._math._freqswitch(self))
1302 s._add_history("freq_switch",varlist)
1303 print_log()
1304 if insitu: self._assign(s)
1305 else: return s
1306
1307 def recalc_azel(self):
1308 """
1309 Recalculate the azimuth and elevation for each position.
1310 Parameters:
1311 none
1312 Example:
1313 """
1314 varlist = vars()
1315 self._recalcazel()
1316 self._add_history("recalc_azel", varlist)
1317 print_log()
1318 return
1319
1320 def __add__(self, other):
1321 varlist = vars()
1322 s = None
1323 if isinstance(other, scantable):
1324 print "scantable + scantable NYI"
1325 return
1326 elif isinstance(other, float):
1327 s = scantable(self._math._unaryop(self, other, "ADD", False))
1328 else:
1329 raise TypeError("Other input is not a scantable or float value")
1330 s._add_history("operator +", varlist)
1331 print_log()
1332 return s
1333
1334 def __sub__(self, other):
1335 """
1336 implicit on all axes and on Tsys
1337 """
1338 varlist = vars()
1339 s = None
1340 if isinstance(other, scantable):
1341 print "scantable - scantable NYI"
1342 return
1343 elif isinstance(other, float):
1344 s = scantable(self._math._unaryop(self, other, "SUB", False))
1345 else:
1346 raise TypeError("Other input is not a scantable or float value")
1347 s._add_history("operator -", varlist)
1348 print_log()
1349 return s
1350
1351 def __mul__(self, other):
1352 """
1353 implicit on all axes and on Tsys
1354 """
1355 varlist = vars()
1356 s = None
1357 if isinstance(other, scantable):
1358 print "scantable * scantable NYI"
1359 return
1360 elif isinstance(other, float):
1361 s = scantable(self._math._unaryop(self, other, "MUL", False))
1362 else:
1363 raise TypeError("Other input is not a scantable or float value")
1364 s._add_history("operator *", varlist)
1365 print_log()
1366 return s
1367
1368
1369 def __div__(self, other):
1370 """
1371 implicit on all axes and on Tsys
1372 """
1373 varlist = vars()
1374 s = None
1375 if isinstance(other, scantable):
1376 print "scantable / scantable NYI"
1377 return
1378 elif isinstance(other, float):
1379 if other == 0.0:
1380 raise ZeroDivisionError("Dividing by zero is not recommended")
1381 s = scantable(self._math._unaryop(self, other, "DIV", False))
1382 else:
1383 raise TypeError("Other input is not a scantable or float value")
1384 s._add_history("operator /", varlist)
1385 print_log()
1386 return s
1387
1388 def get_fit(self, row=0):
1389 """
1390 Print or return the stored fits for a row in the scantable
1391 Parameters:
1392 row: the row which the fit has been applied to.
1393 """
1394 if row > self.nrow():
1395 return
1396 from asap.asapfit import asapfit
1397 fit = asapfit(self._getfit(row))
1398 if rcParams['verbose']:
1399 print fit
1400 return
1401 else:
1402 return fit.as_dict()
1403
1404 def _add_history(self, funcname, parameters):
1405 # create date
1406 sep = "##"
1407 from datetime import datetime
1408 dstr = datetime.now().strftime('%Y/%m/%d %H:%M:%S')
1409 hist = dstr+sep
1410 hist += funcname+sep#cdate+sep
1411 if parameters.has_key('self'): del parameters['self']
1412 for k,v in parameters.iteritems():
1413 if type(v) is dict:
1414 for k2,v2 in v.iteritems():
1415 hist += k2
1416 hist += "="
1417 if isinstance(v2,scantable):
1418 hist += 'scantable'
1419 elif k2 == 'mask':
1420 if isinstance(v2,list) or isinstance(v2,tuple):
1421 hist += str(self._zip_mask(v2))
1422 else:
1423 hist += str(v2)
1424 else:
1425 hist += str(v2)
1426 else:
1427 hist += k
1428 hist += "="
1429 if isinstance(v,scantable):
1430 hist += 'scantable'
1431 elif k == 'mask':
1432 if isinstance(v,list) or isinstance(v,tuple):
1433 hist += str(self._zip_mask(v))
1434 else:
1435 hist += str(v)
1436 else:
1437 hist += str(v)
1438 hist += sep
1439 hist = hist[:-2] # remove trailing '##'
1440 self._addhistory(hist)
1441
1442
1443 def _zip_mask(self, mask):
1444 mask = list(mask)
1445 i = 0
1446 segments = []
1447 while mask[i:].count(1):
1448 i += mask[i:].index(1)
1449 if mask[i:].count(0):
1450 j = i + mask[i:].index(0)
1451 else:
1452 j = len(mask)
1453 segments.append([i,j])
1454 i = j
1455 return segments
1456
1457 def _get_ordinate_label(self):
1458 fu = "("+self.get_fluxunit()+")"
1459 import re
1460 lbl = "Intensity"
1461 if re.match(".K.",fu):
1462 lbl = "Brightness Temperature "+ fu
1463 elif re.match(".Jy.",fu):
1464 lbl = "Flux density "+ fu
1465 return lbl
1466
1467 def _check_ifs(self):
1468 nchans = [self.nchan(i) for i in range(self.nif(-1))]
1469 nchans = filter(lambda t: t > 0, nchans)
1470 return (sum(nchans)/len(nchans) == nchans[0])
1471
1472 def _fill(self, names, unit, average):
1473 import os
1474 varlist = vars()
1475 from asap._asap import stfiller
1476 first = True
1477 fullnames = []
1478 for name in names:
1479 name = os.path.expandvars(name)
1480 name = os.path.expanduser(name)
1481 if not os.path.exists(name):
1482 msg = "File '%s' does not exists" % (name)
1483 if rcParams['verbose']:
1484 asaplog.push(msg)
1485 print asaplog.pop().strip()
1486 return
1487 raise IOError(msg)
1488 fullnames.append(name)
1489 if average:
1490 asaplog.push('Auto averaging integrations')
1491 for name in fullnames:
1492 r = stfiller()
1493 msg = "Importing %s..." % (name)
1494 asaplog.push(msg,False)
1495 print_log()
1496 r._open(name,-1,-1)
1497 r._read()
1498 tbl = r._getdata()
1499 if average:
1500 tbl = self._math._average((tbl,),(),'NONE','SCAN')
1501 #tbl = tbl2
1502 if not first:
1503 tbl = self._math._merge([self, tbl])
1504 #tbl = tbl2
1505 Scantable.__init__(self, tbl)
1506 r._close()
1507 del r,tbl
1508 first = False
1509 if unit is not None:
1510 self.set_fluxunit(unit)
1511 self.set_freqframe(rcParams['scantable.freqframe'])
1512 #self._add_history("scantable", varlist)
1513
Note: See TracBrowser for help on using the repository browser.