source: trunk/python/scantable.py@ 2025

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

New Development: No

JIRA Issue: Yes (CAS-1425)

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs: scantable.parse_maskexpr()

Put in Release Notes: No

Module(s): scantable

Description: select all channels in all IFs for .


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