source: trunk/python/scantable.py@ 2013

Last change on this file since 2013 was 2013, checked in by Kana Sugimoto, 14 years ago

New Development: Yes

JIRA Issue: Yes (CAS-1425)

Ready for Test: Yes

Interface Changes: Yes

What Interface Changed: Two new methods are added to scantable class, i.e., parse_maskexpr and _parse_selection.

Test Programs:

scan=asap.scantable('FILENAME')
scan.parse_maskexpr('<2:-58~1100,2~13:<1200.;>9000.,15')

Put in Release Notes: No

Module(s): scantable

Description:

Added a new public method scantable.parse_maskexpr and an internal method
scantable._parse_selection. scantable..parse_maskexpr parses a CASA type channel
selection syntax string and returns a dictionary of valid IF (key) and masklist
combinations. See help for more details


  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 111.6 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)
[2004]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):
[1118]339 return Scantable._summary(self, True)
[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 """
[976]351 info = Scantable._summary(self, True)
[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.
[1584]1085 Flagged data in the scantable gets 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
[113]1122 def create_mask(self, *args, **kwargs):
[1846]1123 """\
[1118]1124 Compute and return a mask based on [min, max] windows.
[189]1125 The specified windows are to be INCLUDED, when the mask is
[113]1126 applied.
[1846]1127
[102]1128 Parameters:
[1846]1129
[1118]1130 [min, max], [min2, max2], ...
[1024]1131 Pairs of start/end points (inclusive)specifying the regions
[102]1132 to be masked
[1855]1133
[189]1134 invert: optional argument. If specified as True,
1135 return an inverted mask, i.e. the regions
1136 specified are EXCLUDED
[1855]1137
[513]1138 row: create the mask using the specified row for
1139 unit conversions, default is row=0
1140 only necessary if frequency varies over rows.
[1846]1141
1142 Examples::
1143
[113]1144 scan.set_unit('channel')
[1846]1145 # a)
[1118]1146 msk = scan.create_mask([400, 500], [800, 900])
[189]1147 # masks everything outside 400 and 500
[113]1148 # and 800 and 900 in the unit 'channel'
1149
[1846]1150 # b)
[1118]1151 msk = scan.create_mask([400, 500], [800, 900], invert=True)
[189]1152 # masks the regions between 400 and 500
[113]1153 # and 800 and 900 in the unit 'channel'
[1846]1154
1155 # c)
1156 #mask only channel 400
[1554]1157 msk = scan.create_mask([400])
[1846]1158
[102]1159 """
[1554]1160 row = kwargs.get("row", 0)
[513]1161 data = self._getabcissa(row)
[113]1162 u = self._getcoordinfo()[0]
[1859]1163 if u == "":
1164 u = "channel"
1165 msg = "The current mask window unit is %s" % u
1166 i = self._check_ifs()
1167 if not i:
1168 msg += "\nThis mask is only valid for IF=%d" % (self.getif(i))
1169 asaplog.push(msg)
[102]1170 n = self.nchan()
[1295]1171 msk = _n_bools(n, False)
[710]1172 # test if args is a 'list' or a 'normal *args - UGLY!!!
1173
[1118]1174 ws = (isinstance(args[-1][-1], int) or isinstance(args[-1][-1], float)) \
1175 and args or args[0]
[710]1176 for window in ws:
[1554]1177 if len(window) == 1:
1178 window = [window[0], window[0]]
1179 if len(window) == 0 or len(window) > 2:
1180 raise ValueError("A window needs to be defined as [start(, end)]")
[1545]1181 if window[0] > window[1]:
1182 tmp = window[0]
1183 window[0] = window[1]
1184 window[1] = tmp
[102]1185 for i in range(n):
[1024]1186 if data[i] >= window[0] and data[i] <= window[1]:
[1295]1187 msk[i] = True
[113]1188 if kwargs.has_key('invert'):
1189 if kwargs.get('invert'):
[1295]1190 msk = mask_not(msk)
[102]1191 return msk
[710]1192
[1931]1193 def get_masklist(self, mask=None, row=0, silent=False):
[1846]1194 """\
[1819]1195 Compute and return a list of mask windows, [min, max].
[1846]1196
[1819]1197 Parameters:
[1846]1198
[1819]1199 mask: channel mask, created with create_mask.
[1855]1200
[1819]1201 row: calcutate the masklist using the specified row
1202 for unit conversions, default is row=0
1203 only necessary if frequency varies over rows.
[1846]1204
[1819]1205 Returns:
[1846]1206
[1819]1207 [min, max], [min2, max2], ...
1208 Pairs of start/end points (inclusive)specifying
1209 the masked regions
[1846]1210
[1819]1211 """
1212 if not (isinstance(mask,list) or isinstance(mask, tuple)):
1213 raise TypeError("The mask should be list or tuple.")
1214 if len(mask) < 2:
1215 raise TypeError("The mask elements should be > 1")
1216 if self.nchan() != len(mask):
1217 msg = "Number of channels in scantable != number of mask elements"
1218 raise TypeError(msg)
1219 data = self._getabcissa(row)
1220 u = self._getcoordinfo()[0]
[1859]1221 if u == "":
1222 u = "channel"
1223 msg = "The current mask window unit is %s" % u
1224 i = self._check_ifs()
1225 if not i:
1226 msg += "\nThis mask is only valid for IF=%d" % (self.getif(i))
[1931]1227 if not silent:
1228 asaplog.push(msg)
[1819]1229 masklist=[]
1230 ist, ien = None, None
1231 ist, ien=self.get_mask_indices(mask)
1232 if ist is not None and ien is not None:
1233 for i in xrange(len(ist)):
1234 range=[data[ist[i]],data[ien[i]]]
1235 range.sort()
1236 masklist.append([range[0],range[1]])
1237 return masklist
1238
1239 def get_mask_indices(self, mask=None):
[1846]1240 """\
[1819]1241 Compute and Return lists of mask start indices and mask end indices.
[1855]1242
1243 Parameters:
1244
[1819]1245 mask: channel mask, created with create_mask.
[1846]1246
[1819]1247 Returns:
[1846]1248
[1819]1249 List of mask start indices and that of mask end indices,
1250 i.e., [istart1,istart2,....], [iend1,iend2,....].
[1846]1251
[1819]1252 """
1253 if not (isinstance(mask,list) or isinstance(mask, tuple)):
1254 raise TypeError("The mask should be list or tuple.")
1255 if len(mask) < 2:
1256 raise TypeError("The mask elements should be > 1")
1257 istart=[]
1258 iend=[]
1259 if mask[0]: istart.append(0)
1260 for i in range(len(mask)-1):
1261 if not mask[i] and mask[i+1]:
1262 istart.append(i+1)
1263 elif mask[i] and not mask[i+1]:
1264 iend.append(i)
1265 if mask[len(mask)-1]: iend.append(len(mask)-1)
1266 if len(istart) != len(iend):
1267 raise RuntimeError("Numbers of mask start != mask end.")
1268 for i in range(len(istart)):
1269 if istart[i] > iend[i]:
1270 raise RuntimeError("Mask start index > mask end index")
1271 break
1272 return istart,iend
1273
[2013]1274 @asaplog_post_dec
1275 def parse_maskexpr(self,maskstring):
1276 """
1277 Parse CASA type mask selection syntax (IF dependent).
1278
1279 Parameters:
1280 maskstring : A string mask selection expression.
1281 A comma separated selections mean different IF -
1282 channel combinations. IFs and channel selections
1283 are partitioned by a colon, ':'.
1284 examples:
1285 '<2,4~6,9' = IFs 0,1,4,5,6,9 (all channels)
1286 '3:3~45;60' = channels 3 to 45 and 60 in IF 3
1287 '0~1:2~6,8' = channels 2 to 6 in IFs 0,1, and
1288 all channels in IF8
1289 Returns:
1290 A dictionary of selected (valid) IF and masklist pairs,
1291 e.g. {'0': [[50,250],[350,462]], '2': [[100,400],[550,974]]}
1292 """
1293 if not isinstance(maskstring,str):
1294 asaplog.post()
1295 asaplog.push("Invalid mask expression")
1296 asaplog.post("ERROR")
1297
1298 valid_ifs = self.getifnos()
1299 frequnit = self.get_unit()
1300 seldict = {}
1301 ## split each selection
1302 sellist = maskstring.split(',')
1303 for currselstr in sellist:
1304 selset = currselstr.split(':')
1305 # spw and mask string (may include ~, < or >)
1306 spwmasklist = self._parse_selection(selset[0],typestr='integer',
1307 offset=1,minval=min(valid_ifs),
1308 maxval=max(valid_ifs))
1309 for spwlist in spwmasklist:
1310 selspws = []
1311 for ispw in range(spwlist[0],spwlist[1]+1):
1312 # Put into the list only if ispw exists
1313 if valid_ifs.count(ispw):
1314 selspws.append(ispw)
1315 del spwmasklist, spwlist
1316
1317 # parse frequency mask list
1318 if len(selset) > 1:
1319 freqmasklist = self._parse_selection(selset[1],typestr='float',
1320 offset=0.)
1321 else:
1322 # want to select the whole spectrum
1323 freqmasklist = [None]
1324
1325 ## define a dictionary of spw - masklist combination
1326 for ispw in selspws:
1327 #print "working on", ispw
1328 spwstr = str(ispw)
1329 if len(selspws) == 0:
1330 # empty spw
1331 continue
1332 else:
1333 ## want to get min and max of the spw and
1334 ## offset to set for '<' and '>'
1335 if frequnit == 'channel':
1336 minfreq = 0
1337 maxfreq = self.nchan(ifno=ispw)
1338 offset = 0.5
1339 else:
1340 ## This is ugly part. need improvement
1341 for ifrow in xrange(self.nrow()):
1342 if self.getif(ifrow) == ispw:
1343 #print "IF",ispw,"found in row =",ifrow
1344 break
1345 freqcoord = self.get_coordinate(ifrow)
1346 freqs = self._getabcissa(ifrow)
1347 minfreq = min(freqs)
1348 maxfreq = max(freqs)
1349 if len(freqs) == 1:
1350 offset = 0.5
1351 elif frequnit.find('Hz') > 0:
1352 offset = abs(freqcoord.to_frequency(1,unit=frequnit)
1353 -freqcoord.to_frequency(0,unit=frequnit))*0.5
1354 elif frequnit.find('m/s') > 0:
1355 offset = abs(freqcoord.to_velocity(1,unit=frequnit)
1356 -freqcoord.to_velocity(0,unit=frequnit))*0.5
1357 else:
1358 asaplog.post()
1359 asaplog.push("Invalid frequency unit")
1360 asaplog.post("ERROR")
1361 del freqs, freqcoord, ifrow
1362 for freq in freqmasklist:
1363 selmask = freq or [minfreq, maxfreq]
1364 if selmask[0] == None:
1365 ## selection was "<freq[1]".
1366 if selmask[1] < minfreq:
1367 ## avoid adding region selection
1368 selmask = None
1369 else:
1370 selmask = [minfreq,selmask[1]-offset]
1371 elif selmask[1] == None:
1372 ## selection was ">freq[0]"
1373 if selmask[0] > maxfreq:
1374 ## avoid adding region selection
1375 selmask = None
1376 else:
1377 selmask = [selmask[0]+offset,maxfreq]
1378 if selmask:
1379 if not seldict.has_key(spwstr):
1380 # new spw selection
1381 seldict[spwstr] = []
1382 seldict[spwstr] += [selmask]
1383 del minfreq,maxfreq,offset,freq,selmask
1384 del spwstr
1385 del freqmasklist
1386 del valid_ifs
1387 if len(seldict) == 0:
1388 asaplog.post()
1389 asaplog.push("No valid selection in the mask expression: "+maskstring)
1390 asaplog.post("WARN")
1391 return None
1392 msg = "Selected masklist:\n"
1393 for sif, lmask in seldict.iteritems():
1394 msg += " IF"+sif+" - "+str(lmask)+"\n"
1395 asaplog.push(msg)
1396 return seldict
1397
1398 def _parse_selection(self,selstr,typestr='float',offset=0.,minval=None,maxval=None):
1399 """
1400 Parameters:
1401 selstr : The Selection string, e.g., '<3;5~7;100~103;9'
1402 typestr : The type of the values in returned list
1403 ('integer' or 'float')
1404 offset : The offset value to subtract from or add to
1405 the boundary value if the selection string
1406 includes '<' or '>'
1407 minval, maxval : The minimum/maximum values to set if the
1408 selection string includes '<' or '>'.
1409 The list element is filled with None by default.
1410 Returns:
1411 A list of min/max pair of selections.
1412 Example:
1413 _parseSelection('<3;5~7;9',typestr='int',offset=1,minval=0)
1414 returns [[0,2],[5,7],[9,9]]
1415 """
1416 selgroups = selstr.split(';')
1417 sellists = []
1418 if typestr.lower().startswith('int'):
1419 formatfunc = int
1420 else:
1421 formatfunc = float
1422
1423 for currsel in selgroups:
1424 if currsel.find('~') > 0:
1425 minsel = formatfunc(currsel.split('~')[0].strip())
1426 maxsel = formatfunc(currsel.split('~')[1].strip())
1427 elif currsel.strip().startswith('<'):
1428 minsel = minval
1429 maxsel = formatfunc(currsel.split('<')[1].strip()) \
1430 - formatfunc(offset)
1431 elif currsel.strip().startswith('>'):
1432 minsel = formatfunc(currsel.split('>')[1].strip()) \
1433 + formatfunc(offset)
1434 maxsel = maxval
1435 else:
1436 minsel = formatfunc(currsel)
1437 maxsel = formatfunc(currsel)
1438 sellists.append([minsel,maxsel])
1439 return sellists
1440
[1819]1441# def get_restfreqs(self):
1442# """
1443# Get the restfrequency(s) stored in this scantable.
1444# The return value(s) are always of unit 'Hz'
1445# Parameters:
1446# none
1447# Returns:
1448# a list of doubles
1449# """
1450# return list(self._getrestfreqs())
1451
1452 def get_restfreqs(self, ids=None):
[1846]1453 """\
[256]1454 Get the restfrequency(s) stored in this scantable.
1455 The return value(s) are always of unit 'Hz'
[1846]1456
[256]1457 Parameters:
[1846]1458
[1819]1459 ids: (optional) a list of MOLECULE_ID for that restfrequency(s) to
1460 be retrieved
[1846]1461
[256]1462 Returns:
[1846]1463
[1819]1464 dictionary containing ids and a list of doubles for each id
[1846]1465
[256]1466 """
[1819]1467 if ids is None:
1468 rfreqs={}
1469 idlist = self.getmolnos()
1470 for i in idlist:
1471 rfreqs[i]=list(self._getrestfreqs(i))
1472 return rfreqs
1473 else:
1474 if type(ids)==list or type(ids)==tuple:
1475 rfreqs={}
1476 for i in ids:
1477 rfreqs[i]=list(self._getrestfreqs(i))
1478 return rfreqs
1479 else:
1480 return list(self._getrestfreqs(ids))
1481 #return list(self._getrestfreqs(ids))
[102]1482
[931]1483 def set_restfreqs(self, freqs=None, unit='Hz'):
[1846]1484 """\
[931]1485 Set or replace the restfrequency specified and
[1938]1486 if the 'freqs' argument holds a scalar,
[931]1487 then that rest frequency will be applied to all the selected
1488 data. If the 'freqs' argument holds
1489 a vector, then it MUST be of equal or smaller length than
1490 the number of IFs (and the available restfrequencies will be
1491 replaced by this vector). In this case, *all* data have
1492 the restfrequency set per IF according
1493 to the corresponding value you give in the 'freqs' vector.
[1118]1494 E.g. 'freqs=[1e9, 2e9]' would mean IF 0 gets restfreq 1e9 and
[931]1495 IF 1 gets restfreq 2e9.
[1846]1496
[1395]1497 You can also specify the frequencies via a linecatalog.
[1153]1498
[931]1499 Parameters:
[1846]1500
[931]1501 freqs: list of rest frequency values or string idenitfiers
[1855]1502
[931]1503 unit: unit for rest frequency (default 'Hz')
[402]1504
[1846]1505
1506 Example::
1507
[1819]1508 # set the given restfrequency for the all currently selected IFs
[931]1509 scan.set_restfreqs(freqs=1.4e9)
[1845]1510 # set restfrequencies for the n IFs (n > 1) in the order of the
1511 # list, i.e
1512 # IF0 -> 1.4e9, IF1 -> 1.41e9, IF3 -> 1.42e9
1513 # len(list_of_restfreqs) == nIF
1514 # for nIF == 1 the following will set multiple restfrequency for
1515 # that IF
[1819]1516 scan.set_restfreqs(freqs=[1.4e9, 1.41e9, 1.42e9])
[1845]1517 # set multiple restfrequencies per IF. as a list of lists where
1518 # the outer list has nIF elements, the inner s arbitrary
1519 scan.set_restfreqs(freqs=[[1.4e9, 1.41e9], [1.67e9]])
[391]1520
[1846]1521 *Note*:
[1845]1522
[931]1523 To do more sophisticate Restfrequency setting, e.g. on a
1524 source and IF basis, use scantable.set_selection() before using
[1846]1525 this function::
[931]1526
[1846]1527 # provided your scantable is called scan
1528 selection = selector()
1529 selection.set_name("ORION*")
1530 selection.set_ifs([1])
1531 scan.set_selection(selection)
1532 scan.set_restfreqs(freqs=86.6e9)
1533
[931]1534 """
1535 varlist = vars()
[1157]1536 from asap import linecatalog
1537 # simple value
[1118]1538 if isinstance(freqs, int) or isinstance(freqs, float):
[1845]1539 self._setrestfreqs([freqs], [""], unit)
[1157]1540 # list of values
[1118]1541 elif isinstance(freqs, list) or isinstance(freqs, tuple):
[1157]1542 # list values are scalars
[1118]1543 if isinstance(freqs[-1], int) or isinstance(freqs[-1], float):
[1845]1544 if len(freqs) == 1:
1545 self._setrestfreqs(freqs, [""], unit)
1546 else:
1547 # allow the 'old' mode of setting mulitple IFs
1548 sel = selector()
1549 savesel = self._getselection()
1550 iflist = self.getifnos()
1551 if len(freqs)>len(iflist):
1552 raise ValueError("number of elements in list of list "
1553 "exeeds the current IF selections")
1554 iflist = self.getifnos()
1555 for i, fval in enumerate(freqs):
1556 sel.set_ifs(iflist[i])
1557 self._setselection(sel)
1558 self._setrestfreqs([fval], [""], unit)
1559 self._setselection(savesel)
1560
1561 # list values are dict, {'value'=, 'name'=)
[1157]1562 elif isinstance(freqs[-1], dict):
[1845]1563 values = []
1564 names = []
1565 for d in freqs:
1566 values.append(d["value"])
1567 names.append(d["name"])
1568 self._setrestfreqs(values, names, unit)
[1819]1569 elif isinstance(freqs[-1], list) or isinstance(freqs[-1], tuple):
[1157]1570 sel = selector()
1571 savesel = self._getselection()
[1322]1572 iflist = self.getifnos()
[1819]1573 if len(freqs)>len(iflist):
[1845]1574 raise ValueError("number of elements in list of list exeeds"
1575 " the current IF selections")
1576 for i, fval in enumerate(freqs):
[1322]1577 sel.set_ifs(iflist[i])
[1259]1578 self._setselection(sel)
[1845]1579 self._setrestfreqs(fval, [""], unit)
[1157]1580 self._setselection(savesel)
1581 # freqs are to be taken from a linecatalog
[1153]1582 elif isinstance(freqs, linecatalog):
1583 sel = selector()
1584 savesel = self._getselection()
1585 for i in xrange(freqs.nrow()):
[1322]1586 sel.set_ifs(iflist[i])
[1153]1587 self._setselection(sel)
[1845]1588 self._setrestfreqs([freqs.get_frequency(i)],
1589 [freqs.get_name(i)], "MHz")
[1153]1590 # ensure that we are not iterating past nIF
1591 if i == self.nif()-1: break
1592 self._setselection(savesel)
[931]1593 else:
1594 return
1595 self._add_history("set_restfreqs", varlist)
1596
[1360]1597 def shift_refpix(self, delta):
[1846]1598 """\
[1589]1599 Shift the reference pixel of the Spectra Coordinate by an
1600 integer amount.
[1846]1601
[1589]1602 Parameters:
[1846]1603
[1589]1604 delta: the amount to shift by
[1846]1605
1606 *Note*:
1607
[1589]1608 Be careful using this with broadband data.
[1846]1609
[1360]1610 """
[1731]1611 Scantable.shift_refpix(self, delta)
[931]1612
[1862]1613 @asaplog_post_dec
[1259]1614 def history(self, filename=None):
[1846]1615 """\
[1259]1616 Print the history. Optionally to a file.
[1846]1617
[1348]1618 Parameters:
[1846]1619
[1928]1620 filename: The name of the file to save the history to.
[1846]1621
[1259]1622 """
[484]1623 hist = list(self._gethistory())
[794]1624 out = "-"*80
[484]1625 for h in hist:
[489]1626 if h.startswith("---"):
[1857]1627 out = "\n".join([out, h])
[489]1628 else:
1629 items = h.split("##")
1630 date = items[0]
1631 func = items[1]
1632 items = items[2:]
[794]1633 out += "\n"+date+"\n"
1634 out += "Function: %s\n Parameters:" % (func)
[489]1635 for i in items:
[1938]1636 if i == '':
1637 continue
[489]1638 s = i.split("=")
[1118]1639 out += "\n %s = %s" % (s[0], s[1])
[1857]1640 out = "\n".join([out, "-"*80])
[1259]1641 if filename is not None:
1642 if filename is "":
1643 filename = 'scantable_history.txt'
1644 import os
1645 filename = os.path.expandvars(os.path.expanduser(filename))
1646 if not os.path.isdir(filename):
1647 data = open(filename, 'w')
1648 data.write(out)
1649 data.close()
1650 else:
1651 msg = "Illegal file name '%s'." % (filename)
[1859]1652 raise IOError(msg)
1653 return page(out)
[513]1654 #
1655 # Maths business
1656 #
[1862]1657 @asaplog_post_dec
[931]1658 def average_time(self, mask=None, scanav=False, weight='tint', align=False):
[1846]1659 """\
[1070]1660 Return the (time) weighted average of a scan.
[1846]1661
1662 *Note*:
1663
[1070]1664 in channels only - align if necessary
[1846]1665
[513]1666 Parameters:
[1846]1667
[513]1668 mask: an optional mask (only used for 'var' and 'tsys'
1669 weighting)
[1855]1670
[558]1671 scanav: True averages each scan separately
1672 False (default) averages all scans together,
[1855]1673
[1099]1674 weight: Weighting scheme.
1675 'none' (mean no weight)
1676 'var' (1/var(spec) weighted)
1677 'tsys' (1/Tsys**2 weighted)
1678 'tint' (integration time weighted)
1679 'tintsys' (Tint/Tsys**2)
1680 'median' ( median averaging)
[535]1681 The default is 'tint'
[1855]1682
[931]1683 align: align the spectra in velocity before averaging. It takes
1684 the time of the first spectrum as reference time.
[1846]1685
1686 Example::
1687
[513]1688 # time average the scantable without using a mask
[710]1689 newscan = scan.average_time()
[1846]1690
[513]1691 """
1692 varlist = vars()
[1593]1693 weight = weight or 'TINT'
1694 mask = mask or ()
1695 scanav = (scanav and 'SCAN') or 'NONE'
[1118]1696 scan = (self, )
[1859]1697
1698 if align:
1699 scan = (self.freq_align(insitu=False), )
1700 s = None
1701 if weight.upper() == 'MEDIAN':
1702 s = scantable(self._math._averagechannel(scan[0], 'MEDIAN',
1703 scanav))
1704 else:
1705 s = scantable(self._math._average(scan, mask, weight.upper(),
1706 scanav))
[1099]1707 s._add_history("average_time", varlist)
[513]1708 return s
[710]1709
[1862]1710 @asaplog_post_dec
[876]1711 def convert_flux(self, jyperk=None, eta=None, d=None, insitu=None):
[1846]1712 """\
[513]1713 Return a scan where all spectra are converted to either
1714 Jansky or Kelvin depending upon the flux units of the scan table.
1715 By default the function tries to look the values up internally.
1716 If it can't find them (or if you want to over-ride), you must
1717 specify EITHER jyperk OR eta (and D which it will try to look up
1718 also if you don't set it). jyperk takes precedence if you set both.
[1846]1719
[513]1720 Parameters:
[1846]1721
[513]1722 jyperk: the Jy / K conversion factor
[1855]1723
[513]1724 eta: the aperture efficiency
[1855]1725
[1928]1726 d: the geometric diameter (metres)
[1855]1727
[513]1728 insitu: if False a new scantable is returned.
1729 Otherwise, the scaling is done in-situ
1730 The default is taken from .asaprc (False)
[1846]1731
[513]1732 """
1733 if insitu is None: insitu = rcParams['insitu']
[876]1734 self._math._setinsitu(insitu)
[513]1735 varlist = vars()
[1593]1736 jyperk = jyperk or -1.0
1737 d = d or -1.0
1738 eta = eta or -1.0
[876]1739 s = scantable(self._math._convertflux(self, d, eta, jyperk))
1740 s._add_history("convert_flux", varlist)
1741 if insitu: self._assign(s)
1742 else: return s
[513]1743
[1862]1744 @asaplog_post_dec
[876]1745 def gain_el(self, poly=None, filename="", method="linear", insitu=None):
[1846]1746 """\
[513]1747 Return a scan after applying a gain-elevation correction.
1748 The correction can be made via either a polynomial or a
1749 table-based interpolation (and extrapolation if necessary).
1750 You specify polynomial coefficients, an ascii table or neither.
1751 If you specify neither, then a polynomial correction will be made
1752 with built in coefficients known for certain telescopes (an error
1753 will occur if the instrument is not known).
1754 The data and Tsys are *divided* by the scaling factors.
[1846]1755
[513]1756 Parameters:
[1846]1757
[513]1758 poly: Polynomial coefficients (default None) to compute a
1759 gain-elevation correction as a function of
1760 elevation (in degrees).
[1855]1761
[513]1762 filename: The name of an ascii file holding correction factors.
1763 The first row of the ascii file must give the column
1764 names and these MUST include columns
1765 "ELEVATION" (degrees) and "FACTOR" (multiply data
1766 by this) somewhere.
1767 The second row must give the data type of the
1768 column. Use 'R' for Real and 'I' for Integer.
1769 An example file would be
1770 (actual factors are arbitrary) :
1771
1772 TIME ELEVATION FACTOR
1773 R R R
1774 0.1 0 0.8
1775 0.2 20 0.85
1776 0.3 40 0.9
1777 0.4 60 0.85
1778 0.5 80 0.8
1779 0.6 90 0.75
[1855]1780
[513]1781 method: Interpolation method when correcting from a table.
1782 Values are "nearest", "linear" (default), "cubic"
1783 and "spline"
[1855]1784
[513]1785 insitu: if False a new scantable is returned.
1786 Otherwise, the scaling is done in-situ
1787 The default is taken from .asaprc (False)
[1846]1788
[513]1789 """
1790
1791 if insitu is None: insitu = rcParams['insitu']
[876]1792 self._math._setinsitu(insitu)
[513]1793 varlist = vars()
[1593]1794 poly = poly or ()
[513]1795 from os.path import expandvars
1796 filename = expandvars(filename)
[876]1797 s = scantable(self._math._gainel(self, poly, filename, method))
1798 s._add_history("gain_el", varlist)
[1593]1799 if insitu:
1800 self._assign(s)
1801 else:
1802 return s
[710]1803
[1862]1804 @asaplog_post_dec
[931]1805 def freq_align(self, reftime=None, method='cubic', insitu=None):
[1846]1806 """\
[513]1807 Return a scan where all rows have been aligned in frequency/velocity.
1808 The alignment frequency frame (e.g. LSRK) is that set by function
1809 set_freqframe.
[1846]1810
[513]1811 Parameters:
[1855]1812
[513]1813 reftime: reference time to align at. By default, the time of
1814 the first row of data is used.
[1855]1815
[513]1816 method: Interpolation method for regridding the spectra.
1817 Choose from "nearest", "linear", "cubic" (default)
1818 and "spline"
[1855]1819
[513]1820 insitu: if False a new scantable is returned.
1821 Otherwise, the scaling is done in-situ
1822 The default is taken from .asaprc (False)
[1846]1823
[513]1824 """
[931]1825 if insitu is None: insitu = rcParams["insitu"]
[876]1826 self._math._setinsitu(insitu)
[513]1827 varlist = vars()
[1593]1828 reftime = reftime or ""
[931]1829 s = scantable(self._math._freq_align(self, reftime, method))
[876]1830 s._add_history("freq_align", varlist)
1831 if insitu: self._assign(s)
1832 else: return s
[513]1833
[1862]1834 @asaplog_post_dec
[1725]1835 def opacity(self, tau=None, insitu=None):
[1846]1836 """\
[513]1837 Apply an opacity correction. The data
1838 and Tsys are multiplied by the correction factor.
[1846]1839
[513]1840 Parameters:
[1855]1841
[1689]1842 tau: (list of) opacity from which the correction factor is
[513]1843 exp(tau*ZD)
[1689]1844 where ZD is the zenith-distance.
1845 If a list is provided, it has to be of length nIF,
1846 nIF*nPol or 1 and in order of IF/POL, e.g.
1847 [opif0pol0, opif0pol1, opif1pol0 ...]
[1725]1848 if tau is `None` the opacities are determined from a
1849 model.
[1855]1850
[513]1851 insitu: if False a new scantable is returned.
1852 Otherwise, the scaling is done in-situ
1853 The default is taken from .asaprc (False)
[1846]1854
[513]1855 """
1856 if insitu is None: insitu = rcParams['insitu']
[876]1857 self._math._setinsitu(insitu)
[513]1858 varlist = vars()
[1689]1859 if not hasattr(tau, "__len__"):
1860 tau = [tau]
[876]1861 s = scantable(self._math._opacity(self, tau))
1862 s._add_history("opacity", varlist)
1863 if insitu: self._assign(s)
1864 else: return s
[513]1865
[1862]1866 @asaplog_post_dec
[513]1867 def bin(self, width=5, insitu=None):
[1846]1868 """\
[513]1869 Return a scan where all spectra have been binned up.
[1846]1870
[1348]1871 Parameters:
[1846]1872
[513]1873 width: The bin width (default=5) in pixels
[1855]1874
[513]1875 insitu: if False a new scantable is returned.
1876 Otherwise, the scaling is done in-situ
1877 The default is taken from .asaprc (False)
[1846]1878
[513]1879 """
1880 if insitu is None: insitu = rcParams['insitu']
[876]1881 self._math._setinsitu(insitu)
[513]1882 varlist = vars()
[876]1883 s = scantable(self._math._bin(self, width))
[1118]1884 s._add_history("bin", varlist)
[1589]1885 if insitu:
1886 self._assign(s)
1887 else:
1888 return s
[513]1889
[1862]1890 @asaplog_post_dec
[513]1891 def resample(self, width=5, method='cubic', insitu=None):
[1846]1892 """\
[1348]1893 Return a scan where all spectra have been binned up.
[1573]1894
[1348]1895 Parameters:
[1846]1896
[513]1897 width: The bin width (default=5) in pixels
[1855]1898
[513]1899 method: Interpolation method when correcting from a table.
1900 Values are "nearest", "linear", "cubic" (default)
1901 and "spline"
[1855]1902
[513]1903 insitu: if False a new scantable is returned.
1904 Otherwise, the scaling is done in-situ
1905 The default is taken from .asaprc (False)
[1846]1906
[513]1907 """
1908 if insitu is None: insitu = rcParams['insitu']
[876]1909 self._math._setinsitu(insitu)
[513]1910 varlist = vars()
[876]1911 s = scantable(self._math._resample(self, method, width))
[1118]1912 s._add_history("resample", varlist)
[876]1913 if insitu: self._assign(s)
1914 else: return s
[513]1915
[1862]1916 @asaplog_post_dec
[946]1917 def average_pol(self, mask=None, weight='none'):
[1846]1918 """\
[946]1919 Average the Polarisations together.
[1846]1920
[946]1921 Parameters:
[1846]1922
[946]1923 mask: An optional mask defining the region, where the
1924 averaging will be applied. The output will have all
1925 specified points masked.
[1855]1926
[946]1927 weight: Weighting scheme. 'none' (default), 'var' (1/var(spec)
1928 weighted), or 'tsys' (1/Tsys**2 weighted)
[1846]1929
[946]1930 """
1931 varlist = vars()
[1593]1932 mask = mask or ()
[1010]1933 s = scantable(self._math._averagepol(self, mask, weight.upper()))
[1118]1934 s._add_history("average_pol", varlist)
[992]1935 return s
[513]1936
[1862]1937 @asaplog_post_dec
[1145]1938 def average_beam(self, mask=None, weight='none'):
[1846]1939 """\
[1145]1940 Average the Beams together.
[1846]1941
[1145]1942 Parameters:
1943 mask: An optional mask defining the region, where the
1944 averaging will be applied. The output will have all
1945 specified points masked.
[1855]1946
[1145]1947 weight: Weighting scheme. 'none' (default), 'var' (1/var(spec)
1948 weighted), or 'tsys' (1/Tsys**2 weighted)
[1846]1949
[1145]1950 """
1951 varlist = vars()
[1593]1952 mask = mask or ()
[1145]1953 s = scantable(self._math._averagebeams(self, mask, weight.upper()))
1954 s._add_history("average_beam", varlist)
1955 return s
1956
[1586]1957 def parallactify(self, pflag):
[1846]1958 """\
[1843]1959 Set a flag to indicate whether this data should be treated as having
[1617]1960 been 'parallactified' (total phase == 0.0)
[1846]1961
[1617]1962 Parameters:
[1855]1963
[1843]1964 pflag: Bool indicating whether to turn this on (True) or
[1617]1965 off (False)
[1846]1966
[1617]1967 """
[1586]1968 varlist = vars()
1969 self._parallactify(pflag)
1970 self._add_history("parallactify", varlist)
1971
[1862]1972 @asaplog_post_dec
[992]1973 def convert_pol(self, poltype=None):
[1846]1974 """\
[992]1975 Convert the data to a different polarisation type.
[1565]1976 Note that you will need cross-polarisation terms for most conversions.
[1846]1977
[992]1978 Parameters:
[1855]1979
[992]1980 poltype: The new polarisation type. Valid types are:
[1565]1981 "linear", "circular", "stokes" and "linpol"
[1846]1982
[992]1983 """
1984 varlist = vars()
[1859]1985 s = scantable(self._math._convertpol(self, poltype))
[1118]1986 s._add_history("convert_pol", varlist)
[992]1987 return s
1988
[1862]1989 @asaplog_post_dec
[1819]1990 def smooth(self, kernel="hanning", width=5.0, order=2, plot=False, insitu=None):
[1846]1991 """\
[513]1992 Smooth the spectrum by the specified kernel (conserving flux).
[1846]1993
[513]1994 Parameters:
[1846]1995
[513]1996 kernel: The type of smoothing kernel. Select from
[1574]1997 'hanning' (default), 'gaussian', 'boxcar', 'rmedian'
1998 or 'poly'
[1855]1999
[513]2000 width: The width of the kernel in pixels. For hanning this is
2001 ignored otherwise it defauls to 5 pixels.
2002 For 'gaussian' it is the Full Width Half
2003 Maximum. For 'boxcar' it is the full width.
[1574]2004 For 'rmedian' and 'poly' it is the half width.
[1855]2005
[1574]2006 order: Optional parameter for 'poly' kernel (default is 2), to
2007 specify the order of the polnomial. Ignored by all other
2008 kernels.
[1855]2009
[1819]2010 plot: plot the original and the smoothed spectra.
2011 In this each indivual fit has to be approved, by
2012 typing 'y' or 'n'
[1855]2013
[513]2014 insitu: if False a new scantable is returned.
2015 Otherwise, the scaling is done in-situ
2016 The default is taken from .asaprc (False)
[1846]2017
[513]2018 """
2019 if insitu is None: insitu = rcParams['insitu']
[876]2020 self._math._setinsitu(insitu)
[513]2021 varlist = vars()
[1819]2022
2023 if plot: orgscan = self.copy()
2024
[1574]2025 s = scantable(self._math._smooth(self, kernel.lower(), width, order))
[876]2026 s._add_history("smooth", varlist)
[1819]2027
2028 if plot:
2029 if rcParams['plotter.gui']:
2030 from asap.asaplotgui import asaplotgui as asaplot
2031 else:
2032 from asap.asaplot import asaplot
2033 self._p=asaplot()
2034 self._p.set_panels()
2035 ylab=s._get_ordinate_label()
2036 #self._p.palette(0,["#777777","red"])
2037 for r in xrange(s.nrow()):
2038 xsm=s._getabcissa(r)
2039 ysm=s._getspectrum(r)
2040 xorg=orgscan._getabcissa(r)
2041 yorg=orgscan._getspectrum(r)
2042 self._p.clear()
2043 self._p.hold()
2044 self._p.set_axes('ylabel',ylab)
2045 self._p.set_axes('xlabel',s._getabcissalabel(r))
2046 self._p.set_axes('title',s._getsourcename(r))
2047 self._p.set_line(label='Original',color="#777777")
2048 self._p.plot(xorg,yorg)
2049 self._p.set_line(label='Smoothed',color="red")
2050 self._p.plot(xsm,ysm)
2051 ### Ugly part for legend
2052 for i in [0,1]:
2053 self._p.subplots[0]['lines'].append([self._p.subplots[0]['axes'].lines[i]])
2054 self._p.release()
2055 ### Ugly part for legend
2056 self._p.subplots[0]['lines']=[]
2057 res = raw_input("Accept smoothing ([y]/n): ")
2058 if res.upper() == 'N':
2059 s._setspectrum(yorg, r)
2060 self._p.unmap()
2061 self._p = None
2062 del orgscan
2063
[876]2064 if insitu: self._assign(s)
2065 else: return s
[513]2066
[2012]2067
[1862]2068 @asaplog_post_dec
[2012]2069 def cspline_baseline(self, insitu=None, mask=None, npiece=None, clipthresh=None, clipniter=None, plot=None, outlog=None, blfile=None):
[1846]2070 """\
[2012]2071 Return a scan which has been baselined (all rows) by cubic spline function (piecewise cubic polynomial).
[513]2072 Parameters:
[2012]2073 insitu: If False a new scantable is returned.
2074 Otherwise, the scaling is done in-situ
2075 The default is taken from .asaprc (False)
2076 mask: An optional mask
2077 npiece: Number of pieces. (default is 2)
2078 clipthresh: Clipping threshold. (default is 3.0, unit: sigma)
2079 clipniter: maximum number of iteration of 'clipthresh'-sigma clipping (default is 1)
2080 plot: *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
2081 plot the fit and the residual. In this each
2082 indivual fit has to be approved, by typing 'y'
2083 or 'n'
2084 outlog: Output the coefficients of the best-fit
2085 function to logger (default is False)
2086 blfile: Name of a text file in which the best-fit
2087 parameter values to be written
2088 (default is "": no file/logger output)
[1846]2089
[2012]2090 Example:
2091 # return a scan baselined by a cubic spline consisting of 2 pieces (i.e., 1 internal knot),
2092 # also with 3-sigma clipping, iteration up to 4 times
2093 bscan = scan.cspline_baseline(npiece=2,clipthresh=3.0,clipniter=4)
2094 """
2095
2096 varlist = vars()
2097
2098 if insitu is None: insitu = rcParams["insitu"]
2099 if insitu:
2100 workscan = self
2101 else:
2102 workscan = self.copy()
[1855]2103
[2012]2104 nchan = workscan.nchan()
2105
2106 if mask is None: mask = [True for i in xrange(nchan)]
2107 if npiece is None: npiece = 2
2108 if clipthresh is None: clipthresh = 3.0
2109 if clipniter is None: clipniter = 1
2110 if plot is None: plot = False
2111 if outlog is None: outlog = False
2112 if blfile is None: blfile = ""
[1855]2113
[2012]2114 outblfile = (blfile != "") and os.path.exists(os.path.expanduser(os.path.expandvars(blfile)))
2115
2116 try:
2117 #CURRENTLY, PLOT=true UNAVAILABLE UNTIL cubic spline fitting is implemented as a fitter method.
2118 workscan._cspline_baseline(mask, npiece, clipthresh, clipniter, outlog, blfile)
2119
2120 workscan._add_history("cspline_baseline", varlist)
2121
2122 if insitu:
2123 self._assign(workscan)
2124 else:
2125 return workscan
2126
2127 except RuntimeError, e:
2128 msg = "The fit failed, possibly because it didn't converge."
2129 if rcParams["verbose"]:
2130 asaplog.push(str(e))
2131 asaplog.push(str(msg))
2132 return
2133 else:
2134 raise RuntimeError(str(e)+'\n'+msg)
[1855]2135
2136
[2012]2137 def auto_cspline_baseline(self, insitu=None, mask=None, npiece=None, clipthresh=None,
2138 clipniter=None, edge=None, threshold=None,
2139 chan_avg_limit=None, plot=None, outlog=None, blfile=None):
2140 """\
2141 Return a scan which has been baselined (all rows) by cubic spline
2142 function (piecewise cubic polynomial).
2143 Spectral lines are detected first using linefinder and masked out
2144 to avoid them affecting the baseline solution.
2145
2146 Parameters:
[794]2147 insitu: if False a new scantable is returned.
2148 Otherwise, the scaling is done in-situ
2149 The default is taken from .asaprc (False)
[2012]2150 mask: an optional mask retreived from scantable
2151 npiece: Number of pieces. (default is 2)
2152 clipthresh: Clipping threshold. (default is 3.0, unit: sigma)
2153 clipniter: maximum number of iteration of 'clipthresh'-sigma clipping (default is 1)
2154 edge: an optional number of channel to drop at
2155 the edge of spectrum. If only one value is
2156 specified, the same number will be dropped
2157 from both sides of the spectrum. Default
2158 is to keep all channels. Nested tuples
2159 represent individual edge selection for
2160 different IFs (a number of spectral channels
2161 can be different)
2162 threshold: the threshold used by line finder. It is
2163 better to keep it large as only strong lines
2164 affect the baseline solution.
2165 chan_avg_limit:
2166 a maximum number of consequtive spectral
2167 channels to average during the search of
2168 weak and broad lines. The default is no
2169 averaging (and no search for weak lines).
2170 If such lines can affect the fitted baseline
2171 (e.g. a high order polynomial is fitted),
2172 increase this parameter (usually values up
2173 to 8 are reasonable). Most users of this
2174 method should find the default value sufficient.
2175 plot: *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
2176 plot the fit and the residual. In this each
2177 indivual fit has to be approved, by typing 'y'
2178 or 'n'
2179 outlog: Output the coefficients of the best-fit
2180 function to logger (default is False)
2181 blfile: Name of a text file in which the best-fit
2182 parameter values to be written
2183 (default is "": no file/logger output)
[1846]2184
[1907]2185 Example:
[2012]2186 bscan = scan.auto_cspline_baseline(npiece=3, insitu=False)
2187 """
[1846]2188
[2012]2189 varlist = vars()
2190
[513]2191 if insitu is None: insitu = rcParams['insitu']
[2012]2192 if insitu:
2193 workscan = self
2194 else:
[1819]2195 workscan = self.copy()
[2012]2196
2197 nchan = workscan.nchan()
2198
2199 if mask is None: mask = [True for i in xrange(nchan)]
2200 if npiece is None: npiece = 2
2201 if clipthresh is None: clipthresh = 3.0
2202 if clipniter is None: clipniter = 1
2203 if edge is None: edge = (0, 0)
2204 if threshold is None: threshold = 3
2205 if chan_avg_limit is None: chan_avg_limit = 1
2206 if plot is None: plot = False
2207 if outlog is None: outlog = False
2208 if blfile is None: blfile = ""
2209
2210 outblfile = (blfile != "") and os.path.exists(os.path.expanduser(os.path.expandvars(blfile)))
2211
2212 from asap.asaplinefind import linefinder
2213 from asap import _is_sequence_or_number as _is_valid
2214
2215 if not (isinstance(edge, list) or isinstance(edge, tuple)): edge = [ edge ]
2216 individualedge = False;
2217 if len(edge) > 1: individualedge = isinstance(edge[0], list) or isinstance(edge[0], tuple)
2218
2219 if individualedge:
2220 for edgepar in edge:
2221 if not _is_valid(edgepar, int):
2222 raise ValueError, "Each element of the 'edge' tuple has \
2223 to be a pair of integers or an integer."
[1819]2224 else:
[2012]2225 if not _is_valid(edge, int):
2226 raise ValueError, "Parameter 'edge' has to be an integer or a \
2227 pair of integers specified as a tuple. \
2228 Nested tuples are allowed \
2229 to make individual selection for different IFs."
[1819]2230
[2012]2231 if len(edge) > 1:
2232 curedge = edge
[1391]2233 else:
[2012]2234 curedge = edge + edge
[1819]2235
[2012]2236 try:
2237 #CURRENTLY, PLOT=true UNAVAILABLE UNTIL cubic spline fitting is implemented as a fitter method.
2238 if individualedge:
2239 curedge = []
2240 for i in xrange(len(edge)):
2241 curedge += edge[i]
2242
2243 workscan._auto_cspline_baseline(mask, npiece, clipthresh, clipniter, curedge, threshold, chan_avg_limit, outlog, blfile)
2244
2245 workscan._add_history("auto_cspline_baseline", varlist)
[1907]2246
[1856]2247 if insitu:
2248 self._assign(workscan)
2249 else:
2250 return workscan
[2012]2251
2252 except RuntimeError, e:
[1217]2253 msg = "The fit failed, possibly because it didn't converge."
[2012]2254 if rcParams["verbose"]:
2255 asaplog.push(str(e))
2256 asaplog.push(str(msg))
2257 return
2258 else:
2259 raise RuntimeError(str(e)+'\n'+msg)
[513]2260
[2012]2261
[1931]2262 @asaplog_post_dec
[2012]2263 def poly_baseline(self, insitu=None, mask=None, order=None, plot=None, outlog=None, blfile=None):
[1907]2264 """\
2265 Return a scan which has been baselined (all rows) by a polynomial.
2266 Parameters:
[2012]2267 insitu: if False a new scantable is returned.
2268 Otherwise, the scaling is done in-situ
2269 The default is taken from .asaprc (False)
[1907]2270 mask: an optional mask
2271 order: the order of the polynomial (default is 0)
2272 plot: plot the fit and the residual. In this each
2273 indivual fit has to be approved, by typing 'y'
[2012]2274 or 'n'
2275 outlog: Output the coefficients of the best-fit
2276 function to logger (default is False)
2277 blfile: Name of a text file in which the best-fit
2278 parameter values to be written
2279 (default is "": no file/logger output)
2280
[1907]2281 Example:
2282 # return a scan baselined by a third order polynomial,
2283 # not using a mask
2284 bscan = scan.poly_baseline(order=3)
2285 """
[1931]2286
2287 varlist = vars()
2288
[1907]2289 if insitu is None: insitu = rcParams["insitu"]
2290 if insitu:
2291 workscan = self
2292 else:
2293 workscan = self.copy()
2294
2295 nchan = workscan.nchan()
2296
[2012]2297 if mask is None: mask = [True for i in xrange(nchan)]
2298 if order is None: order = 0
2299 if plot is None: plot = False
2300 if outlog is None: outlog = False
2301 if blfile is None: blfile = ""
[1907]2302
[2012]2303 outblfile = (blfile != "") and os.path.exists(os.path.expanduser(os.path.expandvars(blfile)))
2304
[1907]2305 try:
[2012]2306 rows = xrange(workscan.nrow())
[1907]2307
[2012]2308 #if len(rows) > 0: workscan._init_blinfo()
[1907]2309
[2012]2310 if plot:
2311 if outblfile: blf = open(blfile, "a")
2312
[1907]2313 f = fitter()
2314 f.set_function(lpoly=order)
2315 for r in rows:
2316 f.x = workscan._getabcissa(r)
2317 f.y = workscan._getspectrum(r)
2318 f.mask = mask_and(mask, workscan._getmask(r)) # (CAS-1434)
2319 f.data = None
2320 f.fit()
2321
2322 f.plot(residual=True)
2323 accept_fit = raw_input("Accept fit ( [y]/n ): ")
2324 if accept_fit.upper() == "N":
[2012]2325 #workscan._append_blinfo(None, None, None)
[1907]2326 continue
[2012]2327
2328 blpars = f.get_parameters()
2329 masklist = workscan.get_masklist(f.mask, row=r, silent=True)
2330 #workscan._append_blinfo(blpars, masklist, f.mask)
[1907]2331 workscan._setspectrum(f.fitter.getresidual(), r)
2332
[2012]2333 if outblfile:
2334 rms = workscan.get_rms(f.mask, r)
2335 dataout = workscan.format_blparams_row(blpars["params"], blpars["fixed"], rms, str(masklist), r, True)
2336 blf.write(dataout)
2337
[1907]2338 f._p.unmap()
2339 f._p = None
[2012]2340
2341 if outblfile: blf.close()
[1907]2342 else:
[2012]2343 workscan._poly_baseline(mask, order, outlog, blfile)
[1907]2344
2345 workscan._add_history("poly_baseline", varlist)
2346
2347 if insitu:
2348 self._assign(workscan)
2349 else:
2350 return workscan
2351
[1919]2352 except RuntimeError, e:
[1907]2353 msg = "The fit failed, possibly because it didn't converge."
2354 if rcParams["verbose"]:
[1919]2355 asaplog.push(str(e))
[1907]2356 asaplog.push(str(msg))
2357 return
2358 else:
[1919]2359 raise RuntimeError(str(e)+'\n'+msg)
[1907]2360
2361
[2012]2362 def auto_poly_baseline(self, insitu=None, mask=None, order=None, edge=None, threshold=None,
2363 chan_avg_limit=None, plot=None, outlog=None, blfile=None):
[1846]2364 """\
[1931]2365 Return a scan which has been baselined (all rows) by a polynomial.
[880]2366 Spectral lines are detected first using linefinder and masked out
2367 to avoid them affecting the baseline solution.
2368
2369 Parameters:
[2012]2370 insitu: if False a new scantable is returned.
2371 Otherwise, the scaling is done in-situ
2372 The default is taken from .asaprc (False)
[880]2373 mask: an optional mask retreived from scantable
2374 order: the order of the polynomial (default is 0)
[2012]2375 edge: an optional number of channel to drop at
2376 the edge of spectrum. If only one value is
2377 specified, the same number will be dropped
2378 from both sides of the spectrum. Default
2379 is to keep all channels. Nested tuples
2380 represent individual edge selection for
2381 different IFs (a number of spectral channels
2382 can be different)
2383 threshold: the threshold used by line finder. It is
2384 better to keep it large as only strong lines
2385 affect the baseline solution.
[1280]2386 chan_avg_limit:
[2012]2387 a maximum number of consequtive spectral
2388 channels to average during the search of
2389 weak and broad lines. The default is no
2390 averaging (and no search for weak lines).
2391 If such lines can affect the fitted baseline
2392 (e.g. a high order polynomial is fitted),
2393 increase this parameter (usually values up
2394 to 8 are reasonable). Most users of this
2395 method should find the default value sufficient.
[1061]2396 plot: plot the fit and the residual. In this each
2397 indivual fit has to be approved, by typing 'y'
2398 or 'n'
[2012]2399 outlog: Output the coefficients of the best-fit
2400 function to logger (default is False)
2401 blfile: Name of a text file in which the best-fit
2402 parameter values to be written
2403 (default is "": no file/logger output)
[1846]2404
[2012]2405 Example:
2406 bscan = scan.auto_poly_baseline(order=7, insitu=False)
2407 """
[880]2408
[2012]2409 varlist = vars()
[1846]2410
[2012]2411 if insitu is None: insitu = rcParams['insitu']
2412 if insitu:
2413 workscan = self
2414 else:
2415 workscan = self.copy()
[1846]2416
[2012]2417 nchan = workscan.nchan()
2418
2419 if mask is None: mask = [True for i in xrange(nchan)]
2420 if order is None: order = 0
2421 if edge is None: edge = (0, 0)
2422 if threshold is None: threshold = 3
2423 if chan_avg_limit is None: chan_avg_limit = 1
2424 if plot is None: plot = False
2425 if outlog is None: outlog = False
2426 if blfile is None: blfile = ""
[1846]2427
[2012]2428 outblfile = (blfile != "") and os.path.exists(os.path.expanduser(os.path.expandvars(blfile)))
2429
[880]2430 from asap.asaplinefind import linefinder
2431 from asap import _is_sequence_or_number as _is_valid
2432
[2012]2433 if not (isinstance(edge, list) or isinstance(edge, tuple)): edge = [ edge ]
[1118]2434 individualedge = False;
[2012]2435 if len(edge) > 1: individualedge = isinstance(edge[0], list) or isinstance(edge[0], tuple)
[907]2436
[1118]2437 if individualedge:
2438 for edgepar in edge:
2439 if not _is_valid(edgepar, int):
2440 raise ValueError, "Each element of the 'edge' tuple has \
2441 to be a pair of integers or an integer."
[907]2442 else:
[2012]2443 if not _is_valid(edge, int):
2444 raise ValueError, "Parameter 'edge' has to be an integer or a \
2445 pair of integers specified as a tuple. \
2446 Nested tuples are allowed \
2447 to make individual selection for different IFs."
[880]2448
[2012]2449 if len(edge) > 1:
2450 curedge = edge
2451 else:
2452 curedge = edge + edge
[1907]2453
[2012]2454 try:
2455 rows = xrange(workscan.nrow())
2456
2457 #if len(rows) > 0: workscan._init_blinfo()
[880]2458
[2012]2459 if plot:
2460 if outblfile: blf = open(blfile, "a")
2461
2462 fl = linefinder()
2463 fl.set_options(threshold=threshold,avg_limit=chan_avg_limit)
2464 fl.set_scan(workscan)
2465 f = fitter()
2466 f.set_function(lpoly=order)
[880]2467
[2012]2468 for r in rows:
2469 if individualedge:
2470 if len(edge) <= workscan.getif(r):
2471 raise RuntimeError, "Number of edge elements appear to " \
2472 "be less than the number of IFs"
2473 else:
2474 curedge = edge[workscan.getif(r)]
[907]2475
[2012]2476 fl.find_lines(r, mask_and(mask, workscan._getmask(r)), curedge) # (CAS-1434)
2477
2478 f.x = workscan._getabcissa(r)
2479 f.y = workscan._getspectrum(r)
2480 f.mask = fl.get_mask()
2481 f.data = None
2482 f.fit()
2483
2484 f.plot(residual=True)
2485 accept_fit = raw_input("Accept fit ( [y]/n ): ")
2486 if accept_fit.upper() == "N":
2487 #workscan._append_blinfo(None, None, None)
2488 continue
2489
2490 blpars = f.get_parameters()
2491 masklist = workscan.get_masklist(f.mask, row=r, silent=True)
2492 #workscan._append_blinfo(blpars, masklist, f.mask)
2493 workscan._setspectrum(f.fitter.getresidual(), r)
2494
2495 if outblfile:
2496 rms = workscan.get_rms(f.mask, r)
2497 dataout = workscan.format_blparams_row(blpars["params"], blpars["fixed"], rms, str(masklist), r, True)
2498 blf.write(dataout)
2499
2500 f._p.unmap()
2501 f._p = None
2502
2503 if outblfile: blf.close()
2504
2505 else:
2506 if individualedge:
2507 curedge = []
2508 for i in xrange(len(edge)):
2509 curedge += edge[i]
2510
2511 workscan._auto_poly_baseline(mask, order, curedge, threshold, chan_avg_limit, outlog, blfile)
2512
2513 workscan._add_history("auto_poly_baseline", varlist)
2514
2515 if insitu:
2516 self._assign(workscan)
2517 else:
2518 return workscan
2519
2520 except RuntimeError, e:
2521 msg = "The fit failed, possibly because it didn't converge."
2522 if rcParams["verbose"]:
2523 asaplog.push(str(e))
2524 asaplog.push(str(msg))
2525 return
2526 else:
2527 raise RuntimeError(str(e)+'\n'+msg)
2528
2529
2530 ### OBSOLETE ##################################################################
2531 @asaplog_post_dec
2532 def old_poly_baseline(self, mask=None, order=0, plot=False, uselin=False, insitu=None, rows=None):
2533 """
2534 Return a scan which has been baselined (all rows) by a polynomial.
[1907]2535
[2012]2536 Parameters:
2537
2538 mask: an optional mask
2539
2540 order: the order of the polynomial (default is 0)
2541
2542 plot: plot the fit and the residual. In this each
2543 indivual fit has to be approved, by typing 'y'
2544 or 'n'
2545
2546 uselin: use linear polynomial fit
2547
2548 insitu: if False a new scantable is returned.
2549 Otherwise, the scaling is done in-situ
2550 The default is taken from .asaprc (False)
2551
2552 rows: row numbers of spectra to be processed.
2553 (default is None: for all rows)
[1907]2554
[2012]2555 Example:
2556 # return a scan baselined by a third order polynomial,
2557 # not using a mask
2558 bscan = scan.poly_baseline(order=3)
[907]2559
[2012]2560 """
2561 if insitu is None: insitu = rcParams['insitu']
2562 if not insitu:
2563 workscan = self.copy()
2564 else:
2565 workscan = self
2566 varlist = vars()
2567 if mask is None:
2568 mask = [True for i in xrange(self.nchan())]
[919]2569
[2012]2570 try:
2571 f = fitter()
2572 if uselin:
2573 f.set_function(lpoly=order)
2574 else:
2575 f.set_function(poly=order)
[1819]2576
[2012]2577 if rows == None:
2578 rows = xrange(workscan.nrow())
2579 elif isinstance(rows, int):
2580 rows = [ rows ]
[1907]2581
[2012]2582 if len(rows) > 0:
2583 self.blpars = []
2584 self.masklists = []
2585 self.actualmask = []
2586
2587 for r in rows:
2588 f.x = workscan._getabcissa(r)
2589 f.y = workscan._getspectrum(r)
2590 f.mask = mask_and(mask, workscan._getmask(r)) # (CAS-1434)
2591 f.data = None
2592 f.fit()
2593 if plot:
2594 f.plot(residual=True)
2595 x = raw_input("Accept fit ( [y]/n ): ")
2596 if x.upper() == 'N':
2597 self.blpars.append(None)
2598 self.masklists.append(None)
2599 self.actualmask.append(None)
2600 continue
2601 workscan._setspectrum(f.fitter.getresidual(), r)
2602 self.blpars.append(f.get_parameters())
2603 self.masklists.append(workscan.get_masklist(f.mask, row=r, silent=True))
2604 self.actualmask.append(f.mask)
[1819]2605
[1061]2606 if plot:
[2012]2607 f._p.unmap()
2608 f._p = None
2609 workscan._add_history("poly_baseline", varlist)
2610 if insitu:
2611 self._assign(workscan)
2612 else:
2613 return workscan
2614 except RuntimeError:
2615 msg = "The fit failed, possibly because it didn't converge."
2616 raise RuntimeError(msg)
[1819]2617
[2012]2618 def _init_blinfo(self):
2619 """\
2620 Initialise the following three auxiliary members:
2621 blpars : parameters of the best-fit baseline,
2622 masklists : mask data (edge positions of masked channels) and
2623 actualmask : mask data (in boolean list),
2624 to keep for use later (including output to logger/text files).
2625 Used by poly_baseline() and auto_poly_baseline() in case of
2626 'plot=True'.
2627 """
2628 self.blpars = []
2629 self.masklists = []
2630 self.actualmask = []
2631 return
[880]2632
[2012]2633 def _append_blinfo(self, data_blpars, data_masklists, data_actualmask):
2634 """\
2635 Append baseline-fitting related info to blpars, masklist and
2636 actualmask.
2637 """
2638 self.blpars.append(data_blpars)
2639 self.masklists.append(data_masklists)
2640 self.actualmask.append(data_actualmask)
2641 return
2642
[1862]2643 @asaplog_post_dec
[914]2644 def rotate_linpolphase(self, angle):
[1846]2645 """\
[914]2646 Rotate the phase of the complex polarization O=Q+iU correlation.
2647 This is always done in situ in the raw data. So if you call this
2648 function more than once then each call rotates the phase further.
[1846]2649
[914]2650 Parameters:
[1846]2651
[914]2652 angle: The angle (degrees) to rotate (add) by.
[1846]2653
2654 Example::
2655
[914]2656 scan.rotate_linpolphase(2.3)
[1846]2657
[914]2658 """
2659 varlist = vars()
[936]2660 self._math._rotate_linpolphase(self, angle)
[914]2661 self._add_history("rotate_linpolphase", varlist)
2662 return
[710]2663
[1862]2664 @asaplog_post_dec
[914]2665 def rotate_xyphase(self, angle):
[1846]2666 """\
[914]2667 Rotate the phase of the XY correlation. This is always done in situ
2668 in the data. So if you call this function more than once
2669 then each call rotates the phase further.
[1846]2670
[914]2671 Parameters:
[1846]2672
[914]2673 angle: The angle (degrees) to rotate (add) by.
[1846]2674
2675 Example::
2676
[914]2677 scan.rotate_xyphase(2.3)
[1846]2678
[914]2679 """
2680 varlist = vars()
[936]2681 self._math._rotate_xyphase(self, angle)
[914]2682 self._add_history("rotate_xyphase", varlist)
2683 return
2684
[1862]2685 @asaplog_post_dec
[914]2686 def swap_linears(self):
[1846]2687 """\
[1573]2688 Swap the linear polarisations XX and YY, or better the first two
[1348]2689 polarisations as this also works for ciculars.
[914]2690 """
2691 varlist = vars()
[936]2692 self._math._swap_linears(self)
[914]2693 self._add_history("swap_linears", varlist)
2694 return
2695
[1862]2696 @asaplog_post_dec
[914]2697 def invert_phase(self):
[1846]2698 """\
[914]2699 Invert the phase of the complex polarisation
2700 """
2701 varlist = vars()
[936]2702 self._math._invert_phase(self)
[914]2703 self._add_history("invert_phase", varlist)
2704 return
2705
[1862]2706 @asaplog_post_dec
[876]2707 def add(self, offset, insitu=None):
[1846]2708 """\
[513]2709 Return a scan where all spectra have the offset added
[1846]2710
[513]2711 Parameters:
[1846]2712
[513]2713 offset: the offset
[1855]2714
[513]2715 insitu: if False a new scantable is returned.
2716 Otherwise, the scaling is done in-situ
2717 The default is taken from .asaprc (False)
[1846]2718
[513]2719 """
2720 if insitu is None: insitu = rcParams['insitu']
[876]2721 self._math._setinsitu(insitu)
[513]2722 varlist = vars()
[876]2723 s = scantable(self._math._unaryop(self, offset, "ADD", False))
[1118]2724 s._add_history("add", varlist)
[876]2725 if insitu:
2726 self._assign(s)
2727 else:
[513]2728 return s
2729
[1862]2730 @asaplog_post_dec
[1308]2731 def scale(self, factor, tsys=True, insitu=None):
[1846]2732 """\
2733
[1938]2734 Return a scan where all spectra are scaled by the given 'factor'
[1846]2735
[513]2736 Parameters:
[1846]2737
[1819]2738 factor: the scaling factor (float or 1D float list)
[1855]2739
[513]2740 insitu: if False a new scantable is returned.
2741 Otherwise, the scaling is done in-situ
2742 The default is taken from .asaprc (False)
[1855]2743
[513]2744 tsys: if True (default) then apply the operation to Tsys
2745 as well as the data
[1846]2746
[513]2747 """
2748 if insitu is None: insitu = rcParams['insitu']
[876]2749 self._math._setinsitu(insitu)
[513]2750 varlist = vars()
[1819]2751 s = None
2752 import numpy
2753 if isinstance(factor, list) or isinstance(factor, numpy.ndarray):
2754 if isinstance(factor[0], list) or isinstance(factor[0], numpy.ndarray):
2755 from asapmath import _array2dOp
2756 s = _array2dOp( self.copy(), factor, "MUL", tsys )
2757 else:
2758 s = scantable( self._math._arrayop( self.copy(), factor, "MUL", tsys ) )
2759 else:
2760 s = scantable(self._math._unaryop(self.copy(), factor, "MUL", tsys))
[1118]2761 s._add_history("scale", varlist)
[876]2762 if insitu:
2763 self._assign(s)
2764 else:
[513]2765 return s
2766
[1504]2767 def set_sourcetype(self, match, matchtype="pattern",
2768 sourcetype="reference"):
[1846]2769 """\
[1502]2770 Set the type of the source to be an source or reference scan
[1846]2771 using the provided pattern.
2772
[1502]2773 Parameters:
[1846]2774
[1504]2775 match: a Unix style pattern, regular expression or selector
[1855]2776
[1504]2777 matchtype: 'pattern' (default) UNIX style pattern or
2778 'regex' regular expression
[1855]2779
[1502]2780 sourcetype: the type of the source to use (source/reference)
[1846]2781
[1502]2782 """
2783 varlist = vars()
2784 basesel = self.get_selection()
2785 stype = -1
2786 if sourcetype.lower().startswith("r"):
2787 stype = 1
2788 elif sourcetype.lower().startswith("s"):
2789 stype = 0
[1504]2790 else:
[1502]2791 raise ValueError("Illegal sourcetype use s(ource) or r(eference)")
[1504]2792 if matchtype.lower().startswith("p"):
2793 matchtype = "pattern"
2794 elif matchtype.lower().startswith("r"):
2795 matchtype = "regex"
2796 else:
2797 raise ValueError("Illegal matchtype, use p(attern) or r(egex)")
[1502]2798 sel = selector()
2799 if isinstance(match, selector):
2800 sel = match
2801 else:
[1504]2802 sel.set_query("SRCNAME == %s('%s')" % (matchtype, match))
[1502]2803 self.set_selection(basesel+sel)
2804 self._setsourcetype(stype)
2805 self.set_selection(basesel)
[1573]2806 self._add_history("set_sourcetype", varlist)
[1502]2807
[1862]2808 @asaplog_post_dec
[1857]2809 @preserve_selection
[1819]2810 def auto_quotient(self, preserve=True, mode='paired', verify=False):
[1846]2811 """\
[670]2812 This function allows to build quotients automatically.
[1819]2813 It assumes the observation to have the same number of
[670]2814 "ons" and "offs"
[1846]2815
[670]2816 Parameters:
[1846]2817
[710]2818 preserve: you can preserve (default) the continuum or
2819 remove it. The equations used are
[1857]2820
[670]2821 preserve: Output = Toff * (on/off) - Toff
[1857]2822
[1070]2823 remove: Output = Toff * (on/off) - Ton
[1855]2824
[1573]2825 mode: the on/off detection mode
[1348]2826 'paired' (default)
2827 identifies 'off' scans by the
2828 trailing '_R' (Mopra/Parkes) or
2829 '_e'/'_w' (Tid) and matches
2830 on/off pairs from the observing pattern
[1502]2831 'time'
2832 finds the closest off in time
[1348]2833
[1857]2834 .. todo:: verify argument is not implemented
2835
[670]2836 """
[1857]2837 varlist = vars()
[1348]2838 modes = ["time", "paired"]
[670]2839 if not mode in modes:
[876]2840 msg = "please provide valid mode. Valid modes are %s" % (modes)
2841 raise ValueError(msg)
[1348]2842 s = None
2843 if mode.lower() == "paired":
[1857]2844 sel = self.get_selection()
[1875]2845 sel.set_query("SRCTYPE==psoff")
[1356]2846 self.set_selection(sel)
[1348]2847 offs = self.copy()
[1875]2848 sel.set_query("SRCTYPE==pson")
[1356]2849 self.set_selection(sel)
[1348]2850 ons = self.copy()
2851 s = scantable(self._math._quotient(ons, offs, preserve))
2852 elif mode.lower() == "time":
2853 s = scantable(self._math._auto_quotient(self, mode, preserve))
[1118]2854 s._add_history("auto_quotient", varlist)
[876]2855 return s
[710]2856
[1862]2857 @asaplog_post_dec
[1145]2858 def mx_quotient(self, mask = None, weight='median', preserve=True):
[1846]2859 """\
[1143]2860 Form a quotient using "off" beams when observing in "MX" mode.
[1846]2861
[1143]2862 Parameters:
[1846]2863
[1145]2864 mask: an optional mask to be used when weight == 'stddev'
[1855]2865
[1143]2866 weight: How to average the off beams. Default is 'median'.
[1855]2867
[1145]2868 preserve: you can preserve (default) the continuum or
[1855]2869 remove it. The equations used are:
[1846]2870
[1855]2871 preserve: Output = Toff * (on/off) - Toff
2872
2873 remove: Output = Toff * (on/off) - Ton
2874
[1217]2875 """
[1593]2876 mask = mask or ()
[1141]2877 varlist = vars()
2878 on = scantable(self._math._mx_extract(self, 'on'))
[1143]2879 preoff = scantable(self._math._mx_extract(self, 'off'))
2880 off = preoff.average_time(mask=mask, weight=weight, scanav=False)
[1217]2881 from asapmath import quotient
[1145]2882 q = quotient(on, off, preserve)
[1143]2883 q._add_history("mx_quotient", varlist)
[1217]2884 return q
[513]2885
[1862]2886 @asaplog_post_dec
[718]2887 def freq_switch(self, insitu=None):
[1846]2888 """\
[718]2889 Apply frequency switching to the data.
[1846]2890
[718]2891 Parameters:
[1846]2892
[718]2893 insitu: if False a new scantable is returned.
2894 Otherwise, the swictching is done in-situ
2895 The default is taken from .asaprc (False)
[1846]2896
[718]2897 """
2898 if insitu is None: insitu = rcParams['insitu']
[876]2899 self._math._setinsitu(insitu)
[718]2900 varlist = vars()
[876]2901 s = scantable(self._math._freqswitch(self))
[1118]2902 s._add_history("freq_switch", varlist)
[1856]2903 if insitu:
2904 self._assign(s)
2905 else:
2906 return s
[718]2907
[1862]2908 @asaplog_post_dec
[780]2909 def recalc_azel(self):
[1846]2910 """Recalculate the azimuth and elevation for each position."""
[780]2911 varlist = vars()
[876]2912 self._recalcazel()
[780]2913 self._add_history("recalc_azel", varlist)
2914 return
2915
[1862]2916 @asaplog_post_dec
[513]2917 def __add__(self, other):
2918 varlist = vars()
2919 s = None
2920 if isinstance(other, scantable):
[1573]2921 s = scantable(self._math._binaryop(self, other, "ADD"))
[513]2922 elif isinstance(other, float):
[876]2923 s = scantable(self._math._unaryop(self, other, "ADD", False))
[513]2924 else:
[718]2925 raise TypeError("Other input is not a scantable or float value")
[513]2926 s._add_history("operator +", varlist)
2927 return s
2928
[1862]2929 @asaplog_post_dec
[513]2930 def __sub__(self, other):
2931 """
2932 implicit on all axes and on Tsys
2933 """
2934 varlist = vars()
2935 s = None
2936 if isinstance(other, scantable):
[1588]2937 s = scantable(self._math._binaryop(self, other, "SUB"))
[513]2938 elif isinstance(other, float):
[876]2939 s = scantable(self._math._unaryop(self, other, "SUB", False))
[513]2940 else:
[718]2941 raise TypeError("Other input is not a scantable or float value")
[513]2942 s._add_history("operator -", varlist)
2943 return s
[710]2944
[1862]2945 @asaplog_post_dec
[513]2946 def __mul__(self, other):
2947 """
2948 implicit on all axes and on Tsys
2949 """
2950 varlist = vars()
2951 s = None
2952 if isinstance(other, scantable):
[1588]2953 s = scantable(self._math._binaryop(self, other, "MUL"))
[513]2954 elif isinstance(other, float):
[876]2955 s = scantable(self._math._unaryop(self, other, "MUL", False))
[513]2956 else:
[718]2957 raise TypeError("Other input is not a scantable or float value")
[513]2958 s._add_history("operator *", varlist)
2959 return s
2960
[710]2961
[1862]2962 @asaplog_post_dec
[513]2963 def __div__(self, other):
2964 """
2965 implicit on all axes and on Tsys
2966 """
2967 varlist = vars()
2968 s = None
2969 if isinstance(other, scantable):
[1589]2970 s = scantable(self._math._binaryop(self, other, "DIV"))
[513]2971 elif isinstance(other, float):
2972 if other == 0.0:
[718]2973 raise ZeroDivisionError("Dividing by zero is not recommended")
[876]2974 s = scantable(self._math._unaryop(self, other, "DIV", False))
[513]2975 else:
[718]2976 raise TypeError("Other input is not a scantable or float value")
[513]2977 s._add_history("operator /", varlist)
2978 return s
2979
[1862]2980 @asaplog_post_dec
[530]2981 def get_fit(self, row=0):
[1846]2982 """\
[530]2983 Print or return the stored fits for a row in the scantable
[1846]2984
[530]2985 Parameters:
[1846]2986
[530]2987 row: the row which the fit has been applied to.
[1846]2988
[530]2989 """
2990 if row > self.nrow():
2991 return
[976]2992 from asap.asapfit import asapfit
[530]2993 fit = asapfit(self._getfit(row))
[1859]2994 asaplog.push( '%s' %(fit) )
2995 return fit.as_dict()
[530]2996
[1483]2997 def flag_nans(self):
[1846]2998 """\
[1483]2999 Utility function to flag NaN values in the scantable.
3000 """
3001 import numpy
3002 basesel = self.get_selection()
3003 for i in range(self.nrow()):
[1589]3004 sel = self.get_row_selector(i)
3005 self.set_selection(basesel+sel)
[1483]3006 nans = numpy.isnan(self._getspectrum(0))
3007 if numpy.any(nans):
3008 bnans = [ bool(v) for v in nans]
3009 self.flag(bnans)
3010 self.set_selection(basesel)
3011
[1588]3012 def get_row_selector(self, rowno):
[1992]3013 #return selector(beams=self.getbeam(rowno),
3014 # ifs=self.getif(rowno),
3015 # pols=self.getpol(rowno),
3016 # scans=self.getscan(rowno),
3017 # cycles=self.getcycle(rowno))
3018 return selector(rows=[rowno])
[1573]3019
[484]3020 def _add_history(self, funcname, parameters):
[1435]3021 if not rcParams['scantable.history']:
3022 return
[484]3023 # create date
3024 sep = "##"
3025 from datetime import datetime
3026 dstr = datetime.now().strftime('%Y/%m/%d %H:%M:%S')
3027 hist = dstr+sep
3028 hist += funcname+sep#cdate+sep
3029 if parameters.has_key('self'): del parameters['self']
[1118]3030 for k, v in parameters.iteritems():
[484]3031 if type(v) is dict:
[1118]3032 for k2, v2 in v.iteritems():
[484]3033 hist += k2
3034 hist += "="
[1118]3035 if isinstance(v2, scantable):
[484]3036 hist += 'scantable'
3037 elif k2 == 'mask':
[1118]3038 if isinstance(v2, list) or isinstance(v2, tuple):
[513]3039 hist += str(self._zip_mask(v2))
3040 else:
3041 hist += str(v2)
[484]3042 else:
[513]3043 hist += str(v2)
[484]3044 else:
3045 hist += k
3046 hist += "="
[1118]3047 if isinstance(v, scantable):
[484]3048 hist += 'scantable'
3049 elif k == 'mask':
[1118]3050 if isinstance(v, list) or isinstance(v, tuple):
[513]3051 hist += str(self._zip_mask(v))
3052 else:
3053 hist += str(v)
[484]3054 else:
3055 hist += str(v)
3056 hist += sep
3057 hist = hist[:-2] # remove trailing '##'
3058 self._addhistory(hist)
3059
[710]3060
[484]3061 def _zip_mask(self, mask):
3062 mask = list(mask)
3063 i = 0
3064 segments = []
3065 while mask[i:].count(1):
3066 i += mask[i:].index(1)
3067 if mask[i:].count(0):
3068 j = i + mask[i:].index(0)
3069 else:
[710]3070 j = len(mask)
[1118]3071 segments.append([i, j])
[710]3072 i = j
[484]3073 return segments
[714]3074
[626]3075 def _get_ordinate_label(self):
3076 fu = "("+self.get_fluxunit()+")"
3077 import re
3078 lbl = "Intensity"
[1118]3079 if re.match(".K.", fu):
[626]3080 lbl = "Brightness Temperature "+ fu
[1118]3081 elif re.match(".Jy.", fu):
[626]3082 lbl = "Flux density "+ fu
3083 return lbl
[710]3084
[876]3085 def _check_ifs(self):
[1986]3086 #nchans = [self.nchan(i) for i in range(self.nif(-1))]
3087 nchans = [self.nchan(i) for i in self.getifnos()]
[2004]3088 nchans = filter(lambda t: t > 0, nchans)
[876]3089 return (sum(nchans)/len(nchans) == nchans[0])
[976]3090
[1862]3091 @asaplog_post_dec
[1916]3092 #def _fill(self, names, unit, average, getpt, antenna):
3093 def _fill(self, names, unit, average, opts={}):
[976]3094 first = True
3095 fullnames = []
3096 for name in names:
3097 name = os.path.expandvars(name)
3098 name = os.path.expanduser(name)
3099 if not os.path.exists(name):
3100 msg = "File '%s' does not exists" % (name)
3101 raise IOError(msg)
3102 fullnames.append(name)
3103 if average:
3104 asaplog.push('Auto averaging integrations')
[1079]3105 stype = int(rcParams['scantable.storage'].lower() == 'disk')
[976]3106 for name in fullnames:
[1073]3107 tbl = Scantable(stype)
[2004]3108 if is_ms( name ):
3109 r = msfiller( tbl )
3110 else:
3111 r = filler( tbl )
3112 rx = rcParams['scantable.reference']
3113 r.setreferenceexpr(rx)
3114 #r = filler(tbl)
3115 #rx = rcParams['scantable.reference']
3116 #r.setreferenceexpr(rx)
[976]3117 msg = "Importing %s..." % (name)
[1118]3118 asaplog.push(msg, False)
[1916]3119 #opts = {'ms': {'antenna' : antenna, 'getpt': getpt} }
[1904]3120 r.open(name, opts)# antenna, -1, -1, getpt)
[1843]3121 r.fill()
[976]3122 if average:
[1118]3123 tbl = self._math._average((tbl, ), (), 'NONE', 'SCAN')
[976]3124 if not first:
3125 tbl = self._math._merge([self, tbl])
3126 Scantable.__init__(self, tbl)
[1843]3127 r.close()
[1118]3128 del r, tbl
[976]3129 first = False
[1861]3130 #flush log
3131 asaplog.post()
[976]3132 if unit is not None:
3133 self.set_fluxunit(unit)
[1824]3134 if not is_casapy():
3135 self.set_freqframe(rcParams['scantable.freqframe'])
[976]3136
[2012]3137
[1402]3138 def __getitem__(self, key):
3139 if key < 0:
3140 key += self.nrow()
3141 if key >= self.nrow():
3142 raise IndexError("Row index out of range.")
3143 return self._getspectrum(key)
3144
3145 def __setitem__(self, key, value):
3146 if key < 0:
3147 key += self.nrow()
3148 if key >= self.nrow():
3149 raise IndexError("Row index out of range.")
3150 if not hasattr(value, "__len__") or \
3151 len(value) > self.nchan(self.getif(key)):
3152 raise ValueError("Spectrum length doesn't match.")
3153 return self._setspectrum(value, key)
3154
3155 def __len__(self):
3156 return self.nrow()
3157
3158 def __iter__(self):
3159 for i in range(len(self)):
3160 yield self[i]
Note: See TracBrowser for help on using the repository browser.