source: trunk/python/scantable.py@ 1002

Last change on this file since 1002 was 1001, checked in by mar637, 19 years ago

added flagging

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