source: trunk/python/scantable.py@ 1023

Last change on this file since 1023 was 1010, checked in by mar637, 19 years ago

Fix to Ticket #17 - average_pol not working.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 55.2 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 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
611 a)
612 msk = scan.set_mask([400,500],[800,900])
613 # masks everything outside 400 and 500
614 # and 800 and 900 in the unit 'channel'
615
616 b)
617 msk = scan.set_mask([400,500],[800,900], invert=True)
618 # masks the regions between 400 and 500
619 # and 800 and 900 in the unit 'channel'
620
621 """
622 row = 0
623 if kwargs.has_key("row"):
624 row = kwargs.get("row")
625 data = self._getabcissa(row)
626 u = self._getcoordinfo()[0]
627 if rcParams['verbose']:
628 if u == "": u = "channel"
629 from asap import asaplog
630 msg = "The current mask window unit is %s" % u
631 if not self._check_ifs():
632 msg += "\nThis mask is only valid for IF=%d" % (self.getif(i))
633 asaplog.push(msg)
634 n = self.nchan()
635 msk = zeros(n)
636 # test if args is a 'list' or a 'normal *args - UGLY!!!
637
638 ws = (isinstance(args[-1][-1],int) or isinstance(args[-1][-1],float)) and args or args[0]
639 for window in ws:
640 if (len(window) != 2 or window[0] > window[1] ):
641 raise TypeError("A window needs to be defined as [min,max]")
642 for i in range(n):
643 if data[i] >= window[0] and data[i] < window[1]:
644 msk[i] = 1
645 if kwargs.has_key('invert'):
646 if kwargs.get('invert'):
647 from numarray import logical_not
648 msk = logical_not(msk)
649 print_log()
650 return msk
651
652 def get_restfreqs(self):
653 """
654 Get the restfrequency(s) stored in this scantable.
655 The return value(s) are always of unit 'Hz'
656 Parameters:
657 none
658 Returns:
659 a list of doubles
660 """
661 return list(self._getrestfreqs())
662
663
664 def set_restfreqs(self, freqs=None, unit='Hz'):
665 """
666 Set or replace the restfrequency specified and
667 If the 'freqs' argument holds a scalar,
668 then that rest frequency will be applied to all the selected
669 data. If the 'freqs' argument holds
670 a vector, then it MUST be of equal or smaller length than
671 the number of IFs (and the available restfrequencies will be
672 replaced by this vector). In this case, *all* data have
673 the restfrequency set per IF according
674 to the corresponding value you give in the 'freqs' vector.
675 E.g. 'freqs=[1e9,2e9]' would mean IF 0 gets restfreq 1e9 and
676 IF 1 gets restfreq 2e9.
677 You can also specify the frequencies via known line names
678 from the built-in Lovas table.
679 Parameters:
680 freqs: list of rest frequency values or string idenitfiers
681 unit: unit for rest frequency (default 'Hz')
682
683 Example:
684 # set the given restfrequency for the whole table
685 scan.set_restfreqs(freqs=1.4e9)
686 # If thee number of IFs in the data is >= 2 the IF0 gets the first
687 # value IF1 the second...
688 scan.set_restfreqs(freqs=[1.4e9,1.67e9])
689 #set the given restfrequency for the whole table (by name)
690 scan.set_restfreqs(freqs="OH1667")
691
692 Note:
693 To do more sophisticate Restfrequency setting, e.g. on a
694 source and IF basis, use scantable.set_selection() before using
695 this function.
696 # provide your scantable is call scan
697 selection = selector()
698 selection.set_name("ORION*")
699 selection.set_ifs([1])
700 scan.set_selection(selection)
701 scan.set_restfreqs(freqs=86.6e9)
702
703 """
704 varlist = vars()
705
706 t = type(freqs)
707 if isinstance(freqs, int) or isinstance(freqs,float):
708 self._setrestfreqs(freqs, unit)
709 elif isinstance(freqs, list) or isinstance(freqs,tuple):
710 if isinstance(freqs[-1], int) or isinstance(freqs[-1],float):
711 sel = selector()
712 savesel = self._getselection()
713 for i in xrange(len(freqs)):
714 sel.set_ifs([i])
715 self._setselection(sel)
716 self._setrestfreqs(freqs[i], unit)
717 self._setselection(savesel)
718 elif isinstance(freqs[-1], str):
719 # not yet implemented
720 pass
721 else:
722 return
723 self._add_history("set_restfreqs", varlist)
724
725
726
727 def history(self):
728 hist = list(self._gethistory())
729 out = "-"*80
730 for h in hist:
731 if h.startswith("---"):
732 out += "\n"+h
733 else:
734 items = h.split("##")
735 date = items[0]
736 func = items[1]
737 items = items[2:]
738 out += "\n"+date+"\n"
739 out += "Function: %s\n Parameters:" % (func)
740 for i in items:
741 s = i.split("=")
742 out += "\n %s = %s" % (s[0],s[1])
743 out += "\n"+"-"*80
744 try:
745 from IPython.genutils import page as pager
746 except ImportError:
747 from pydoc import pager
748 pager(out)
749 return
750
751 #
752 # Maths business
753 #
754
755 def average_time(self, mask=None, scanav=False, weight='tint', align=False):
756 """
757 Return the (time) average of a scan, or apply it 'insitu'.
758 Note:
759 in channels only
760 The cursor of the output scan is set to 0.
761 Parameters:
762 one scan or comma separated scans
763 mask: an optional mask (only used for 'var' and 'tsys'
764 weighting)
765 scanav: True averages each scan separately
766 False (default) averages all scans together,
767 weight: Weighting scheme. 'none', 'var' (1/var(spec)
768 weighted), 'tsys' (1/Tsys**2 weighted), 'tint'
769 (integration time weighted) or 'tintsys' (Tint/Tsys**2).
770 The default is 'tint'
771 align: align the spectra in velocity before averaging. It takes
772 the time of the first spectrum as reference time.
773 Example:
774 # time average the scantable without using a mask
775 newscan = scan.average_time()
776 """
777 varlist = vars()
778 if weight is None: weight = 'TINT'
779 if mask is None: mask = ()
780 if scanav:
781 scanav = "SCAN"
782 else:
783 scanav = "NONE"
784 scan = (self,)
785 try:
786 if align:
787 scan = (self.freq_align(insitu=False),)
788 s = scantable(self._math._average(scan, mask, weight.upper(),
789 scanav))
790 except RuntimeError,msg:
791 if rcParams['verbose']:
792 print msg
793 return
794 else: raise
795 s._add_history("average_time",varlist)
796 print_log()
797 return s
798
799 def convert_flux(self, jyperk=None, eta=None, d=None, insitu=None):
800 """
801 Return a scan where all spectra are converted to either
802 Jansky or Kelvin depending upon the flux units of the scan table.
803 By default the function tries to look the values up internally.
804 If it can't find them (or if you want to over-ride), you must
805 specify EITHER jyperk OR eta (and D which it will try to look up
806 also if you don't set it). jyperk takes precedence if you set both.
807 Parameters:
808 jyperk: the Jy / K conversion factor
809 eta: the aperture efficiency
810 d: the geomtric diameter (metres)
811 insitu: if False a new scantable is returned.
812 Otherwise, the scaling is done in-situ
813 The default is taken from .asaprc (False)
814 allaxes: if True apply to all spectra. Otherwise
815 apply only to the selected (beam/pol/if)spectra only
816 The default is taken from .asaprc (True if none)
817 """
818 if insitu is None: insitu = rcParams['insitu']
819 self._math._setinsitu(insitu)
820 varlist = vars()
821 if jyperk is None: jyperk = -1.0
822 if d is None: d = -1.0
823 if eta is None: eta = -1.0
824 s = scantable(self._math._convertflux(self, d, eta, jyperk))
825 s._add_history("convert_flux", varlist)
826 print_log()
827 if insitu: self._assign(s)
828 else: return s
829
830 def gain_el(self, poly=None, filename="", method="linear", insitu=None):
831 """
832 Return a scan after applying a gain-elevation correction.
833 The correction can be made via either a polynomial or a
834 table-based interpolation (and extrapolation if necessary).
835 You specify polynomial coefficients, an ascii table or neither.
836 If you specify neither, then a polynomial correction will be made
837 with built in coefficients known for certain telescopes (an error
838 will occur if the instrument is not known).
839 The data and Tsys are *divided* by the scaling factors.
840 Parameters:
841 poly: Polynomial coefficients (default None) to compute a
842 gain-elevation correction as a function of
843 elevation (in degrees).
844 filename: The name of an ascii file holding correction factors.
845 The first row of the ascii file must give the column
846 names and these MUST include columns
847 "ELEVATION" (degrees) and "FACTOR" (multiply data
848 by this) somewhere.
849 The second row must give the data type of the
850 column. Use 'R' for Real and 'I' for Integer.
851 An example file would be
852 (actual factors are arbitrary) :
853
854 TIME ELEVATION FACTOR
855 R R R
856 0.1 0 0.8
857 0.2 20 0.85
858 0.3 40 0.9
859 0.4 60 0.85
860 0.5 80 0.8
861 0.6 90 0.75
862 method: Interpolation method when correcting from a table.
863 Values are "nearest", "linear" (default), "cubic"
864 and "spline"
865 insitu: if False a new scantable is returned.
866 Otherwise, the scaling is done in-situ
867 The default is taken from .asaprc (False)
868 """
869
870 if insitu is None: insitu = rcParams['insitu']
871 self._math._setinsitu(insitu)
872 varlist = vars()
873 if poly is None:
874 poly = ()
875 from os.path import expandvars
876 filename = expandvars(filename)
877 s = scantable(self._math._gainel(self, poly, filename, method))
878 s._add_history("gain_el", varlist)
879 print_log()
880 if insitu: self._assign(s)
881 else: return s
882
883 def freq_align(self, reftime=None, method='cubic', insitu=None):
884 """
885 Return a scan where all rows have been aligned in frequency/velocity.
886 The alignment frequency frame (e.g. LSRK) is that set by function
887 set_freqframe.
888 Parameters:
889 reftime: reference time to align at. By default, the time of
890 the first row of data is used.
891 method: Interpolation method for regridding the spectra.
892 Choose from "nearest", "linear", "cubic" (default)
893 and "spline"
894 insitu: if False a new scantable is returned.
895 Otherwise, the scaling is done in-situ
896 The default is taken from .asaprc (False)
897 """
898 if insitu is None: insitu = rcParams["insitu"]
899 self._math._setinsitu(insitu)
900 varlist = vars()
901 if reftime is None: reftime = ""
902 s = scantable(self._math._freq_align(self, reftime, method))
903 s._add_history("freq_align", varlist)
904 print_log()
905 if insitu: self._assign(s)
906 else: return s
907
908 def opacity(self, tau, insitu=None):
909 """
910 Apply an opacity correction. The data
911 and Tsys are multiplied by the correction factor.
912 Parameters:
913 tau: Opacity from which the correction factor is
914 exp(tau*ZD)
915 where ZD is the zenith-distance
916 insitu: if False a new scantable is returned.
917 Otherwise, the scaling is done in-situ
918 The default is taken from .asaprc (False)
919 """
920 if insitu is None: insitu = rcParams['insitu']
921 self._math._setinsitu(insitu)
922 varlist = vars()
923 s = scantable(self._math._opacity(self, tau))
924 s._add_history("opacity", varlist)
925 print_log()
926 if insitu: self._assign(s)
927 else: return s
928
929 def bin(self, width=5, insitu=None):
930 """
931 Return a scan where all spectra have been binned up.
932 width: The bin width (default=5) in pixels
933 insitu: if False a new scantable is returned.
934 Otherwise, the scaling is done in-situ
935 The default is taken from .asaprc (False)
936 """
937 if insitu is None: insitu = rcParams['insitu']
938 self._math._setinsitu(insitu)
939 varlist = vars()
940 s = scantable(self._math._bin(self, width))
941 s._add_history("bin",varlist)
942 print_log()
943 if insitu: self._assign(s)
944 else: return s
945
946
947 def resample(self, width=5, method='cubic', insitu=None):
948 """
949 Return a scan where all spectra have been binned up
950 width: The bin width (default=5) in pixels
951 method: Interpolation method when correcting from a table.
952 Values are "nearest", "linear", "cubic" (default)
953 and "spline"
954 insitu: if False a new scantable is returned.
955 Otherwise, the scaling is done in-situ
956 The default is taken from .asaprc (False)
957 """
958 if insitu is None: insitu = rcParams['insitu']
959 self._math._setinsitu(insitu)
960 varlist = vars()
961 s = scantable(self._math._resample(self, method, width))
962 s._add_history("resample",varlist)
963 print_log()
964 if insitu: self._assign(s)
965 else: return s
966
967
968 def average_pol(self, mask=None, weight='none'):
969 """
970 Average the Polarisations together.
971 Parameters:
972 mask: An optional mask defining the region, where the
973 averaging will be applied. The output will have all
974 specified points masked.
975 weight: Weighting scheme. 'none' (default), 'var' (1/var(spec)
976 weighted), or 'tsys' (1/Tsys**2 weighted)
977 """
978 varlist = vars()
979 if mask is None:
980 mask = ()
981 s = scantable(self._math._averagepol(self, mask, weight.upper()))
982 s._add_history("average_pol",varlist)
983 print_log()
984 return s
985
986 def convert_pol(self, poltype=None):
987 """
988 Convert the data to a different polarisation type.
989 Parameters:
990 poltype: The new polarisation type. Valid types are:
991 "linear", "stokes" and "circular"
992 """
993 varlist = vars()
994 try:
995 s = scantable(self._math._convertpol(self, poltype))
996 except RuntimeError,msg:
997 if rcParams['verbose']:
998 print msg
999 return
1000 else:
1001 raise
1002 s._add_history("convert_pol",varlist)
1003 print_log()
1004 return s
1005
1006 def smooth(self, kernel="hanning", width=5.0, insitu=None):
1007 """
1008 Smooth the spectrum by the specified kernel (conserving flux).
1009 Parameters:
1010 scan: The input scan
1011 kernel: The type of smoothing kernel. Select from
1012 'hanning' (default), 'gaussian' and 'boxcar'.
1013 The first three characters are sufficient.
1014 width: The width of the kernel in pixels. For hanning this is
1015 ignored otherwise it defauls to 5 pixels.
1016 For 'gaussian' it is the Full Width Half
1017 Maximum. For 'boxcar' it is the full width.
1018 insitu: if False a new scantable is returned.
1019 Otherwise, the scaling is done in-situ
1020 The default is taken from .asaprc (False)
1021 Example:
1022 none
1023 """
1024 if insitu is None: insitu = rcParams['insitu']
1025 self._math._setinsitu(insitu)
1026 varlist = vars()
1027 s = scantable(self._math._smooth(self,kernel,width))
1028 s._add_history("smooth", varlist)
1029 print_log()
1030 if insitu: self._assign(s)
1031 else: return s
1032
1033
1034 def poly_baseline(self, mask=None, order=0, insitu=None):
1035 """
1036 Return a scan which has been baselined (all rows) by a polynomial.
1037 Parameters:
1038 scan: a scantable
1039 mask: an optional mask
1040 order: the order of the polynomial (default is 0)
1041 insitu: if False a new scantable is returned.
1042 Otherwise, the scaling is done in-situ
1043 The default is taken from .asaprc (False)
1044 allaxes: If True (default) apply to all spectra. Otherwise
1045 apply only to the selected (beam/pol/if)spectra only
1046 The default is taken from .asaprc (True if none)
1047 Example:
1048 # return a scan baselined by a third order polynomial,
1049 # not using a mask
1050 bscan = scan.poly_baseline(order=3)
1051 """
1052 if insitu is None: insitu = rcParams['insitu']
1053 varlist = vars()
1054 if mask is None:
1055 from numarray import ones
1056 mask = list(ones(self.nchan(-1)))
1057 from asap.asapfitter import fitter
1058 f = fitter()
1059 f.set_scan(self, mask)
1060 f.set_function(poly=order)
1061 s = f.auto_fit(insitu)
1062 s._add_history("poly_baseline", varlist)
1063 print_log()
1064 if insitu: self._assign(s)
1065 else: return s
1066
1067 def auto_poly_baseline(self, mask=[], edge=(0,0), order=0,
1068 threshold=3, insitu=None):
1069 """
1070 Return a scan which has been baselined (all rows) by a polynomial.
1071 Spectral lines are detected first using linefinder and masked out
1072 to avoid them affecting the baseline solution.
1073
1074 Parameters:
1075 mask: an optional mask retreived from scantable
1076 edge: an optional number of channel to drop at
1077 the edge of spectrum. If only one value is
1078 specified, the same number will be dropped from
1079 both sides of the spectrum. Default is to keep
1080 all channels. Nested tuples represent individual
1081 edge selection for different IFs (a number of spectral
1082 channels can be different)
1083 order: the order of the polynomial (default is 0)
1084 threshold: the threshold used by line finder. It is better to
1085 keep it large as only strong lines affect the
1086 baseline solution.
1087 insitu: if False a new scantable is returned.
1088 Otherwise, the scaling is done in-situ
1089 The default is taken from .asaprc (False)
1090
1091 Example:
1092 scan2=scan.auto_poly_baseline(order=7)
1093 """
1094 if insitu is None: insitu = rcParams['insitu']
1095 varlist = vars()
1096 from asap.asapfitter import fitter
1097 from asap.asaplinefind import linefinder
1098 from asap import _is_sequence_or_number as _is_valid
1099
1100 # check whether edge is set up for each IF individually
1101 individualEdge = False;
1102 if len(edge)>1:
1103 if isinstance(edge[0],list) or isinstance(edge[0],tuple):
1104 individualEdge = True;
1105
1106 if not _is_valid(edge, int) and not individualEdge:
1107 raise ValueError, "Parameter 'edge' has to be an integer or a \
1108 pair of integers specified as a tuple. Nested tuples are allowed \
1109 to make individual selection for different IFs."
1110
1111 curedge = (0,0)
1112 if individualEdge:
1113 for edge_par in edge:
1114 if not _is_valid(edge,int):
1115 raise ValueError, "Each element of the 'edge' tuple has \
1116 to be a pair of integers or an integer."
1117 else:
1118 curedge = edge;
1119
1120 # setup fitter
1121 f = fitter()
1122 f.set_function(poly=order)
1123
1124 # setup line finder
1125 fl=linefinder()
1126 fl.set_options(threshold=threshold)
1127
1128 if not insitu:
1129 workscan=self.copy()
1130 else:
1131 workscan=self
1132
1133 fl.set_scan(workscan)
1134
1135 rows=range(workscan.nrow())
1136 from asap import asaplog
1137 asaplog.push("Processing:")
1138 for r in rows:
1139 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))
1140 asaplog.push(msg, False)
1141
1142 # figure out edge parameter
1143 if individualEdge:
1144 if len(edge)>=workscan.getif(r):
1145 raise RuntimeError, "Number of edge elements appear to be less than the number of IFs"
1146 curedge = edge[workscan.getif(r)]
1147
1148 # setup line finder
1149 fl.find_lines(r,mask,curedge)
1150 f.set_scan(workscan, fl.get_mask())
1151 f.x = workscan._getabcissa(r)
1152 f.y = workscan._getspectrum(r)
1153 f.data = None
1154 f.fit()
1155 x = f.get_parameters()
1156 workscan._setspectrum(f.fitter.getresidual(), r)
1157 workscan._add_history("poly_baseline", varlist)
1158 if insitu:
1159 self._assign(workscan)
1160 else:
1161 return workscan
1162
1163 def rotate_linpolphase(self, angle):
1164 """
1165 Rotate the phase of the complex polarization O=Q+iU correlation.
1166 This is always done in situ in the raw data. So if you call this
1167 function more than once then each call rotates the phase further.
1168 Parameters:
1169 angle: The angle (degrees) to rotate (add) by.
1170 Examples:
1171 scan.rotate_linpolphase(2.3)
1172 """
1173 varlist = vars()
1174 self._math._rotate_linpolphase(self, angle)
1175 self._add_history("rotate_linpolphase", varlist)
1176 print_log()
1177 return
1178
1179
1180 def rotate_xyphase(self, angle):
1181 """
1182 Rotate the phase of the XY correlation. This is always done in situ
1183 in the data. So if you call this function more than once
1184 then each call rotates the phase further.
1185 Parameters:
1186 angle: The angle (degrees) to rotate (add) by.
1187 Examples:
1188 scan.rotate_xyphase(2.3)
1189 """
1190 varlist = vars()
1191 self._math._rotate_xyphase(self, angle)
1192 self._add_history("rotate_xyphase", varlist)
1193 print_log()
1194 return
1195
1196 def swap_linears(self):
1197 """
1198 Swap the linear polarisations XX and YY
1199 """
1200 varlist = vars()
1201 self._math._swap_linears(self)
1202 self._add_history("swap_linears", varlist)
1203 print_log()
1204 return
1205
1206 def invert_phase(self):
1207 """
1208 Invert the phase of the complex polarisation
1209 """
1210 varlist = vars()
1211 self._math._invert_phase(self)
1212 self._add_history("invert_phase", varlist)
1213 print_log()
1214 return
1215
1216 def add(self, offset, insitu=None):
1217 """
1218 Return a scan where all spectra have the offset added
1219 Parameters:
1220 offset: the offset
1221 insitu: if False a new scantable is returned.
1222 Otherwise, the scaling is done in-situ
1223 The default is taken from .asaprc (False)
1224 """
1225 if insitu is None: insitu = rcParams['insitu']
1226 self._math._setinsitu(insitu)
1227 varlist = vars()
1228 s = scantable(self._math._unaryop(self, offset, "ADD", False))
1229 s._add_history("add",varlist)
1230 print_log()
1231 if insitu:
1232 self._assign(s)
1233 else:
1234 return s
1235
1236 def scale(self, factor, tsys=True, insitu=None,):
1237 """
1238 Return a scan where all spectra are scaled by the give 'factor'
1239 Parameters:
1240 factor: the scaling factor
1241 insitu: if False a new scantable is returned.
1242 Otherwise, the scaling is done in-situ
1243 The default is taken from .asaprc (False)
1244 tsys: if True (default) then apply the operation to Tsys
1245 as well as the data
1246 """
1247 if insitu is None: insitu = rcParams['insitu']
1248 self._math._setinsitu(insitu)
1249 varlist = vars()
1250 s = scantable(self._math._unaryop(self, factor, "MUL", tsys))
1251 s._add_history("scale",varlist)
1252 print_log()
1253 if insitu:
1254 self._assign(s)
1255 else:
1256 return s
1257
1258 def auto_quotient(self, mode='time', preserve=True):
1259 """
1260 This function allows to build quotients automatically.
1261 It assumes the observation to have the same numer of
1262 "ons" and "offs"
1263 It will support "closest off in time" in the future
1264 Parameters:
1265 mode: the on/off detection mode; 'suffix' (default)
1266 'suffix' identifies 'off' scans by the
1267 trailing '_R' (Mopra/Parkes) or
1268 '_e'/'_w' (Tid)
1269 preserve: you can preserve (default) the continuum or
1270 remove it. The equations used are
1271 preserve: Output = Toff * (on/off) - Toff
1272 remove: Output = Tref * (on/off) - Ton
1273 """
1274 modes = ["time"]
1275 if not mode in modes:
1276 msg = "please provide valid mode. Valid modes are %s" % (modes)
1277 raise ValueError(msg)
1278 varlist = vars()
1279 s = scantable(self._math._quotient(self, mode, preserve))
1280 s._add_history("auto_quotient",varlist)
1281 print_log()
1282 return s
1283
1284
1285
1286
1287 def freq_switch(self, insitu=None):
1288 """
1289 Apply frequency switching to the data.
1290 Parameters:
1291 insitu: if False a new scantable is returned.
1292 Otherwise, the swictching is done in-situ
1293 The default is taken from .asaprc (False)
1294 Example:
1295 none
1296 """
1297 if insitu is None: insitu = rcParams['insitu']
1298 self._math._setinsitu(insitu)
1299 varlist = vars()
1300 s = scantable(self._math._freqswitch(self))
1301 s._add_history("freq_switch",varlist)
1302 print_log()
1303 if insitu: self._assign(s)
1304 else: return s
1305
1306 def recalc_azel(self):
1307 """
1308 Recalculate the azimuth and elevation for each position.
1309 Parameters:
1310 none
1311 Example:
1312 """
1313 varlist = vars()
1314 self._recalcazel()
1315 self._add_history("recalc_azel", varlist)
1316 print_log()
1317 return
1318
1319 def __add__(self, other):
1320 varlist = vars()
1321 s = None
1322 if isinstance(other, scantable):
1323 print "scantable + scantable NYI"
1324 return
1325 elif isinstance(other, float):
1326 s = scantable(self._math._unaryop(self, other, "ADD", False))
1327 else:
1328 raise TypeError("Other input is not a scantable or float value")
1329 s._add_history("operator +", varlist)
1330 print_log()
1331 return s
1332
1333 def __sub__(self, other):
1334 """
1335 implicit on all axes and on Tsys
1336 """
1337 varlist = vars()
1338 s = None
1339 if isinstance(other, scantable):
1340 print "scantable - scantable NYI"
1341 return
1342 elif isinstance(other, float):
1343 s = scantable(self._math._unaryop(self, other, "SUB", False))
1344 else:
1345 raise TypeError("Other input is not a scantable or float value")
1346 s._add_history("operator -", varlist)
1347 print_log()
1348 return s
1349
1350 def __mul__(self, other):
1351 """
1352 implicit on all axes and on Tsys
1353 """
1354 varlist = vars()
1355 s = None
1356 if isinstance(other, scantable):
1357 print "scantable * scantable NYI"
1358 return
1359 elif isinstance(other, float):
1360 s = scantable(self._math._unaryop(self, other, "MUL", False))
1361 else:
1362 raise TypeError("Other input is not a scantable or float value")
1363 s._add_history("operator *", varlist)
1364 print_log()
1365 return s
1366
1367
1368 def __div__(self, other):
1369 """
1370 implicit on all axes and on Tsys
1371 """
1372 varlist = vars()
1373 s = None
1374 if isinstance(other, scantable):
1375 print "scantable / scantable NYI"
1376 return
1377 elif isinstance(other, float):
1378 if other == 0.0:
1379 raise ZeroDivisionError("Dividing by zero is not recommended")
1380 s = scantable(self._math._unaryop(self, other, "DIV", False))
1381 else:
1382 raise TypeError("Other input is not a scantable or float value")
1383 s._add_history("operator /", varlist)
1384 print_log()
1385 return s
1386
1387 def get_fit(self, row=0):
1388 """
1389 Print or return the stored fits for a row in the scantable
1390 Parameters:
1391 row: the row which the fit has been applied to.
1392 """
1393 if row > self.nrow():
1394 return
1395 from asap.asapfit import asapfit
1396 fit = asapfit(self._getfit(row))
1397 if rcParams['verbose']:
1398 print fit
1399 return
1400 else:
1401 return fit.as_dict()
1402
1403 def _add_history(self, funcname, parameters):
1404 # create date
1405 sep = "##"
1406 from datetime import datetime
1407 dstr = datetime.now().strftime('%Y/%m/%d %H:%M:%S')
1408 hist = dstr+sep
1409 hist += funcname+sep#cdate+sep
1410 if parameters.has_key('self'): del parameters['self']
1411 for k,v in parameters.iteritems():
1412 if type(v) is dict:
1413 for k2,v2 in v.iteritems():
1414 hist += k2
1415 hist += "="
1416 if isinstance(v2,scantable):
1417 hist += 'scantable'
1418 elif k2 == 'mask':
1419 if isinstance(v2,list) or isinstance(v2,tuple):
1420 hist += str(self._zip_mask(v2))
1421 else:
1422 hist += str(v2)
1423 else:
1424 hist += str(v2)
1425 else:
1426 hist += k
1427 hist += "="
1428 if isinstance(v,scantable):
1429 hist += 'scantable'
1430 elif k == 'mask':
1431 if isinstance(v,list) or isinstance(v,tuple):
1432 hist += str(self._zip_mask(v))
1433 else:
1434 hist += str(v)
1435 else:
1436 hist += str(v)
1437 hist += sep
1438 hist = hist[:-2] # remove trailing '##'
1439 self._addhistory(hist)
1440
1441
1442 def _zip_mask(self, mask):
1443 mask = list(mask)
1444 i = 0
1445 segments = []
1446 while mask[i:].count(1):
1447 i += mask[i:].index(1)
1448 if mask[i:].count(0):
1449 j = i + mask[i:].index(0)
1450 else:
1451 j = len(mask)
1452 segments.append([i,j])
1453 i = j
1454 return segments
1455
1456 def _get_ordinate_label(self):
1457 fu = "("+self.get_fluxunit()+")"
1458 import re
1459 lbl = "Intensity"
1460 if re.match(".K.",fu):
1461 lbl = "Brightness Temperature "+ fu
1462 elif re.match(".Jy.",fu):
1463 lbl = "Flux density "+ fu
1464 return lbl
1465
1466 def _check_ifs(self):
1467 nchans = [self.nchan(i) for i in range(self.nif(-1))]
1468 nchans = filter(lambda t: t > 0, nchans)
1469 return (sum(nchans)/len(nchans) == nchans[0])
1470
1471 def _fill(self, names, unit, average):
1472 import os
1473 varlist = vars()
1474 from asap._asap import stfiller
1475 first = True
1476 fullnames = []
1477 for name in names:
1478 name = os.path.expandvars(name)
1479 name = os.path.expanduser(name)
1480 if not os.path.exists(name):
1481 msg = "File '%s' does not exists" % (name)
1482 if rcParams['verbose']:
1483 asaplog.push(msg)
1484 print asaplog.pop().strip()
1485 return
1486 raise IOError(msg)
1487 fullnames.append(name)
1488 if average:
1489 asaplog.push('Auto averaging integrations')
1490 for name in fullnames:
1491 r = stfiller()
1492 msg = "Importing %s..." % (name)
1493 asaplog.push(msg,False)
1494 print_log()
1495 r._open(name,-1,-1)
1496 r._read()
1497 tbl = r._getdata()
1498 if average:
1499 tbl = self._math._average((tbl,),(),'NONE','SCAN')
1500 #tbl = tbl2
1501 if not first:
1502 tbl = self._math._merge([self, tbl])
1503 #tbl = tbl2
1504 Scantable.__init__(self, tbl)
1505 r._close()
1506 del r,tbl
1507 first = False
1508 if unit is not None:
1509 self.set_fluxunit(unit)
1510 self.set_freqframe(rcParams['scantable.freqframe'])
1511 #self._add_history("scantable", varlist)
1512
Note: See TracBrowser for help on using the repository browser.