source: trunk/python/scantable.py@ 1769

Last change on this file since 1769 was 1731, checked in by Malte Marquarding, 15 years ago

Fixed wrong base class call in shift_refix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 71.4 KB
RevLine 
[1697]1import os
[1691]2try:
3 from functools import wraps as wraps_dec
4except ImportError:
5 from asap.compatibility import wraps as wraps_dec
6
[876]7from asap._asap import Scantable
[226]8from asap import rcParams
[1589]9from asap import print_log, print_log_dec
[1118]10from asap import asaplog
[946]11from asap import selector
[1153]12from asap import linecatalog
[1600]13from asap.coordinate import coordinate
[1295]14from asap import _n_bools, mask_not, mask_and, mask_or
[102]15
[1689]16
17def preserve_selection(func):
[1691]18 @wraps_dec(func)
[1689]19 def wrap(obj, *args, **kw):
20 basesel = obj.get_selection()
21 val = func(obj, *args, **kw)
22 obj.set_selection(basesel)
23 return val
24 return wrap
25
26
[1697]27def is_scantable(filename):
28 return (os.path.isdir(filename)
29 and not os.path.exists(filename+'/table.f1')
30 and os.path.exists(filename+'/table.info'))
31
32
[876]33class scantable(Scantable):
[102]34 """
35 The ASAP container for scans
36 """
[1589]37 @print_log_dec
[1586]38 def __init__(self, filename, average=None, unit=None, parallactify=None):
[102]39 """
40 Create a scantable from a saved one or make a reference
41 Parameters:
[181]42 filename: the name of an asap table on disk
43 or
44 the name of a rpfits/sdfits/ms file
45 (integrations within scans are auto averaged
46 and the whole file is read)
47 or
48 [advanced] a reference to an existing
[102]49 scantable
[484]50 average: average all integrations withinb a scan on read.
51 The default (True) is taken from .asaprc.
52 unit: brightness unit; must be consistent with K or Jy.
[340]53 Over-rides the default selected by the reader
54 (input rpfits/sdfits/ms) or replaces the value
55 in existing scantables
[1586]56 parallactify: Indcicate that the data had been parallatified.
57 Default is taken form rc file.
[710]58 """
[976]59 if average is None:
[710]60 average = rcParams['scantable.autoaverage']
[1593]61 parallactify = parallactify or rcParams['scantable.parallactify']
[1259]62 varlist = vars()
[876]63 from asap._asap import stmath
64 self._math = stmath()
65 if isinstance(filename, Scantable):
66 Scantable.__init__(self, filename)
[181]67 else:
[1697]68 if isinstance(filename, str):
[976]69 filename = os.path.expandvars(filename)
70 filename = os.path.expanduser(filename)
71 if not os.path.exists(filename):
72 s = "File '%s' not found." % (filename)
[718]73 if rcParams['verbose']:
[976]74 asaplog.push(s)
[718]75 return
[976]76 raise IOError(s)
[1697]77 if is_scantable(filename):
78 ondisk = rcParams['scantable.storage'] == 'disk'
79 Scantable.__init__(self, filename, ondisk)
80 if unit is not None:
81 self.set_fluxunit(unit)
82 self.set_freqframe(rcParams['scantable.freqframe'])
[226]83 else:
[1118]84 self._fill([filename], unit, average)
85 elif (isinstance(filename, list) or isinstance(filename, tuple)) \
[976]86 and isinstance(filename[-1], str):
87 self._fill(filename, unit, average)
[1586]88 self.parallactify(parallactify)
[1259]89 self._add_history("scantable", varlist)
[102]90
[1589]91 @print_log_dec
[876]92 def save(self, name=None, format=None, overwrite=False):
[116]93 """
[1280]94 Store the scantable on disk. This can be an asap (aips++) Table,
95 SDFITS or MS2 format.
[116]96 Parameters:
[1093]97 name: the name of the outputfile. For format "ASCII"
98 this is the root file name (data in 'name'.txt
[497]99 and header in 'name'_header.txt)
[116]100 format: an optional file format. Default is ASAP.
[280]101 Allowed are - 'ASAP' (save as ASAP [aips++] Table),
[194]102 'SDFITS' (save as SDFITS file)
[200]103 'ASCII' (saves as ascii text file)
[226]104 'MS2' (saves as an aips++
105 MeasurementSet V2)
[1573]106 'FITS' (save as image FITS - not
[1443]107 readable by class)
108 'CLASS' (save as FITS readable by CLASS)
[411]109 overwrite: If the file should be overwritten if it exists.
[256]110 The default False is to return with warning
[411]111 without writing the output. USE WITH CARE.
[116]112 Example:
113 scan.save('myscan.asap')
[1118]114 scan.save('myscan.sdfits', 'SDFITS')
[116]115 """
[411]116 from os import path
[1593]117 format = format or rcParams['scantable.save']
[256]118 suffix = '.'+format.lower()
[1118]119 if name is None or name == "":
[256]120 name = 'scantable'+suffix
[718]121 msg = "No filename given. Using default name %s..." % name
122 asaplog.push(msg)
[411]123 name = path.expandvars(name)
[256]124 if path.isfile(name) or path.isdir(name):
125 if not overwrite:
[718]126 msg = "File %s exists." % name
127 if rcParams['verbose']:
128 print msg
129 return
130 else:
131 raise IOError(msg)
[451]132 format2 = format.upper()
133 if format2 == 'ASAP':
[116]134 self._save(name)
135 else:
[989]136 from asap._asap import stwriter as stw
[1118]137 writer = stw(format2)
138 writer.write(self, name)
[116]139 return
140
[102]141 def copy(self):
142 """
143 Return a copy of this scantable.
[1348]144 Note:
145 This makes a full (deep) copy. scan2 = scan1 makes a reference.
[102]146 Parameters:
[113]147 none
[102]148 Example:
149 copiedscan = scan.copy()
150 """
[876]151 sd = scantable(Scantable._copy(self))
[113]152 return sd
153
[1093]154 def drop_scan(self, scanid=None):
155 """
156 Return a new scantable where the specified scan number(s) has(have)
157 been dropped.
158 Parameters:
159 scanid: a (list of) scan number(s)
160 """
161 from asap import _is_sequence_or_number as _is_valid
162 from asap import _to_list
163 from asap import unique
164 if not _is_valid(scanid):
165 if rcParams['verbose']:
166 print "Please specify a scanno to drop from the scantable"
167 return
168 else:
169 raise RuntimeError("No scan given")
170 try:
171 scanid = _to_list(scanid)
172 allscans = unique([ self.getscan(i) for i in range(self.nrow())])
173 for sid in scanid: allscans.remove(sid)
[1118]174 if len(allscans) == 0:
175 raise ValueError("Can't remove all scans")
[1093]176 except ValueError:
177 if rcParams['verbose']:
178 print "Couldn't find any match."
179 return
180 else: raise
181 try:
[1593]182 sel = selector(scans=allscans)
[1594]183 return self._select_copy(sel)
[1093]184 except RuntimeError:
[1118]185 if rcParams['verbose']:
186 print "Couldn't find any match."
187 else:
188 raise
[1093]189
[1594]190 def _select_copy(self, selection):
191 orig = self.get_selection()
192 self.set_selection(orig+selection)
193 cp = self.copy()
194 self.set_selection(orig)
195 return cp
196
[102]197 def get_scan(self, scanid=None):
198 """
199 Return a specific scan (by scanno) or collection of scans (by
200 source name) in a new scantable.
[1348]201 Note:
202 See scantable.drop_scan() for the inverse operation.
[102]203 Parameters:
[513]204 scanid: a (list of) scanno or a source name, unix-style
205 patterns are accepted for source name matching, e.g.
206 '*_R' gets all 'ref scans
[102]207 Example:
[513]208 # get all scans containing the source '323p459'
209 newscan = scan.get_scan('323p459')
210 # get all 'off' scans
211 refscans = scan.get_scan('*_R')
212 # get a susbset of scans by scanno (as listed in scan.summary())
[1118]213 newscan = scan.get_scan([0, 2, 7, 10])
[102]214 """
215 if scanid is None:
[718]216 if rcParams['verbose']:
[1118]217 print "Please specify a scan no or name to " \
218 "retrieve from the scantable"
[718]219 return
220 else:
221 raise RuntimeError("No scan given")
222
[102]223 try:
[946]224 bsel = self.get_selection()
225 sel = selector()
[102]226 if type(scanid) is str:
[946]227 sel.set_name(scanid)
[1594]228 return self._select_copy(sel)
[102]229 elif type(scanid) is int:
[946]230 sel.set_scans([scanid])
[1594]231 return self._select_copy(sel)
[381]232 elif type(scanid) is list:
[946]233 sel.set_scans(scanid)
[1594]234 return self._select_copy(sel)
[381]235 else:
[718]236 msg = "Illegal scanid type, use 'int' or 'list' if ints."
237 if rcParams['verbose']:
238 print msg
239 else:
240 raise TypeError(msg)
[102]241 except RuntimeError:
[718]242 if rcParams['verbose']: print "Couldn't find any match."
243 else: raise
[102]244
245 def __str__(self):
[1118]246 return Scantable._summary(self, True)
[102]247
[976]248 def summary(self, filename=None):
[102]249 """
250 Print a summary of the contents of this scantable.
251 Parameters:
252 filename: the name of a file to write the putput to
253 Default - no file output
254 """
[976]255 info = Scantable._summary(self, True)
[102]256 if filename is not None:
[256]257 if filename is "":
258 filename = 'scantable_summary.txt'
[415]259 from os.path import expandvars, isdir
[411]260 filename = expandvars(filename)
[415]261 if not isdir(filename):
[413]262 data = open(filename, 'w')
263 data.write(info)
264 data.close()
265 else:
[718]266 msg = "Illegal file name '%s'." % (filename)
267 if rcParams['verbose']:
268 print msg
269 else:
270 raise IOError(msg)
271 if rcParams['verbose']:
[794]272 try:
273 from IPython.genutils import page as pager
274 except ImportError:
275 from pydoc import pager
276 pager(info)
[718]277 else:
278 return info
[710]279
[1512]280 def get_spectrum(self, rowno):
[1471]281 """Return the spectrum for the current row in the scantable as a list.
282 Parameters:
[1573]283 rowno: the row number to retrieve the spectrum from
[1471]284 """
285 return self._getspectrum(rowno)
[946]286
[1471]287 def get_mask(self, rowno):
288 """Return the mask for the current row in the scantable as a list.
289 Parameters:
[1573]290 rowno: the row number to retrieve the mask from
[1471]291 """
292 return self._getmask(rowno)
293
294 def set_spectrum(self, spec, rowno):
295 """Return the spectrum for the current row in the scantable as a list.
296 Parameters:
297 spec: the spectrum
[1573]298 rowno: the row number to set the spectrum for
[1471]299 """
300 assert(len(spec) == self.nchan())
301 return self._setspectrum(spec, rowno)
302
[1600]303 def get_coordinate(self, rowno):
304 """Return the (spectral) coordinate for a a given 'rowno'.
305 NOTE:
306 * This coordinate is only valid until a scantable method modifies
307 the frequency axis.
308 * This coordinate does contain the original frequency set-up
309 NOT the new frame. The conversions however are done using the user
310 specified frame (e.g. LSRK/TOPO). To get the 'real' coordinate,
311 use scantable.freq_align first. Without it there is no closure,
312 i.e.
313 c = myscan.get_coordinate(0)
314 c.to_frequency(c.get_reference_pixel()) != c.get_reference_value()
315
316 Parameters:
317 rowno: the row number for the spectral coordinate
318
319 """
320 return coordinate(Scantable.get_coordinate(self, rowno))
321
[946]322 def get_selection(self):
323 """
[1005]324 Get the selection object currently set on this scantable.
325 Parameters:
326 none
327 Example:
328 sel = scan.get_selection()
329 sel.set_ifs(0) # select IF 0
330 scan.set_selection(sel) # apply modified selection
[946]331 """
332 return selector(self._getselection())
333
[1576]334 def set_selection(self, selection=None, **kw):
[946]335 """
[1005]336 Select a subset of the data. All following operations on this scantable
337 are only applied to thi selection.
338 Parameters:
[1697]339 selection: a selector object (default unset the selection),
340
341 or
342
343 any combination of
344 "pols", "ifs", "beams", "scans", "cycles", "name", "query"
345
[1005]346 Examples:
347 sel = selector() # create a selection object
[1118]348 self.set_scans([0, 3]) # select SCANNO 0 and 3
[1005]349 scan.set_selection(sel) # set the selection
350 scan.summary() # will only print summary of scanno 0 an 3
351 scan.set_selection() # unset the selection
[1697]352 # or the equivalent
353 scan.set_selection(scans=[0,3])
354 scan.summary() # will only print summary of scanno 0 an 3
355 scan.set_selection() # unset the selection
[946]356 """
[1576]357 if selection is None:
358 # reset
359 if len(kw) == 0:
360 selection = selector()
361 else:
362 # try keywords
363 for k in kw:
364 if k not in selector.fields:
365 raise KeyError("Invalid selection key '%s', valid keys are %s" % (k, selector.fields))
366 selection = selector(**kw)
[946]367 self._setselection(selection)
368
[876]369 def stats(self, stat='stddev', mask=None):
[102]370 """
[135]371 Determine the specified statistic of the current beam/if/pol
[102]372 Takes a 'mask' as an optional parameter to specify which
373 channels should be excluded.
374 Parameters:
[135]375 stat: 'min', 'max', 'sumsq', 'sum', 'mean'
376 'var', 'stddev', 'avdev', 'rms', 'median'
377 mask: an optional mask specifying where the statistic
[102]378 should be determined.
379 Example:
[113]380 scan.set_unit('channel')
[1118]381 msk = scan.create_mask([100, 200], [500, 600])
[135]382 scan.stats(stat='mean', mask=m)
[102]383 """
[1593]384 mask = mask or []
[876]385 if not self._check_ifs():
[1118]386 raise ValueError("Cannot apply mask as the IFs have different "
387 "number of channels. Please use setselection() "
388 "to select individual IFs")
[256]389
[876]390 statvals = self._math._stats(self, mask, stat)
[1590]391 def cb(i):
392 return statvals[i]
[256]393
[1590]394 return self._row_callback(cb, stat)
[102]395
[1118]396 def stddev(self, mask=None):
[135]397 """
398 Determine the standard deviation of the current beam/if/pol
399 Takes a 'mask' as an optional parameter to specify which
400 channels should be excluded.
401 Parameters:
402 mask: an optional mask specifying where the standard
403 deviation should be determined.
404
405 Example:
406 scan.set_unit('channel')
[1118]407 msk = scan.create_mask([100, 200], [500, 600])
[135]408 scan.stddev(mask=m)
409 """
[1118]410 return self.stats(stat='stddev', mask=mask);
[135]411
[1003]412
[1259]413 def get_column_names(self):
[1003]414 """
415 Return a list of column names, which can be used for selection.
416 """
[1259]417 return list(Scantable.get_column_names(self))
[1003]418
[1730]419 def get_tsys(self, row=-1):
[113]420 """
421 Return the System temperatures.
422 Returns:
[876]423 a list of Tsys values for the current selection
[113]424 """
[1730]425 if row > -1:
426 return self._get_column(self._gettsys, row)
[876]427 return self._row_callback(self._gettsys, "Tsys")
[256]428
[1730]429
430 def get_weather(self, row=-1):
431 values = self._get_column(self._get_weather, row)
432 if row > -1:
433 return {'temperature': values[0],
434 'pressure': values[1], 'humidity' : values[2],
435 'windspeed' : values[3], 'windaz' : values[4]
436 }
437 else:
438 out = []
439 for r in values:
440
441 out.append({'temperature': r[0],
442 'pressure': r[1], 'humidity' : r[2],
443 'windspeed' : r[3], 'windaz' : r[4]
444 })
445 return out
446
[876]447 def _row_callback(self, callback, label):
448 out = ""
[1118]449 outvec = []
[1590]450 sep = '-'*50
[876]451 for i in range(self.nrow()):
452 tm = self._gettime(i)
453 src = self._getsourcename(i)
[1590]454 out += 'Scan[%d] (%s) ' % (self.getscan(i), src)
[876]455 out += 'Time[%s]:\n' % (tm)
[1590]456 if self.nbeam(-1) > 1:
457 out += ' Beam[%d] ' % (self.getbeam(i))
458 if self.nif(-1) > 1: out += ' IF[%d] ' % (self.getif(i))
459 if self.npol(-1) > 1: out += ' Pol[%d] ' % (self.getpol(i))
[876]460 outvec.append(callback(i))
461 out += '= %3.3f\n' % (outvec[i])
[1590]462 out += sep+'\n'
[876]463 if rcParams['verbose']:
[1590]464 print sep
[876]465 print " %s" % (label)
[1590]466 print sep
[876]467 print out
[1175]468 return outvec
[256]469
[1070]470 def _get_column(self, callback, row=-1):
471 """
472 """
473 if row == -1:
474 return [callback(i) for i in range(self.nrow())]
475 else:
[1730]476 if 0 <= row < self.nrow():
[1070]477 return callback(row)
[256]478
[1070]479
[1348]480 def get_time(self, row=-1, asdatetime=False):
[113]481 """
482 Get a list of time stamps for the observations.
[1348]483 Return a datetime object for each integration time stamp in the scantable.
[113]484 Parameters:
[1348]485 row: row no of integration. Default -1 return all rows
486 asdatetime: return values as datetime objects rather than strings
[113]487 Example:
488 none
489 """
[1175]490 from time import strptime
491 from datetime import datetime
[1392]492 times = self._get_column(self._gettime, row)
[1348]493 if not asdatetime:
[1392]494 return times
[1175]495 format = "%Y/%m/%d/%H:%M:%S"
496 if isinstance(times, list):
497 return [datetime(*strptime(i, format)[:6]) for i in times]
498 else:
499 return datetime(*strptime(times, format)[:6])
[102]500
[1348]501
502 def get_inttime(self, row=-1):
503 """
504 Get a list of integration times for the observations.
505 Return a time in seconds for each integration in the scantable.
506 Parameters:
507 row: row no of integration. Default -1 return all rows.
508 Example:
509 none
510 """
[1573]511 return self._get_column(self._getinttime, row)
[1348]512
[1573]513
[714]514 def get_sourcename(self, row=-1):
515 """
[794]516 Get a list source names for the observations.
[714]517 Return a string for each integration in the scantable.
518 Parameters:
[1348]519 row: row no of integration. Default -1 return all rows.
[714]520 Example:
521 none
522 """
[1070]523 return self._get_column(self._getsourcename, row)
[714]524
[794]525 def get_elevation(self, row=-1):
526 """
527 Get a list of elevations for the observations.
528 Return a float for each integration in the scantable.
529 Parameters:
[1348]530 row: row no of integration. Default -1 return all rows.
[794]531 Example:
532 none
533 """
[1070]534 return self._get_column(self._getelevation, row)
[794]535
536 def get_azimuth(self, row=-1):
537 """
538 Get a list of azimuths for the observations.
539 Return a float for each integration in the scantable.
540 Parameters:
[1348]541 row: row no of integration. Default -1 return all rows.
[794]542 Example:
543 none
544 """
[1070]545 return self._get_column(self._getazimuth, row)
[794]546
547 def get_parangle(self, row=-1):
548 """
549 Get a list of parallactic angles for the observations.
550 Return a float for each integration in the scantable.
551 Parameters:
[1348]552 row: row no of integration. Default -1 return all rows.
[794]553 Example:
554 none
555 """
[1070]556 return self._get_column(self._getparangle, row)
[794]557
[1070]558 def get_direction(self, row=-1):
559 """
560 Get a list of Positions on the sky (direction) for the observations.
[1594]561 Return a string for each integration in the scantable.
[1070]562 Parameters:
563 row: row no of integration. Default -1 return all rows
564 Example:
565 none
566 """
567 return self._get_column(self._getdirection, row)
568
[1391]569 def get_directionval(self, row=-1):
570 """
571 Get a list of Positions on the sky (direction) for the observations.
572 Return a float for each integration in the scantable.
573 Parameters:
574 row: row no of integration. Default -1 return all rows
575 Example:
576 none
577 """
578 return self._get_column(self._getdirectionvec, row)
579
[1730]580 @print_log_dec
[102]581 def set_unit(self, unit='channel'):
582 """
583 Set the unit for all following operations on this scantable
584 Parameters:
585 unit: optional unit, default is 'channel'
[1118]586 one of '*Hz', 'km/s', 'channel', ''
[102]587 """
[484]588 varlist = vars()
[1118]589 if unit in ['', 'pixel', 'channel']:
[113]590 unit = ''
591 inf = list(self._getcoordinfo())
592 inf[0] = unit
593 self._setcoordinfo(inf)
[1118]594 self._add_history("set_unit", varlist)
[113]595
[1589]596 @print_log_dec
[484]597 def set_instrument(self, instr):
[358]598 """
[1348]599 Set the instrument for subsequent processing.
[358]600 Parameters:
[710]601 instr: Select from 'ATPKSMB', 'ATPKSHOH', 'ATMOPRA',
[407]602 'DSS-43' (Tid), 'CEDUNA', and 'HOBART'
[358]603 """
604 self._setInstrument(instr)
[1118]605 self._add_history("set_instument", vars())
[358]606
[1589]607 @print_log_dec
[1190]608 def set_feedtype(self, feedtype):
609 """
610 Overwrite the feed type, which might not be set correctly.
611 Parameters:
612 feedtype: 'linear' or 'circular'
613 """
614 self._setfeedtype(feedtype)
615 self._add_history("set_feedtype", vars())
616
[1589]617 @print_log_dec
[276]618 def set_doppler(self, doppler='RADIO'):
619 """
620 Set the doppler for all following operations on this scantable.
621 Parameters:
622 doppler: One of 'RADIO', 'OPTICAL', 'Z', 'BETA', 'GAMMA'
623 """
[484]624 varlist = vars()
[276]625 inf = list(self._getcoordinfo())
626 inf[2] = doppler
627 self._setcoordinfo(inf)
[1118]628 self._add_history("set_doppler", vars())
[710]629
[1589]630 @print_log_dec
[226]631 def set_freqframe(self, frame=None):
[113]632 """
633 Set the frame type of the Spectral Axis.
634 Parameters:
[591]635 frame: an optional frame type, default 'LSRK'. Valid frames are:
[1118]636 'REST', 'TOPO', 'LSRD', 'LSRK', 'BARY',
637 'GEO', 'GALACTO', 'LGROUP', 'CMB'
[113]638 Examples:
639 scan.set_freqframe('BARY')
640 """
[1593]641 frame = frame or rcParams['scantable.freqframe']
[484]642 varlist = vars()
[1118]643 valid = ['REST', 'TOPO', 'LSRD', 'LSRK', 'BARY', \
644 'GEO', 'GALACTO', 'LGROUP', 'CMB']
[591]645
[989]646 if frame in valid:
[113]647 inf = list(self._getcoordinfo())
648 inf[1] = frame
649 self._setcoordinfo(inf)
[1118]650 self._add_history("set_freqframe", varlist)
[102]651 else:
[1118]652 msg = "Please specify a valid freq type. Valid types are:\n", valid
[718]653 if rcParams['verbose']:
654 print msg
655 else:
656 raise TypeError(msg)
[710]657
[989]658 def set_dirframe(self, frame=""):
659 """
660 Set the frame type of the Direction on the sky.
661 Parameters:
662 frame: an optional frame type, default ''. Valid frames are:
663 'J2000', 'B1950', 'GALACTIC'
664 Examples:
665 scan.set_dirframe('GALACTIC')
666 """
667 varlist = vars()
668 try:
669 Scantable.set_dirframe(self, frame)
[1118]670 except RuntimeError, msg:
[989]671 if rcParams['verbose']:
672 print msg
673 else:
674 raise
[1118]675 self._add_history("set_dirframe", varlist)
[989]676
[113]677 def get_unit(self):
678 """
679 Get the default unit set in this scantable
680 Returns:
681 A unit string
682 """
683 inf = self._getcoordinfo()
684 unit = inf[0]
685 if unit == '': unit = 'channel'
686 return unit
[102]687
[158]688 def get_abcissa(self, rowno=0):
[102]689 """
[158]690 Get the abcissa in the current coordinate setup for the currently
[113]691 selected Beam/IF/Pol
692 Parameters:
[226]693 rowno: an optional row number in the scantable. Default is the
694 first row, i.e. rowno=0
[113]695 Returns:
[1348]696 The abcissa values and the format string (as a dictionary)
[113]697 """
[256]698 abc = self._getabcissa(rowno)
[710]699 lbl = self._getabcissalabel(rowno)
[158]700 return abc, lbl
[113]701
[1118]702 def flag(self, mask=None):
[1001]703 """
704 Flag the selected data using an optional channel mask.
705 Parameters:
706 mask: an optional channel mask, created with create_mask. Default
707 (no mask) is all channels.
708 """
709 varlist = vars()
[1593]710 mask = mask or []
[1001]711 try:
712 self._flag(mask)
[1118]713 except RuntimeError, msg:
[1001]714 if rcParams['verbose']:
715 print msg
716 return
717 else: raise
718 self._add_history("flag", varlist)
719
[1589]720 @print_log_dec
[1584]721 def lag_flag(self, start, end, unit="MHz", insitu=None):
[1192]722 """
723 Flag the data in 'lag' space by providing a frequency to remove.
[1584]724 Flagged data in the scantable gets interpolated over the region.
[1192]725 No taper is applied.
726 Parameters:
[1579]727 start: the start frequency (really a period within the
728 bandwidth) or period to remove
729 end: the end frequency or period to remove
[1584]730 unit: the frequency unit (default "MHz") or "" for
[1579]731 explicit lag channels
[1203]732 Notes:
[1579]733 It is recommended to flag edges of the band or strong
[1348]734 signals beforehand.
[1192]735 """
736 if insitu is None: insitu = rcParams['insitu']
737 self._math._setinsitu(insitu)
738 varlist = vars()
[1579]739 base = { "GHz": 1000000000., "MHz": 1000000., "kHz": 1000., "Hz": 1.}
740 if not (unit == "" or base.has_key(unit)):
[1192]741 raise ValueError("%s is not a valid unit." % unit)
742 try:
[1579]743 if unit == "":
744 s = scantable(self._math._lag_flag(self, start, end, "lags"))
745 else:
746 s = scantable(self._math._lag_flag(self, start*base[unit],
747 end*base[unit], "frequency"))
[1192]748 except RuntimeError, msg:
749 if rcParams['verbose']:
750 print msg
751 return
752 else: raise
753 s._add_history("lag_flag", varlist)
754 if insitu:
755 self._assign(s)
756 else:
757 return s
[1001]758
[1589]759 @print_log_dec
[113]760 def create_mask(self, *args, **kwargs):
761 """
[1118]762 Compute and return a mask based on [min, max] windows.
[189]763 The specified windows are to be INCLUDED, when the mask is
[113]764 applied.
[102]765 Parameters:
[1118]766 [min, max], [min2, max2], ...
[1024]767 Pairs of start/end points (inclusive)specifying the regions
[102]768 to be masked
[189]769 invert: optional argument. If specified as True,
770 return an inverted mask, i.e. the regions
771 specified are EXCLUDED
[513]772 row: create the mask using the specified row for
773 unit conversions, default is row=0
774 only necessary if frequency varies over rows.
[102]775 Example:
[113]776 scan.set_unit('channel')
777 a)
[1118]778 msk = scan.create_mask([400, 500], [800, 900])
[189]779 # masks everything outside 400 and 500
[113]780 # and 800 and 900 in the unit 'channel'
781
782 b)
[1118]783 msk = scan.create_mask([400, 500], [800, 900], invert=True)
[189]784 # masks the regions between 400 and 500
[113]785 # and 800 and 900 in the unit 'channel'
[1024]786 c)
787 mask only channel 400
[1554]788 msk = scan.create_mask([400])
[102]789 """
[1554]790 row = kwargs.get("row", 0)
[513]791 data = self._getabcissa(row)
[113]792 u = self._getcoordinfo()[0]
[718]793 if rcParams['verbose']:
[113]794 if u == "": u = "channel"
[718]795 msg = "The current mask window unit is %s" % u
[1118]796 i = self._check_ifs()
797 if not i:
[876]798 msg += "\nThis mask is only valid for IF=%d" % (self.getif(i))
[718]799 asaplog.push(msg)
[102]800 n = self.nchan()
[1295]801 msk = _n_bools(n, False)
[710]802 # test if args is a 'list' or a 'normal *args - UGLY!!!
803
[1118]804 ws = (isinstance(args[-1][-1], int) or isinstance(args[-1][-1], float)) \
805 and args or args[0]
[710]806 for window in ws:
[1554]807 if len(window) == 1:
808 window = [window[0], window[0]]
809 if len(window) == 0 or len(window) > 2:
810 raise ValueError("A window needs to be defined as [start(, end)]")
[1545]811 if window[0] > window[1]:
812 tmp = window[0]
813 window[0] = window[1]
814 window[1] = tmp
[102]815 for i in range(n):
[1024]816 if data[i] >= window[0] and data[i] <= window[1]:
[1295]817 msk[i] = True
[113]818 if kwargs.has_key('invert'):
819 if kwargs.get('invert'):
[1295]820 msk = mask_not(msk)
[102]821 return msk
[710]822
[256]823 def get_restfreqs(self):
824 """
825 Get the restfrequency(s) stored in this scantable.
826 The return value(s) are always of unit 'Hz'
827 Parameters:
828 none
829 Returns:
830 a list of doubles
831 """
832 return list(self._getrestfreqs())
[102]833
[402]834
[931]835 def set_restfreqs(self, freqs=None, unit='Hz'):
836 """
837 Set or replace the restfrequency specified and
838 If the 'freqs' argument holds a scalar,
839 then that rest frequency will be applied to all the selected
840 data. If the 'freqs' argument holds
841 a vector, then it MUST be of equal or smaller length than
842 the number of IFs (and the available restfrequencies will be
843 replaced by this vector). In this case, *all* data have
844 the restfrequency set per IF according
845 to the corresponding value you give in the 'freqs' vector.
[1118]846 E.g. 'freqs=[1e9, 2e9]' would mean IF 0 gets restfreq 1e9 and
[931]847 IF 1 gets restfreq 2e9.
[1395]848 You can also specify the frequencies via a linecatalog.
[1153]849
[931]850 Parameters:
851 freqs: list of rest frequency values or string idenitfiers
852 unit: unit for rest frequency (default 'Hz')
[402]853
[931]854 Example:
855 # set the given restfrequency for the whole table
856 scan.set_restfreqs(freqs=1.4e9)
[1348]857 # If thee number of IFs in the data is >= 2 IF0 gets the first
[931]858 # value IF1 the second...
[1118]859 scan.set_restfreqs(freqs=[1.4e9, 1.67e9])
[931]860 #set the given restfrequency for the whole table (by name)
861 scan.set_restfreqs(freqs="OH1667")
[391]862
[931]863 Note:
864 To do more sophisticate Restfrequency setting, e.g. on a
865 source and IF basis, use scantable.set_selection() before using
866 this function.
[1593]867 # provide your scantable is called scan
[931]868 selection = selector()
869 selection.set_name("ORION*")
870 selection.set_ifs([1])
871 scan.set_selection(selection)
872 scan.set_restfreqs(freqs=86.6e9)
873
874 """
875 varlist = vars()
[1157]876 from asap import linecatalog
877 # simple value
[1118]878 if isinstance(freqs, int) or isinstance(freqs, float):
[1153]879 self._setrestfreqs(freqs, "",unit)
[1157]880 # list of values
[1118]881 elif isinstance(freqs, list) or isinstance(freqs, tuple):
[1157]882 # list values are scalars
[1118]883 if isinstance(freqs[-1], int) or isinstance(freqs[-1], float):
[931]884 sel = selector()
885 savesel = self._getselection()
[1322]886 iflist = self.getifnos()
[931]887 for i in xrange(len(freqs)):
[1322]888 sel.set_ifs(iflist[i])
[931]889 self._setselection(sel)
[1153]890 self._setrestfreqs(freqs[i], "",unit)
[931]891 self._setselection(savesel)
[1157]892 # list values are tuples, (value, name)
893 elif isinstance(freqs[-1], dict):
894 sel = selector()
895 savesel = self._getselection()
[1322]896 iflist = self.getifnos()
[1157]897 for i in xrange(len(freqs)):
[1322]898 sel.set_ifs(iflist[i])
[1259]899 self._setselection(sel)
[1157]900 self._setrestfreqs(freqs[i]["value"],
901 freqs[i]["name"], "MHz")
902 self._setselection(savesel)
903 # freqs are to be taken from a linecatalog
[1153]904 elif isinstance(freqs, linecatalog):
905 sel = selector()
906 savesel = self._getselection()
[1322]907 iflist = self.getifnos()
[1153]908 for i in xrange(freqs.nrow()):
[1322]909 sel.set_ifs(iflist[i])
[1153]910 self._setselection(sel)
911 self._setrestfreqs(freqs.get_frequency(i),
912 freqs.get_name(i), "MHz")
913 # ensure that we are not iterating past nIF
914 if i == self.nif()-1: break
915 self._setselection(savesel)
[931]916 else:
917 return
918 self._add_history("set_restfreqs", varlist)
919
[1360]920 def shift_refpix(self, delta):
[1589]921 """
922 Shift the reference pixel of the Spectra Coordinate by an
923 integer amount.
924 Parameters:
925 delta: the amount to shift by
[1360]926 Note:
[1589]927 Be careful using this with broadband data.
[1360]928 """
[1731]929 Scantable.shift_refpix(self, delta)
[931]930
[1259]931 def history(self, filename=None):
932 """
933 Print the history. Optionally to a file.
[1348]934 Parameters:
935 filename: The name of the file to save the history to.
[1259]936 """
[484]937 hist = list(self._gethistory())
[794]938 out = "-"*80
[484]939 for h in hist:
[489]940 if h.startswith("---"):
[794]941 out += "\n"+h
[489]942 else:
943 items = h.split("##")
944 date = items[0]
945 func = items[1]
946 items = items[2:]
[794]947 out += "\n"+date+"\n"
948 out += "Function: %s\n Parameters:" % (func)
[489]949 for i in items:
950 s = i.split("=")
[1118]951 out += "\n %s = %s" % (s[0], s[1])
[794]952 out += "\n"+"-"*80
[1259]953 if filename is not None:
954 if filename is "":
955 filename = 'scantable_history.txt'
956 import os
957 filename = os.path.expandvars(os.path.expanduser(filename))
958 if not os.path.isdir(filename):
959 data = open(filename, 'w')
960 data.write(out)
961 data.close()
962 else:
963 msg = "Illegal file name '%s'." % (filename)
964 if rcParams['verbose']:
965 print msg
966 else:
967 raise IOError(msg)
968 if rcParams['verbose']:
969 try:
970 from IPython.genutils import page as pager
971 except ImportError:
972 from pydoc import pager
973 pager(out)
974 else:
975 return out
[484]976 return
[513]977 #
978 # Maths business
979 #
[1589]980 @print_log_dec
[931]981 def average_time(self, mask=None, scanav=False, weight='tint', align=False):
[513]982 """
[1070]983 Return the (time) weighted average of a scan.
[513]984 Note:
[1070]985 in channels only - align if necessary
[513]986 Parameters:
987 mask: an optional mask (only used for 'var' and 'tsys'
988 weighting)
[558]989 scanav: True averages each scan separately
990 False (default) averages all scans together,
[1099]991 weight: Weighting scheme.
992 'none' (mean no weight)
993 'var' (1/var(spec) weighted)
994 'tsys' (1/Tsys**2 weighted)
995 'tint' (integration time weighted)
996 'tintsys' (Tint/Tsys**2)
997 'median' ( median averaging)
[535]998 The default is 'tint'
[931]999 align: align the spectra in velocity before averaging. It takes
1000 the time of the first spectrum as reference time.
[513]1001 Example:
1002 # time average the scantable without using a mask
[710]1003 newscan = scan.average_time()
[513]1004 """
1005 varlist = vars()
[1593]1006 weight = weight or 'TINT'
1007 mask = mask or ()
1008 scanav = (scanav and 'SCAN') or 'NONE'
[1118]1009 scan = (self, )
[989]1010 try:
[1118]1011 if align:
1012 scan = (self.freq_align(insitu=False), )
1013 s = None
1014 if weight.upper() == 'MEDIAN':
1015 s = scantable(self._math._averagechannel(scan[0], 'MEDIAN',
1016 scanav))
1017 else:
1018 s = scantable(self._math._average(scan, mask, weight.upper(),
1019 scanav))
1020 except RuntimeError, msg:
[989]1021 if rcParams['verbose']:
1022 print msg
1023 return
1024 else: raise
[1099]1025 s._add_history("average_time", varlist)
[513]1026 return s
[710]1027
[1589]1028 @print_log_dec
[876]1029 def convert_flux(self, jyperk=None, eta=None, d=None, insitu=None):
[513]1030 """
1031 Return a scan where all spectra are converted to either
1032 Jansky or Kelvin depending upon the flux units of the scan table.
1033 By default the function tries to look the values up internally.
1034 If it can't find them (or if you want to over-ride), you must
1035 specify EITHER jyperk OR eta (and D which it will try to look up
1036 also if you don't set it). jyperk takes precedence if you set both.
1037 Parameters:
1038 jyperk: the Jy / K conversion factor
1039 eta: the aperture efficiency
1040 d: the geomtric diameter (metres)
1041 insitu: if False a new scantable is returned.
1042 Otherwise, the scaling is done in-situ
1043 The default is taken from .asaprc (False)
1044 """
1045 if insitu is None: insitu = rcParams['insitu']
[876]1046 self._math._setinsitu(insitu)
[513]1047 varlist = vars()
[1593]1048 jyperk = jyperk or -1.0
1049 d = d or -1.0
1050 eta = eta or -1.0
[876]1051 s = scantable(self._math._convertflux(self, d, eta, jyperk))
1052 s._add_history("convert_flux", varlist)
1053 if insitu: self._assign(s)
1054 else: return s
[513]1055
[1589]1056 @print_log_dec
[876]1057 def gain_el(self, poly=None, filename="", method="linear", insitu=None):
[513]1058 """
1059 Return a scan after applying a gain-elevation correction.
1060 The correction can be made via either a polynomial or a
1061 table-based interpolation (and extrapolation if necessary).
1062 You specify polynomial coefficients, an ascii table or neither.
1063 If you specify neither, then a polynomial correction will be made
1064 with built in coefficients known for certain telescopes (an error
1065 will occur if the instrument is not known).
1066 The data and Tsys are *divided* by the scaling factors.
1067 Parameters:
1068 poly: Polynomial coefficients (default None) to compute a
1069 gain-elevation correction as a function of
1070 elevation (in degrees).
1071 filename: The name of an ascii file holding correction factors.
1072 The first row of the ascii file must give the column
1073 names and these MUST include columns
1074 "ELEVATION" (degrees) and "FACTOR" (multiply data
1075 by this) somewhere.
1076 The second row must give the data type of the
1077 column. Use 'R' for Real and 'I' for Integer.
1078 An example file would be
1079 (actual factors are arbitrary) :
1080
1081 TIME ELEVATION FACTOR
1082 R R R
1083 0.1 0 0.8
1084 0.2 20 0.85
1085 0.3 40 0.9
1086 0.4 60 0.85
1087 0.5 80 0.8
1088 0.6 90 0.75
1089 method: Interpolation method when correcting from a table.
1090 Values are "nearest", "linear" (default), "cubic"
1091 and "spline"
1092 insitu: if False a new scantable is returned.
1093 Otherwise, the scaling is done in-situ
1094 The default is taken from .asaprc (False)
1095 """
1096
1097 if insitu is None: insitu = rcParams['insitu']
[876]1098 self._math._setinsitu(insitu)
[513]1099 varlist = vars()
[1593]1100 poly = poly or ()
[513]1101 from os.path import expandvars
1102 filename = expandvars(filename)
[876]1103 s = scantable(self._math._gainel(self, poly, filename, method))
1104 s._add_history("gain_el", varlist)
[1593]1105 if insitu:
1106 self._assign(s)
1107 else:
1108 return s
[710]1109
[1589]1110 @print_log_dec
[931]1111 def freq_align(self, reftime=None, method='cubic', insitu=None):
[513]1112 """
1113 Return a scan where all rows have been aligned in frequency/velocity.
1114 The alignment frequency frame (e.g. LSRK) is that set by function
1115 set_freqframe.
1116 Parameters:
1117 reftime: reference time to align at. By default, the time of
1118 the first row of data is used.
1119 method: Interpolation method for regridding the spectra.
1120 Choose from "nearest", "linear", "cubic" (default)
1121 and "spline"
1122 insitu: if False a new scantable is returned.
1123 Otherwise, the scaling is done in-situ
1124 The default is taken from .asaprc (False)
1125 """
[931]1126 if insitu is None: insitu = rcParams["insitu"]
[876]1127 self._math._setinsitu(insitu)
[513]1128 varlist = vars()
[1593]1129 reftime = reftime or ""
[931]1130 s = scantable(self._math._freq_align(self, reftime, method))
[876]1131 s._add_history("freq_align", varlist)
1132 if insitu: self._assign(s)
1133 else: return s
[513]1134
[1589]1135 @print_log_dec
[1725]1136 def opacity(self, tau=None, insitu=None):
[513]1137 """
1138 Apply an opacity correction. The data
1139 and Tsys are multiplied by the correction factor.
1140 Parameters:
[1689]1141 tau: (list of) opacity from which the correction factor is
[513]1142 exp(tau*ZD)
[1689]1143 where ZD is the zenith-distance.
1144 If a list is provided, it has to be of length nIF,
1145 nIF*nPol or 1 and in order of IF/POL, e.g.
1146 [opif0pol0, opif0pol1, opif1pol0 ...]
[1725]1147 if tau is `None` the opacities are determined from a
1148 model.
[513]1149 insitu: if False a new scantable is returned.
1150 Otherwise, the scaling is done in-situ
1151 The default is taken from .asaprc (False)
1152 """
1153 if insitu is None: insitu = rcParams['insitu']
[876]1154 self._math._setinsitu(insitu)
[513]1155 varlist = vars()
[1689]1156 if not hasattr(tau, "__len__"):
1157 tau = [tau]
[876]1158 s = scantable(self._math._opacity(self, tau))
1159 s._add_history("opacity", varlist)
1160 if insitu: self._assign(s)
1161 else: return s
[513]1162
[1589]1163 @print_log_dec
[513]1164 def bin(self, width=5, insitu=None):
1165 """
1166 Return a scan where all spectra have been binned up.
[1348]1167 Parameters:
[513]1168 width: The bin width (default=5) in pixels
1169 insitu: if False a new scantable is returned.
1170 Otherwise, the scaling is done in-situ
1171 The default is taken from .asaprc (False)
1172 """
1173 if insitu is None: insitu = rcParams['insitu']
[876]1174 self._math._setinsitu(insitu)
[513]1175 varlist = vars()
[876]1176 s = scantable(self._math._bin(self, width))
[1118]1177 s._add_history("bin", varlist)
[1589]1178 if insitu:
1179 self._assign(s)
1180 else:
1181 return s
[513]1182
[1589]1183 @print_log_dec
[513]1184 def resample(self, width=5, method='cubic', insitu=None):
1185 """
[1348]1186 Return a scan where all spectra have been binned up.
[1573]1187
[1348]1188 Parameters:
[513]1189 width: The bin width (default=5) in pixels
1190 method: Interpolation method when correcting from a table.
1191 Values are "nearest", "linear", "cubic" (default)
1192 and "spline"
1193 insitu: if False a new scantable is returned.
1194 Otherwise, the scaling is done in-situ
1195 The default is taken from .asaprc (False)
1196 """
1197 if insitu is None: insitu = rcParams['insitu']
[876]1198 self._math._setinsitu(insitu)
[513]1199 varlist = vars()
[876]1200 s = scantable(self._math._resample(self, method, width))
[1118]1201 s._add_history("resample", varlist)
[876]1202 if insitu: self._assign(s)
1203 else: return s
[513]1204
[1589]1205 @print_log_dec
[946]1206 def average_pol(self, mask=None, weight='none'):
1207 """
1208 Average the Polarisations together.
1209 Parameters:
1210 mask: An optional mask defining the region, where the
1211 averaging will be applied. The output will have all
1212 specified points masked.
1213 weight: Weighting scheme. 'none' (default), 'var' (1/var(spec)
1214 weighted), or 'tsys' (1/Tsys**2 weighted)
1215 """
1216 varlist = vars()
[1593]1217 mask = mask or ()
[1010]1218 s = scantable(self._math._averagepol(self, mask, weight.upper()))
[1118]1219 s._add_history("average_pol", varlist)
[992]1220 return s
[513]1221
[1589]1222 @print_log_dec
[1145]1223 def average_beam(self, mask=None, weight='none'):
1224 """
1225 Average the Beams together.
1226 Parameters:
1227 mask: An optional mask defining the region, where the
1228 averaging will be applied. The output will have all
1229 specified points masked.
1230 weight: Weighting scheme. 'none' (default), 'var' (1/var(spec)
1231 weighted), or 'tsys' (1/Tsys**2 weighted)
1232 """
1233 varlist = vars()
[1593]1234 mask = mask or ()
[1145]1235 s = scantable(self._math._averagebeams(self, mask, weight.upper()))
1236 s._add_history("average_beam", varlist)
1237 return s
1238
[1586]1239 def parallactify(self, pflag):
[1617]1240 """
1241 Set a flag to inidcate whether this data should be treated as having
1242 been 'parallactified' (total phase == 0.0)
1243 Parameters:
1244 pflag: Bool inidcating whether to turn this on (True) or
1245 off (False)
1246 """
[1586]1247 varlist = vars()
1248 self._parallactify(pflag)
1249 self._add_history("parallactify", varlist)
1250
[1589]1251 @print_log_dec
[992]1252 def convert_pol(self, poltype=None):
1253 """
1254 Convert the data to a different polarisation type.
[1565]1255 Note that you will need cross-polarisation terms for most conversions.
[992]1256 Parameters:
1257 poltype: The new polarisation type. Valid types are:
[1565]1258 "linear", "circular", "stokes" and "linpol"
[992]1259 """
1260 varlist = vars()
1261 try:
1262 s = scantable(self._math._convertpol(self, poltype))
[1118]1263 except RuntimeError, msg:
[992]1264 if rcParams['verbose']:
[1118]1265 print msg
1266 return
[992]1267 else:
1268 raise
[1118]1269 s._add_history("convert_pol", varlist)
[992]1270 return s
1271
[1589]1272 @print_log_dec
[1574]1273 def smooth(self, kernel="hanning", width=5.0, order=2, insitu=None):
[513]1274 """
1275 Smooth the spectrum by the specified kernel (conserving flux).
1276 Parameters:
1277 kernel: The type of smoothing kernel. Select from
[1574]1278 'hanning' (default), 'gaussian', 'boxcar', 'rmedian'
1279 or 'poly'
[513]1280 width: The width of the kernel in pixels. For hanning this is
1281 ignored otherwise it defauls to 5 pixels.
1282 For 'gaussian' it is the Full Width Half
1283 Maximum. For 'boxcar' it is the full width.
[1574]1284 For 'rmedian' and 'poly' it is the half width.
1285 order: Optional parameter for 'poly' kernel (default is 2), to
1286 specify the order of the polnomial. Ignored by all other
1287 kernels.
[513]1288 insitu: if False a new scantable is returned.
1289 Otherwise, the scaling is done in-situ
1290 The default is taken from .asaprc (False)
1291 Example:
1292 none
1293 """
1294 if insitu is None: insitu = rcParams['insitu']
[876]1295 self._math._setinsitu(insitu)
[513]1296 varlist = vars()
[1574]1297 s = scantable(self._math._smooth(self, kernel.lower(), width, order))
[876]1298 s._add_history("smooth", varlist)
1299 if insitu: self._assign(s)
1300 else: return s
[513]1301
[1589]1302 @print_log_dec
[1617]1303 def poly_baseline(self, mask=None, order=0, plot=False, uselin=False,
1304 insitu=None):
[513]1305 """
1306 Return a scan which has been baselined (all rows) by a polynomial.
1307 Parameters:
[794]1308 mask: an optional mask
1309 order: the order of the polynomial (default is 0)
[1061]1310 plot: plot the fit and the residual. In this each
1311 indivual fit has to be approved, by typing 'y'
1312 or 'n'
[1391]1313 uselin: use linear polynomial fit
[794]1314 insitu: if False a new scantable is returned.
1315 Otherwise, the scaling is done in-situ
1316 The default is taken from .asaprc (False)
[513]1317 Example:
1318 # return a scan baselined by a third order polynomial,
1319 # not using a mask
1320 bscan = scan.poly_baseline(order=3)
[579]1321 """
[513]1322 if insitu is None: insitu = rcParams['insitu']
1323 varlist = vars()
1324 if mask is None:
[1295]1325 mask = [True for i in xrange(self.nchan(-1))]
[513]1326 from asap.asapfitter import fitter
[1217]1327 try:
1328 f = fitter()
1329 f.set_scan(self, mask)
[1391]1330 #f.set_function(poly=order)
1331 if uselin:
1332 f.set_function(lpoly=order)
1333 else:
1334 f.set_function(poly=order)
[1217]1335 s = f.auto_fit(insitu, plot=plot)
1336 s._add_history("poly_baseline", varlist)
1337 if insitu: self._assign(s)
1338 else: return s
1339 except RuntimeError:
1340 msg = "The fit failed, possibly because it didn't converge."
1341 if rcParams['verbose']:
1342 print msg
1343 return
1344 else:
1345 raise RuntimeError(msg)
[513]1346
[1118]1347 def auto_poly_baseline(self, mask=[], edge=(0, 0), order=0,
[1280]1348 threshold=3, chan_avg_limit=1, plot=False,
1349 insitu=None):
[880]1350 """
1351 Return a scan which has been baselined (all rows) by a polynomial.
1352 Spectral lines are detected first using linefinder and masked out
1353 to avoid them affecting the baseline solution.
1354
1355 Parameters:
1356 mask: an optional mask retreived from scantable
1357 edge: an optional number of channel to drop at
1358 the edge of spectrum. If only one value is
1359 specified, the same number will be dropped from
1360 both sides of the spectrum. Default is to keep
[907]1361 all channels. Nested tuples represent individual
[976]1362 edge selection for different IFs (a number of spectral
1363 channels can be different)
[880]1364 order: the order of the polynomial (default is 0)
1365 threshold: the threshold used by line finder. It is better to
1366 keep it large as only strong lines affect the
1367 baseline solution.
[1280]1368 chan_avg_limit:
1369 a maximum number of consequtive spectral channels to
1370 average during the search of weak and broad lines.
1371 The default is no averaging (and no search for weak
1372 lines). If such lines can affect the fitted baseline
1373 (e.g. a high order polynomial is fitted), increase this
1374 parameter (usually values up to 8 are reasonable). Most
1375 users of this method should find the default value
1376 sufficient.
[1061]1377 plot: plot the fit and the residual. In this each
1378 indivual fit has to be approved, by typing 'y'
1379 or 'n'
[880]1380 insitu: if False a new scantable is returned.
1381 Otherwise, the scaling is done in-situ
1382 The default is taken from .asaprc (False)
1383
1384 Example:
1385 scan2=scan.auto_poly_baseline(order=7)
1386 """
1387 if insitu is None: insitu = rcParams['insitu']
1388 varlist = vars()
1389 from asap.asapfitter import fitter
1390 from asap.asaplinefind import linefinder
1391 from asap import _is_sequence_or_number as _is_valid
1392
[976]1393 # check whether edge is set up for each IF individually
[1118]1394 individualedge = False;
1395 if len(edge) > 1:
1396 if isinstance(edge[0], list) or isinstance(edge[0], tuple):
1397 individualedge = True;
[907]1398
[1118]1399 if not _is_valid(edge, int) and not individualedge:
[909]1400 raise ValueError, "Parameter 'edge' has to be an integer or a \
[907]1401 pair of integers specified as a tuple. Nested tuples are allowed \
1402 to make individual selection for different IFs."
[919]1403
[1118]1404 curedge = (0, 0)
1405 if individualedge:
1406 for edgepar in edge:
1407 if not _is_valid(edgepar, int):
1408 raise ValueError, "Each element of the 'edge' tuple has \
1409 to be a pair of integers or an integer."
[907]1410 else:
[1118]1411 curedge = edge;
[880]1412
1413 # setup fitter
1414 f = fitter()
1415 f.set_function(poly=order)
1416
1417 # setup line finder
[1118]1418 fl = linefinder()
[1268]1419 fl.set_options(threshold=threshold,avg_limit=chan_avg_limit)
[880]1420
1421 if not insitu:
[1118]1422 workscan = self.copy()
[880]1423 else:
[1118]1424 workscan = self
[880]1425
[907]1426 fl.set_scan(workscan)
1427
[1118]1428 rows = range(workscan.nrow())
[880]1429 asaplog.push("Processing:")
1430 for r in rows:
[1118]1431 msg = " Scan[%d] Beam[%d] IF[%d] Pol[%d] Cycle[%d]" % \
1432 (workscan.getscan(r), workscan.getbeam(r), workscan.getif(r), \
1433 workscan.getpol(r), workscan.getcycle(r))
[880]1434 asaplog.push(msg, False)
[907]1435
[976]1436 # figure out edge parameter
[1118]1437 if individualedge:
1438 if len(edge) >= workscan.getif(r):
1439 raise RuntimeError, "Number of edge elements appear to " \
1440 "be less than the number of IFs"
1441 curedge = edge[workscan.getif(r)]
[919]1442
[976]1443 # setup line finder
[1118]1444 fl.find_lines(r, mask, curedge)
[1536]1445 f.set_data(workscan._getabcissa(r), workscan._getspectrum(r),
1446 mask_and(workscan._getmask(r), fl.get_mask()))
[880]1447 f.fit()
1448 x = f.get_parameters()
[1061]1449 if plot:
1450 f.plot(residual=True)
1451 x = raw_input("Accept fit ( [y]/n ): ")
1452 if x.upper() == 'N':
1453 continue
[880]1454 workscan._setspectrum(f.fitter.getresidual(), r)
[1061]1455 if plot:
1456 f._p.unmap()
1457 f._p = None
1458 workscan._add_history("auto_poly_baseline", varlist)
[880]1459 if insitu:
1460 self._assign(workscan)
1461 else:
1462 return workscan
1463
[1589]1464 @print_log_dec
[914]1465 def rotate_linpolphase(self, angle):
1466 """
1467 Rotate the phase of the complex polarization O=Q+iU correlation.
1468 This is always done in situ in the raw data. So if you call this
1469 function more than once then each call rotates the phase further.
1470 Parameters:
1471 angle: The angle (degrees) to rotate (add) by.
1472 Examples:
1473 scan.rotate_linpolphase(2.3)
1474 """
1475 varlist = vars()
[936]1476 self._math._rotate_linpolphase(self, angle)
[914]1477 self._add_history("rotate_linpolphase", varlist)
1478 return
[710]1479
[1589]1480 @print_log_dec
[914]1481 def rotate_xyphase(self, angle):
1482 """
1483 Rotate the phase of the XY correlation. This is always done in situ
1484 in the data. So if you call this function more than once
1485 then each call rotates the phase further.
1486 Parameters:
1487 angle: The angle (degrees) to rotate (add) by.
1488 Examples:
1489 scan.rotate_xyphase(2.3)
1490 """
1491 varlist = vars()
[936]1492 self._math._rotate_xyphase(self, angle)
[914]1493 self._add_history("rotate_xyphase", varlist)
1494 return
1495
[1589]1496 @print_log_dec
[914]1497 def swap_linears(self):
1498 """
[1573]1499 Swap the linear polarisations XX and YY, or better the first two
[1348]1500 polarisations as this also works for ciculars.
[914]1501 """
1502 varlist = vars()
[936]1503 self._math._swap_linears(self)
[914]1504 self._add_history("swap_linears", varlist)
1505 return
1506
[1589]1507 @print_log_dec
[914]1508 def invert_phase(self):
1509 """
1510 Invert the phase of the complex polarisation
1511 """
1512 varlist = vars()
[936]1513 self._math._invert_phase(self)
[914]1514 self._add_history("invert_phase", varlist)
1515 return
1516
[1589]1517 @print_log_dec
[876]1518 def add(self, offset, insitu=None):
[513]1519 """
1520 Return a scan where all spectra have the offset added
1521 Parameters:
1522 offset: the offset
1523 insitu: if False a new scantable is returned.
1524 Otherwise, the scaling is done in-situ
1525 The default is taken from .asaprc (False)
1526 """
1527 if insitu is None: insitu = rcParams['insitu']
[876]1528 self._math._setinsitu(insitu)
[513]1529 varlist = vars()
[876]1530 s = scantable(self._math._unaryop(self, offset, "ADD", False))
[1118]1531 s._add_history("add", varlist)
[876]1532 if insitu:
1533 self._assign(s)
1534 else:
[513]1535 return s
1536
[1589]1537 @print_log_dec
[1308]1538 def scale(self, factor, tsys=True, insitu=None):
[513]1539 """
1540 Return a scan where all spectra are scaled by the give 'factor'
1541 Parameters:
1542 factor: the scaling factor
1543 insitu: if False a new scantable is returned.
1544 Otherwise, the scaling is done in-situ
1545 The default is taken from .asaprc (False)
1546 tsys: if True (default) then apply the operation to Tsys
1547 as well as the data
1548 """
1549 if insitu is None: insitu = rcParams['insitu']
[876]1550 self._math._setinsitu(insitu)
[513]1551 varlist = vars()
[876]1552 s = scantable(self._math._unaryop(self, factor, "MUL", tsys))
[1118]1553 s._add_history("scale", varlist)
[876]1554 if insitu:
1555 self._assign(s)
1556 else:
[513]1557 return s
1558
[1504]1559 def set_sourcetype(self, match, matchtype="pattern",
1560 sourcetype="reference"):
[1502]1561 """
1562 Set the type of the source to be an source or reference scan
1563 using the provided pattern:
1564 Parameters:
[1504]1565 match: a Unix style pattern, regular expression or selector
1566 matchtype: 'pattern' (default) UNIX style pattern or
1567 'regex' regular expression
[1502]1568 sourcetype: the type of the source to use (source/reference)
1569 """
1570 varlist = vars()
1571 basesel = self.get_selection()
1572 stype = -1
1573 if sourcetype.lower().startswith("r"):
1574 stype = 1
1575 elif sourcetype.lower().startswith("s"):
1576 stype = 0
[1504]1577 else:
[1502]1578 raise ValueError("Illegal sourcetype use s(ource) or r(eference)")
[1504]1579 if matchtype.lower().startswith("p"):
1580 matchtype = "pattern"
1581 elif matchtype.lower().startswith("r"):
1582 matchtype = "regex"
1583 else:
1584 raise ValueError("Illegal matchtype, use p(attern) or r(egex)")
[1502]1585 sel = selector()
1586 if isinstance(match, selector):
1587 sel = match
1588 else:
[1504]1589 sel.set_query("SRCNAME == %s('%s')" % (matchtype, match))
[1502]1590 self.set_selection(basesel+sel)
1591 self._setsourcetype(stype)
1592 self.set_selection(basesel)
[1573]1593 self._add_history("set_sourcetype", varlist)
[1502]1594
[1589]1595 @print_log_dec
[1348]1596 def auto_quotient(self, preserve=True, mode='paired'):
[670]1597 """
1598 This function allows to build quotients automatically.
1599 It assumes the observation to have the same numer of
1600 "ons" and "offs"
1601 Parameters:
[710]1602 preserve: you can preserve (default) the continuum or
1603 remove it. The equations used are
[670]1604 preserve: Output = Toff * (on/off) - Toff
[1070]1605 remove: Output = Toff * (on/off) - Ton
[1573]1606 mode: the on/off detection mode
[1348]1607 'paired' (default)
1608 identifies 'off' scans by the
1609 trailing '_R' (Mopra/Parkes) or
1610 '_e'/'_w' (Tid) and matches
1611 on/off pairs from the observing pattern
[1502]1612 'time'
1613 finds the closest off in time
[1348]1614
[670]1615 """
[1348]1616 modes = ["time", "paired"]
[670]1617 if not mode in modes:
[876]1618 msg = "please provide valid mode. Valid modes are %s" % (modes)
1619 raise ValueError(msg)
1620 varlist = vars()
[1348]1621 s = None
1622 if mode.lower() == "paired":
1623 basesel = self.get_selection()
[1356]1624 sel = selector()+basesel
1625 sel.set_query("SRCTYPE==1")
1626 self.set_selection(sel)
[1348]1627 offs = self.copy()
1628 sel.set_query("SRCTYPE==0")
[1356]1629 self.set_selection(sel)
[1348]1630 ons = self.copy()
1631 s = scantable(self._math._quotient(ons, offs, preserve))
1632 self.set_selection(basesel)
1633 elif mode.lower() == "time":
1634 s = scantable(self._math._auto_quotient(self, mode, preserve))
[1118]1635 s._add_history("auto_quotient", varlist)
[876]1636 return s
[710]1637
[1589]1638 @print_log_dec
[1145]1639 def mx_quotient(self, mask = None, weight='median', preserve=True):
[1141]1640 """
[1143]1641 Form a quotient using "off" beams when observing in "MX" mode.
1642 Parameters:
[1145]1643 mask: an optional mask to be used when weight == 'stddev'
[1143]1644 weight: How to average the off beams. Default is 'median'.
[1145]1645 preserve: you can preserve (default) the continuum or
1646 remove it. The equations used are
1647 preserve: Output = Toff * (on/off) - Toff
1648 remove: Output = Toff * (on/off) - Ton
[1217]1649 """
[1593]1650 mask = mask or ()
[1141]1651 varlist = vars()
1652 on = scantable(self._math._mx_extract(self, 'on'))
[1143]1653 preoff = scantable(self._math._mx_extract(self, 'off'))
1654 off = preoff.average_time(mask=mask, weight=weight, scanav=False)
[1217]1655 from asapmath import quotient
[1145]1656 q = quotient(on, off, preserve)
[1143]1657 q._add_history("mx_quotient", varlist)
[1217]1658 return q
[513]1659
[1589]1660 @print_log_dec
[718]1661 def freq_switch(self, insitu=None):
1662 """
1663 Apply frequency switching to the data.
1664 Parameters:
1665 insitu: if False a new scantable is returned.
1666 Otherwise, the swictching is done in-situ
1667 The default is taken from .asaprc (False)
1668 Example:
1669 none
1670 """
1671 if insitu is None: insitu = rcParams['insitu']
[876]1672 self._math._setinsitu(insitu)
[718]1673 varlist = vars()
[876]1674 s = scantable(self._math._freqswitch(self))
[1118]1675 s._add_history("freq_switch", varlist)
[876]1676 if insitu: self._assign(s)
1677 else: return s
[718]1678
[1589]1679 @print_log_dec
[780]1680 def recalc_azel(self):
1681 """
1682 Recalculate the azimuth and elevation for each position.
1683 Parameters:
1684 none
1685 Example:
1686 """
1687 varlist = vars()
[876]1688 self._recalcazel()
[780]1689 self._add_history("recalc_azel", varlist)
1690 return
1691
[1589]1692 @print_log_dec
[513]1693 def __add__(self, other):
1694 varlist = vars()
1695 s = None
1696 if isinstance(other, scantable):
[1573]1697 s = scantable(self._math._binaryop(self, other, "ADD"))
[513]1698 elif isinstance(other, float):
[876]1699 s = scantable(self._math._unaryop(self, other, "ADD", False))
[513]1700 else:
[718]1701 raise TypeError("Other input is not a scantable or float value")
[513]1702 s._add_history("operator +", varlist)
1703 return s
1704
[1589]1705 @print_log_dec
[513]1706 def __sub__(self, other):
1707 """
1708 implicit on all axes and on Tsys
1709 """
1710 varlist = vars()
1711 s = None
1712 if isinstance(other, scantable):
[1588]1713 s = scantable(self._math._binaryop(self, other, "SUB"))
[513]1714 elif isinstance(other, float):
[876]1715 s = scantable(self._math._unaryop(self, other, "SUB", False))
[513]1716 else:
[718]1717 raise TypeError("Other input is not a scantable or float value")
[513]1718 s._add_history("operator -", varlist)
1719 return s
[710]1720
[1589]1721 @print_log_dec
[513]1722 def __mul__(self, other):
1723 """
1724 implicit on all axes and on Tsys
1725 """
1726 varlist = vars()
1727 s = None
1728 if isinstance(other, scantable):
[1588]1729 s = scantable(self._math._binaryop(self, other, "MUL"))
[513]1730 elif isinstance(other, float):
[876]1731 s = scantable(self._math._unaryop(self, other, "MUL", False))
[513]1732 else:
[718]1733 raise TypeError("Other input is not a scantable or float value")
[513]1734 s._add_history("operator *", varlist)
1735 return s
1736
[710]1737
[1589]1738 @print_log_dec
[513]1739 def __div__(self, other):
1740 """
1741 implicit on all axes and on Tsys
1742 """
1743 varlist = vars()
1744 s = None
1745 if isinstance(other, scantable):
[1589]1746 s = scantable(self._math._binaryop(self, other, "DIV"))
[513]1747 elif isinstance(other, float):
1748 if other == 0.0:
[718]1749 raise ZeroDivisionError("Dividing by zero is not recommended")
[876]1750 s = scantable(self._math._unaryop(self, other, "DIV", False))
[513]1751 else:
[718]1752 raise TypeError("Other input is not a scantable or float value")
[513]1753 s._add_history("operator /", varlist)
1754 return s
1755
[530]1756 def get_fit(self, row=0):
1757 """
1758 Print or return the stored fits for a row in the scantable
1759 Parameters:
1760 row: the row which the fit has been applied to.
1761 """
1762 if row > self.nrow():
1763 return
[976]1764 from asap.asapfit import asapfit
[530]1765 fit = asapfit(self._getfit(row))
[718]1766 if rcParams['verbose']:
[530]1767 print fit
1768 return
1769 else:
1770 return fit.as_dict()
1771
[1483]1772 def flag_nans(self):
1773 """
1774 Utility function to flag NaN values in the scantable.
1775 """
1776 import numpy
1777 basesel = self.get_selection()
1778 for i in range(self.nrow()):
[1589]1779 sel = self.get_row_selector(i)
1780 self.set_selection(basesel+sel)
[1483]1781 nans = numpy.isnan(self._getspectrum(0))
1782 if numpy.any(nans):
1783 bnans = [ bool(v) for v in nans]
1784 self.flag(bnans)
1785 self.set_selection(basesel)
1786
[1588]1787 def get_row_selector(self, rowno):
1788 return selector(beams=self.getbeam(rowno),
1789 ifs=self.getif(rowno),
1790 pols=self.getpol(rowno),
1791 scans=self.getscan(rowno),
1792 cycles=self.getcycle(rowno))
[1573]1793
[484]1794 def _add_history(self, funcname, parameters):
[1435]1795 if not rcParams['scantable.history']:
1796 return
[484]1797 # create date
1798 sep = "##"
1799 from datetime import datetime
1800 dstr = datetime.now().strftime('%Y/%m/%d %H:%M:%S')
1801 hist = dstr+sep
1802 hist += funcname+sep#cdate+sep
1803 if parameters.has_key('self'): del parameters['self']
[1118]1804 for k, v in parameters.iteritems():
[484]1805 if type(v) is dict:
[1118]1806 for k2, v2 in v.iteritems():
[484]1807 hist += k2
1808 hist += "="
[1118]1809 if isinstance(v2, scantable):
[484]1810 hist += 'scantable'
1811 elif k2 == 'mask':
[1118]1812 if isinstance(v2, list) or isinstance(v2, tuple):
[513]1813 hist += str(self._zip_mask(v2))
1814 else:
1815 hist += str(v2)
[484]1816 else:
[513]1817 hist += str(v2)
[484]1818 else:
1819 hist += k
1820 hist += "="
[1118]1821 if isinstance(v, scantable):
[484]1822 hist += 'scantable'
1823 elif k == 'mask':
[1118]1824 if isinstance(v, list) or isinstance(v, tuple):
[513]1825 hist += str(self._zip_mask(v))
1826 else:
1827 hist += str(v)
[484]1828 else:
1829 hist += str(v)
1830 hist += sep
1831 hist = hist[:-2] # remove trailing '##'
1832 self._addhistory(hist)
1833
[710]1834
[484]1835 def _zip_mask(self, mask):
1836 mask = list(mask)
1837 i = 0
1838 segments = []
1839 while mask[i:].count(1):
1840 i += mask[i:].index(1)
1841 if mask[i:].count(0):
1842 j = i + mask[i:].index(0)
1843 else:
[710]1844 j = len(mask)
[1118]1845 segments.append([i, j])
[710]1846 i = j
[484]1847 return segments
[714]1848
[626]1849 def _get_ordinate_label(self):
1850 fu = "("+self.get_fluxunit()+")"
1851 import re
1852 lbl = "Intensity"
[1118]1853 if re.match(".K.", fu):
[626]1854 lbl = "Brightness Temperature "+ fu
[1118]1855 elif re.match(".Jy.", fu):
[626]1856 lbl = "Flux density "+ fu
1857 return lbl
[710]1858
[876]1859 def _check_ifs(self):
1860 nchans = [self.nchan(i) for i in range(self.nif(-1))]
[889]1861 nchans = filter(lambda t: t > 0, nchans)
[876]1862 return (sum(nchans)/len(nchans) == nchans[0])
[976]1863
1864 def _fill(self, names, unit, average):
1865 import os
1866 from asap._asap import stfiller
1867 first = True
1868 fullnames = []
1869 for name in names:
1870 name = os.path.expandvars(name)
1871 name = os.path.expanduser(name)
1872 if not os.path.exists(name):
1873 msg = "File '%s' does not exists" % (name)
1874 if rcParams['verbose']:
1875 asaplog.push(msg)
1876 print asaplog.pop().strip()
1877 return
1878 raise IOError(msg)
1879 fullnames.append(name)
1880 if average:
1881 asaplog.push('Auto averaging integrations')
[1079]1882 stype = int(rcParams['scantable.storage'].lower() == 'disk')
[976]1883 for name in fullnames:
[1073]1884 tbl = Scantable(stype)
1885 r = stfiller(tbl)
[1504]1886 rx = rcParams['scantable.reference']
1887 r._setreferenceexpr(rx)
[976]1888 msg = "Importing %s..." % (name)
[1118]1889 asaplog.push(msg, False)
[976]1890 print_log()
[1118]1891 r._open(name, -1, -1)
[976]1892 r._read()
1893 if average:
[1118]1894 tbl = self._math._average((tbl, ), (), 'NONE', 'SCAN')
[976]1895 if not first:
1896 tbl = self._math._merge([self, tbl])
1897 Scantable.__init__(self, tbl)
1898 r._close()
[1118]1899 del r, tbl
[976]1900 first = False
1901 if unit is not None:
1902 self.set_fluxunit(unit)
1903 self.set_freqframe(rcParams['scantable.freqframe'])
1904
[1402]1905 def __getitem__(self, key):
1906 if key < 0:
1907 key += self.nrow()
1908 if key >= self.nrow():
1909 raise IndexError("Row index out of range.")
1910 return self._getspectrum(key)
1911
1912 def __setitem__(self, key, value):
1913 if key < 0:
1914 key += self.nrow()
1915 if key >= self.nrow():
1916 raise IndexError("Row index out of range.")
1917 if not hasattr(value, "__len__") or \
1918 len(value) > self.nchan(self.getif(key)):
1919 raise ValueError("Spectrum length doesn't match.")
1920 return self._setspectrum(value, key)
1921
1922 def __len__(self):
1923 return self.nrow()
1924
1925 def __iter__(self):
1926 for i in range(len(self)):
1927 yield self[i]
Note: See TracBrowser for help on using the repository browser.