source: trunk/python/scantable.py@ 2178

Last change on this file since 2178 was 2178, checked in by Malte Marquarding, 13 years ago

Removed redundant argument 'verbose'

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 128.1 KB
RevLine 
[1846]1"""This module defines the scantable class."""
2
[1697]3import os
[1948]4import numpy
[1691]5try:
6 from functools import wraps as wraps_dec
7except ImportError:
8 from asap.compatibility import wraps as wraps_dec
9
[1824]10from asap.env import is_casapy
[876]11from asap._asap import Scantable
[2004]12from asap._asap import filler, msfiller
[1824]13from asap.parameters import rcParams
[1862]14from asap.logging import asaplog, asaplog_post_dec
[1824]15from asap.selector import selector
16from asap.linecatalog import linecatalog
[1600]17from asap.coordinate import coordinate
[1859]18from asap.utils import _n_bools, mask_not, mask_and, mask_or, page
[1907]19from asap.asapfitter import fitter
[102]20
[1689]21
22def preserve_selection(func):
[1691]23 @wraps_dec(func)
[1689]24 def wrap(obj, *args, **kw):
25 basesel = obj.get_selection()
[1857]26 try:
27 val = func(obj, *args, **kw)
28 finally:
29 obj.set_selection(basesel)
[1689]30 return val
31 return wrap
32
[1846]33def is_scantable(filename):
34 """Is the given file a scantable?
[1689]35
[1846]36 Parameters:
37
38 filename: the name of the file/directory to test
39
40 """
[1883]41 if ( os.path.isdir(filename)
42 and os.path.exists(filename+'/table.info')
43 and os.path.exists(filename+'/table.dat') ):
44 f=open(filename+'/table.info')
45 l=f.readline()
46 f.close()
47 #if ( l.find('Scantable') != -1 ):
48 if ( l.find('Measurement Set') == -1 ):
49 return True
50 else:
51 return False
52 else:
53 return False
54## return (os.path.isdir(filename)
55## and not os.path.exists(filename+'/table.f1')
56## and os.path.exists(filename+'/table.info'))
[1697]57
[1883]58def is_ms(filename):
59 """Is the given file a MeasurementSet?
[1697]60
[1883]61 Parameters:
62
63 filename: the name of the file/directory to test
64
65 """
66 if ( os.path.isdir(filename)
67 and os.path.exists(filename+'/table.info')
68 and os.path.exists(filename+'/table.dat') ):
69 f=open(filename+'/table.info')
70 l=f.readline()
71 f.close()
72 if ( l.find('Measurement Set') != -1 ):
73 return True
74 else:
75 return False
76 else:
77 return False
78
[876]79class scantable(Scantable):
[1846]80 """\
81 The ASAP container for scans (single-dish data).
[102]82 """
[1819]83
[1862]84 @asaplog_post_dec
[1916]85 #def __init__(self, filename, average=None, unit=None, getpt=None,
86 # antenna=None, parallactify=None):
87 def __init__(self, filename, average=None, unit=None, parallactify=None, **args):
[1846]88 """\
[102]89 Create a scantable from a saved one or make a reference
[1846]90
[102]91 Parameters:
[1846]92
93 filename: the name of an asap table on disk
94 or
95 the name of a rpfits/sdfits/ms file
96 (integrations within scans are auto averaged
97 and the whole file is read) or
98 [advanced] a reference to an existing scantable
99
100 average: average all integrations withinb a scan on read.
101 The default (True) is taken from .asaprc.
102
[484]103 unit: brightness unit; must be consistent with K or Jy.
[1846]104 Over-rides the default selected by the filler
105 (input rpfits/sdfits/ms) or replaces the value
106 in existing scantables
107
108 getpt: for MeasurementSet input data only:
109 If True, all pointing data are filled.
110 The deafult is False, which makes time to load
111 the MS data faster in some cases.
112
[1920]113 antenna: for MeasurementSet input data only:
114 Antenna selection. integer (id) or string (name or id).
[1846]115
116 parallactify: Indicate that the data had been parallatified. Default
117 is taken from rc file.
118
[710]119 """
[976]120 if average is None:
[710]121 average = rcParams['scantable.autoaverage']
[1916]122 #if getpt is None:
123 # getpt = True
124 #if antenna is not None:
125 # asaplog.push("Antenna selection currently unsupported."
126 # "Using ''")
127 # asaplog.post('WARN')
128 #if antenna is None:
129 # antenna = ''
130 #elif type(antenna) == int:
131 # antenna = '%s' % antenna
132 #elif type(antenna) == list:
133 # tmpstr = ''
134 # for i in range( len(antenna) ):
135 # if type(antenna[i]) == int:
136 # tmpstr = tmpstr + ('%s,'%(antenna[i]))
137 # elif type(antenna[i]) == str:
138 # tmpstr=tmpstr+antenna[i]+','
139 # else:
140 # raise TypeError('Bad antenna selection.')
141 # antenna = tmpstr.rstrip(',')
[1593]142 parallactify = parallactify or rcParams['scantable.parallactify']
[1259]143 varlist = vars()
[876]144 from asap._asap import stmath
[1819]145 self._math = stmath( rcParams['insitu'] )
[876]146 if isinstance(filename, Scantable):
147 Scantable.__init__(self, filename)
[181]148 else:
[1697]149 if isinstance(filename, str):
[976]150 filename = os.path.expandvars(filename)
151 filename = os.path.expanduser(filename)
152 if not os.path.exists(filename):
153 s = "File '%s' not found." % (filename)
154 raise IOError(s)
[1697]155 if is_scantable(filename):
156 ondisk = rcParams['scantable.storage'] == 'disk'
157 Scantable.__init__(self, filename, ondisk)
158 if unit is not None:
159 self.set_fluxunit(unit)
[2008]160 if average:
161 self._assign( self.average_time( scanav=True ) )
[1819]162 # do not reset to the default freqframe
163 #self.set_freqframe(rcParams['scantable.freqframe'])
[1883]164 #elif os.path.isdir(filename) \
165 # and not os.path.exists(filename+'/table.f1'):
166 elif is_ms(filename):
[1916]167 # Measurement Set
168 opts={'ms': {}}
169 mskeys=['getpt','antenna']
170 for key in mskeys:
171 if key in args.keys():
172 opts['ms'][key] = args[key]
173 #self._fill([filename], unit, average, getpt, antenna)
174 self._fill([filename], unit, average, opts)
[1893]175 elif os.path.isfile(filename):
[1916]176 #self._fill([filename], unit, average, getpt, antenna)
177 self._fill([filename], unit, average)
[1883]178 else:
[1819]179 msg = "The given file '%s'is not a valid " \
180 "asap table." % (filename)
[1859]181 raise IOError(msg)
[1118]182 elif (isinstance(filename, list) or isinstance(filename, tuple)) \
[976]183 and isinstance(filename[-1], str):
[1916]184 #self._fill(filename, unit, average, getpt, antenna)
185 self._fill(filename, unit, average)
[1586]186 self.parallactify(parallactify)
[1259]187 self._add_history("scantable", varlist)
[102]188
[1862]189 @asaplog_post_dec
[876]190 def save(self, name=None, format=None, overwrite=False):
[1846]191 """\
[1280]192 Store the scantable on disk. This can be an asap (aips++) Table,
193 SDFITS or MS2 format.
[1846]194
[116]195 Parameters:
[1846]196
[1093]197 name: the name of the outputfile. For format "ASCII"
198 this is the root file name (data in 'name'.txt
[497]199 and header in 'name'_header.txt)
[1855]200
[116]201 format: an optional file format. Default is ASAP.
[1855]202 Allowed are:
203
204 * 'ASAP' (save as ASAP [aips++] Table),
205 * 'SDFITS' (save as SDFITS file)
206 * 'ASCII' (saves as ascii text file)
207 * 'MS2' (saves as an casacore MeasurementSet V2)
208 * 'FITS' (save as image FITS - not readable by class)
209 * 'CLASS' (save as FITS readable by CLASS)
210
[411]211 overwrite: If the file should be overwritten if it exists.
[256]212 The default False is to return with warning
[411]213 without writing the output. USE WITH CARE.
[1855]214
[1846]215 Example::
216
[116]217 scan.save('myscan.asap')
[1118]218 scan.save('myscan.sdfits', 'SDFITS')
[1846]219
[116]220 """
[411]221 from os import path
[1593]222 format = format or rcParams['scantable.save']
[256]223 suffix = '.'+format.lower()
[1118]224 if name is None or name == "":
[256]225 name = 'scantable'+suffix
[718]226 msg = "No filename given. Using default name %s..." % name
227 asaplog.push(msg)
[411]228 name = path.expandvars(name)
[256]229 if path.isfile(name) or path.isdir(name):
230 if not overwrite:
[718]231 msg = "File %s exists." % name
[1859]232 raise IOError(msg)
[451]233 format2 = format.upper()
234 if format2 == 'ASAP':
[116]235 self._save(name)
[2029]236 elif format2 == 'MS2':
237 msopt = {'ms': {'overwrite': overwrite } }
238 from asap._asap import mswriter
239 writer = mswriter( self )
240 writer.write( name, msopt )
[116]241 else:
[989]242 from asap._asap import stwriter as stw
[1118]243 writer = stw(format2)
244 writer.write(self, name)
[116]245 return
246
[102]247 def copy(self):
[1846]248 """Return a copy of this scantable.
249
250 *Note*:
251
[1348]252 This makes a full (deep) copy. scan2 = scan1 makes a reference.
[1846]253
254 Example::
255
[102]256 copiedscan = scan.copy()
[1846]257
[102]258 """
[876]259 sd = scantable(Scantable._copy(self))
[113]260 return sd
261
[1093]262 def drop_scan(self, scanid=None):
[1846]263 """\
[1093]264 Return a new scantable where the specified scan number(s) has(have)
265 been dropped.
[1846]266
[1093]267 Parameters:
[1846]268
[1093]269 scanid: a (list of) scan number(s)
[1846]270
[1093]271 """
272 from asap import _is_sequence_or_number as _is_valid
273 from asap import _to_list
274 from asap import unique
275 if not _is_valid(scanid):
[1859]276 raise RuntimeError( 'Please specify a scanno to drop from the scantable' )
277 scanid = _to_list(scanid)
278 allscans = unique([ self.getscan(i) for i in range(self.nrow())])
279 for sid in scanid: allscans.remove(sid)
280 if len(allscans) == 0:
281 raise ValueError("Can't remove all scans")
282 sel = selector(scans=allscans)
283 return self._select_copy(sel)
[1093]284
[1594]285 def _select_copy(self, selection):
286 orig = self.get_selection()
287 self.set_selection(orig+selection)
288 cp = self.copy()
289 self.set_selection(orig)
290 return cp
291
[102]292 def get_scan(self, scanid=None):
[1855]293 """\
[102]294 Return a specific scan (by scanno) or collection of scans (by
295 source name) in a new scantable.
[1846]296
297 *Note*:
298
[1348]299 See scantable.drop_scan() for the inverse operation.
[1846]300
[102]301 Parameters:
[1846]302
[513]303 scanid: a (list of) scanno or a source name, unix-style
304 patterns are accepted for source name matching, e.g.
305 '*_R' gets all 'ref scans
[1846]306
307 Example::
308
[513]309 # get all scans containing the source '323p459'
310 newscan = scan.get_scan('323p459')
311 # get all 'off' scans
312 refscans = scan.get_scan('*_R')
313 # get a susbset of scans by scanno (as listed in scan.summary())
[1118]314 newscan = scan.get_scan([0, 2, 7, 10])
[1846]315
[102]316 """
317 if scanid is None:
[1859]318 raise RuntimeError( 'Please specify a scan no or name to '
319 'retrieve from the scantable' )
[102]320 try:
[946]321 bsel = self.get_selection()
322 sel = selector()
[102]323 if type(scanid) is str:
[946]324 sel.set_name(scanid)
[1594]325 return self._select_copy(sel)
[102]326 elif type(scanid) is int:
[946]327 sel.set_scans([scanid])
[1594]328 return self._select_copy(sel)
[381]329 elif type(scanid) is list:
[946]330 sel.set_scans(scanid)
[1594]331 return self._select_copy(sel)
[381]332 else:
[718]333 msg = "Illegal scanid type, use 'int' or 'list' if ints."
[1859]334 raise TypeError(msg)
[102]335 except RuntimeError:
[1859]336 raise
[102]337
338 def __str__(self):
[2178]339 return Scantable._summary(self)
[102]340
[976]341 def summary(self, filename=None):
[1846]342 """\
[102]343 Print a summary of the contents of this scantable.
[1846]344
[102]345 Parameters:
[1846]346
[1931]347 filename: the name of a file to write the putput to
[102]348 Default - no file output
[1846]349
[102]350 """
[2178]351 info = Scantable._summary(self)
[102]352 if filename is not None:
[256]353 if filename is "":
354 filename = 'scantable_summary.txt'
[415]355 from os.path import expandvars, isdir
[411]356 filename = expandvars(filename)
[415]357 if not isdir(filename):
[413]358 data = open(filename, 'w')
359 data.write(info)
360 data.close()
361 else:
[718]362 msg = "Illegal file name '%s'." % (filename)
[1859]363 raise IOError(msg)
364 return page(info)
[710]365
[1512]366 def get_spectrum(self, rowno):
[1471]367 """Return the spectrum for the current row in the scantable as a list.
[1846]368
[1471]369 Parameters:
[1846]370
[1573]371 rowno: the row number to retrieve the spectrum from
[1846]372
[1471]373 """
374 return self._getspectrum(rowno)
[946]375
[1471]376 def get_mask(self, rowno):
377 """Return the mask for the current row in the scantable as a list.
[1846]378
[1471]379 Parameters:
[1846]380
[1573]381 rowno: the row number to retrieve the mask from
[1846]382
[1471]383 """
384 return self._getmask(rowno)
385
386 def set_spectrum(self, spec, rowno):
[1938]387 """Set the spectrum for the current row in the scantable.
[1846]388
[1471]389 Parameters:
[1846]390
[1855]391 spec: the new spectrum
[1846]392
[1855]393 rowno: the row number to set the spectrum for
394
[1471]395 """
396 assert(len(spec) == self.nchan())
397 return self._setspectrum(spec, rowno)
398
[1600]399 def get_coordinate(self, rowno):
400 """Return the (spectral) coordinate for a a given 'rowno'.
[1846]401
402 *Note*:
403
[1600]404 * This coordinate is only valid until a scantable method modifies
405 the frequency axis.
406 * This coordinate does contain the original frequency set-up
407 NOT the new frame. The conversions however are done using the user
408 specified frame (e.g. LSRK/TOPO). To get the 'real' coordinate,
409 use scantable.freq_align first. Without it there is no closure,
[1846]410 i.e.::
[1600]411
[1846]412 c = myscan.get_coordinate(0)
413 c.to_frequency(c.get_reference_pixel()) != c.get_reference_value()
414
[1600]415 Parameters:
[1846]416
[1600]417 rowno: the row number for the spectral coordinate
418
419 """
420 return coordinate(Scantable.get_coordinate(self, rowno))
421
[946]422 def get_selection(self):
[1846]423 """\
[1005]424 Get the selection object currently set on this scantable.
[1846]425
426 Example::
427
[1005]428 sel = scan.get_selection()
429 sel.set_ifs(0) # select IF 0
430 scan.set_selection(sel) # apply modified selection
[1846]431
[946]432 """
433 return selector(self._getselection())
434
[1576]435 def set_selection(self, selection=None, **kw):
[1846]436 """\
[1005]437 Select a subset of the data. All following operations on this scantable
438 are only applied to thi selection.
[1846]439
[1005]440 Parameters:
[1697]441
[1846]442 selection: a selector object (default unset the selection), or
443 any combination of "pols", "ifs", "beams", "scans",
444 "cycles", "name", "query"
[1697]445
[1846]446 Examples::
[1697]447
[1005]448 sel = selector() # create a selection object
[1118]449 self.set_scans([0, 3]) # select SCANNO 0 and 3
[1005]450 scan.set_selection(sel) # set the selection
451 scan.summary() # will only print summary of scanno 0 an 3
452 scan.set_selection() # unset the selection
[1697]453 # or the equivalent
454 scan.set_selection(scans=[0,3])
455 scan.summary() # will only print summary of scanno 0 an 3
456 scan.set_selection() # unset the selection
[1846]457
[946]458 """
[1576]459 if selection is None:
460 # reset
461 if len(kw) == 0:
462 selection = selector()
463 else:
464 # try keywords
465 for k in kw:
466 if k not in selector.fields:
467 raise KeyError("Invalid selection key '%s', valid keys are %s" % (k, selector.fields))
468 selection = selector(**kw)
[946]469 self._setselection(selection)
470
[1819]471 def get_row(self, row=0, insitu=None):
[1846]472 """\
[1819]473 Select a row in the scantable.
474 Return a scantable with single row.
[1846]475
[1819]476 Parameters:
[1846]477
478 row: row no of integration, default is 0.
479 insitu: if False a new scantable is returned. Otherwise, the
480 scaling is done in-situ. The default is taken from .asaprc
481 (False)
482
[1819]483 """
484 if insitu is None: insitu = rcParams['insitu']
485 if not insitu:
486 workscan = self.copy()
487 else:
488 workscan = self
489 # Select a row
490 sel=selector()
[1992]491 sel.set_rows([row])
492 #sel.set_scans([workscan.getscan(row)])
493 #sel.set_cycles([workscan.getcycle(row)])
494 #sel.set_beams([workscan.getbeam(row)])
495 #sel.set_ifs([workscan.getif(row)])
496 #sel.set_polarisations([workscan.getpol(row)])
497 #sel.set_name(workscan._getsourcename(row))
[1819]498 workscan.set_selection(sel)
499 if not workscan.nrow() == 1:
500 msg = "Cloud not identify single row. %d rows selected."%(workscan.nrow())
501 raise RuntimeError(msg)
502 del sel
503 if insitu:
504 self._assign(workscan)
505 else:
506 return workscan
507
[1862]508 @asaplog_post_dec
[1907]509 def stats(self, stat='stddev', mask=None, form='3.3f', row=None):
[1846]510 """\
[135]511 Determine the specified statistic of the current beam/if/pol
[102]512 Takes a 'mask' as an optional parameter to specify which
513 channels should be excluded.
[1846]514
[102]515 Parameters:
[1846]516
[1819]517 stat: 'min', 'max', 'min_abc', 'max_abc', 'sumsq', 'sum',
518 'mean', 'var', 'stddev', 'avdev', 'rms', 'median'
[1855]519
[135]520 mask: an optional mask specifying where the statistic
[102]521 should be determined.
[1855]522
[1819]523 form: format string to print statistic values
[1846]524
[1907]525 row: row number of spectrum to process.
526 (default is None: for all rows)
[1846]527
[1907]528 Example:
[113]529 scan.set_unit('channel')
[1118]530 msk = scan.create_mask([100, 200], [500, 600])
[135]531 scan.stats(stat='mean', mask=m)
[1846]532
[102]533 """
[1593]534 mask = mask or []
[876]535 if not self._check_ifs():
[1118]536 raise ValueError("Cannot apply mask as the IFs have different "
537 "number of channels. Please use setselection() "
538 "to select individual IFs")
[1819]539 rtnabc = False
540 if stat.lower().endswith('_abc'): rtnabc = True
541 getchan = False
542 if stat.lower().startswith('min') or stat.lower().startswith('max'):
543 chan = self._math._minmaxchan(self, mask, stat)
544 getchan = True
545 statvals = []
[1907]546 if not rtnabc:
547 if row == None:
548 statvals = self._math._stats(self, mask, stat)
549 else:
550 statvals = self._math._statsrow(self, mask, stat, int(row))
[256]551
[1819]552 #def cb(i):
553 # return statvals[i]
[256]554
[1819]555 #return self._row_callback(cb, stat)
[102]556
[1819]557 label=stat
558 #callback=cb
559 out = ""
560 #outvec = []
561 sep = '-'*50
[1907]562
563 if row == None:
564 rows = xrange(self.nrow())
565 elif isinstance(row, int):
566 rows = [ row ]
567
568 for i in rows:
[1819]569 refstr = ''
570 statunit= ''
571 if getchan:
572 qx, qy = self.chan2data(rowno=i, chan=chan[i])
573 if rtnabc:
574 statvals.append(qx['value'])
575 refstr = ('(value: %'+form) % (qy['value'])+' ['+qy['unit']+'])'
576 statunit= '['+qx['unit']+']'
577 else:
578 refstr = ('(@ %'+form) % (qx['value'])+' ['+qx['unit']+'])'
579
580 tm = self._gettime(i)
581 src = self._getsourcename(i)
582 out += 'Scan[%d] (%s) ' % (self.getscan(i), src)
583 out += 'Time[%s]:\n' % (tm)
[1907]584 if self.nbeam(-1) > 1: out += ' Beam[%d] ' % (self.getbeam(i))
585 if self.nif(-1) > 1: out += ' IF[%d] ' % (self.getif(i))
586 if self.npol(-1) > 1: out += ' Pol[%d] ' % (self.getpol(i))
[1819]587 #outvec.append(callback(i))
[1907]588 if len(rows) > 1:
589 # out += ('= %'+form) % (outvec[i]) +' '+refstr+'\n'
590 out += ('= %'+form) % (statvals[i]) +' '+refstr+'\n'
591 else:
592 # out += ('= %'+form) % (outvec[0]) +' '+refstr+'\n'
593 out += ('= %'+form) % (statvals[0]) +' '+refstr+'\n'
[1819]594 out += sep+"\n"
595
[1859]596 import os
597 if os.environ.has_key( 'USER' ):
598 usr = os.environ['USER']
599 else:
600 import commands
601 usr = commands.getoutput( 'whoami' )
602 tmpfile = '/tmp/tmp_'+usr+'_casapy_asap_scantable_stats'
603 f = open(tmpfile,'w')
604 print >> f, sep
605 print >> f, ' %s %s' % (label, statunit)
606 print >> f, sep
607 print >> f, out
608 f.close()
609 f = open(tmpfile,'r')
610 x = f.readlines()
611 f.close()
612 asaplog.push(''.join(x), False)
613
[1819]614 return statvals
615
616 def chan2data(self, rowno=0, chan=0):
[1846]617 """\
[1819]618 Returns channel/frequency/velocity and spectral value
619 at an arbitrary row and channel in the scantable.
[1846]620
[1819]621 Parameters:
[1846]622
[1819]623 rowno: a row number in the scantable. Default is the
624 first row, i.e. rowno=0
[1855]625
[1819]626 chan: a channel in the scantable. Default is the first
627 channel, i.e. pos=0
[1846]628
[1819]629 """
630 if isinstance(rowno, int) and isinstance(chan, int):
631 qx = {'unit': self.get_unit(),
632 'value': self._getabcissa(rowno)[chan]}
633 qy = {'unit': self.get_fluxunit(),
634 'value': self._getspectrum(rowno)[chan]}
635 return qx, qy
636
[1118]637 def stddev(self, mask=None):
[1846]638 """\
[135]639 Determine the standard deviation of the current beam/if/pol
640 Takes a 'mask' as an optional parameter to specify which
641 channels should be excluded.
[1846]642
[135]643 Parameters:
[1846]644
[135]645 mask: an optional mask specifying where the standard
646 deviation should be determined.
647
[1846]648 Example::
649
[135]650 scan.set_unit('channel')
[1118]651 msk = scan.create_mask([100, 200], [500, 600])
[135]652 scan.stddev(mask=m)
[1846]653
[135]654 """
[1118]655 return self.stats(stat='stddev', mask=mask);
[135]656
[1003]657
[1259]658 def get_column_names(self):
[1846]659 """\
[1003]660 Return a list of column names, which can be used for selection.
661 """
[1259]662 return list(Scantable.get_column_names(self))
[1003]663
[1730]664 def get_tsys(self, row=-1):
[1846]665 """\
[113]666 Return the System temperatures.
[1846]667
668 Parameters:
669
670 row: the rowno to get the information for. (default all rows)
671
[113]672 Returns:
[1846]673
[876]674 a list of Tsys values for the current selection
[1846]675
[113]676 """
[1730]677 if row > -1:
678 return self._get_column(self._gettsys, row)
[876]679 return self._row_callback(self._gettsys, "Tsys")
[256]680
[1730]681
682 def get_weather(self, row=-1):
[1846]683 """\
684 Return the weather informations.
685
686 Parameters:
687
688 row: the rowno to get the information for. (default all rows)
689
690 Returns:
691
692 a dict or list of of dicts of values for the current selection
693
694 """
695
[1730]696 values = self._get_column(self._get_weather, row)
697 if row > -1:
698 return {'temperature': values[0],
699 'pressure': values[1], 'humidity' : values[2],
700 'windspeed' : values[3], 'windaz' : values[4]
701 }
702 else:
703 out = []
704 for r in values:
705
706 out.append({'temperature': r[0],
707 'pressure': r[1], 'humidity' : r[2],
708 'windspeed' : r[3], 'windaz' : r[4]
709 })
710 return out
711
[876]712 def _row_callback(self, callback, label):
713 out = ""
[1118]714 outvec = []
[1590]715 sep = '-'*50
[876]716 for i in range(self.nrow()):
717 tm = self._gettime(i)
718 src = self._getsourcename(i)
[1590]719 out += 'Scan[%d] (%s) ' % (self.getscan(i), src)
[876]720 out += 'Time[%s]:\n' % (tm)
[1590]721 if self.nbeam(-1) > 1:
722 out += ' Beam[%d] ' % (self.getbeam(i))
723 if self.nif(-1) > 1: out += ' IF[%d] ' % (self.getif(i))
724 if self.npol(-1) > 1: out += ' Pol[%d] ' % (self.getpol(i))
[876]725 outvec.append(callback(i))
726 out += '= %3.3f\n' % (outvec[i])
[1590]727 out += sep+'\n'
[1859]728
729 asaplog.push(sep)
730 asaplog.push(" %s" % (label))
731 asaplog.push(sep)
732 asaplog.push(out)
[1861]733 asaplog.post()
[1175]734 return outvec
[256]735
[1947]736 def _get_column(self, callback, row=-1, *args):
[1070]737 """
738 """
739 if row == -1:
[1947]740 return [callback(i, *args) for i in range(self.nrow())]
[1070]741 else:
[1819]742 if 0 <= row < self.nrow():
[1947]743 return callback(row, *args)
[256]744
[1070]745
[1948]746 def get_time(self, row=-1, asdatetime=False, prec=-1):
[1846]747 """\
[113]748 Get a list of time stamps for the observations.
[1938]749 Return a datetime object or a string (default) for each
750 integration time stamp in the scantable.
[1846]751
[113]752 Parameters:
[1846]753
[1348]754 row: row no of integration. Default -1 return all rows
[1855]755
[1348]756 asdatetime: return values as datetime objects rather than strings
[1846]757
[1948]758 prec: number of digits shown. Default -1 to automatic calculation.
759 Note this number is equals to the digits of MVTime,
760 i.e., 0<prec<3: dates with hh:: only,
761 <5: with hh:mm:, <7 or 0: with hh:mm:ss,
[1947]762 and 6> : with hh:mm:ss.tt... (prec-6 t's added)
763
[113]764 """
[1175]765 from datetime import datetime
[1948]766 if prec < 0:
767 # automagically set necessary precision +1
[1950]768 prec = 7 - numpy.floor(numpy.log10(numpy.min(self.get_inttime(row))))
[1948]769 prec = max(6, int(prec))
770 else:
771 prec = max(0, prec)
772 if asdatetime:
773 #precision can be 1 millisecond at max
774 prec = min(12, prec)
775
[1947]776 times = self._get_column(self._gettime, row, prec)
[1348]777 if not asdatetime:
[1392]778 return times
[1947]779 format = "%Y/%m/%d/%H:%M:%S.%f"
780 if prec < 7:
781 nsub = 1 + (((6-prec)/2) % 3)
782 substr = [".%f","%S","%M"]
783 for i in range(nsub):
784 format = format.replace(substr[i],"")
[1175]785 if isinstance(times, list):
[1947]786 return [datetime.strptime(i, format) for i in times]
[1175]787 else:
[1947]788 return datetime.strptime(times, format)
[102]789
[1348]790
791 def get_inttime(self, row=-1):
[1846]792 """\
[1348]793 Get a list of integration times for the observations.
794 Return a time in seconds for each integration in the scantable.
[1846]795
[1348]796 Parameters:
[1846]797
[1348]798 row: row no of integration. Default -1 return all rows.
[1846]799
[1348]800 """
[1573]801 return self._get_column(self._getinttime, row)
[1348]802
[1573]803
[714]804 def get_sourcename(self, row=-1):
[1846]805 """\
[794]806 Get a list source names for the observations.
[714]807 Return a string for each integration in the scantable.
808 Parameters:
[1846]809
[1348]810 row: row no of integration. Default -1 return all rows.
[1846]811
[714]812 """
[1070]813 return self._get_column(self._getsourcename, row)
[714]814
[794]815 def get_elevation(self, row=-1):
[1846]816 """\
[794]817 Get a list of elevations for the observations.
818 Return a float for each integration in the scantable.
[1846]819
[794]820 Parameters:
[1846]821
[1348]822 row: row no of integration. Default -1 return all rows.
[1846]823
[794]824 """
[1070]825 return self._get_column(self._getelevation, row)
[794]826
827 def get_azimuth(self, row=-1):
[1846]828 """\
[794]829 Get a list of azimuths for the observations.
830 Return a float for each integration in the scantable.
[1846]831
[794]832 Parameters:
[1348]833 row: row no of integration. Default -1 return all rows.
[1846]834
[794]835 """
[1070]836 return self._get_column(self._getazimuth, row)
[794]837
838 def get_parangle(self, row=-1):
[1846]839 """\
[794]840 Get a list of parallactic angles for the observations.
841 Return a float for each integration in the scantable.
[1846]842
[794]843 Parameters:
[1846]844
[1348]845 row: row no of integration. Default -1 return all rows.
[1846]846
[794]847 """
[1070]848 return self._get_column(self._getparangle, row)
[794]849
[1070]850 def get_direction(self, row=-1):
851 """
852 Get a list of Positions on the sky (direction) for the observations.
[1594]853 Return a string for each integration in the scantable.
[1855]854
[1070]855 Parameters:
[1855]856
[1070]857 row: row no of integration. Default -1 return all rows
[1855]858
[1070]859 """
860 return self._get_column(self._getdirection, row)
861
[1391]862 def get_directionval(self, row=-1):
[1846]863 """\
[1391]864 Get a list of Positions on the sky (direction) for the observations.
865 Return a float for each integration in the scantable.
[1846]866
[1391]867 Parameters:
[1846]868
[1391]869 row: row no of integration. Default -1 return all rows
[1846]870
[1391]871 """
872 return self._get_column(self._getdirectionvec, row)
873
[1862]874 @asaplog_post_dec
[102]875 def set_unit(self, unit='channel'):
[1846]876 """\
[102]877 Set the unit for all following operations on this scantable
[1846]878
[102]879 Parameters:
[1846]880
881 unit: optional unit, default is 'channel'. Use one of '*Hz',
882 'km/s', 'channel' or equivalent ''
883
[102]884 """
[484]885 varlist = vars()
[1118]886 if unit in ['', 'pixel', 'channel']:
[113]887 unit = ''
888 inf = list(self._getcoordinfo())
889 inf[0] = unit
890 self._setcoordinfo(inf)
[1118]891 self._add_history("set_unit", varlist)
[113]892
[1862]893 @asaplog_post_dec
[484]894 def set_instrument(self, instr):
[1846]895 """\
[1348]896 Set the instrument for subsequent processing.
[1846]897
[358]898 Parameters:
[1846]899
[710]900 instr: Select from 'ATPKSMB', 'ATPKSHOH', 'ATMOPRA',
[407]901 'DSS-43' (Tid), 'CEDUNA', and 'HOBART'
[1846]902
[358]903 """
904 self._setInstrument(instr)
[1118]905 self._add_history("set_instument", vars())
[358]906
[1862]907 @asaplog_post_dec
[1190]908 def set_feedtype(self, feedtype):
[1846]909 """\
[1190]910 Overwrite the feed type, which might not be set correctly.
[1846]911
[1190]912 Parameters:
[1846]913
[1190]914 feedtype: 'linear' or 'circular'
[1846]915
[1190]916 """
917 self._setfeedtype(feedtype)
918 self._add_history("set_feedtype", vars())
919
[1862]920 @asaplog_post_dec
[276]921 def set_doppler(self, doppler='RADIO'):
[1846]922 """\
[276]923 Set the doppler for all following operations on this scantable.
[1846]924
[276]925 Parameters:
[1846]926
[276]927 doppler: One of 'RADIO', 'OPTICAL', 'Z', 'BETA', 'GAMMA'
[1846]928
[276]929 """
[484]930 varlist = vars()
[276]931 inf = list(self._getcoordinfo())
932 inf[2] = doppler
933 self._setcoordinfo(inf)
[1118]934 self._add_history("set_doppler", vars())
[710]935
[1862]936 @asaplog_post_dec
[226]937 def set_freqframe(self, frame=None):
[1846]938 """\
[113]939 Set the frame type of the Spectral Axis.
[1846]940
[113]941 Parameters:
[1846]942
[591]943 frame: an optional frame type, default 'LSRK'. Valid frames are:
[1819]944 'TOPO', 'LSRD', 'LSRK', 'BARY',
[1118]945 'GEO', 'GALACTO', 'LGROUP', 'CMB'
[1846]946
947 Example::
948
[113]949 scan.set_freqframe('BARY')
[1846]950
[113]951 """
[1593]952 frame = frame or rcParams['scantable.freqframe']
[484]953 varlist = vars()
[1819]954 # "REST" is not implemented in casacore
955 #valid = ['REST', 'TOPO', 'LSRD', 'LSRK', 'BARY', \
956 # 'GEO', 'GALACTO', 'LGROUP', 'CMB']
957 valid = ['TOPO', 'LSRD', 'LSRK', 'BARY', \
[1118]958 'GEO', 'GALACTO', 'LGROUP', 'CMB']
[591]959
[989]960 if frame in valid:
[113]961 inf = list(self._getcoordinfo())
962 inf[1] = frame
963 self._setcoordinfo(inf)
[1118]964 self._add_history("set_freqframe", varlist)
[102]965 else:
[1118]966 msg = "Please specify a valid freq type. Valid types are:\n", valid
[1859]967 raise TypeError(msg)
[710]968
[1862]969 @asaplog_post_dec
[989]970 def set_dirframe(self, frame=""):
[1846]971 """\
[989]972 Set the frame type of the Direction on the sky.
[1846]973
[989]974 Parameters:
[1846]975
[989]976 frame: an optional frame type, default ''. Valid frames are:
977 'J2000', 'B1950', 'GALACTIC'
[1846]978
979 Example:
980
[989]981 scan.set_dirframe('GALACTIC')
[1846]982
[989]983 """
984 varlist = vars()
[1859]985 Scantable.set_dirframe(self, frame)
[1118]986 self._add_history("set_dirframe", varlist)
[989]987
[113]988 def get_unit(self):
[1846]989 """\
[113]990 Get the default unit set in this scantable
[1846]991
[113]992 Returns:
[1846]993
[113]994 A unit string
[1846]995
[113]996 """
997 inf = self._getcoordinfo()
998 unit = inf[0]
999 if unit == '': unit = 'channel'
1000 return unit
[102]1001
[1862]1002 @asaplog_post_dec
[158]1003 def get_abcissa(self, rowno=0):
[1846]1004 """\
[158]1005 Get the abcissa in the current coordinate setup for the currently
[113]1006 selected Beam/IF/Pol
[1846]1007
[113]1008 Parameters:
[1846]1009
[226]1010 rowno: an optional row number in the scantable. Default is the
1011 first row, i.e. rowno=0
[1846]1012
[113]1013 Returns:
[1846]1014
[1348]1015 The abcissa values and the format string (as a dictionary)
[1846]1016
[113]1017 """
[256]1018 abc = self._getabcissa(rowno)
[710]1019 lbl = self._getabcissalabel(rowno)
[158]1020 return abc, lbl
[113]1021
[1862]1022 @asaplog_post_dec
[1994]1023 def flag(self, row=-1, mask=None, unflag=False):
[1846]1024 """\
[1001]1025 Flag the selected data using an optional channel mask.
[1846]1026
[1001]1027 Parameters:
[1846]1028
[1994]1029 row: an optional row number in the scantable.
1030 Default -1 flags all rows
1031
[1001]1032 mask: an optional channel mask, created with create_mask. Default
1033 (no mask) is all channels.
[1855]1034
[1819]1035 unflag: if True, unflag the data
[1846]1036
[1001]1037 """
1038 varlist = vars()
[1593]1039 mask = mask or []
[1994]1040 self._flag(row, mask, unflag)
[1001]1041 self._add_history("flag", varlist)
1042
[1862]1043 @asaplog_post_dec
[1819]1044 def flag_row(self, rows=[], unflag=False):
[1846]1045 """\
[1819]1046 Flag the selected data in row-based manner.
[1846]1047
[1819]1048 Parameters:
[1846]1049
[1843]1050 rows: list of row numbers to be flagged. Default is no row
1051 (must be explicitly specified to execute row-based flagging).
[1855]1052
[1819]1053 unflag: if True, unflag the data.
[1846]1054
[1819]1055 """
1056 varlist = vars()
[1859]1057 self._flag_row(rows, unflag)
[1819]1058 self._add_history("flag_row", varlist)
1059
[1862]1060 @asaplog_post_dec
[1819]1061 def clip(self, uthres=None, dthres=None, clipoutside=True, unflag=False):
[1846]1062 """\
[1819]1063 Flag the selected data outside a specified range (in channel-base)
[1846]1064
[1819]1065 Parameters:
[1846]1066
[1819]1067 uthres: upper threshold.
[1855]1068
[1819]1069 dthres: lower threshold
[1846]1070
[1819]1071 clipoutside: True for flagging data outside the range [dthres:uthres].
[1928]1072 False for flagging data inside the range.
[1855]1073
[1846]1074 unflag: if True, unflag the data.
1075
[1819]1076 """
1077 varlist = vars()
[1859]1078 self._clip(uthres, dthres, clipoutside, unflag)
[1819]1079 self._add_history("clip", varlist)
1080
[1862]1081 @asaplog_post_dec
[1584]1082 def lag_flag(self, start, end, unit="MHz", insitu=None):
[1846]1083 """\
[1192]1084 Flag the data in 'lag' space by providing a frequency to remove.
[2177]1085 Flagged data in the scantable get interpolated over the region.
[1192]1086 No taper is applied.
[1846]1087
[1192]1088 Parameters:
[1846]1089
[1579]1090 start: the start frequency (really a period within the
1091 bandwidth) or period to remove
[1855]1092
[1579]1093 end: the end frequency or period to remove
[1855]1094
[1584]1095 unit: the frequency unit (default "MHz") or "" for
[1579]1096 explicit lag channels
[1846]1097
1098 *Notes*:
1099
[1579]1100 It is recommended to flag edges of the band or strong
[1348]1101 signals beforehand.
[1846]1102
[1192]1103 """
1104 if insitu is None: insitu = rcParams['insitu']
1105 self._math._setinsitu(insitu)
1106 varlist = vars()
[1579]1107 base = { "GHz": 1000000000., "MHz": 1000000., "kHz": 1000., "Hz": 1.}
1108 if not (unit == "" or base.has_key(unit)):
[1192]1109 raise ValueError("%s is not a valid unit." % unit)
[1859]1110 if unit == "":
1111 s = scantable(self._math._lag_flag(self, start, end, "lags"))
1112 else:
1113 s = scantable(self._math._lag_flag(self, start*base[unit],
1114 end*base[unit], "frequency"))
[1192]1115 s._add_history("lag_flag", varlist)
1116 if insitu:
1117 self._assign(s)
1118 else:
1119 return s
[1001]1120
[1862]1121 @asaplog_post_dec
[2177]1122 def fft(self, getrealimag=False, rowno=[]):
1123 """\
1124 Apply FFT to the spectra.
1125 Flagged data in the scantable get interpolated over the region.
1126
1127 Parameters:
1128
1129 getrealimag: If True, returns the real and imaginary part
1130 values of the complex results.
1131 If False (the default), returns the amplitude
1132 (absolute value) normalised with Ndata/2 and
1133 phase (argument, in unit of radian).
1134
1135 rowno: The row number(s) to be processed. int, list
1136 and tuple are accepted. By default ([]), FFT
1137 is applied to the whole data.
1138
1139 Returns:
1140
1141 A dictionary containing two keys, i.e., 'real' and 'imag' for
1142 getrealimag = True, or 'ampl' and 'phase' for getrealimag = False,
1143 respectively.
1144 The value for each key is a list of lists containing the FFT
1145 results from each spectrum.
1146
1147 """
1148 if getrealimag:
1149 res = {"real":[], "imag":[]} # return real and imaginary values
1150 else:
1151 res = {"ampl":[], "phase":[]} # return amplitude and phase(argument)
1152
1153 if isinstance(rowno, int):
1154 rowno = [rowno]
1155 elif not (isinstance(rowno, list) or isinstance(rowno, tuple)):
1156 raise TypeError("The row number(s) should be int, list or tuple.")
1157
1158 nrow = len(rowno)
1159 if nrow == 0: nrow = self.nrow() # by default, calculate for all rows.
1160
1161 fspec = self._math._fft(self, rowno, getrealimag)
1162 nspec = len(fspec)/nrow
1163
1164 i = 0
1165 while (i < nrow):
1166 v1 = []
1167 v2 = []
1168
1169 j = 0
1170 while (j < nspec):
1171 k = i*nspec + j
1172 v1.append(fspec[k])
1173 v2.append(fspec[k+1])
1174 j += 2
1175
1176 if getrealimag:
1177 res["real"].append(v1)
1178 res["imag"].append(v2)
1179 else:
1180 res["ampl"].append(v1)
1181 res["phase"].append(v2)
1182
1183 i += 1
1184
1185 return res
1186
1187 @asaplog_post_dec
[113]1188 def create_mask(self, *args, **kwargs):
[1846]1189 """\
[1118]1190 Compute and return a mask based on [min, max] windows.
[189]1191 The specified windows are to be INCLUDED, when the mask is
[113]1192 applied.
[1846]1193
[102]1194 Parameters:
[1846]1195
[1118]1196 [min, max], [min2, max2], ...
[1024]1197 Pairs of start/end points (inclusive)specifying the regions
[102]1198 to be masked
[1855]1199
[189]1200 invert: optional argument. If specified as True,
1201 return an inverted mask, i.e. the regions
1202 specified are EXCLUDED
[1855]1203
[513]1204 row: create the mask using the specified row for
1205 unit conversions, default is row=0
1206 only necessary if frequency varies over rows.
[1846]1207
1208 Examples::
1209
[113]1210 scan.set_unit('channel')
[1846]1211 # a)
[1118]1212 msk = scan.create_mask([400, 500], [800, 900])
[189]1213 # masks everything outside 400 and 500
[113]1214 # and 800 and 900 in the unit 'channel'
1215
[1846]1216 # b)
[1118]1217 msk = scan.create_mask([400, 500], [800, 900], invert=True)
[189]1218 # masks the regions between 400 and 500
[113]1219 # and 800 and 900 in the unit 'channel'
[1846]1220
1221 # c)
1222 #mask only channel 400
[1554]1223 msk = scan.create_mask([400])
[1846]1224
[102]1225 """
[1554]1226 row = kwargs.get("row", 0)
[513]1227 data = self._getabcissa(row)
[113]1228 u = self._getcoordinfo()[0]
[1859]1229 if u == "":
1230 u = "channel"
1231 msg = "The current mask window unit is %s" % u
1232 i = self._check_ifs()
1233 if not i:
1234 msg += "\nThis mask is only valid for IF=%d" % (self.getif(i))
1235 asaplog.push(msg)
[102]1236 n = self.nchan()
[1295]1237 msk = _n_bools(n, False)
[710]1238 # test if args is a 'list' or a 'normal *args - UGLY!!!
1239
[1118]1240 ws = (isinstance(args[-1][-1], int) or isinstance(args[-1][-1], float)) \
1241 and args or args[0]
[710]1242 for window in ws:
[1554]1243 if len(window) == 1:
1244 window = [window[0], window[0]]
1245 if len(window) == 0 or len(window) > 2:
1246 raise ValueError("A window needs to be defined as [start(, end)]")
[1545]1247 if window[0] > window[1]:
1248 tmp = window[0]
1249 window[0] = window[1]
1250 window[1] = tmp
[102]1251 for i in range(n):
[1024]1252 if data[i] >= window[0] and data[i] <= window[1]:
[1295]1253 msk[i] = True
[113]1254 if kwargs.has_key('invert'):
1255 if kwargs.get('invert'):
[1295]1256 msk = mask_not(msk)
[102]1257 return msk
[710]1258
[1931]1259 def get_masklist(self, mask=None, row=0, silent=False):
[1846]1260 """\
[1819]1261 Compute and return a list of mask windows, [min, max].
[1846]1262
[1819]1263 Parameters:
[1846]1264
[1819]1265 mask: channel mask, created with create_mask.
[1855]1266
[1819]1267 row: calcutate the masklist using the specified row
1268 for unit conversions, default is row=0
1269 only necessary if frequency varies over rows.
[1846]1270
[1819]1271 Returns:
[1846]1272
[1819]1273 [min, max], [min2, max2], ...
1274 Pairs of start/end points (inclusive)specifying
1275 the masked regions
[1846]1276
[1819]1277 """
1278 if not (isinstance(mask,list) or isinstance(mask, tuple)):
1279 raise TypeError("The mask should be list or tuple.")
1280 if len(mask) < 2:
1281 raise TypeError("The mask elements should be > 1")
1282 if self.nchan() != len(mask):
1283 msg = "Number of channels in scantable != number of mask elements"
1284 raise TypeError(msg)
1285 data = self._getabcissa(row)
1286 u = self._getcoordinfo()[0]
[1859]1287 if u == "":
1288 u = "channel"
1289 msg = "The current mask window unit is %s" % u
1290 i = self._check_ifs()
1291 if not i:
1292 msg += "\nThis mask is only valid for IF=%d" % (self.getif(i))
[1931]1293 if not silent:
1294 asaplog.push(msg)
[1819]1295 masklist=[]
1296 ist, ien = None, None
1297 ist, ien=self.get_mask_indices(mask)
1298 if ist is not None and ien is not None:
1299 for i in xrange(len(ist)):
1300 range=[data[ist[i]],data[ien[i]]]
1301 range.sort()
1302 masklist.append([range[0],range[1]])
1303 return masklist
1304
1305 def get_mask_indices(self, mask=None):
[1846]1306 """\
[1819]1307 Compute and Return lists of mask start indices and mask end indices.
[1855]1308
1309 Parameters:
1310
[1819]1311 mask: channel mask, created with create_mask.
[1846]1312
[1819]1313 Returns:
[1846]1314
[1819]1315 List of mask start indices and that of mask end indices,
1316 i.e., [istart1,istart2,....], [iend1,iend2,....].
[1846]1317
[1819]1318 """
1319 if not (isinstance(mask,list) or isinstance(mask, tuple)):
1320 raise TypeError("The mask should be list or tuple.")
1321 if len(mask) < 2:
1322 raise TypeError("The mask elements should be > 1")
1323 istart=[]
1324 iend=[]
1325 if mask[0]: istart.append(0)
1326 for i in range(len(mask)-1):
1327 if not mask[i] and mask[i+1]:
1328 istart.append(i+1)
1329 elif mask[i] and not mask[i+1]:
1330 iend.append(i)
1331 if mask[len(mask)-1]: iend.append(len(mask)-1)
1332 if len(istart) != len(iend):
1333 raise RuntimeError("Numbers of mask start != mask end.")
1334 for i in range(len(istart)):
1335 if istart[i] > iend[i]:
1336 raise RuntimeError("Mask start index > mask end index")
1337 break
1338 return istart,iend
1339
[2013]1340 @asaplog_post_dec
1341 def parse_maskexpr(self,maskstring):
1342 """
1343 Parse CASA type mask selection syntax (IF dependent).
1344
1345 Parameters:
1346 maskstring : A string mask selection expression.
1347 A comma separated selections mean different IF -
1348 channel combinations. IFs and channel selections
1349 are partitioned by a colon, ':'.
1350 examples:
[2015]1351 '' = all IFs (all channels)
[2013]1352 '<2,4~6,9' = IFs 0,1,4,5,6,9 (all channels)
1353 '3:3~45;60' = channels 3 to 45 and 60 in IF 3
1354 '0~1:2~6,8' = channels 2 to 6 in IFs 0,1, and
1355 all channels in IF8
1356 Returns:
1357 A dictionary of selected (valid) IF and masklist pairs,
1358 e.g. {'0': [[50,250],[350,462]], '2': [[100,400],[550,974]]}
1359 """
1360 if not isinstance(maskstring,str):
1361 asaplog.post()
1362 asaplog.push("Invalid mask expression")
1363 asaplog.post("ERROR")
1364
1365 valid_ifs = self.getifnos()
1366 frequnit = self.get_unit()
1367 seldict = {}
[2015]1368 if maskstring == "":
1369 maskstring = str(valid_ifs)[1:-1]
[2013]1370 ## split each selection
1371 sellist = maskstring.split(',')
1372 for currselstr in sellist:
1373 selset = currselstr.split(':')
1374 # spw and mask string (may include ~, < or >)
1375 spwmasklist = self._parse_selection(selset[0],typestr='integer',
1376 offset=1,minval=min(valid_ifs),
1377 maxval=max(valid_ifs))
1378 for spwlist in spwmasklist:
1379 selspws = []
1380 for ispw in range(spwlist[0],spwlist[1]+1):
1381 # Put into the list only if ispw exists
1382 if valid_ifs.count(ispw):
1383 selspws.append(ispw)
1384 del spwmasklist, spwlist
1385
1386 # parse frequency mask list
1387 if len(selset) > 1:
1388 freqmasklist = self._parse_selection(selset[1],typestr='float',
1389 offset=0.)
1390 else:
1391 # want to select the whole spectrum
1392 freqmasklist = [None]
1393
1394 ## define a dictionary of spw - masklist combination
1395 for ispw in selspws:
1396 #print "working on", ispw
1397 spwstr = str(ispw)
1398 if len(selspws) == 0:
1399 # empty spw
1400 continue
1401 else:
1402 ## want to get min and max of the spw and
1403 ## offset to set for '<' and '>'
1404 if frequnit == 'channel':
1405 minfreq = 0
1406 maxfreq = self.nchan(ifno=ispw)
1407 offset = 0.5
1408 else:
1409 ## This is ugly part. need improvement
1410 for ifrow in xrange(self.nrow()):
1411 if self.getif(ifrow) == ispw:
1412 #print "IF",ispw,"found in row =",ifrow
1413 break
1414 freqcoord = self.get_coordinate(ifrow)
1415 freqs = self._getabcissa(ifrow)
1416 minfreq = min(freqs)
1417 maxfreq = max(freqs)
1418 if len(freqs) == 1:
1419 offset = 0.5
1420 elif frequnit.find('Hz') > 0:
1421 offset = abs(freqcoord.to_frequency(1,unit=frequnit)
1422 -freqcoord.to_frequency(0,unit=frequnit))*0.5
1423 elif frequnit.find('m/s') > 0:
1424 offset = abs(freqcoord.to_velocity(1,unit=frequnit)
1425 -freqcoord.to_velocity(0,unit=frequnit))*0.5
1426 else:
1427 asaplog.post()
1428 asaplog.push("Invalid frequency unit")
1429 asaplog.post("ERROR")
1430 del freqs, freqcoord, ifrow
1431 for freq in freqmasklist:
1432 selmask = freq or [minfreq, maxfreq]
1433 if selmask[0] == None:
1434 ## selection was "<freq[1]".
1435 if selmask[1] < minfreq:
1436 ## avoid adding region selection
1437 selmask = None
1438 else:
1439 selmask = [minfreq,selmask[1]-offset]
1440 elif selmask[1] == None:
1441 ## selection was ">freq[0]"
1442 if selmask[0] > maxfreq:
1443 ## avoid adding region selection
1444 selmask = None
1445 else:
1446 selmask = [selmask[0]+offset,maxfreq]
1447 if selmask:
1448 if not seldict.has_key(spwstr):
1449 # new spw selection
1450 seldict[spwstr] = []
1451 seldict[spwstr] += [selmask]
1452 del minfreq,maxfreq,offset,freq,selmask
1453 del spwstr
1454 del freqmasklist
1455 del valid_ifs
1456 if len(seldict) == 0:
1457 asaplog.post()
1458 asaplog.push("No valid selection in the mask expression: "+maskstring)
1459 asaplog.post("WARN")
1460 return None
1461 msg = "Selected masklist:\n"
1462 for sif, lmask in seldict.iteritems():
1463 msg += " IF"+sif+" - "+str(lmask)+"\n"
1464 asaplog.push(msg)
1465 return seldict
1466
1467 def _parse_selection(self,selstr,typestr='float',offset=0.,minval=None,maxval=None):
1468 """
1469 Parameters:
1470 selstr : The Selection string, e.g., '<3;5~7;100~103;9'
1471 typestr : The type of the values in returned list
1472 ('integer' or 'float')
1473 offset : The offset value to subtract from or add to
1474 the boundary value if the selection string
1475 includes '<' or '>'
1476 minval, maxval : The minimum/maximum values to set if the
1477 selection string includes '<' or '>'.
1478 The list element is filled with None by default.
1479 Returns:
1480 A list of min/max pair of selections.
1481 Example:
1482 _parseSelection('<3;5~7;9',typestr='int',offset=1,minval=0)
1483 returns [[0,2],[5,7],[9,9]]
1484 """
1485 selgroups = selstr.split(';')
1486 sellists = []
1487 if typestr.lower().startswith('int'):
1488 formatfunc = int
1489 else:
1490 formatfunc = float
1491
1492 for currsel in selgroups:
1493 if currsel.find('~') > 0:
1494 minsel = formatfunc(currsel.split('~')[0].strip())
1495 maxsel = formatfunc(currsel.split('~')[1].strip())
1496 elif currsel.strip().startswith('<'):
1497 minsel = minval
1498 maxsel = formatfunc(currsel.split('<')[1].strip()) \
1499 - formatfunc(offset)
1500 elif currsel.strip().startswith('>'):
1501 minsel = formatfunc(currsel.split('>')[1].strip()) \
1502 + formatfunc(offset)
1503 maxsel = maxval
1504 else:
1505 minsel = formatfunc(currsel)
1506 maxsel = formatfunc(currsel)
1507 sellists.append([minsel,maxsel])
1508 return sellists
1509
[1819]1510# def get_restfreqs(self):
1511# """
1512# Get the restfrequency(s) stored in this scantable.
1513# The return value(s) are always of unit 'Hz'
1514# Parameters:
1515# none
1516# Returns:
1517# a list of doubles
1518# """
1519# return list(self._getrestfreqs())
1520
1521 def get_restfreqs(self, ids=None):
[1846]1522 """\
[256]1523 Get the restfrequency(s) stored in this scantable.
1524 The return value(s) are always of unit 'Hz'
[1846]1525
[256]1526 Parameters:
[1846]1527
[1819]1528 ids: (optional) a list of MOLECULE_ID for that restfrequency(s) to
1529 be retrieved
[1846]1530
[256]1531 Returns:
[1846]1532
[1819]1533 dictionary containing ids and a list of doubles for each id
[1846]1534
[256]1535 """
[1819]1536 if ids is None:
1537 rfreqs={}
1538 idlist = self.getmolnos()
1539 for i in idlist:
1540 rfreqs[i]=list(self._getrestfreqs(i))
1541 return rfreqs
1542 else:
1543 if type(ids)==list or type(ids)==tuple:
1544 rfreqs={}
1545 for i in ids:
1546 rfreqs[i]=list(self._getrestfreqs(i))
1547 return rfreqs
1548 else:
1549 return list(self._getrestfreqs(ids))
1550 #return list(self._getrestfreqs(ids))
[102]1551
[931]1552 def set_restfreqs(self, freqs=None, unit='Hz'):
[1846]1553 """\
[931]1554 Set or replace the restfrequency specified and
[1938]1555 if the 'freqs' argument holds a scalar,
[931]1556 then that rest frequency will be applied to all the selected
1557 data. If the 'freqs' argument holds
1558 a vector, then it MUST be of equal or smaller length than
1559 the number of IFs (and the available restfrequencies will be
1560 replaced by this vector). In this case, *all* data have
1561 the restfrequency set per IF according
1562 to the corresponding value you give in the 'freqs' vector.
[1118]1563 E.g. 'freqs=[1e9, 2e9]' would mean IF 0 gets restfreq 1e9 and
[931]1564 IF 1 gets restfreq 2e9.
[1846]1565
[1395]1566 You can also specify the frequencies via a linecatalog.
[1153]1567
[931]1568 Parameters:
[1846]1569
[931]1570 freqs: list of rest frequency values or string idenitfiers
[1855]1571
[931]1572 unit: unit for rest frequency (default 'Hz')
[402]1573
[1846]1574
1575 Example::
1576
[1819]1577 # set the given restfrequency for the all currently selected IFs
[931]1578 scan.set_restfreqs(freqs=1.4e9)
[1845]1579 # set restfrequencies for the n IFs (n > 1) in the order of the
1580 # list, i.e
1581 # IF0 -> 1.4e9, IF1 -> 1.41e9, IF3 -> 1.42e9
1582 # len(list_of_restfreqs) == nIF
1583 # for nIF == 1 the following will set multiple restfrequency for
1584 # that IF
[1819]1585 scan.set_restfreqs(freqs=[1.4e9, 1.41e9, 1.42e9])
[1845]1586 # set multiple restfrequencies per IF. as a list of lists where
1587 # the outer list has nIF elements, the inner s arbitrary
1588 scan.set_restfreqs(freqs=[[1.4e9, 1.41e9], [1.67e9]])
[391]1589
[1846]1590 *Note*:
[1845]1591
[931]1592 To do more sophisticate Restfrequency setting, e.g. on a
1593 source and IF basis, use scantable.set_selection() before using
[1846]1594 this function::
[931]1595
[1846]1596 # provided your scantable is called scan
1597 selection = selector()
1598 selection.set_name("ORION*")
1599 selection.set_ifs([1])
1600 scan.set_selection(selection)
1601 scan.set_restfreqs(freqs=86.6e9)
1602
[931]1603 """
1604 varlist = vars()
[1157]1605 from asap import linecatalog
1606 # simple value
[1118]1607 if isinstance(freqs, int) or isinstance(freqs, float):
[1845]1608 self._setrestfreqs([freqs], [""], unit)
[1157]1609 # list of values
[1118]1610 elif isinstance(freqs, list) or isinstance(freqs, tuple):
[1157]1611 # list values are scalars
[1118]1612 if isinstance(freqs[-1], int) or isinstance(freqs[-1], float):
[1845]1613 if len(freqs) == 1:
1614 self._setrestfreqs(freqs, [""], unit)
1615 else:
1616 # allow the 'old' mode of setting mulitple IFs
1617 sel = selector()
1618 savesel = self._getselection()
1619 iflist = self.getifnos()
1620 if len(freqs)>len(iflist):
1621 raise ValueError("number of elements in list of list "
1622 "exeeds the current IF selections")
1623 iflist = self.getifnos()
1624 for i, fval in enumerate(freqs):
1625 sel.set_ifs(iflist[i])
1626 self._setselection(sel)
1627 self._setrestfreqs([fval], [""], unit)
1628 self._setselection(savesel)
1629
1630 # list values are dict, {'value'=, 'name'=)
[1157]1631 elif isinstance(freqs[-1], dict):
[1845]1632 values = []
1633 names = []
1634 for d in freqs:
1635 values.append(d["value"])
1636 names.append(d["name"])
1637 self._setrestfreqs(values, names, unit)
[1819]1638 elif isinstance(freqs[-1], list) or isinstance(freqs[-1], tuple):
[1157]1639 sel = selector()
1640 savesel = self._getselection()
[1322]1641 iflist = self.getifnos()
[1819]1642 if len(freqs)>len(iflist):
[1845]1643 raise ValueError("number of elements in list of list exeeds"
1644 " the current IF selections")
1645 for i, fval in enumerate(freqs):
[1322]1646 sel.set_ifs(iflist[i])
[1259]1647 self._setselection(sel)
[1845]1648 self._setrestfreqs(fval, [""], unit)
[1157]1649 self._setselection(savesel)
1650 # freqs are to be taken from a linecatalog
[1153]1651 elif isinstance(freqs, linecatalog):
1652 sel = selector()
1653 savesel = self._getselection()
1654 for i in xrange(freqs.nrow()):
[1322]1655 sel.set_ifs(iflist[i])
[1153]1656 self._setselection(sel)
[1845]1657 self._setrestfreqs([freqs.get_frequency(i)],
1658 [freqs.get_name(i)], "MHz")
[1153]1659 # ensure that we are not iterating past nIF
1660 if i == self.nif()-1: break
1661 self._setselection(savesel)
[931]1662 else:
1663 return
1664 self._add_history("set_restfreqs", varlist)
1665
[1360]1666 def shift_refpix(self, delta):
[1846]1667 """\
[1589]1668 Shift the reference pixel of the Spectra Coordinate by an
1669 integer amount.
[1846]1670
[1589]1671 Parameters:
[1846]1672
[1589]1673 delta: the amount to shift by
[1846]1674
1675 *Note*:
1676
[1589]1677 Be careful using this with broadband data.
[1846]1678
[1360]1679 """
[1731]1680 Scantable.shift_refpix(self, delta)
[931]1681
[1862]1682 @asaplog_post_dec
[1259]1683 def history(self, filename=None):
[1846]1684 """\
[1259]1685 Print the history. Optionally to a file.
[1846]1686
[1348]1687 Parameters:
[1846]1688
[1928]1689 filename: The name of the file to save the history to.
[1846]1690
[1259]1691 """
[484]1692 hist = list(self._gethistory())
[794]1693 out = "-"*80
[484]1694 for h in hist:
[489]1695 if h.startswith("---"):
[1857]1696 out = "\n".join([out, h])
[489]1697 else:
1698 items = h.split("##")
1699 date = items[0]
1700 func = items[1]
1701 items = items[2:]
[794]1702 out += "\n"+date+"\n"
1703 out += "Function: %s\n Parameters:" % (func)
[489]1704 for i in items:
[1938]1705 if i == '':
1706 continue
[489]1707 s = i.split("=")
[1118]1708 out += "\n %s = %s" % (s[0], s[1])
[1857]1709 out = "\n".join([out, "-"*80])
[1259]1710 if filename is not None:
1711 if filename is "":
1712 filename = 'scantable_history.txt'
1713 import os
1714 filename = os.path.expandvars(os.path.expanduser(filename))
1715 if not os.path.isdir(filename):
1716 data = open(filename, 'w')
1717 data.write(out)
1718 data.close()
1719 else:
1720 msg = "Illegal file name '%s'." % (filename)
[1859]1721 raise IOError(msg)
1722 return page(out)
[513]1723 #
1724 # Maths business
1725 #
[1862]1726 @asaplog_post_dec
[931]1727 def average_time(self, mask=None, scanav=False, weight='tint', align=False):
[1846]1728 """\
[1070]1729 Return the (time) weighted average of a scan.
[1846]1730
1731 *Note*:
1732
[1070]1733 in channels only - align if necessary
[1846]1734
[513]1735 Parameters:
[1846]1736
[513]1737 mask: an optional mask (only used for 'var' and 'tsys'
1738 weighting)
[1855]1739
[558]1740 scanav: True averages each scan separately
1741 False (default) averages all scans together,
[1855]1742
[1099]1743 weight: Weighting scheme.
1744 'none' (mean no weight)
1745 'var' (1/var(spec) weighted)
1746 'tsys' (1/Tsys**2 weighted)
1747 'tint' (integration time weighted)
1748 'tintsys' (Tint/Tsys**2)
1749 'median' ( median averaging)
[535]1750 The default is 'tint'
[1855]1751
[931]1752 align: align the spectra in velocity before averaging. It takes
1753 the time of the first spectrum as reference time.
[1846]1754
1755 Example::
1756
[513]1757 # time average the scantable without using a mask
[710]1758 newscan = scan.average_time()
[1846]1759
[513]1760 """
1761 varlist = vars()
[1593]1762 weight = weight or 'TINT'
1763 mask = mask or ()
1764 scanav = (scanav and 'SCAN') or 'NONE'
[1118]1765 scan = (self, )
[1859]1766
1767 if align:
1768 scan = (self.freq_align(insitu=False), )
1769 s = None
1770 if weight.upper() == 'MEDIAN':
1771 s = scantable(self._math._averagechannel(scan[0], 'MEDIAN',
1772 scanav))
1773 else:
1774 s = scantable(self._math._average(scan, mask, weight.upper(),
1775 scanav))
[1099]1776 s._add_history("average_time", varlist)
[513]1777 return s
[710]1778
[1862]1779 @asaplog_post_dec
[876]1780 def convert_flux(self, jyperk=None, eta=None, d=None, insitu=None):
[1846]1781 """\
[513]1782 Return a scan where all spectra are converted to either
1783 Jansky or Kelvin depending upon the flux units of the scan table.
1784 By default the function tries to look the values up internally.
1785 If it can't find them (or if you want to over-ride), you must
1786 specify EITHER jyperk OR eta (and D which it will try to look up
1787 also if you don't set it). jyperk takes precedence if you set both.
[1846]1788
[513]1789 Parameters:
[1846]1790
[513]1791 jyperk: the Jy / K conversion factor
[1855]1792
[513]1793 eta: the aperture efficiency
[1855]1794
[1928]1795 d: the geometric diameter (metres)
[1855]1796
[513]1797 insitu: if False a new scantable is returned.
1798 Otherwise, the scaling is done in-situ
1799 The default is taken from .asaprc (False)
[1846]1800
[513]1801 """
1802 if insitu is None: insitu = rcParams['insitu']
[876]1803 self._math._setinsitu(insitu)
[513]1804 varlist = vars()
[1593]1805 jyperk = jyperk or -1.0
1806 d = d or -1.0
1807 eta = eta or -1.0
[876]1808 s = scantable(self._math._convertflux(self, d, eta, jyperk))
1809 s._add_history("convert_flux", varlist)
1810 if insitu: self._assign(s)
1811 else: return s
[513]1812
[1862]1813 @asaplog_post_dec
[876]1814 def gain_el(self, poly=None, filename="", method="linear", insitu=None):
[1846]1815 """\
[513]1816 Return a scan after applying a gain-elevation correction.
1817 The correction can be made via either a polynomial or a
1818 table-based interpolation (and extrapolation if necessary).
1819 You specify polynomial coefficients, an ascii table or neither.
1820 If you specify neither, then a polynomial correction will be made
1821 with built in coefficients known for certain telescopes (an error
1822 will occur if the instrument is not known).
1823 The data and Tsys are *divided* by the scaling factors.
[1846]1824
[513]1825 Parameters:
[1846]1826
[513]1827 poly: Polynomial coefficients (default None) to compute a
1828 gain-elevation correction as a function of
1829 elevation (in degrees).
[1855]1830
[513]1831 filename: The name of an ascii file holding correction factors.
1832 The first row of the ascii file must give the column
1833 names and these MUST include columns
1834 "ELEVATION" (degrees) and "FACTOR" (multiply data
1835 by this) somewhere.
1836 The second row must give the data type of the
1837 column. Use 'R' for Real and 'I' for Integer.
1838 An example file would be
1839 (actual factors are arbitrary) :
1840
1841 TIME ELEVATION FACTOR
1842 R R R
1843 0.1 0 0.8
1844 0.2 20 0.85
1845 0.3 40 0.9
1846 0.4 60 0.85
1847 0.5 80 0.8
1848 0.6 90 0.75
[1855]1849
[513]1850 method: Interpolation method when correcting from a table.
1851 Values are "nearest", "linear" (default), "cubic"
1852 and "spline"
[1855]1853
[513]1854 insitu: if False a new scantable is returned.
1855 Otherwise, the scaling is done in-situ
1856 The default is taken from .asaprc (False)
[1846]1857
[513]1858 """
1859
1860 if insitu is None: insitu = rcParams['insitu']
[876]1861 self._math._setinsitu(insitu)
[513]1862 varlist = vars()
[1593]1863 poly = poly or ()
[513]1864 from os.path import expandvars
1865 filename = expandvars(filename)
[876]1866 s = scantable(self._math._gainel(self, poly, filename, method))
1867 s._add_history("gain_el", varlist)
[1593]1868 if insitu:
1869 self._assign(s)
1870 else:
1871 return s
[710]1872
[1862]1873 @asaplog_post_dec
[931]1874 def freq_align(self, reftime=None, method='cubic', insitu=None):
[1846]1875 """\
[513]1876 Return a scan where all rows have been aligned in frequency/velocity.
1877 The alignment frequency frame (e.g. LSRK) is that set by function
1878 set_freqframe.
[1846]1879
[513]1880 Parameters:
[1855]1881
[513]1882 reftime: reference time to align at. By default, the time of
1883 the first row of data is used.
[1855]1884
[513]1885 method: Interpolation method for regridding the spectra.
1886 Choose from "nearest", "linear", "cubic" (default)
1887 and "spline"
[1855]1888
[513]1889 insitu: if False a new scantable is returned.
1890 Otherwise, the scaling is done in-situ
1891 The default is taken from .asaprc (False)
[1846]1892
[513]1893 """
[931]1894 if insitu is None: insitu = rcParams["insitu"]
[876]1895 self._math._setinsitu(insitu)
[513]1896 varlist = vars()
[1593]1897 reftime = reftime or ""
[931]1898 s = scantable(self._math._freq_align(self, reftime, method))
[876]1899 s._add_history("freq_align", varlist)
1900 if insitu: self._assign(s)
1901 else: return s
[513]1902
[1862]1903 @asaplog_post_dec
[1725]1904 def opacity(self, tau=None, insitu=None):
[1846]1905 """\
[513]1906 Apply an opacity correction. The data
1907 and Tsys are multiplied by the correction factor.
[1846]1908
[513]1909 Parameters:
[1855]1910
[1689]1911 tau: (list of) opacity from which the correction factor is
[513]1912 exp(tau*ZD)
[1689]1913 where ZD is the zenith-distance.
1914 If a list is provided, it has to be of length nIF,
1915 nIF*nPol or 1 and in order of IF/POL, e.g.
1916 [opif0pol0, opif0pol1, opif1pol0 ...]
[1725]1917 if tau is `None` the opacities are determined from a
1918 model.
[1855]1919
[513]1920 insitu: if False a new scantable is returned.
1921 Otherwise, the scaling is done in-situ
1922 The default is taken from .asaprc (False)
[1846]1923
[513]1924 """
1925 if insitu is None: insitu = rcParams['insitu']
[876]1926 self._math._setinsitu(insitu)
[513]1927 varlist = vars()
[1689]1928 if not hasattr(tau, "__len__"):
1929 tau = [tau]
[876]1930 s = scantable(self._math._opacity(self, tau))
1931 s._add_history("opacity", varlist)
1932 if insitu: self._assign(s)
1933 else: return s
[513]1934
[1862]1935 @asaplog_post_dec
[513]1936 def bin(self, width=5, insitu=None):
[1846]1937 """\
[513]1938 Return a scan where all spectra have been binned up.
[1846]1939
[1348]1940 Parameters:
[1846]1941
[513]1942 width: The bin width (default=5) in pixels
[1855]1943
[513]1944 insitu: if False a new scantable is returned.
1945 Otherwise, the scaling is done in-situ
1946 The default is taken from .asaprc (False)
[1846]1947
[513]1948 """
1949 if insitu is None: insitu = rcParams['insitu']
[876]1950 self._math._setinsitu(insitu)
[513]1951 varlist = vars()
[876]1952 s = scantable(self._math._bin(self, width))
[1118]1953 s._add_history("bin", varlist)
[1589]1954 if insitu:
1955 self._assign(s)
1956 else:
1957 return s
[513]1958
[1862]1959 @asaplog_post_dec
[513]1960 def resample(self, width=5, method='cubic', insitu=None):
[1846]1961 """\
[1348]1962 Return a scan where all spectra have been binned up.
[1573]1963
[1348]1964 Parameters:
[1846]1965
[513]1966 width: The bin width (default=5) in pixels
[1855]1967
[513]1968 method: Interpolation method when correcting from a table.
1969 Values are "nearest", "linear", "cubic" (default)
1970 and "spline"
[1855]1971
[513]1972 insitu: if False a new scantable is returned.
1973 Otherwise, the scaling is done in-situ
1974 The default is taken from .asaprc (False)
[1846]1975
[513]1976 """
1977 if insitu is None: insitu = rcParams['insitu']
[876]1978 self._math._setinsitu(insitu)
[513]1979 varlist = vars()
[876]1980 s = scantable(self._math._resample(self, method, width))
[1118]1981 s._add_history("resample", varlist)
[876]1982 if insitu: self._assign(s)
1983 else: return s
[513]1984
[1862]1985 @asaplog_post_dec
[946]1986 def average_pol(self, mask=None, weight='none'):
[1846]1987 """\
[946]1988 Average the Polarisations together.
[1846]1989
[946]1990 Parameters:
[1846]1991
[946]1992 mask: An optional mask defining the region, where the
1993 averaging will be applied. The output will have all
1994 specified points masked.
[1855]1995
[946]1996 weight: Weighting scheme. 'none' (default), 'var' (1/var(spec)
1997 weighted), or 'tsys' (1/Tsys**2 weighted)
[1846]1998
[946]1999 """
2000 varlist = vars()
[1593]2001 mask = mask or ()
[1010]2002 s = scantable(self._math._averagepol(self, mask, weight.upper()))
[1118]2003 s._add_history("average_pol", varlist)
[992]2004 return s
[513]2005
[1862]2006 @asaplog_post_dec
[1145]2007 def average_beam(self, mask=None, weight='none'):
[1846]2008 """\
[1145]2009 Average the Beams together.
[1846]2010
[1145]2011 Parameters:
2012 mask: An optional mask defining the region, where the
2013 averaging will be applied. The output will have all
2014 specified points masked.
[1855]2015
[1145]2016 weight: Weighting scheme. 'none' (default), 'var' (1/var(spec)
2017 weighted), or 'tsys' (1/Tsys**2 weighted)
[1846]2018
[1145]2019 """
2020 varlist = vars()
[1593]2021 mask = mask or ()
[1145]2022 s = scantable(self._math._averagebeams(self, mask, weight.upper()))
2023 s._add_history("average_beam", varlist)
2024 return s
2025
[1586]2026 def parallactify(self, pflag):
[1846]2027 """\
[1843]2028 Set a flag to indicate whether this data should be treated as having
[1617]2029 been 'parallactified' (total phase == 0.0)
[1846]2030
[1617]2031 Parameters:
[1855]2032
[1843]2033 pflag: Bool indicating whether to turn this on (True) or
[1617]2034 off (False)
[1846]2035
[1617]2036 """
[1586]2037 varlist = vars()
2038 self._parallactify(pflag)
2039 self._add_history("parallactify", varlist)
2040
[1862]2041 @asaplog_post_dec
[992]2042 def convert_pol(self, poltype=None):
[1846]2043 """\
[992]2044 Convert the data to a different polarisation type.
[1565]2045 Note that you will need cross-polarisation terms for most conversions.
[1846]2046
[992]2047 Parameters:
[1855]2048
[992]2049 poltype: The new polarisation type. Valid types are:
[1565]2050 "linear", "circular", "stokes" and "linpol"
[1846]2051
[992]2052 """
2053 varlist = vars()
[1859]2054 s = scantable(self._math._convertpol(self, poltype))
[1118]2055 s._add_history("convert_pol", varlist)
[992]2056 return s
2057
[1862]2058 @asaplog_post_dec
[1819]2059 def smooth(self, kernel="hanning", width=5.0, order=2, plot=False, insitu=None):
[1846]2060 """\
[513]2061 Smooth the spectrum by the specified kernel (conserving flux).
[1846]2062
[513]2063 Parameters:
[1846]2064
[513]2065 kernel: The type of smoothing kernel. Select from
[1574]2066 'hanning' (default), 'gaussian', 'boxcar', 'rmedian'
2067 or 'poly'
[1855]2068
[513]2069 width: The width of the kernel in pixels. For hanning this is
2070 ignored otherwise it defauls to 5 pixels.
2071 For 'gaussian' it is the Full Width Half
2072 Maximum. For 'boxcar' it is the full width.
[1574]2073 For 'rmedian' and 'poly' it is the half width.
[1855]2074
[1574]2075 order: Optional parameter for 'poly' kernel (default is 2), to
2076 specify the order of the polnomial. Ignored by all other
2077 kernels.
[1855]2078
[1819]2079 plot: plot the original and the smoothed spectra.
2080 In this each indivual fit has to be approved, by
2081 typing 'y' or 'n'
[1855]2082
[513]2083 insitu: if False a new scantable is returned.
2084 Otherwise, the scaling is done in-situ
2085 The default is taken from .asaprc (False)
[1846]2086
[513]2087 """
2088 if insitu is None: insitu = rcParams['insitu']
[876]2089 self._math._setinsitu(insitu)
[513]2090 varlist = vars()
[1819]2091
2092 if plot: orgscan = self.copy()
2093
[1574]2094 s = scantable(self._math._smooth(self, kernel.lower(), width, order))
[876]2095 s._add_history("smooth", varlist)
[1819]2096
2097 if plot:
[2150]2098 from asap.asapplotter import new_asaplot
2099 theplot = new_asaplot(rcParams['plotter.gui'])
2100 theplot.set_panels()
[1819]2101 ylab=s._get_ordinate_label()
[2150]2102 #theplot.palette(0,["#777777","red"])
[1819]2103 for r in xrange(s.nrow()):
2104 xsm=s._getabcissa(r)
2105 ysm=s._getspectrum(r)
2106 xorg=orgscan._getabcissa(r)
2107 yorg=orgscan._getspectrum(r)
[2150]2108 theplot.clear()
2109 theplot.hold()
2110 theplot.set_axes('ylabel',ylab)
2111 theplot.set_axes('xlabel',s._getabcissalabel(r))
2112 theplot.set_axes('title',s._getsourcename(r))
2113 theplot.set_line(label='Original',color="#777777")
2114 theplot.plot(xorg,yorg)
2115 theplot.set_line(label='Smoothed',color="red")
2116 theplot.plot(xsm,ysm)
[1819]2117 ### Ugly part for legend
2118 for i in [0,1]:
[2150]2119 theplot.subplots[0]['lines'].append([theplot.subplots[0]['axes'].lines[i]])
2120 theplot.release()
[1819]2121 ### Ugly part for legend
[2150]2122 theplot.subplots[0]['lines']=[]
[1819]2123 res = raw_input("Accept smoothing ([y]/n): ")
2124 if res.upper() == 'N':
2125 s._setspectrum(yorg, r)
[2150]2126 theplot.quit()
2127 del theplot
[1819]2128 del orgscan
2129
[876]2130 if insitu: self._assign(s)
2131 else: return s
[513]2132
[2012]2133
[1862]2134 @asaplog_post_dec
[2081]2135 def sinusoid_baseline(self, insitu=None, mask=None, nwave=None, maxwavelength=None,
2136 clipthresh=None, clipniter=None, plot=None, getresidual=None, outlog=None, blfile=None):
[2047]2137 """\
[2094]2138 Return a scan which has been baselined (all rows) with sinusoidal functions.
[2047]2139 Parameters:
[2081]2140 insitu: If False a new scantable is returned.
2141 Otherwise, the scaling is done in-situ
2142 The default is taken from .asaprc (False)
2143 mask: An optional mask
2144 nwave: the maximum wave number of sinusoids within
2145 maxwavelength * (spectral range).
2146 The default is 3 (i.e., sinusoids with wave
2147 number of 0(=constant), 1, 2, and 3 are
2148 used for fitting). Also it is possible to
2149 explicitly specify all the wave numbers to
2150 be used, by giving a list including them
2151 (e.g. [0,1,2,15,16]).
2152 maxwavelength: the longest sinusoidal wavelength. The
2153 default is 1.0 (unit: spectral range).
2154 clipthresh: Clipping threshold. (default is 3.0, unit: sigma)
[2129]2155 clipniter: maximum number of iteration of 'clipthresh'-sigma clipping (default is 0)
[2081]2156 plot: *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
2157 plot the fit and the residual. In this each
2158 indivual fit has to be approved, by typing 'y'
2159 or 'n'
2160 getresidual: if False, returns best-fit values instead of
2161 residual. (default is True)
2162 outlog: Output the coefficients of the best-fit
2163 function to logger (default is False)
2164 blfile: Name of a text file in which the best-fit
2165 parameter values to be written
2166 (default is "": no file/logger output)
[2047]2167
2168 Example:
2169 # return a scan baselined by a combination of sinusoidal curves having
[2081]2170 # wave numbers in spectral window up to 10,
[2047]2171 # also with 3-sigma clipping, iteration up to 4 times
[2081]2172 bscan = scan.sinusoid_baseline(nwave=10,clipthresh=3.0,clipniter=4)
2173
2174 Note:
2175 The best-fit parameter values output in logger and/or blfile are now
2176 based on specunit of 'channel'.
[2047]2177 """
2178
2179 varlist = vars()
2180
2181 if insitu is None: insitu = rcParams["insitu"]
2182 if insitu:
2183 workscan = self
2184 else:
2185 workscan = self.copy()
2186
2187 nchan = workscan.nchan()
2188
[2081]2189 if mask is None: mask = [True for i in xrange(nchan)]
2190 if nwave is None: nwave = 3
2191 if maxwavelength is None: maxwavelength = 1.0
2192 if clipthresh is None: clipthresh = 3.0
[2129]2193 if clipniter is None: clipniter = 0
[2081]2194 if plot is None: plot = False
2195 if getresidual is None: getresidual = True
2196 if outlog is None: outlog = False
2197 if blfile is None: blfile = ""
[2047]2198
[2081]2199 if isinstance(nwave, int):
2200 in_nwave = nwave
2201 nwave = []
2202 for i in xrange(in_nwave+1): nwave.append(i)
2203
[2047]2204 outblfile = (blfile != "") and os.path.exists(os.path.expanduser(os.path.expandvars(blfile)))
2205
2206 try:
[2081]2207 #CURRENTLY, PLOT=true is UNAVAILABLE UNTIL sinusoidal fitting is implemented as a fitter method.
2208 workscan._sinusoid_baseline(mask, nwave, maxwavelength, clipthresh, clipniter, getresidual, outlog, blfile)
[2047]2209
2210 workscan._add_history("sinusoid_baseline", varlist)
2211
2212 if insitu:
2213 self._assign(workscan)
2214 else:
2215 return workscan
2216
2217 except RuntimeError, e:
2218 msg = "The fit failed, possibly because it didn't converge."
2219 if rcParams["verbose"]:
2220 asaplog.push(str(e))
2221 asaplog.push(str(msg))
2222 return
2223 else:
2224 raise RuntimeError(str(e)+'\n'+msg)
2225
2226
[2081]2227 def auto_sinusoid_baseline(self, insitu=None, mask=None, nwave=None, maxwavelength=None,
[2047]2228 clipthresh=None, clipniter=None, edge=None, threshold=None,
[2081]2229 chan_avg_limit=None, plot=None, getresidual=None, outlog=None, blfile=None):
[2047]2230 """\
[2094]2231 Return a scan which has been baselined (all rows) with sinusoidal functions.
[2047]2232 Spectral lines are detected first using linefinder and masked out
2233 to avoid them affecting the baseline solution.
2234
2235 Parameters:
[2081]2236 insitu: if False a new scantable is returned.
2237 Otherwise, the scaling is done in-situ
2238 The default is taken from .asaprc (False)
2239 mask: an optional mask retreived from scantable
2240 nwave: the maximum wave number of sinusoids within
2241 maxwavelength * (spectral range).
2242 The default is 3 (i.e., sinusoids with wave
2243 number of 0(=constant), 1, 2, and 3 are
2244 used for fitting). Also it is possible to
2245 explicitly specify all the wave numbers to
2246 be used, by giving a list including them
2247 (e.g. [0,1,2,15,16]).
2248 maxwavelength: the longest sinusoidal wavelength. The
2249 default is 1.0 (unit: spectral range).
2250 clipthresh: Clipping threshold. (default is 3.0, unit: sigma)
[2129]2251 clipniter: maximum number of iteration of 'clipthresh'-sigma clipping (default is 0)
[2081]2252 edge: an optional number of channel to drop at
2253 the edge of spectrum. If only one value is
2254 specified, the same number will be dropped
2255 from both sides of the spectrum. Default
2256 is to keep all channels. Nested tuples
2257 represent individual edge selection for
2258 different IFs (a number of spectral channels
2259 can be different)
2260 threshold: the threshold used by line finder. It is
2261 better to keep it large as only strong lines
2262 affect the baseline solution.
2263 chan_avg_limit:a maximum number of consequtive spectral
2264 channels to average during the search of
2265 weak and broad lines. The default is no
2266 averaging (and no search for weak lines).
2267 If such lines can affect the fitted baseline
2268 (e.g. a high order polynomial is fitted),
2269 increase this parameter (usually values up
2270 to 8 are reasonable). Most users of this
2271 method should find the default value sufficient.
2272 plot: *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
2273 plot the fit and the residual. In this each
2274 indivual fit has to be approved, by typing 'y'
2275 or 'n'
2276 getresidual: if False, returns best-fit values instead of
2277 residual. (default is True)
2278 outlog: Output the coefficients of the best-fit
2279 function to logger (default is False)
2280 blfile: Name of a text file in which the best-fit
2281 parameter values to be written
2282 (default is "": no file/logger output)
[2047]2283
2284 Example:
[2081]2285 bscan = scan.auto_sinusoid_baseline(nwave=10, insitu=False)
2286
2287 Note:
2288 The best-fit parameter values output in logger and/or blfile are now
2289 based on specunit of 'channel'.
[2047]2290 """
2291
2292 varlist = vars()
2293
2294 if insitu is None: insitu = rcParams['insitu']
2295 if insitu:
2296 workscan = self
2297 else:
2298 workscan = self.copy()
2299
2300 nchan = workscan.nchan()
2301
[2081]2302 if mask is None: mask = [True for i in xrange(nchan)]
2303 if nwave is None: nwave = 3
2304 if maxwavelength is None: maxwavelength = 1.0
2305 if clipthresh is None: clipthresh = 3.0
[2129]2306 if clipniter is None: clipniter = 0
[2081]2307 if edge is None: edge = (0,0)
2308 if threshold is None: threshold = 3
[2047]2309 if chan_avg_limit is None: chan_avg_limit = 1
[2081]2310 if plot is None: plot = False
2311 if getresidual is None: getresidual = True
2312 if outlog is None: outlog = False
2313 if blfile is None: blfile = ""
[2047]2314
2315 outblfile = (blfile != "") and os.path.exists(os.path.expanduser(os.path.expandvars(blfile)))
2316
2317 from asap.asaplinefind import linefinder
2318 from asap import _is_sequence_or_number as _is_valid
2319
[2081]2320 if isinstance(nwave, int):
2321 in_nwave = nwave
2322 nwave = []
2323 for i in xrange(in_nwave+1): nwave.append(i)
2324
[2047]2325 if not (isinstance(edge, list) or isinstance(edge, tuple)): edge = [ edge ]
2326 individualedge = False;
2327 if len(edge) > 1: individualedge = isinstance(edge[0], list) or isinstance(edge[0], tuple)
2328
2329 if individualedge:
2330 for edgepar in edge:
2331 if not _is_valid(edgepar, int):
2332 raise ValueError, "Each element of the 'edge' tuple has \
2333 to be a pair of integers or an integer."
2334 else:
2335 if not _is_valid(edge, int):
2336 raise ValueError, "Parameter 'edge' has to be an integer or a \
2337 pair of integers specified as a tuple. \
2338 Nested tuples are allowed \
2339 to make individual selection for different IFs."
2340
2341 if len(edge) > 1:
2342 curedge = edge
2343 else:
2344 curedge = edge + edge
2345
2346 try:
[2081]2347 #CURRENTLY, PLOT=true is UNAVAILABLE UNTIL sinusoidal fitting is implemented as a fitter method.
[2047]2348 if individualedge:
2349 curedge = []
2350 for i in xrange(len(edge)):
2351 curedge += edge[i]
2352
[2081]2353 workscan._auto_sinusoid_baseline(mask, nwave, maxwavelength, clipthresh, clipniter, curedge, threshold, chan_avg_limit, getresidual, outlog, blfile)
[2047]2354
2355 workscan._add_history("auto_sinusoid_baseline", varlist)
2356
2357 if insitu:
2358 self._assign(workscan)
2359 else:
2360 return workscan
2361
2362 except RuntimeError, e:
2363 msg = "The fit failed, possibly because it didn't converge."
2364 if rcParams["verbose"]:
2365 asaplog.push(str(e))
2366 asaplog.push(str(msg))
2367 return
2368 else:
2369 raise RuntimeError(str(e)+'\n'+msg)
2370
2371
2372 @asaplog_post_dec
[2094]2373 def cspline_baseline(self, insitu=None, mask=None, npiece=None,
2374 clipthresh=None, clipniter=None, plot=None, getresidual=None, outlog=None, blfile=None):
[1846]2375 """\
[2012]2376 Return a scan which has been baselined (all rows) by cubic spline function (piecewise cubic polynomial).
[513]2377 Parameters:
[2012]2378 insitu: If False a new scantable is returned.
2379 Otherwise, the scaling is done in-situ
2380 The default is taken from .asaprc (False)
2381 mask: An optional mask
2382 npiece: Number of pieces. (default is 2)
2383 clipthresh: Clipping threshold. (default is 3.0, unit: sigma)
[2129]2384 clipniter: maximum number of iteration of 'clipthresh'-sigma clipping (default is 0)
[2012]2385 plot: *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
2386 plot the fit and the residual. In this each
2387 indivual fit has to be approved, by typing 'y'
2388 or 'n'
[2094]2389 getresidual:if False, returns best-fit values instead of
2390 residual. (default is True)
[2012]2391 outlog: Output the coefficients of the best-fit
2392 function to logger (default is False)
2393 blfile: Name of a text file in which the best-fit
2394 parameter values to be written
2395 (default is "": no file/logger output)
[1846]2396
[2012]2397 Example:
2398 # return a scan baselined by a cubic spline consisting of 2 pieces (i.e., 1 internal knot),
2399 # also with 3-sigma clipping, iteration up to 4 times
2400 bscan = scan.cspline_baseline(npiece=2,clipthresh=3.0,clipniter=4)
[2081]2401
2402 Note:
2403 The best-fit parameter values output in logger and/or blfile are now
2404 based on specunit of 'channel'.
[2012]2405 """
2406
2407 varlist = vars()
2408
2409 if insitu is None: insitu = rcParams["insitu"]
2410 if insitu:
2411 workscan = self
2412 else:
2413 workscan = self.copy()
[1855]2414
[2012]2415 nchan = workscan.nchan()
2416
[2094]2417 if mask is None: mask = [True for i in xrange(nchan)]
2418 if npiece is None: npiece = 2
2419 if clipthresh is None: clipthresh = 3.0
[2129]2420 if clipniter is None: clipniter = 0
[2094]2421 if plot is None: plot = False
2422 if getresidual is None: getresidual = True
2423 if outlog is None: outlog = False
2424 if blfile is None: blfile = ""
[1855]2425
[2012]2426 outblfile = (blfile != "") and os.path.exists(os.path.expanduser(os.path.expandvars(blfile)))
2427
2428 try:
2429 #CURRENTLY, PLOT=true UNAVAILABLE UNTIL cubic spline fitting is implemented as a fitter method.
[2094]2430 workscan._cspline_baseline(mask, npiece, clipthresh, clipniter, getresidual, outlog, blfile)
[2012]2431
2432 workscan._add_history("cspline_baseline", varlist)
2433
2434 if insitu:
2435 self._assign(workscan)
2436 else:
2437 return workscan
2438
2439 except RuntimeError, e:
2440 msg = "The fit failed, possibly because it didn't converge."
2441 if rcParams["verbose"]:
2442 asaplog.push(str(e))
2443 asaplog.push(str(msg))
2444 return
2445 else:
2446 raise RuntimeError(str(e)+'\n'+msg)
[1855]2447
2448
[2012]2449 def auto_cspline_baseline(self, insitu=None, mask=None, npiece=None, clipthresh=None,
2450 clipniter=None, edge=None, threshold=None,
[2094]2451 chan_avg_limit=None, getresidual=None, plot=None, outlog=None, blfile=None):
[2012]2452 """\
2453 Return a scan which has been baselined (all rows) by cubic spline
2454 function (piecewise cubic polynomial).
2455 Spectral lines are detected first using linefinder and masked out
2456 to avoid them affecting the baseline solution.
2457
2458 Parameters:
[794]2459 insitu: if False a new scantable is returned.
2460 Otherwise, the scaling is done in-situ
2461 The default is taken from .asaprc (False)
[2012]2462 mask: an optional mask retreived from scantable
2463 npiece: Number of pieces. (default is 2)
2464 clipthresh: Clipping threshold. (default is 3.0, unit: sigma)
[2129]2465 clipniter: maximum number of iteration of 'clipthresh'-sigma clipping (default is 0)
[2012]2466 edge: an optional number of channel to drop at
2467 the edge of spectrum. If only one value is
2468 specified, the same number will be dropped
2469 from both sides of the spectrum. Default
2470 is to keep all channels. Nested tuples
2471 represent individual edge selection for
2472 different IFs (a number of spectral channels
2473 can be different)
2474 threshold: the threshold used by line finder. It is
2475 better to keep it large as only strong lines
2476 affect the baseline solution.
2477 chan_avg_limit:
2478 a maximum number of consequtive spectral
2479 channels to average during the search of
2480 weak and broad lines. The default is no
2481 averaging (and no search for weak lines).
2482 If such lines can affect the fitted baseline
2483 (e.g. a high order polynomial is fitted),
2484 increase this parameter (usually values up
2485 to 8 are reasonable). Most users of this
2486 method should find the default value sufficient.
2487 plot: *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
2488 plot the fit and the residual. In this each
2489 indivual fit has to be approved, by typing 'y'
2490 or 'n'
[2094]2491 getresidual:if False, returns best-fit values instead of
2492 residual. (default is True)
[2012]2493 outlog: Output the coefficients of the best-fit
2494 function to logger (default is False)
2495 blfile: Name of a text file in which the best-fit
2496 parameter values to be written
2497 (default is "": no file/logger output)
[1846]2498
[1907]2499 Example:
[2012]2500 bscan = scan.auto_cspline_baseline(npiece=3, insitu=False)
[2081]2501
2502 Note:
2503 The best-fit parameter values output in logger and/or blfile are now
2504 based on specunit of 'channel'.
[2012]2505 """
[1846]2506
[2012]2507 varlist = vars()
2508
[513]2509 if insitu is None: insitu = rcParams['insitu']
[2012]2510 if insitu:
2511 workscan = self
2512 else:
[1819]2513 workscan = self.copy()
[2012]2514
2515 nchan = workscan.nchan()
2516
[2094]2517 if mask is None: mask = [True for i in xrange(nchan)]
2518 if npiece is None: npiece = 2
2519 if clipthresh is None: clipthresh = 3.0
[2129]2520 if clipniter is None: clipniter = 0
[2094]2521 if edge is None: edge = (0, 0)
2522 if threshold is None: threshold = 3
[2012]2523 if chan_avg_limit is None: chan_avg_limit = 1
[2094]2524 if plot is None: plot = False
2525 if getresidual is None: getresidual = True
2526 if outlog is None: outlog = False
2527 if blfile is None: blfile = ""
[2012]2528
2529 outblfile = (blfile != "") and os.path.exists(os.path.expanduser(os.path.expandvars(blfile)))
2530
2531 from asap.asaplinefind import linefinder
2532 from asap import _is_sequence_or_number as _is_valid
2533
2534 if not (isinstance(edge, list) or isinstance(edge, tuple)): edge = [ edge ]
2535 individualedge = False;
2536 if len(edge) > 1: individualedge = isinstance(edge[0], list) or isinstance(edge[0], tuple)
2537
2538 if individualedge:
2539 for edgepar in edge:
2540 if not _is_valid(edgepar, int):
2541 raise ValueError, "Each element of the 'edge' tuple has \
2542 to be a pair of integers or an integer."
[1819]2543 else:
[2012]2544 if not _is_valid(edge, int):
2545 raise ValueError, "Parameter 'edge' has to be an integer or a \
2546 pair of integers specified as a tuple. \
2547 Nested tuples are allowed \
2548 to make individual selection for different IFs."
[1819]2549
[2012]2550 if len(edge) > 1:
2551 curedge = edge
[1391]2552 else:
[2012]2553 curedge = edge + edge
[1819]2554
[2012]2555 try:
2556 #CURRENTLY, PLOT=true UNAVAILABLE UNTIL cubic spline fitting is implemented as a fitter method.
2557 if individualedge:
2558 curedge = []
2559 for i in xrange(len(edge)):
2560 curedge += edge[i]
2561
[2094]2562 workscan._auto_cspline_baseline(mask, npiece, clipthresh, clipniter, curedge, threshold, chan_avg_limit, getresidual, outlog, blfile)
[2012]2563
2564 workscan._add_history("auto_cspline_baseline", varlist)
[1907]2565
[1856]2566 if insitu:
2567 self._assign(workscan)
2568 else:
2569 return workscan
[2012]2570
2571 except RuntimeError, e:
[1217]2572 msg = "The fit failed, possibly because it didn't converge."
[2012]2573 if rcParams["verbose"]:
2574 asaplog.push(str(e))
2575 asaplog.push(str(msg))
2576 return
2577 else:
2578 raise RuntimeError(str(e)+'\n'+msg)
[513]2579
[2012]2580
[1931]2581 @asaplog_post_dec
[2094]2582 def poly_baseline(self, insitu=None, mask=None, order=None, plot=None, getresidual=None, outlog=None, blfile=None):
[1907]2583 """\
2584 Return a scan which has been baselined (all rows) by a polynomial.
2585 Parameters:
[2012]2586 insitu: if False a new scantable is returned.
2587 Otherwise, the scaling is done in-situ
2588 The default is taken from .asaprc (False)
[1907]2589 mask: an optional mask
2590 order: the order of the polynomial (default is 0)
2591 plot: plot the fit and the residual. In this each
2592 indivual fit has to be approved, by typing 'y'
[2012]2593 or 'n'
[2094]2594 getresidual:if False, returns best-fit values instead of
2595 residual. (default is True)
[2012]2596 outlog: Output the coefficients of the best-fit
2597 function to logger (default is False)
2598 blfile: Name of a text file in which the best-fit
2599 parameter values to be written
2600 (default is "": no file/logger output)
2601
[1907]2602 Example:
2603 # return a scan baselined by a third order polynomial,
2604 # not using a mask
2605 bscan = scan.poly_baseline(order=3)
2606 """
[1931]2607
2608 varlist = vars()
2609
[1907]2610 if insitu is None: insitu = rcParams["insitu"]
2611 if insitu:
2612 workscan = self
2613 else:
2614 workscan = self.copy()
2615
2616 nchan = workscan.nchan()
2617
[2094]2618 if mask is None: mask = [True for i in xrange(nchan)]
2619 if order is None: order = 0
2620 if plot is None: plot = False
2621 if getresidual is None: getresidual = True
2622 if outlog is None: outlog = False
2623 if blfile is None: blfile = ""
[1907]2624
[2012]2625 outblfile = (blfile != "") and os.path.exists(os.path.expanduser(os.path.expandvars(blfile)))
2626
[1907]2627 try:
[2012]2628 rows = xrange(workscan.nrow())
[1907]2629
[2012]2630 #if len(rows) > 0: workscan._init_blinfo()
[1907]2631
[2012]2632 if plot:
2633 if outblfile: blf = open(blfile, "a")
2634
[1907]2635 f = fitter()
2636 f.set_function(lpoly=order)
2637 for r in rows:
2638 f.x = workscan._getabcissa(r)
2639 f.y = workscan._getspectrum(r)
2640 f.mask = mask_and(mask, workscan._getmask(r)) # (CAS-1434)
2641 f.data = None
2642 f.fit()
2643
2644 f.plot(residual=True)
2645 accept_fit = raw_input("Accept fit ( [y]/n ): ")
2646 if accept_fit.upper() == "N":
[2012]2647 #workscan._append_blinfo(None, None, None)
[1907]2648 continue
[2012]2649
2650 blpars = f.get_parameters()
2651 masklist = workscan.get_masklist(f.mask, row=r, silent=True)
2652 #workscan._append_blinfo(blpars, masklist, f.mask)
[2094]2653 workscan._setspectrum((f.fitter.getresidual() if getresidual else f.fitter.getfit()), r)
[1907]2654
[2012]2655 if outblfile:
2656 rms = workscan.get_rms(f.mask, r)
2657 dataout = workscan.format_blparams_row(blpars["params"], blpars["fixed"], rms, str(masklist), r, True)
2658 blf.write(dataout)
2659
[1907]2660 f._p.unmap()
2661 f._p = None
[2012]2662
2663 if outblfile: blf.close()
[1907]2664 else:
[2094]2665 workscan._poly_baseline(mask, order, getresidual, outlog, blfile)
[1907]2666
2667 workscan._add_history("poly_baseline", varlist)
2668
2669 if insitu:
2670 self._assign(workscan)
2671 else:
2672 return workscan
2673
[1919]2674 except RuntimeError, e:
[1907]2675 msg = "The fit failed, possibly because it didn't converge."
2676 if rcParams["verbose"]:
[1919]2677 asaplog.push(str(e))
[1907]2678 asaplog.push(str(msg))
2679 return
2680 else:
[1919]2681 raise RuntimeError(str(e)+'\n'+msg)
[1907]2682
2683
[2012]2684 def auto_poly_baseline(self, insitu=None, mask=None, order=None, edge=None, threshold=None,
[2094]2685 chan_avg_limit=None, plot=None, getresidual=None, outlog=None, blfile=None):
[1846]2686 """\
[1931]2687 Return a scan which has been baselined (all rows) by a polynomial.
[880]2688 Spectral lines are detected first using linefinder and masked out
2689 to avoid them affecting the baseline solution.
2690
2691 Parameters:
[2012]2692 insitu: if False a new scantable is returned.
2693 Otherwise, the scaling is done in-situ
2694 The default is taken from .asaprc (False)
[880]2695 mask: an optional mask retreived from scantable
2696 order: the order of the polynomial (default is 0)
[2012]2697 edge: an optional number of channel to drop at
2698 the edge of spectrum. If only one value is
2699 specified, the same number will be dropped
2700 from both sides of the spectrum. Default
2701 is to keep all channels. Nested tuples
2702 represent individual edge selection for
2703 different IFs (a number of spectral channels
2704 can be different)
2705 threshold: the threshold used by line finder. It is
2706 better to keep it large as only strong lines
2707 affect the baseline solution.
[1280]2708 chan_avg_limit:
[2012]2709 a maximum number of consequtive spectral
2710 channels to average during the search of
2711 weak and broad lines. The default is no
2712 averaging (and no search for weak lines).
2713 If such lines can affect the fitted baseline
2714 (e.g. a high order polynomial is fitted),
2715 increase this parameter (usually values up
2716 to 8 are reasonable). Most users of this
2717 method should find the default value sufficient.
[1061]2718 plot: plot the fit and the residual. In this each
2719 indivual fit has to be approved, by typing 'y'
2720 or 'n'
[2094]2721 getresidual:if False, returns best-fit values instead of
2722 residual. (default is True)
[2012]2723 outlog: Output the coefficients of the best-fit
2724 function to logger (default is False)
2725 blfile: Name of a text file in which the best-fit
2726 parameter values to be written
2727 (default is "": no file/logger output)
[1846]2728
[2012]2729 Example:
2730 bscan = scan.auto_poly_baseline(order=7, insitu=False)
2731 """
[880]2732
[2012]2733 varlist = vars()
[1846]2734
[2012]2735 if insitu is None: insitu = rcParams['insitu']
2736 if insitu:
2737 workscan = self
2738 else:
2739 workscan = self.copy()
[1846]2740
[2012]2741 nchan = workscan.nchan()
2742
[2094]2743 if mask is None: mask = [True for i in xrange(nchan)]
2744 if order is None: order = 0
2745 if edge is None: edge = (0, 0)
2746 if threshold is None: threshold = 3
[2012]2747 if chan_avg_limit is None: chan_avg_limit = 1
[2094]2748 if plot is None: plot = False
2749 if getresidual is None: getresidual = True
2750 if outlog is None: outlog = False
2751 if blfile is None: blfile = ""
[1846]2752
[2012]2753 outblfile = (blfile != "") and os.path.exists(os.path.expanduser(os.path.expandvars(blfile)))
2754
[880]2755 from asap.asaplinefind import linefinder
2756 from asap import _is_sequence_or_number as _is_valid
2757
[2012]2758 if not (isinstance(edge, list) or isinstance(edge, tuple)): edge = [ edge ]
[1118]2759 individualedge = False;
[2012]2760 if len(edge) > 1: individualedge = isinstance(edge[0], list) or isinstance(edge[0], tuple)
[907]2761
[1118]2762 if individualedge:
2763 for edgepar in edge:
2764 if not _is_valid(edgepar, int):
2765 raise ValueError, "Each element of the 'edge' tuple has \
2766 to be a pair of integers or an integer."
[907]2767 else:
[2012]2768 if not _is_valid(edge, int):
2769 raise ValueError, "Parameter 'edge' has to be an integer or a \
2770 pair of integers specified as a tuple. \
2771 Nested tuples are allowed \
2772 to make individual selection for different IFs."
[880]2773
[2012]2774 if len(edge) > 1:
2775 curedge = edge
2776 else:
2777 curedge = edge + edge
[1907]2778
[2012]2779 try:
2780 rows = xrange(workscan.nrow())
2781
2782 #if len(rows) > 0: workscan._init_blinfo()
[880]2783
[2012]2784 if plot:
2785 if outblfile: blf = open(blfile, "a")
2786
2787 fl = linefinder()
2788 fl.set_options(threshold=threshold,avg_limit=chan_avg_limit)
2789 fl.set_scan(workscan)
2790 f = fitter()
2791 f.set_function(lpoly=order)
[880]2792
[2012]2793 for r in rows:
2794 if individualedge:
2795 if len(edge) <= workscan.getif(r):
2796 raise RuntimeError, "Number of edge elements appear to " \
2797 "be less than the number of IFs"
2798 else:
2799 curedge = edge[workscan.getif(r)]
[907]2800
[2012]2801 fl.find_lines(r, mask_and(mask, workscan._getmask(r)), curedge) # (CAS-1434)
2802
2803 f.x = workscan._getabcissa(r)
2804 f.y = workscan._getspectrum(r)
2805 f.mask = fl.get_mask()
2806 f.data = None
2807 f.fit()
2808
2809 f.plot(residual=True)
2810 accept_fit = raw_input("Accept fit ( [y]/n ): ")
2811 if accept_fit.upper() == "N":
2812 #workscan._append_blinfo(None, None, None)
2813 continue
2814
2815 blpars = f.get_parameters()
2816 masklist = workscan.get_masklist(f.mask, row=r, silent=True)
2817 #workscan._append_blinfo(blpars, masklist, f.mask)
[2094]2818 workscan._setspectrum((f.fitter.getresidual() if getresidual else f.fitter.getfit()), r)
[2012]2819
2820 if outblfile:
2821 rms = workscan.get_rms(f.mask, r)
2822 dataout = workscan.format_blparams_row(blpars["params"], blpars["fixed"], rms, str(masklist), r, True)
2823 blf.write(dataout)
2824
2825 f._p.unmap()
2826 f._p = None
2827
2828 if outblfile: blf.close()
2829
2830 else:
2831 if individualedge:
2832 curedge = []
2833 for i in xrange(len(edge)):
2834 curedge += edge[i]
2835
[2094]2836 workscan._auto_poly_baseline(mask, order, curedge, threshold, chan_avg_limit, getresidual, outlog, blfile)
[2012]2837
2838 workscan._add_history("auto_poly_baseline", varlist)
2839
2840 if insitu:
2841 self._assign(workscan)
2842 else:
2843 return workscan
2844
2845 except RuntimeError, e:
2846 msg = "The fit failed, possibly because it didn't converge."
2847 if rcParams["verbose"]:
2848 asaplog.push(str(e))
2849 asaplog.push(str(msg))
2850 return
2851 else:
2852 raise RuntimeError(str(e)+'\n'+msg)
2853
2854
2855 ### OBSOLETE ##################################################################
2856 @asaplog_post_dec
2857 def old_poly_baseline(self, mask=None, order=0, plot=False, uselin=False, insitu=None, rows=None):
2858 """
2859 Return a scan which has been baselined (all rows) by a polynomial.
[1907]2860
[2012]2861 Parameters:
2862
2863 mask: an optional mask
2864
2865 order: the order of the polynomial (default is 0)
2866
2867 plot: plot the fit and the residual. In this each
2868 indivual fit has to be approved, by typing 'y'
2869 or 'n'
2870
2871 uselin: use linear polynomial fit
2872
2873 insitu: if False a new scantable is returned.
2874 Otherwise, the scaling is done in-situ
2875 The default is taken from .asaprc (False)
2876
2877 rows: row numbers of spectra to be processed.
2878 (default is None: for all rows)
[1907]2879
[2012]2880 Example:
2881 # return a scan baselined by a third order polynomial,
2882 # not using a mask
2883 bscan = scan.poly_baseline(order=3)
[907]2884
[2012]2885 """
2886 if insitu is None: insitu = rcParams['insitu']
2887 if not insitu:
2888 workscan = self.copy()
2889 else:
2890 workscan = self
2891 varlist = vars()
2892 if mask is None:
2893 mask = [True for i in xrange(self.nchan())]
[919]2894
[2012]2895 try:
2896 f = fitter()
2897 if uselin:
2898 f.set_function(lpoly=order)
2899 else:
2900 f.set_function(poly=order)
[1819]2901
[2012]2902 if rows == None:
2903 rows = xrange(workscan.nrow())
2904 elif isinstance(rows, int):
2905 rows = [ rows ]
[1907]2906
[2012]2907 if len(rows) > 0:
2908 self.blpars = []
2909 self.masklists = []
2910 self.actualmask = []
2911
2912 for r in rows:
2913 f.x = workscan._getabcissa(r)
2914 f.y = workscan._getspectrum(r)
2915 f.mask = mask_and(mask, workscan._getmask(r)) # (CAS-1434)
2916 f.data = None
2917 f.fit()
2918 if plot:
2919 f.plot(residual=True)
2920 x = raw_input("Accept fit ( [y]/n ): ")
2921 if x.upper() == 'N':
2922 self.blpars.append(None)
2923 self.masklists.append(None)
2924 self.actualmask.append(None)
2925 continue
2926 workscan._setspectrum(f.fitter.getresidual(), r)
2927 self.blpars.append(f.get_parameters())
2928 self.masklists.append(workscan.get_masklist(f.mask, row=r, silent=True))
2929 self.actualmask.append(f.mask)
[1819]2930
[1061]2931 if plot:
[2012]2932 f._p.unmap()
2933 f._p = None
2934 workscan._add_history("poly_baseline", varlist)
2935 if insitu:
2936 self._assign(workscan)
2937 else:
2938 return workscan
2939 except RuntimeError:
2940 msg = "The fit failed, possibly because it didn't converge."
2941 raise RuntimeError(msg)
[1819]2942
[2012]2943 def _init_blinfo(self):
2944 """\
2945 Initialise the following three auxiliary members:
2946 blpars : parameters of the best-fit baseline,
2947 masklists : mask data (edge positions of masked channels) and
2948 actualmask : mask data (in boolean list),
2949 to keep for use later (including output to logger/text files).
2950 Used by poly_baseline() and auto_poly_baseline() in case of
2951 'plot=True'.
2952 """
2953 self.blpars = []
2954 self.masklists = []
2955 self.actualmask = []
2956 return
[880]2957
[2012]2958 def _append_blinfo(self, data_blpars, data_masklists, data_actualmask):
2959 """\
2960 Append baseline-fitting related info to blpars, masklist and
2961 actualmask.
2962 """
2963 self.blpars.append(data_blpars)
2964 self.masklists.append(data_masklists)
2965 self.actualmask.append(data_actualmask)
2966 return
2967
[1862]2968 @asaplog_post_dec
[914]2969 def rotate_linpolphase(self, angle):
[1846]2970 """\
[914]2971 Rotate the phase of the complex polarization O=Q+iU correlation.
2972 This is always done in situ in the raw data. So if you call this
2973 function more than once then each call rotates the phase further.
[1846]2974
[914]2975 Parameters:
[1846]2976
[914]2977 angle: The angle (degrees) to rotate (add) by.
[1846]2978
2979 Example::
2980
[914]2981 scan.rotate_linpolphase(2.3)
[1846]2982
[914]2983 """
2984 varlist = vars()
[936]2985 self._math._rotate_linpolphase(self, angle)
[914]2986 self._add_history("rotate_linpolphase", varlist)
2987 return
[710]2988
[1862]2989 @asaplog_post_dec
[914]2990 def rotate_xyphase(self, angle):
[1846]2991 """\
[914]2992 Rotate the phase of the XY correlation. This is always done in situ
2993 in the data. So if you call this function more than once
2994 then each call rotates the phase further.
[1846]2995
[914]2996 Parameters:
[1846]2997
[914]2998 angle: The angle (degrees) to rotate (add) by.
[1846]2999
3000 Example::
3001
[914]3002 scan.rotate_xyphase(2.3)
[1846]3003
[914]3004 """
3005 varlist = vars()
[936]3006 self._math._rotate_xyphase(self, angle)
[914]3007 self._add_history("rotate_xyphase", varlist)
3008 return
3009
[1862]3010 @asaplog_post_dec
[914]3011 def swap_linears(self):
[1846]3012 """\
[1573]3013 Swap the linear polarisations XX and YY, or better the first two
[1348]3014 polarisations as this also works for ciculars.
[914]3015 """
3016 varlist = vars()
[936]3017 self._math._swap_linears(self)
[914]3018 self._add_history("swap_linears", varlist)
3019 return
3020
[1862]3021 @asaplog_post_dec
[914]3022 def invert_phase(self):
[1846]3023 """\
[914]3024 Invert the phase of the complex polarisation
3025 """
3026 varlist = vars()
[936]3027 self._math._invert_phase(self)
[914]3028 self._add_history("invert_phase", varlist)
3029 return
3030
[1862]3031 @asaplog_post_dec
[876]3032 def add(self, offset, insitu=None):
[1846]3033 """\
[513]3034 Return a scan where all spectra have the offset added
[1846]3035
[513]3036 Parameters:
[1846]3037
[513]3038 offset: the offset
[1855]3039
[513]3040 insitu: if False a new scantable is returned.
3041 Otherwise, the scaling is done in-situ
3042 The default is taken from .asaprc (False)
[1846]3043
[513]3044 """
3045 if insitu is None: insitu = rcParams['insitu']
[876]3046 self._math._setinsitu(insitu)
[513]3047 varlist = vars()
[876]3048 s = scantable(self._math._unaryop(self, offset, "ADD", False))
[1118]3049 s._add_history("add", varlist)
[876]3050 if insitu:
3051 self._assign(s)
3052 else:
[513]3053 return s
3054
[1862]3055 @asaplog_post_dec
[1308]3056 def scale(self, factor, tsys=True, insitu=None):
[1846]3057 """\
3058
[1938]3059 Return a scan where all spectra are scaled by the given 'factor'
[1846]3060
[513]3061 Parameters:
[1846]3062
[1819]3063 factor: the scaling factor (float or 1D float list)
[1855]3064
[513]3065 insitu: if False a new scantable is returned.
3066 Otherwise, the scaling is done in-situ
3067 The default is taken from .asaprc (False)
[1855]3068
[513]3069 tsys: if True (default) then apply the operation to Tsys
3070 as well as the data
[1846]3071
[513]3072 """
3073 if insitu is None: insitu = rcParams['insitu']
[876]3074 self._math._setinsitu(insitu)
[513]3075 varlist = vars()
[1819]3076 s = None
3077 import numpy
3078 if isinstance(factor, list) or isinstance(factor, numpy.ndarray):
3079 if isinstance(factor[0], list) or isinstance(factor[0], numpy.ndarray):
3080 from asapmath import _array2dOp
3081 s = _array2dOp( self.copy(), factor, "MUL", tsys )
3082 else:
3083 s = scantable( self._math._arrayop( self.copy(), factor, "MUL", tsys ) )
3084 else:
3085 s = scantable(self._math._unaryop(self.copy(), factor, "MUL", tsys))
[1118]3086 s._add_history("scale", varlist)
[876]3087 if insitu:
3088 self._assign(s)
3089 else:
[513]3090 return s
3091
[1504]3092 def set_sourcetype(self, match, matchtype="pattern",
3093 sourcetype="reference"):
[1846]3094 """\
[1502]3095 Set the type of the source to be an source or reference scan
[1846]3096 using the provided pattern.
3097
[1502]3098 Parameters:
[1846]3099
[1504]3100 match: a Unix style pattern, regular expression or selector
[1855]3101
[1504]3102 matchtype: 'pattern' (default) UNIX style pattern or
3103 'regex' regular expression
[1855]3104
[1502]3105 sourcetype: the type of the source to use (source/reference)
[1846]3106
[1502]3107 """
3108 varlist = vars()
3109 basesel = self.get_selection()
3110 stype = -1
3111 if sourcetype.lower().startswith("r"):
3112 stype = 1
3113 elif sourcetype.lower().startswith("s"):
3114 stype = 0
[1504]3115 else:
[1502]3116 raise ValueError("Illegal sourcetype use s(ource) or r(eference)")
[1504]3117 if matchtype.lower().startswith("p"):
3118 matchtype = "pattern"
3119 elif matchtype.lower().startswith("r"):
3120 matchtype = "regex"
3121 else:
3122 raise ValueError("Illegal matchtype, use p(attern) or r(egex)")
[1502]3123 sel = selector()
3124 if isinstance(match, selector):
3125 sel = match
3126 else:
[1504]3127 sel.set_query("SRCNAME == %s('%s')" % (matchtype, match))
[1502]3128 self.set_selection(basesel+sel)
3129 self._setsourcetype(stype)
3130 self.set_selection(basesel)
[1573]3131 self._add_history("set_sourcetype", varlist)
[1502]3132
[1862]3133 @asaplog_post_dec
[1857]3134 @preserve_selection
[1819]3135 def auto_quotient(self, preserve=True, mode='paired', verify=False):
[1846]3136 """\
[670]3137 This function allows to build quotients automatically.
[1819]3138 It assumes the observation to have the same number of
[670]3139 "ons" and "offs"
[1846]3140
[670]3141 Parameters:
[1846]3142
[710]3143 preserve: you can preserve (default) the continuum or
3144 remove it. The equations used are
[1857]3145
[670]3146 preserve: Output = Toff * (on/off) - Toff
[1857]3147
[1070]3148 remove: Output = Toff * (on/off) - Ton
[1855]3149
[1573]3150 mode: the on/off detection mode
[1348]3151 'paired' (default)
3152 identifies 'off' scans by the
3153 trailing '_R' (Mopra/Parkes) or
3154 '_e'/'_w' (Tid) and matches
3155 on/off pairs from the observing pattern
[1502]3156 'time'
3157 finds the closest off in time
[1348]3158
[1857]3159 .. todo:: verify argument is not implemented
3160
[670]3161 """
[1857]3162 varlist = vars()
[1348]3163 modes = ["time", "paired"]
[670]3164 if not mode in modes:
[876]3165 msg = "please provide valid mode. Valid modes are %s" % (modes)
3166 raise ValueError(msg)
[1348]3167 s = None
3168 if mode.lower() == "paired":
[1857]3169 sel = self.get_selection()
[1875]3170 sel.set_query("SRCTYPE==psoff")
[1356]3171 self.set_selection(sel)
[1348]3172 offs = self.copy()
[1875]3173 sel.set_query("SRCTYPE==pson")
[1356]3174 self.set_selection(sel)
[1348]3175 ons = self.copy()
3176 s = scantable(self._math._quotient(ons, offs, preserve))
3177 elif mode.lower() == "time":
3178 s = scantable(self._math._auto_quotient(self, mode, preserve))
[1118]3179 s._add_history("auto_quotient", varlist)
[876]3180 return s
[710]3181
[1862]3182 @asaplog_post_dec
[1145]3183 def mx_quotient(self, mask = None, weight='median', preserve=True):
[1846]3184 """\
[1143]3185 Form a quotient using "off" beams when observing in "MX" mode.
[1846]3186
[1143]3187 Parameters:
[1846]3188
[1145]3189 mask: an optional mask to be used when weight == 'stddev'
[1855]3190
[1143]3191 weight: How to average the off beams. Default is 'median'.
[1855]3192
[1145]3193 preserve: you can preserve (default) the continuum or
[1855]3194 remove it. The equations used are:
[1846]3195
[1855]3196 preserve: Output = Toff * (on/off) - Toff
3197
3198 remove: Output = Toff * (on/off) - Ton
3199
[1217]3200 """
[1593]3201 mask = mask or ()
[1141]3202 varlist = vars()
3203 on = scantable(self._math._mx_extract(self, 'on'))
[1143]3204 preoff = scantable(self._math._mx_extract(self, 'off'))
3205 off = preoff.average_time(mask=mask, weight=weight, scanav=False)
[1217]3206 from asapmath import quotient
[1145]3207 q = quotient(on, off, preserve)
[1143]3208 q._add_history("mx_quotient", varlist)
[1217]3209 return q
[513]3210
[1862]3211 @asaplog_post_dec
[718]3212 def freq_switch(self, insitu=None):
[1846]3213 """\
[718]3214 Apply frequency switching to the data.
[1846]3215
[718]3216 Parameters:
[1846]3217
[718]3218 insitu: if False a new scantable is returned.
3219 Otherwise, the swictching is done in-situ
3220 The default is taken from .asaprc (False)
[1846]3221
[718]3222 """
3223 if insitu is None: insitu = rcParams['insitu']
[876]3224 self._math._setinsitu(insitu)
[718]3225 varlist = vars()
[876]3226 s = scantable(self._math._freqswitch(self))
[1118]3227 s._add_history("freq_switch", varlist)
[1856]3228 if insitu:
3229 self._assign(s)
3230 else:
3231 return s
[718]3232
[1862]3233 @asaplog_post_dec
[780]3234 def recalc_azel(self):
[1846]3235 """Recalculate the azimuth and elevation for each position."""
[780]3236 varlist = vars()
[876]3237 self._recalcazel()
[780]3238 self._add_history("recalc_azel", varlist)
3239 return
3240
[1862]3241 @asaplog_post_dec
[513]3242 def __add__(self, other):
3243 varlist = vars()
3244 s = None
3245 if isinstance(other, scantable):
[1573]3246 s = scantable(self._math._binaryop(self, other, "ADD"))
[513]3247 elif isinstance(other, float):
[876]3248 s = scantable(self._math._unaryop(self, other, "ADD", False))
[2144]3249 elif isinstance(other, list) or isinstance(other, numpy.ndarray):
3250 if isinstance(other[0], list) or isinstance(other[0], numpy.ndarray):
3251 from asapmath import _array2dOp
3252 s = _array2dOp( self.copy(), other, "ADD", False )
3253 else:
3254 s = scantable( self._math._arrayop( self.copy(), other, "ADD", False ) )
[513]3255 else:
[718]3256 raise TypeError("Other input is not a scantable or float value")
[513]3257 s._add_history("operator +", varlist)
3258 return s
3259
[1862]3260 @asaplog_post_dec
[513]3261 def __sub__(self, other):
3262 """
3263 implicit on all axes and on Tsys
3264 """
3265 varlist = vars()
3266 s = None
3267 if isinstance(other, scantable):
[1588]3268 s = scantable(self._math._binaryop(self, other, "SUB"))
[513]3269 elif isinstance(other, float):
[876]3270 s = scantable(self._math._unaryop(self, other, "SUB", False))
[2144]3271 elif isinstance(other, list) or isinstance(other, numpy.ndarray):
3272 if isinstance(other[0], list) or isinstance(other[0], numpy.ndarray):
3273 from asapmath import _array2dOp
3274 s = _array2dOp( self.copy(), other, "SUB", False )
3275 else:
3276 s = scantable( self._math._arrayop( self.copy(), other, "SUB", False ) )
[513]3277 else:
[718]3278 raise TypeError("Other input is not a scantable or float value")
[513]3279 s._add_history("operator -", varlist)
3280 return s
[710]3281
[1862]3282 @asaplog_post_dec
[513]3283 def __mul__(self, other):
3284 """
3285 implicit on all axes and on Tsys
3286 """
3287 varlist = vars()
3288 s = None
3289 if isinstance(other, scantable):
[1588]3290 s = scantable(self._math._binaryop(self, other, "MUL"))
[513]3291 elif isinstance(other, float):
[876]3292 s = scantable(self._math._unaryop(self, other, "MUL", False))
[2144]3293 elif isinstance(other, list) or isinstance(other, numpy.ndarray):
3294 if isinstance(other[0], list) or isinstance(other[0], numpy.ndarray):
3295 from asapmath import _array2dOp
3296 s = _array2dOp( self.copy(), other, "MUL", False )
3297 else:
3298 s = scantable( self._math._arrayop( self.copy(), other, "MUL", False ) )
[513]3299 else:
[718]3300 raise TypeError("Other input is not a scantable or float value")
[513]3301 s._add_history("operator *", varlist)
3302 return s
3303
[710]3304
[1862]3305 @asaplog_post_dec
[513]3306 def __div__(self, other):
3307 """
3308 implicit on all axes and on Tsys
3309 """
3310 varlist = vars()
3311 s = None
3312 if isinstance(other, scantable):
[1589]3313 s = scantable(self._math._binaryop(self, other, "DIV"))
[513]3314 elif isinstance(other, float):
3315 if other == 0.0:
[718]3316 raise ZeroDivisionError("Dividing by zero is not recommended")
[876]3317 s = scantable(self._math._unaryop(self, other, "DIV", False))
[2144]3318 elif isinstance(other, list) or isinstance(other, numpy.ndarray):
3319 if isinstance(other[0], list) or isinstance(other[0], numpy.ndarray):
3320 from asapmath import _array2dOp
3321 s = _array2dOp( self.copy(), other, "DIV", False )
3322 else:
3323 s = scantable( self._math._arrayop( self.copy(), other, "DIV", False ) )
[513]3324 else:
[718]3325 raise TypeError("Other input is not a scantable or float value")
[513]3326 s._add_history("operator /", varlist)
3327 return s
3328
[1862]3329 @asaplog_post_dec
[530]3330 def get_fit(self, row=0):
[1846]3331 """\
[530]3332 Print or return the stored fits for a row in the scantable
[1846]3333
[530]3334 Parameters:
[1846]3335
[530]3336 row: the row which the fit has been applied to.
[1846]3337
[530]3338 """
3339 if row > self.nrow():
3340 return
[976]3341 from asap.asapfit import asapfit
[530]3342 fit = asapfit(self._getfit(row))
[1859]3343 asaplog.push( '%s' %(fit) )
3344 return fit.as_dict()
[530]3345
[1483]3346 def flag_nans(self):
[1846]3347 """\
[1483]3348 Utility function to flag NaN values in the scantable.
3349 """
3350 import numpy
3351 basesel = self.get_selection()
3352 for i in range(self.nrow()):
[1589]3353 sel = self.get_row_selector(i)
3354 self.set_selection(basesel+sel)
[1483]3355 nans = numpy.isnan(self._getspectrum(0))
3356 if numpy.any(nans):
3357 bnans = [ bool(v) for v in nans]
3358 self.flag(bnans)
3359 self.set_selection(basesel)
3360
[1588]3361 def get_row_selector(self, rowno):
[1992]3362 #return selector(beams=self.getbeam(rowno),
3363 # ifs=self.getif(rowno),
3364 # pols=self.getpol(rowno),
3365 # scans=self.getscan(rowno),
3366 # cycles=self.getcycle(rowno))
3367 return selector(rows=[rowno])
[1573]3368
[484]3369 def _add_history(self, funcname, parameters):
[1435]3370 if not rcParams['scantable.history']:
3371 return
[484]3372 # create date
3373 sep = "##"
3374 from datetime import datetime
3375 dstr = datetime.now().strftime('%Y/%m/%d %H:%M:%S')
3376 hist = dstr+sep
3377 hist += funcname+sep#cdate+sep
3378 if parameters.has_key('self'): del parameters['self']
[1118]3379 for k, v in parameters.iteritems():
[484]3380 if type(v) is dict:
[1118]3381 for k2, v2 in v.iteritems():
[484]3382 hist += k2
3383 hist += "="
[1118]3384 if isinstance(v2, scantable):
[484]3385 hist += 'scantable'
3386 elif k2 == 'mask':
[1118]3387 if isinstance(v2, list) or isinstance(v2, tuple):
[513]3388 hist += str(self._zip_mask(v2))
3389 else:
3390 hist += str(v2)
[484]3391 else:
[513]3392 hist += str(v2)
[484]3393 else:
3394 hist += k
3395 hist += "="
[1118]3396 if isinstance(v, scantable):
[484]3397 hist += 'scantable'
3398 elif k == 'mask':
[1118]3399 if isinstance(v, list) or isinstance(v, tuple):
[513]3400 hist += str(self._zip_mask(v))
3401 else:
3402 hist += str(v)
[484]3403 else:
3404 hist += str(v)
3405 hist += sep
3406 hist = hist[:-2] # remove trailing '##'
3407 self._addhistory(hist)
3408
[710]3409
[484]3410 def _zip_mask(self, mask):
3411 mask = list(mask)
3412 i = 0
3413 segments = []
3414 while mask[i:].count(1):
3415 i += mask[i:].index(1)
3416 if mask[i:].count(0):
3417 j = i + mask[i:].index(0)
3418 else:
[710]3419 j = len(mask)
[1118]3420 segments.append([i, j])
[710]3421 i = j
[484]3422 return segments
[714]3423
[626]3424 def _get_ordinate_label(self):
3425 fu = "("+self.get_fluxunit()+")"
3426 import re
3427 lbl = "Intensity"
[1118]3428 if re.match(".K.", fu):
[626]3429 lbl = "Brightness Temperature "+ fu
[1118]3430 elif re.match(".Jy.", fu):
[626]3431 lbl = "Flux density "+ fu
3432 return lbl
[710]3433
[876]3434 def _check_ifs(self):
[1986]3435 #nchans = [self.nchan(i) for i in range(self.nif(-1))]
3436 nchans = [self.nchan(i) for i in self.getifnos()]
[2004]3437 nchans = filter(lambda t: t > 0, nchans)
[876]3438 return (sum(nchans)/len(nchans) == nchans[0])
[976]3439
[1862]3440 @asaplog_post_dec
[1916]3441 #def _fill(self, names, unit, average, getpt, antenna):
3442 def _fill(self, names, unit, average, opts={}):
[976]3443 first = True
3444 fullnames = []
3445 for name in names:
3446 name = os.path.expandvars(name)
3447 name = os.path.expanduser(name)
3448 if not os.path.exists(name):
3449 msg = "File '%s' does not exists" % (name)
3450 raise IOError(msg)
3451 fullnames.append(name)
3452 if average:
3453 asaplog.push('Auto averaging integrations')
[1079]3454 stype = int(rcParams['scantable.storage'].lower() == 'disk')
[976]3455 for name in fullnames:
[1073]3456 tbl = Scantable(stype)
[2004]3457 if is_ms( name ):
3458 r = msfiller( tbl )
3459 else:
3460 r = filler( tbl )
3461 rx = rcParams['scantable.reference']
3462 r.setreferenceexpr(rx)
3463 #r = filler(tbl)
3464 #rx = rcParams['scantable.reference']
3465 #r.setreferenceexpr(rx)
[976]3466 msg = "Importing %s..." % (name)
[1118]3467 asaplog.push(msg, False)
[1916]3468 #opts = {'ms': {'antenna' : antenna, 'getpt': getpt} }
[1904]3469 r.open(name, opts)# antenna, -1, -1, getpt)
[1843]3470 r.fill()
[976]3471 if average:
[1118]3472 tbl = self._math._average((tbl, ), (), 'NONE', 'SCAN')
[976]3473 if not first:
3474 tbl = self._math._merge([self, tbl])
3475 Scantable.__init__(self, tbl)
[1843]3476 r.close()
[1118]3477 del r, tbl
[976]3478 first = False
[1861]3479 #flush log
3480 asaplog.post()
[976]3481 if unit is not None:
3482 self.set_fluxunit(unit)
[1824]3483 if not is_casapy():
3484 self.set_freqframe(rcParams['scantable.freqframe'])
[976]3485
[2012]3486
[1402]3487 def __getitem__(self, key):
3488 if key < 0:
3489 key += self.nrow()
3490 if key >= self.nrow():
3491 raise IndexError("Row index out of range.")
3492 return self._getspectrum(key)
3493
3494 def __setitem__(self, key, value):
3495 if key < 0:
3496 key += self.nrow()
3497 if key >= self.nrow():
3498 raise IndexError("Row index out of range.")
3499 if not hasattr(value, "__len__") or \
3500 len(value) > self.nchan(self.getif(key)):
3501 raise ValueError("Spectrum length doesn't match.")
3502 return self._setspectrum(value, key)
3503
3504 def __len__(self):
3505 return self.nrow()
3506
3507 def __iter__(self):
3508 for i in range(len(self)):
3509 yield self[i]
Note: See TracBrowser for help on using the repository browser.