source: trunk/python/scantable.py@ 3122

Last change on this file since 3122 was 3010, checked in by WataruKawasaki, 10 years ago

New Development: No

JIRA Issue: Yes CAS-6599

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs:

Put in Release Notes: No

Module(s): sd

Description: modified so that sd.scantable.stats() returns None for flagged rows and rows with all channels flagged.


  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 209.1 KB
Line 
1"""This module defines the scantable class."""
2
3import os
4import re
5import tempfile
6import numpy
7try:
8 from functools import wraps as wraps_dec
9except ImportError:
10 from asap.compatibility import wraps as wraps_dec
11
12from asap.env import is_casapy
13from asap._asap import Scantable
14from asap._asap import filler, msfiller
15from asap.parameters import rcParams
16from asap.logging import asaplog, asaplog_post_dec
17from asap.selector import selector
18from asap.linecatalog import linecatalog
19from asap.coordinate import coordinate
20from asap.utils import _n_bools, mask_not, mask_and, mask_or, page
21from asap.asapfitter import fitter
22
23def preserve_selection(func):
24 @wraps_dec(func)
25 def wrap(obj, *args, **kw):
26 basesel = obj.get_selection()
27 try:
28 val = func(obj, *args, **kw)
29 finally:
30 obj.set_selection(basesel)
31 return val
32 return wrap
33
34def is_scantable(filename):
35 """Is the given file a scantable?
36
37 Parameters:
38
39 filename: the name of the file/directory to test
40
41 """
42 if ( os.path.isdir(filename)
43 and os.path.exists(filename+'/table.info')
44 and os.path.exists(filename+'/table.dat') ):
45 f=open(filename+'/table.info')
46 l=f.readline()
47 f.close()
48 match_pattern = '^Type = (Scantable)? *$'
49 if re.match(match_pattern,l):
50 return True
51 else:
52 return False
53 else:
54 return False
55## return (os.path.isdir(filename)
56## and not os.path.exists(filename+'/table.f1')
57## and os.path.exists(filename+'/table.info'))
58
59def is_ms(filename):
60 """Is the given file a MeasurementSet?
61
62 Parameters:
63
64 filename: the name of the file/directory to test
65
66 """
67 if ( os.path.isdir(filename)
68 and os.path.exists(filename+'/table.info')
69 and os.path.exists(filename+'/table.dat') ):
70 f=open(filename+'/table.info')
71 l=f.readline()
72 f.close()
73 if ( l.find('Measurement Set') != -1 ):
74 return True
75 else:
76 return False
77 else:
78 return False
79
80def normalise_edge_param(edge):
81 """\
82 Convert a given edge value to a one-dimensional array that can be
83 given to baseline-fitting/subtraction functions.
84 The length of the output value will be an even because values for
85 the both sides of spectra are to be contained for each IF. When
86 the length is 2, the values will be applied to all IFs. If the length
87 is larger than 2, it will be 2*ifnos().
88 Accepted format of edge include:
89 * an integer - will be used for both sides of spectra of all IFs.
90 e.g. 10 is converted to [10,10]
91 * an empty list/tuple [] - converted to [0, 0] and used for all IFs.
92 * a list/tuple containing an integer - same as the above case.
93 e.g. [10] is converted to [10,10]
94 * a list/tuple containing two integers - will be used for all IFs.
95 e.g. [5,10] is output as it is. no need to convert.
96 * a list/tuple of lists/tuples containing TWO integers -
97 each element of edge will be used for each IF.
98 e.g. [[5,10],[15,20]] - [5,10] for IF[0] and [15,20] for IF[1].
99
100 If an element contains the same integer values, the input 'edge'
101 parameter can be given in a simpler shape in the following cases:
102 ** when len(edge)!=2
103 any elements containing the same values can be replaced
104 to single integers.
105 e.g. [[15,15]] can be simplified to [15] (or [15,15] or 15 also).
106 e.g. [[1,1],[2,2],[3,3]] can be simplified to [1,2,3].
107 ** when len(edge)=2
108 care is needed for this case: ONLY ONE of the
109 elements can be a single integer,
110 e.g. [[5,5],[10,10]] can be simplified to [5,[10,10]]
111 or [[5,5],10], but can NOT be simplified to [5,10].
112 when [5,10] given, it is interpreted as
113 [[5,10],[5,10],[5,10],...] instead, as shown before.
114 """
115 from asap import _is_sequence_or_number as _is_valid
116 if isinstance(edge, list) or isinstance(edge, tuple):
117 for edgepar in edge:
118 if not _is_valid(edgepar, int):
119 raise ValueError, "Each element of the 'edge' tuple has \
120 to be a pair of integers or an integer."
121 if isinstance(edgepar, list) or isinstance(edgepar, tuple):
122 if len(edgepar) != 2:
123 raise ValueError, "Each element of the 'edge' tuple has \
124 to be a pair of integers or an integer."
125 else:
126 if not _is_valid(edge, int):
127 raise ValueError, "Parameter 'edge' has to be an integer or a \
128 pair of integers specified as a tuple. \
129 Nested tuples are allowed \
130 to make individual selection for different IFs."
131
132
133 if isinstance(edge, int):
134 edge = [ edge, edge ] # e.g. 3 => [3,3]
135 elif isinstance(edge, list) or isinstance(edge, tuple):
136 if len(edge) == 0:
137 edge = [0, 0] # e.g. [] => [0,0]
138 elif len(edge) == 1:
139 if isinstance(edge[0], int):
140 edge = [ edge[0], edge[0] ] # e.g. [1] => [1,1]
141
142 commonedge = True
143 if len(edge) > 2: commonedge = False
144 else:
145 for edgepar in edge:
146 if isinstance(edgepar, list) or isinstance(edgepar, tuple):
147 commonedge = False
148 break
149
150 if commonedge:
151 if len(edge) > 1:
152 norm_edge = edge
153 else:
154 norm_edge = edge + edge
155 else:
156 norm_edge = []
157 for edgepar in edge:
158 if isinstance(edgepar, int):
159 norm_edge += [edgepar, edgepar]
160 else:
161 norm_edge += edgepar
162
163 return norm_edge
164
165def raise_fitting_failure_exception(e):
166 msg = "The fit failed, possibly because it didn't converge."
167 if rcParams["verbose"]:
168 asaplog.push(str(e))
169 asaplog.push(str(msg))
170 else:
171 raise RuntimeError(str(e)+'\n'+msg)
172
173def pack_progress_params(showprogress, minnrow):
174 return str(showprogress).lower() + ',' + str(minnrow)
175
176def pack_blinfo(blinfo, maxirow):
177 """\
178 convert a dictionary or a list of dictionaries of baseline info
179 into a list of comma-separated strings.
180 """
181 if isinstance(blinfo, dict):
182 res = do_pack_blinfo(blinfo, maxirow)
183 return [res] if res != '' else []
184 elif isinstance(blinfo, list) or isinstance(blinfo, tuple):
185 res = []
186 for i in xrange(len(blinfo)):
187 resi = do_pack_blinfo(blinfo[i], maxirow)
188 if resi != '':
189 res.append(resi)
190 return res
191
192def do_pack_blinfo(blinfo, maxirow):
193 """\
194 convert a dictionary of baseline info for a spectrum into
195 a comma-separated string.
196 """
197 dinfo = {}
198 for key in ['row', 'blfunc', 'masklist']:
199 if blinfo.has_key(key):
200 val = blinfo[key]
201 if key == 'row':
202 irow = val
203 if isinstance(val, list) or isinstance(val, tuple):
204 slval = []
205 for i in xrange(len(val)):
206 if isinstance(val[i], list) or isinstance(val[i], tuple):
207 for j in xrange(len(val[i])):
208 slval.append(str(val[i][j]))
209 else:
210 slval.append(str(val[i]))
211 sval = ",".join(slval)
212 else:
213 sval = str(val)
214
215 dinfo[key] = sval
216 else:
217 raise ValueError("'"+key+"' is missing in blinfo.")
218
219 if irow >= maxirow: return ''
220
221 for key in ['order', 'npiece', 'nwave']:
222 if blinfo.has_key(key):
223 val = blinfo[key]
224 if isinstance(val, list) or isinstance(val, tuple):
225 slval = []
226 for i in xrange(len(val)):
227 slval.append(str(val[i]))
228 sval = ",".join(slval)
229 else:
230 sval = str(val)
231 dinfo[key] = sval
232
233 fspec_keys = {'poly': 'order', 'chebyshev': 'order', 'cspline': 'npiece', 'sinusoid': 'nwave'}
234
235 fspec_key = fspec_keys[dinfo['blfunc']]
236 if not blinfo.has_key(fspec_key):
237 raise ValueError("'"+fspec_key+"' is missing in blinfo.")
238
239 clip_params_n = 0
240 for key in ['clipthresh', 'clipniter']:
241 if blinfo.has_key(key):
242 clip_params_n += 1
243 dinfo[key] = str(blinfo[key])
244
245 if clip_params_n == 0:
246 dinfo['clipthresh'] = '0.0'
247 dinfo['clipniter'] = '0'
248 elif clip_params_n != 2:
249 raise ValueError("both 'clipthresh' and 'clipniter' must be given for n-sigma clipping.")
250
251 lf_params_n = 0
252 for key in ['thresh', 'edge', 'chan_avg_limit']:
253 if blinfo.has_key(key):
254 lf_params_n += 1
255 val = blinfo[key]
256 if isinstance(val, list) or isinstance(val, tuple):
257 slval = []
258 for i in xrange(len(val)):
259 slval.append(str(val[i]))
260 sval = ",".join(slval)
261 else:
262 sval = str(val)
263 dinfo[key] = sval
264
265 if lf_params_n == 3:
266 dinfo['use_linefinder'] = 'true'
267 elif lf_params_n == 0:
268 dinfo['use_linefinder'] = 'false'
269 dinfo['thresh'] = ''
270 dinfo['edge'] = ''
271 dinfo['chan_avg_limit'] = ''
272 else:
273 raise ValueError("all of 'thresh', 'edge' and 'chan_avg_limit' must be given to use linefinder.")
274
275 slblinfo = [dinfo['row'], dinfo['blfunc'], dinfo[fspec_key], dinfo['masklist'], \
276 dinfo['clipthresh'], dinfo['clipniter'], \
277 dinfo['use_linefinder'], dinfo['thresh'], dinfo['edge'], dinfo['chan_avg_limit']]
278
279 return ":".join(slblinfo)
280
281def parse_fitresult(sres):
282 """\
283 Parse the returned value of apply_bltable() or sub_baseline() and
284 extract row number, the best-fit coefficients and rms, then pack
285 them into a dictionary.
286 The input value is generated by Scantable::packFittingResults() and
287 formatted as 'row:coeff[0],coeff[1],..,coeff[n-1]:rms'.
288 """
289 res = []
290 for i in xrange(len(sres)):
291 (srow, scoeff, srms) = sres[i].split(":")
292 row = int(srow)
293 rms = float(srms)
294 lscoeff = scoeff.split(",")
295 coeff = []
296 for j in xrange(len(lscoeff)):
297 coeff.append(float(lscoeff[j]))
298 res.append({'row': row, 'coeff': coeff, 'rms': rms})
299
300 return res
301
302def is_number(s):
303 s = s.strip()
304 res = True
305 try:
306 a = float(s)
307 res = True
308 except:
309 res = False
310 finally:
311 return res
312
313def is_frequency(s):
314 s = s.strip()
315 return (s[-2:].lower() == "hz")
316
317def get_freq_by_string(s1, s2):
318 if not (is_number(s1) and is_frequency(s2)):
319 raise RuntimeError("Invalid input string.")
320
321 prefix_list = ["a", "f", "p", "n", "u", "m", ".", "k", "M", "G", "T", "P", "E"]
322 factor_list = [1e-18, 1e-15, 1e-12, 1e-9, 1e-6, 1e-3, 1.0, 1e+3, 1e+6, 1e+9, 1e+12, 1e+15, 1e+18]
323
324 s1 = s1.strip()
325 s2 = s2.strip()
326
327 prefix = s2[-3:-2]
328 if is_number(prefix):
329 res1 = float(s1)
330 res2 = float(s2[:-2])
331 else:
332 factor = factor_list[prefix_list.index(prefix)]
333 res1 = float(s1) * factor
334 res2 = float(s2[:-3]) * factor
335
336 return (res1, res2)
337
338def is_velocity(s):
339 s = s.strip()
340 return (s[-3:].lower() == "m/s")
341
342def get_velocity_by_string(s1, s2):
343 if not (is_number(s1) and is_velocity(s2)):
344 raise RuntimeError("Invalid input string.")
345
346 # note that the default velocity unit is km/s
347 prefix_list = [".", "k"]
348 factor_list = [1e-3, 1.0]
349
350 s1 = s1.strip()
351 s2 = s2.strip()
352
353 prefix = s2[-4:-3]
354 if is_number(prefix): # in case velocity unit m/s
355 res1 = float(s1) * 1e-3
356 res2 = float(s2[:-3]) * 1e-3
357 else:
358 factor = factor_list[prefix_list.index(prefix)]
359 res1 = float(s1) * factor
360 res2 = float(s2[:-4]) * factor
361
362 return (res1, res2)
363
364def get_frequency_by_velocity(restfreq, vel, doppler):
365 # vel is in unit of km/s
366
367 # speed of light
368 vel_c = 299792.458
369
370 import math
371 r = vel / vel_c
372
373 if doppler.lower() == 'radio':
374 return restfreq * (1.0 - r)
375 if doppler.lower() == 'optical':
376 return restfreq / (1.0 + r)
377 else:
378 return restfreq * math.sqrt((1.0 - r) / (1.0 + r))
379
380def get_restfreq_in_Hz(s_restfreq):
381 value = 0.0
382 unit = ""
383 s = s_restfreq.replace(" ","")
384
385 for i in range(len(s))[::-1]:
386 if s[i].isalpha():
387 unit = s[i] + unit
388 else:
389 value = float(s[0:i+1])
390 break
391
392 if (unit == "") or (unit.lower() == "hz"):
393 return value
394 elif (len(unit) == 3) and (unit[1:3].lower() == "hz"):
395 unitprefix = unit[0]
396 factor = 1.0
397
398 prefix_list = ["a", "f", "p", "n", "u", "m", ".", "k", "M", "G", "T", "P", "E"]
399 factor_list = [1e-18, 1e-15, 1e-12, 1e-9, 1e-6, 1e-3, 1.0, 1e+3, 1e+6, 1e+9, 1e+12, 1e+15, 1e+18]
400 factor = factor_list[prefix_list.index(unitprefix)]
401 """
402 if (unitprefix == 'a'):
403 factor = 1.0e-18
404 elif (unitprefix == 'f'):
405 factor = 1.0e-15
406 elif (unitprefix == 'p'):
407 factor = 1.0e-12
408 elif (unitprefix == 'n'):
409 factor = 1.0e-9
410 elif (unitprefix == 'u'):
411 factor = 1.0e-6
412 elif (unitprefix == 'm'):
413 factor = 1.0e-3
414 elif (unitprefix == 'k'):
415 factor = 1.0e+3
416 elif (unitprefix == 'M'):
417 factor = 1.0e+6
418 elif (unitprefix == 'G'):
419 factor = 1.0e+9
420 elif (unitprefix == 'T'):
421 factor = 1.0e+12
422 elif (unitprefix == 'P'):
423 factor = 1.0e+15
424 elif (unitprefix == 'E'):
425 factor = 1.0e+18
426 """
427 return value*factor
428 else:
429 mesg = "wrong unit of restfreq."
430 raise Exception, mesg
431
432def normalise_restfreq(in_restfreq):
433 if isinstance(in_restfreq, float):
434 return in_restfreq
435 elif isinstance(in_restfreq, int) or isinstance(in_restfreq, long):
436 return float(in_restfreq)
437 elif isinstance(in_restfreq, str):
438 return get_restfreq_in_Hz(in_restfreq)
439 elif isinstance(in_restfreq, list) or isinstance(in_restfreq, numpy.ndarray):
440 if isinstance(in_restfreq, numpy.ndarray):
441 if len(in_restfreq.shape) > 1:
442 mesg = "given in numpy.ndarray, in_restfreq must be 1-D."
443 raise Exception, mesg
444
445 res = []
446 for i in xrange(len(in_restfreq)):
447 elem = in_restfreq[i]
448 if isinstance(elem, float):
449 res.append(elem)
450 elif isinstance(elem, int) or isinstance(elem, long):
451 res.append(float(elem))
452 elif isinstance(elem, str):
453 res.append(get_restfreq_in_Hz(elem))
454 elif isinstance(elem, dict):
455 if isinstance(elem["value"], float):
456 res.append(elem)
457 elif isinstance(elem["value"], int):
458 dictelem = {}
459 dictelem["name"] = elem["name"]
460 dictelem["value"] = float(elem["value"])
461 res.append(dictelem)
462 elif isinstance(elem["value"], str):
463 dictelem = {}
464 dictelem["name"] = elem["name"]
465 dictelem["value"] = get_restfreq_in_Hz(elem["value"])
466 res.append(dictelem)
467 else:
468 mesg = "restfreq elements must be float, int, or string."
469 raise Exception, mesg
470 return res
471 else:
472 mesg = "wrong type of restfreq given."
473 raise Exception, mesg
474
475def set_restfreq(s, restfreq):
476 rfset = (restfreq != '') and (restfreq != [])
477 if rfset:
478 s.set_restfreqs(normalise_restfreq(restfreq))
479
480class scantable(Scantable):
481 """\
482 The ASAP container for scans (single-dish data).
483 """
484
485 @asaplog_post_dec
486 def __init__(self, filename, average=None, unit=None, parallactify=None,
487 **args):
488 """\
489 Create a scantable from a saved one or make a reference
490
491 Parameters:
492
493 filename: the name of an asap table on disk
494 or
495 the name of a rpfits/sdfits/ms file
496 (integrations within scans are auto averaged
497 and the whole file is read) or
498 [advanced] a reference to an existing scantable
499
500 average: average all integrations withinb a scan on read.
501 The default (True) is taken from .asaprc.
502
503 unit: brightness unit; must be consistent with K or Jy.
504 Over-rides the default selected by the filler
505 (input rpfits/sdfits/ms) or replaces the value
506 in existing scantables
507
508 antenna: for MeasurementSet input data only:
509 Antenna selection. integer (id) or string
510 (name or id).
511
512 parallactify: Indicate that the data had been parallactified.
513 Default (false) is taken from rc file.
514
515 getpt: Whether to import direction from MS/POINTING
516 table properly or not.
517 This is effective only when filename is MS.
518 The default (True) is to import direction
519 from MS/POINTING.
520 """
521 if average is None:
522 average = rcParams['scantable.autoaverage']
523 parallactify = parallactify or rcParams['scantable.parallactify']
524 varlist = vars()
525 from asap._asap import stmath
526 self._math = stmath( rcParams['insitu'] )
527 if isinstance(filename, Scantable):
528 Scantable.__init__(self, filename)
529 else:
530 if isinstance(filename, str):
531 filename = os.path.expandvars(filename)
532 filename = os.path.expanduser(filename)
533 if not os.path.exists(filename):
534 s = "File '%s' not found." % (filename)
535 raise IOError(s)
536 if is_scantable(filename):
537 ondisk = rcParams['scantable.storage'] == 'disk'
538 Scantable.__init__(self, filename, ondisk)
539 if unit is not None:
540 self.set_fluxunit(unit)
541 if average:
542 self._assign( self.average_time( scanav=True ) )
543 # do not reset to the default freqframe
544 #self.set_freqframe(rcParams['scantable.freqframe'])
545 elif is_ms(filename):
546 # Measurement Set
547 opts={'ms': {}}
548 mskeys=['getpt','antenna']
549 for key in mskeys:
550 if key in args.keys():
551 opts['ms'][key] = args[key]
552 self._fill([filename], unit, average, opts)
553 elif os.path.isfile(filename):
554 opts={'nro': {}}
555 nrokeys=['freqref']
556 for key in nrokeys:
557 if key in args.keys():
558 opts['nro'][key] = args[key]
559 self._fill([filename], unit, average, opts)
560 # only apply to new data not "copy constructor"
561 self.parallactify(parallactify)
562 else:
563 msg = "The given file '%s'is not a valid " \
564 "asap table." % (filename)
565 raise IOError(msg)
566 elif (isinstance(filename, list) or isinstance(filename, tuple)) \
567 and isinstance(filename[-1], str):
568 self._fill(filename, unit, average)
569 self.parallactify(parallactify)
570 self._add_history("scantable", varlist)
571
572 @asaplog_post_dec
573 def save(self, name=None, format=None, overwrite=False):
574 """\
575 Store the scantable on disk. This can be an asap (aips++) Table,
576 SDFITS or MS2 format.
577
578 Parameters:
579
580 name: the name of the outputfile. For format 'ASCII'
581 this is the root file name (data in 'name'.txt
582 and header in 'name'_header.txt)
583
584 format: an optional file format. Default is ASAP.
585 Allowed are:
586
587 * 'ASAP' (save as ASAP [aips++] Table),
588 * 'SDFITS' (save as SDFITS file)
589 * 'ASCII' (saves as ascii text file)
590 * 'MS2' (saves as an casacore MeasurementSet V2)
591 * 'FITS' (save as image FITS - not readable by
592 class)
593 * 'CLASS' (save as FITS readable by CLASS)
594
595 overwrite: If the file should be overwritten if it exists.
596 The default False is to return with warning
597 without writing the output. USE WITH CARE.
598
599 Example::
600
601 scan.save('myscan.asap')
602 scan.save('myscan.sdfits', 'SDFITS')
603
604 """
605 from os import path
606 format = format or rcParams['scantable.save']
607 suffix = '.'+format.lower()
608 if name is None or name == "":
609 name = 'scantable'+suffix
610 msg = "No filename given. Using default name %s..." % name
611 asaplog.push(msg)
612 name = path.expandvars(name)
613 if path.isfile(name) or path.isdir(name):
614 if not overwrite:
615 msg = "File %s exists." % name
616 raise IOError(msg)
617 format2 = format.upper()
618 if format2 == 'ASAP':
619 self._save(name)
620 elif format2 == 'MS2':
621 msopt = {'ms': {'overwrite': overwrite } }
622 from asap._asap import mswriter
623 writer = mswriter( self )
624 writer.write( name, msopt )
625 else:
626 from asap._asap import stwriter as stw
627 writer = stw(format2)
628 writer.write(self, name)
629 return
630
631 def copy(self):
632 """Return a copy of this scantable.
633
634 *Note*:
635
636 This makes a full (deep) copy. scan2 = scan1 makes a reference.
637
638 Example::
639
640 copiedscan = scan.copy()
641
642 """
643 sd = scantable(Scantable._copy(self))
644 return sd
645
646 def drop_scan(self, scanid=None):
647 """\
648 Return a new scantable where the specified scan number(s) has(have)
649 been dropped.
650
651 Parameters:
652
653 scanid: a (list of) scan number(s)
654
655 """
656 from asap import _is_sequence_or_number as _is_valid
657 from asap import _to_list
658 from asap import unique
659 if not _is_valid(scanid):
660 raise RuntimeError( 'Please specify a scanno to drop from the'
661 ' scantable' )
662 scanid = _to_list(scanid)
663 allscans = unique([ self.getscan(i) for i in range(self.nrow())])
664 for sid in scanid: allscans.remove(sid)
665 if len(allscans) == 0:
666 raise ValueError("Can't remove all scans")
667 sel = selector(scans=allscans)
668 return self._select_copy(sel)
669
670 def _select_copy(self, selection):
671 orig = self.get_selection()
672 self.set_selection(orig+selection)
673 cp = self.copy()
674 self.set_selection(orig)
675 return cp
676
677 def get_scan(self, scanid=None):
678 """\
679 Return a specific scan (by scanno) or collection of scans (by
680 source name) in a new scantable.
681
682 *Note*:
683
684 See scantable.drop_scan() for the inverse operation.
685
686 Parameters:
687
688 scanid: a (list of) scanno or a source name, unix-style
689 patterns are accepted for source name matching, e.g.
690 '*_R' gets all 'ref scans
691
692 Example::
693
694 # get all scans containing the source '323p459'
695 newscan = scan.get_scan('323p459')
696 # get all 'off' scans
697 refscans = scan.get_scan('*_R')
698 # get a susbset of scans by scanno (as listed in scan.summary())
699 newscan = scan.get_scan([0, 2, 7, 10])
700
701 """
702 if scanid is None:
703 raise RuntimeError( 'Please specify a scan no or name to '
704 'retrieve from the scantable' )
705 try:
706 bsel = self.get_selection()
707 sel = selector()
708 if type(scanid) is str:
709 sel.set_name(scanid)
710 return self._select_copy(sel)
711 elif type(scanid) is int:
712 sel.set_scans([scanid])
713 return self._select_copy(sel)
714 elif type(scanid) is list:
715 sel.set_scans(scanid)
716 return self._select_copy(sel)
717 else:
718 msg = "Illegal scanid type, use 'int' or 'list' if ints."
719 raise TypeError(msg)
720 except RuntimeError:
721 raise
722
723 def __str__(self):
724 tempFile = tempfile.NamedTemporaryFile()
725 Scantable._summary(self, tempFile.name)
726 tempFile.seek(0)
727 asaplog.clear()
728 return tempFile.file.read()
729
730 @asaplog_post_dec
731 def summary(self, filename=None):
732 """\
733 Print a summary of the contents of this scantable.
734
735 Parameters:
736
737 filename: the name of a file to write the putput to
738 Default - no file output
739
740 """
741 if filename is not None:
742 if filename is "":
743 filename = 'scantable_summary.txt'
744 from os.path import expandvars, isdir
745 filename = expandvars(filename)
746 if isdir(filename):
747 msg = "Illegal file name '%s'." % (filename)
748 raise IOError(msg)
749 else:
750 filename = ""
751 Scantable._summary(self, filename)
752
753 def get_spectrum(self, rowno):
754 """Return the spectrum for the current row in the scantable as a list.
755
756 Parameters:
757
758 rowno: the row number to retrieve the spectrum from
759
760 """
761 return self._getspectrum(rowno)
762
763 def get_mask(self, rowno):
764 """Return the mask for the current row in the scantable as a list.
765
766 Parameters:
767
768 rowno: the row number to retrieve the mask from
769
770 """
771 return self._getmask(rowno)
772
773 def set_spectrum(self, spec, rowno):
774 """Set the spectrum for the current row in the scantable.
775
776 Parameters:
777
778 spec: the new spectrum
779
780 rowno: the row number to set the spectrum for
781
782 """
783 assert(len(spec) == self.nchan(self.getif(rowno)))
784 return self._setspectrum(spec, rowno)
785
786 def get_coordinate(self, rowno):
787 """Return the (spectral) coordinate for a a given 'rowno'.
788
789 *Note*:
790
791 * This coordinate is only valid until a scantable method modifies
792 the frequency axis.
793 * This coordinate does contain the original frequency set-up
794 NOT the new frame. The conversions however are done using the user
795 specified frame (e.g. LSRK/TOPO). To get the 'real' coordinate,
796 use scantable.freq_align first. Without it there is no closure,
797 i.e.::
798
799 c = myscan.get_coordinate(0)
800 c.to_frequency(c.get_reference_pixel()) != c.get_reference_value()
801
802 Parameters:
803
804 rowno: the row number for the spectral coordinate
805
806 """
807 return coordinate(Scantable.get_coordinate(self, rowno))
808
809 def get_selection(self):
810 """\
811 Get the selection object currently set on this scantable.
812
813 Example::
814
815 sel = scan.get_selection()
816 sel.set_ifs(0) # select IF 0
817 scan.set_selection(sel) # apply modified selection
818
819 """
820 return selector(self._getselection())
821
822 def set_selection(self, selection=None, **kw):
823 """\
824 Select a subset of the data. All following operations on this scantable
825 are only applied to thi selection.
826
827 Parameters:
828
829 selection: a selector object (default unset the selection), or
830 any combination of 'pols', 'ifs', 'beams', 'scans',
831 'cycles', 'name', 'query'
832
833 Examples::
834
835 sel = selector() # create a selection object
836 self.set_scans([0, 3]) # select SCANNO 0 and 3
837 scan.set_selection(sel) # set the selection
838 scan.summary() # will only print summary of scanno 0 an 3
839 scan.set_selection() # unset the selection
840 # or the equivalent
841 scan.set_selection(scans=[0,3])
842 scan.summary() # will only print summary of scanno 0 an 3
843 scan.set_selection() # unset the selection
844
845 """
846 if selection is None:
847 # reset
848 if len(kw) == 0:
849 selection = selector()
850 else:
851 # try keywords
852 for k in kw:
853 if k not in selector.fields:
854 raise KeyError("Invalid selection key '%s', "
855 "valid keys are %s" % (k,
856 selector.fields))
857 selection = selector(**kw)
858 self._setselection(selection)
859
860 def get_row(self, row=0, insitu=None):
861 """\
862 Select a row in the scantable.
863 Return a scantable with single row.
864
865 Parameters:
866
867 row: row no of integration, default is 0.
868 insitu: if False a new scantable is returned. Otherwise, the
869 scaling is done in-situ. The default is taken from .asaprc
870 (False)
871
872 """
873 if insitu is None:
874 insitu = rcParams['insitu']
875 if not insitu:
876 workscan = self.copy()
877 else:
878 workscan = self
879 # Select a row
880 sel = selector()
881 sel.set_rows([row])
882 workscan.set_selection(sel)
883 if not workscan.nrow() == 1:
884 msg = "Could not identify single row. %d rows selected." \
885 % (workscan.nrow())
886 raise RuntimeError(msg)
887 if insitu:
888 self._assign(workscan)
889 else:
890 return workscan
891
892 @asaplog_post_dec
893 def stats(self, stat='stddev', mask=None, form='3.3f', row=None, skip_flaggedrow=False):
894 """\
895 Determine the specified statistic of the current beam/if/pol
896 Takes a 'mask' as an optional parameter to specify which
897 channels should be excluded.
898
899 Parameters:
900
901 stat: 'min', 'max', 'min_abc', 'max_abc', 'sumsq', 'sum',
902 'mean', 'var', 'stddev', 'avdev', 'rms', 'median'
903
904 mask: an optional mask specifying where the statistic
905 should be determined.
906
907 form: format string to print statistic values
908
909 row: row number of spectrum to process.
910 (default is None: for all rows)
911
912 skip_flaggedrow: if True, skip outputting text for flagged
913 spectra. default is False.
914
915 Example:
916 scan.set_unit('channel')
917 msk = scan.create_mask([100, 200], [500, 600])
918 scan.stats(stat='mean', mask=m)
919
920 """
921 mask = mask or []
922 if not self._check_ifs():
923 raise ValueError("Cannot apply mask as the IFs have different "
924 "number of channels. Please use setselection() "
925 "to select individual IFs")
926 getchan = False
927 if stat.lower().startswith('min') or stat.lower().startswith('max'):
928 chan = self._math._minmaxchan(self, mask, stat)
929 getchan = True
930 statvals = []
931
932 rtnabc = False
933 if stat.lower().endswith('_abc'):
934 rtnabc = True
935 else:
936 if row == None:
937 statvals = self._math._stats(self, mask, stat)
938 else:
939 statvals = self._math._statsrow(self, mask, stat, int(row))
940
941 #def cb(i):
942 # return statvals[i]
943
944 #return self._row_callback(cb, stat)
945
946 label=stat
947 #callback=cb
948 out = ""
949 #outvec = []
950 sep = '-'*50
951
952 if row == None:
953 rows = xrange(self.nrow())
954 elif isinstance(row, int):
955 rows = [ row ]
956
957 for i in rows:
958 refstr = ''
959 statunit= ''
960 if getchan:
961 if self._is_all_chan_flagged(i):
962 if rtnabc:
963 statvals.append(None)
964 else:
965 qx, qy = self.chan2data(rowno=i, chan=chan[i])
966 if rtnabc:
967 statvals.append(qx['value'])
968 refstr = ('(value: %'+form) % (qy['value'])+' ['+qy['unit']+'])'
969 statunit= '['+qx['unit']+']'
970 else:
971 refstr = ('(@ %'+form) % (qx['value'])+' ['+qx['unit']+'])'
972
973 if self._is_all_chan_flagged(i):
974 if not rtnabc:
975 statvals[i] = None
976 if skip_flaggedrow:
977 continue
978
979 tm = self._gettime(i)
980 src = self._getsourcename(i)
981 out += 'Scan[%d] (%s) ' % (self.getscan(i), src)
982 out += 'Time[%s]:\n' % (tm)
983 if self.nbeam(-1) > 1: out += ' Beam[%d] ' % (self.getbeam(i))
984 if self.nif(-1) > 1: out += ' IF[%d] ' % (self.getif(i))
985 if self.npol(-1) > 1: out += ' Pol[%d] ' % (self.getpol(i))
986 #outvec.append(callback(i))
987 if len(rows) > 1:
988 # out += ('= %'+form) % (outvec[i]) +' '+refstr+'\n'
989 if statvals[i] is None:
990 out += ('= None(flagged)') + ' '+refstr+'\n'
991 else:
992 out += ('= %'+form) % (statvals[i]) +' '+refstr+'\n'
993 else:
994 # out += ('= %'+form) % (outvec[0]) +' '+refstr+'\n'
995 if statvals[0] is None:
996 out += ('= None(flagged)') + ' '+refstr+'\n'
997 else:
998 out += ('= %'+form) % (statvals[0]) +' '+refstr+'\n'
999 out += sep+"\n"
1000
1001 import os
1002 if os.environ.has_key( 'USER' ):
1003 usr = os.environ['USER']
1004 else:
1005 import commands
1006 usr = commands.getoutput( 'whoami' )
1007 tmpfile = '/tmp/tmp_'+usr+'_casapy_asap_scantable_stats'
1008 f = open(tmpfile,'w')
1009 print >> f, sep
1010 print >> f, ' %s %s' % (label, statunit)
1011 print >> f, sep
1012 print >> f, out
1013 f.close()
1014 f = open(tmpfile,'r')
1015 x = f.readlines()
1016 f.close()
1017 asaplog.push(''.join(x), False)
1018
1019 if skip_flaggedrow:
1020 nstatvals = len(statvals)
1021 for i in reversed(xrange(nstatvals)):
1022 if statvals[i] is None:
1023 del statvals[i]
1024 return statvals
1025
1026 def chan2data(self, rowno=0, chan=0):
1027 """\
1028 Returns channel/frequency/velocity and spectral value
1029 at an arbitrary row and channel in the scantable.
1030
1031 Parameters:
1032
1033 rowno: a row number in the scantable. Default is the
1034 first row, i.e. rowno=0
1035
1036 chan: a channel in the scantable. Default is the first
1037 channel, i.e. pos=0
1038
1039 """
1040 if isinstance(rowno, int) and isinstance(chan, int):
1041 qx = {'unit': self.get_unit(),
1042 'value': self._getabcissa(rowno)[chan]}
1043 qy = {'unit': self.get_fluxunit(),
1044 'value': self._getspectrum(rowno)[chan]}
1045 return qx, qy
1046
1047 def stddev(self, mask=None):
1048 """\
1049 Determine the standard deviation of the current beam/if/pol
1050 Takes a 'mask' as an optional parameter to specify which
1051 channels should be excluded.
1052
1053 Parameters:
1054
1055 mask: an optional mask specifying where the standard
1056 deviation should be determined.
1057
1058 Example::
1059
1060 scan.set_unit('channel')
1061 msk = scan.create_mask([100, 200], [500, 600])
1062 scan.stddev(mask=m)
1063
1064 """
1065 return self.stats(stat='stddev', mask=mask);
1066
1067
1068 def get_column_names(self):
1069 """\
1070 Return a list of column names, which can be used for selection.
1071 """
1072 return list(Scantable.get_column_names(self))
1073
1074 def get_tsys(self, row=-1):
1075 """\
1076 Return the System temperatures.
1077
1078 Parameters:
1079
1080 row: the rowno to get the information for. (default all rows)
1081
1082 Returns:
1083
1084 a list of Tsys values for the current selection
1085
1086 """
1087 if row > -1:
1088 return self._get_column(self._gettsys, row)
1089 return self._row_callback(self._gettsys, "Tsys")
1090
1091 def get_tsysspectrum(self, row=-1):
1092 """\
1093 Return the channel dependent system temperatures.
1094
1095 Parameters:
1096
1097 row: the rowno to get the information for. (default all rows)
1098
1099 Returns:
1100
1101 a list of Tsys values for the current selection
1102
1103 """
1104 return self._get_column( self._gettsysspectrum, row )
1105
1106 def set_tsys(self, values, row=-1):
1107 """\
1108 Set the Tsys value(s) of the given 'row' or the whole scantable
1109 (selection).
1110
1111 Parameters:
1112
1113 values: a scalar or list (if Tsys is a vector) of Tsys value(s)
1114 row: the row number to apply Tsys values to.
1115 (default all rows)
1116
1117 """
1118
1119 if not hasattr(values, "__len__"):
1120 values = [values]
1121 self._settsys(values, row)
1122
1123 def get_weather(self, row=-1):
1124 """\
1125 Return the weather information.
1126
1127 Parameters:
1128
1129 row: the rowno to get the information for. (default all rows)
1130
1131 Returns:
1132
1133 a dict or list of of dicts of values for the current selection
1134
1135 """
1136 if row >= len(self):
1137 raise IndexError("row out of range")
1138 values = self._get_column(self._get_weather, row)
1139 if row > -1:
1140 return {'temperature': values[0],
1141 'pressure': values[1], 'humidity' : values[2],
1142 'windspeed' : values[3], 'windaz' : values[4]
1143 }
1144 else:
1145 out = []
1146 for r in values:
1147 out.append({'temperature': r[0],
1148 'pressure': r[1], 'humidity' : r[2],
1149 'windspeed' : r[3], 'windaz' : r[4]
1150 })
1151 return out
1152
1153 def _row_callback(self, callback, label):
1154 out = ""
1155 outvec = []
1156 sep = '-'*50
1157 for i in range(self.nrow()):
1158 tm = self._gettime(i)
1159 src = self._getsourcename(i)
1160 out += 'Scan[%d] (%s) ' % (self.getscan(i), src)
1161 out += 'Time[%s]:\n' % (tm)
1162 if self.nbeam(-1) > 1:
1163 out += ' Beam[%d] ' % (self.getbeam(i))
1164 if self.nif(-1) > 1: out += ' IF[%d] ' % (self.getif(i))
1165 if self.npol(-1) > 1: out += ' Pol[%d] ' % (self.getpol(i))
1166 outvec.append(callback(i))
1167 out += '= %3.3f\n' % (outvec[i])
1168 out += sep+'\n'
1169
1170 asaplog.push(sep)
1171 asaplog.push(" %s" % (label))
1172 asaplog.push(sep)
1173 asaplog.push(out)
1174 asaplog.post()
1175 return outvec
1176
1177 def _get_column(self, callback, row=-1, *args):
1178 """
1179 """
1180 if row == -1:
1181 return [callback(i, *args) for i in range(self.nrow())]
1182 else:
1183 if 0 <= row < self.nrow():
1184 return callback(row, *args)
1185
1186
1187 def get_time(self, row=-1, asdatetime=False, prec=-1):
1188 """\
1189 Get a list of time stamps for the observations.
1190 Return a datetime object or a string (default) for each
1191 integration time stamp in the scantable.
1192
1193 Parameters:
1194
1195 row: row no of integration. Default -1 return all rows
1196
1197 asdatetime: return values as datetime objects rather than strings
1198
1199 prec: number of digits shown. Default -1 to automatic
1200 calculation.
1201 Note this number is equals to the digits of MVTime,
1202 i.e., 0<prec<3: dates with hh:: only,
1203 <5: with hh:mm:, <7 or 0: with hh:mm:ss,
1204 and 6> : with hh:mm:ss.tt... (prec-6 t's added)
1205
1206 """
1207 from datetime import datetime
1208 if prec < 0:
1209 # automagically set necessary precision +1
1210 prec = 7 - \
1211 numpy.floor(numpy.log10(numpy.min(self.get_inttime(row))))
1212 prec = max(6, int(prec))
1213 else:
1214 prec = max(0, prec)
1215 if asdatetime:
1216 #precision can be 1 millisecond at max
1217 prec = min(12, prec)
1218
1219 times = self._get_column(self._gettime, row, prec)
1220 if not asdatetime:
1221 return times
1222 format = "%Y/%m/%d/%H:%M:%S.%f"
1223 if prec < 7:
1224 nsub = 1 + (((6-prec)/2) % 3)
1225 substr = [".%f","%S","%M"]
1226 for i in range(nsub):
1227 format = format.replace(substr[i],"")
1228 if isinstance(times, list):
1229 return [datetime.strptime(i, format) for i in times]
1230 else:
1231 return datetime.strptime(times, format)
1232
1233
1234 def get_inttime(self, row=-1):
1235 """\
1236 Get a list of integration times for the observations.
1237 Return a time in seconds for each integration in the scantable.
1238
1239 Parameters:
1240
1241 row: row no of integration. Default -1 return all rows.
1242
1243 """
1244 return self._get_column(self._getinttime, row)
1245
1246
1247 def get_sourcename(self, row=-1):
1248 """\
1249 Get a list source names for the observations.
1250 Return a string for each integration in the scantable.
1251 Parameters:
1252
1253 row: row no of integration. Default -1 return all rows.
1254
1255 """
1256 return self._get_column(self._getsourcename, row)
1257
1258 def get_elevation(self, row=-1):
1259 """\
1260 Get a list of elevations for the observations.
1261 Return a float for each integration in the scantable.
1262
1263 Parameters:
1264
1265 row: row no of integration. Default -1 return all rows.
1266
1267 """
1268 return self._get_column(self._getelevation, row)
1269
1270 def get_azimuth(self, row=-1):
1271 """\
1272 Get a list of azimuths for the observations.
1273 Return a float for each integration in the scantable.
1274
1275 Parameters:
1276 row: row no of integration. Default -1 return all rows.
1277
1278 """
1279 return self._get_column(self._getazimuth, row)
1280
1281 def get_parangle(self, row=-1):
1282 """\
1283 Get a list of parallactic angles for the observations.
1284 Return a float for each integration in the scantable.
1285
1286 Parameters:
1287
1288 row: row no of integration. Default -1 return all rows.
1289
1290 """
1291 return self._get_column(self._getparangle, row)
1292
1293 def get_direction(self, row=-1):
1294 """
1295 Get a list of Positions on the sky (direction) for the observations.
1296 Return a string for each integration in the scantable.
1297
1298 Parameters:
1299
1300 row: row no of integration. Default -1 return all rows
1301
1302 """
1303 return self._get_column(self._getdirection, row)
1304
1305 def get_directionval(self, row=-1):
1306 """\
1307 Get a list of Positions on the sky (direction) for the observations.
1308 Return a float for each integration in the scantable.
1309
1310 Parameters:
1311
1312 row: row no of integration. Default -1 return all rows
1313
1314 """
1315 return self._get_column(self._getdirectionvec, row)
1316
1317 @asaplog_post_dec
1318 def set_unit(self, unit='channel'):
1319 """\
1320 Set the unit for all following operations on this scantable
1321
1322 Parameters:
1323
1324 unit: optional unit, default is 'channel'. Use one of '*Hz',
1325 'km/s', 'channel' or equivalent ''
1326
1327 """
1328 varlist = vars()
1329 if unit in ['', 'pixel', 'channel']:
1330 unit = ''
1331 inf = list(self._getcoordinfo())
1332 inf[0] = unit
1333 self._setcoordinfo(inf)
1334 self._add_history("set_unit", varlist)
1335
1336 @asaplog_post_dec
1337 def set_instrument(self, instr):
1338 """\
1339 Set the instrument for subsequent processing.
1340
1341 Parameters:
1342
1343 instr: Select from 'ATPKSMB', 'ATPKSHOH', 'ATMOPRA',
1344 'DSS-43' (Tid), 'CEDUNA', and 'HOBART'
1345
1346 """
1347 self._setInstrument(instr)
1348 self._add_history("set_instument", vars())
1349
1350 @asaplog_post_dec
1351 def set_feedtype(self, feedtype):
1352 """\
1353 Overwrite the feed type, which might not be set correctly.
1354
1355 Parameters:
1356
1357 feedtype: 'linear' or 'circular'
1358
1359 """
1360 self._setfeedtype(feedtype)
1361 self._add_history("set_feedtype", vars())
1362
1363 @asaplog_post_dec
1364 def get_doppler(self):
1365 """\
1366 Get the doppler.
1367 """
1368 return self._getcoordinfo()[2]
1369
1370 @asaplog_post_dec
1371 def set_doppler(self, doppler='RADIO'):
1372 """\
1373 Set the doppler for all following operations on this scantable.
1374
1375 Parameters:
1376
1377 doppler: One of 'RADIO', 'OPTICAL', 'Z', 'BETA', 'GAMMA'
1378
1379 """
1380 varlist = vars()
1381 inf = list(self._getcoordinfo())
1382 inf[2] = doppler
1383 self._setcoordinfo(inf)
1384 self._add_history("set_doppler", vars())
1385
1386 @asaplog_post_dec
1387 def set_freqframe(self, frame=None):
1388 """\
1389 Set the frame type of the Spectral Axis.
1390
1391 Parameters:
1392
1393 frame: an optional frame type, default 'LSRK'. Valid frames are:
1394 'TOPO', 'LSRD', 'LSRK', 'BARY',
1395 'GEO', 'GALACTO', 'LGROUP', 'CMB'
1396
1397 Example::
1398
1399 scan.set_freqframe('BARY')
1400
1401 """
1402 frame = frame or rcParams['scantable.freqframe']
1403 varlist = vars()
1404 # "REST" is not implemented in casacore
1405 #valid = ['REST', 'TOPO', 'LSRD', 'LSRK', 'BARY', \
1406 # 'GEO', 'GALACTO', 'LGROUP', 'CMB']
1407 valid = ['TOPO', 'LSRD', 'LSRK', 'BARY', \
1408 'GEO', 'GALACTO', 'LGROUP', 'CMB']
1409
1410 if frame in valid:
1411 inf = list(self._getcoordinfo())
1412 inf[1] = frame
1413 self._setcoordinfo(inf)
1414 self._add_history("set_freqframe", varlist)
1415 else:
1416 msg = "Please specify a valid freq type. Valid types are:\n", valid
1417 raise TypeError(msg)
1418
1419 @asaplog_post_dec
1420 def set_dirframe(self, frame=""):
1421 """\
1422 Set the frame type of the Direction on the sky.
1423
1424 Parameters:
1425
1426 frame: an optional frame type, default ''. Valid frames are:
1427 'J2000', 'B1950', 'GALACTIC'
1428
1429 Example:
1430
1431 scan.set_dirframe('GALACTIC')
1432
1433 """
1434 varlist = vars()
1435 Scantable.set_dirframe(self, frame)
1436 self._add_history("set_dirframe", varlist)
1437
1438 def get_unit(self):
1439 """\
1440 Get the default unit set in this scantable
1441
1442 Returns:
1443
1444 A unit string
1445
1446 """
1447 inf = self._getcoordinfo()
1448 unit = inf[0]
1449 if unit == '': unit = 'channel'
1450 return unit
1451
1452 @asaplog_post_dec
1453 def get_abcissa(self, rowno=0):
1454 """\
1455 Get the abcissa in the current coordinate setup for the currently
1456 selected Beam/IF/Pol
1457
1458 Parameters:
1459
1460 rowno: an optional row number in the scantable. Default is the
1461 first row, i.e. rowno=0
1462
1463 Returns:
1464
1465 The abcissa values and the format string (as a dictionary)
1466
1467 """
1468 abc = self._getabcissa(rowno)
1469 lbl = self._getabcissalabel(rowno)
1470 return abc, lbl
1471
1472 @asaplog_post_dec
1473 def flag(self, mask=None, unflag=False, row=-1):
1474 """\
1475 Flag the selected data using an optional channel mask.
1476
1477 Parameters:
1478
1479 mask: an optional channel mask, created with create_mask. Default
1480 (no mask) is all channels.
1481
1482 unflag: if True, unflag the data
1483
1484 row: an optional row number in the scantable.
1485 Default -1 flags all rows
1486
1487 """
1488 varlist = vars()
1489 mask = mask or []
1490 self._flag(row, mask, unflag)
1491 self._add_history("flag", varlist)
1492
1493 @asaplog_post_dec
1494 def flag_row(self, rows=None, unflag=False):
1495 """\
1496 Flag the selected data in row-based manner.
1497
1498 Parameters:
1499
1500 rows: list of row numbers to be flagged. Default is no row
1501 (must be explicitly specified to execute row-based
1502 flagging).
1503
1504 unflag: if True, unflag the data.
1505
1506 """
1507 varlist = vars()
1508 if rows is None:
1509 rows = []
1510 self._flag_row(rows, unflag)
1511 self._add_history("flag_row", varlist)
1512
1513 @asaplog_post_dec
1514 def clip(self, uthres=None, dthres=None, clipoutside=True, unflag=False):
1515 """\
1516 Flag the selected data outside a specified range (in channel-base)
1517
1518 Parameters:
1519
1520 uthres: upper threshold.
1521
1522 dthres: lower threshold
1523
1524 clipoutside: True for flagging data outside the range
1525 [dthres:uthres].
1526 False for flagging data inside the range.
1527
1528 unflag: if True, unflag the data.
1529
1530 """
1531 varlist = vars()
1532 self._clip(uthres, dthres, clipoutside, unflag)
1533 self._add_history("clip", varlist)
1534
1535 @asaplog_post_dec
1536 def lag_flag(self, start, end, unit="MHz", insitu=None):
1537 """\
1538 Flag the data in 'lag' space by providing a frequency to remove.
1539 Flagged data in the scantable get interpolated over the region.
1540 No taper is applied.
1541
1542 Parameters:
1543
1544 start: the start frequency (really a period within the
1545 bandwidth) or period to remove
1546
1547 end: the end frequency or period to remove
1548
1549 unit: the frequency unit (default 'MHz') or '' for
1550 explicit lag channels
1551
1552 *Notes*:
1553
1554 It is recommended to flag edges of the band or strong
1555 signals beforehand.
1556
1557 """
1558 if insitu is None: insitu = rcParams['insitu']
1559 self._math._setinsitu(insitu)
1560 varlist = vars()
1561 base = { "GHz": 1000000000., "MHz": 1000000., "kHz": 1000., "Hz": 1.}
1562 if not (unit == "" or base.has_key(unit)):
1563 raise ValueError("%s is not a valid unit." % unit)
1564 if unit == "":
1565 s = scantable(self._math._lag_flag(self, start, end, "lags"))
1566 else:
1567 s = scantable(self._math._lag_flag(self, start*base[unit],
1568 end*base[unit], "frequency"))
1569 s._add_history("lag_flag", varlist)
1570 if insitu:
1571 self._assign(s)
1572 else:
1573 return s
1574
1575 @asaplog_post_dec
1576 def fft(self, rowno=None, mask=None, getrealimag=False):
1577 """\
1578 Apply FFT to the spectra.
1579 Flagged data in the scantable get interpolated over the region.
1580
1581 Parameters:
1582
1583 rowno: The row number(s) to be processed. int, list
1584 and tuple are accepted. By default (None), FFT
1585 is applied to the whole data.
1586
1587 mask: Auxiliary channel mask(s). Given as a boolean
1588 list, it is applied to all specified rows.
1589 A list of boolean lists can also be used to
1590 apply different masks. In the latter case, the
1591 length of 'mask' must be the same as that of
1592 'rowno'. The default is None.
1593
1594 getrealimag: If True, returns the real and imaginary part
1595 values of the complex results.
1596 If False (the default), returns the amplitude
1597 (absolute value) normalised with Ndata/2 and
1598 phase (argument, in unit of radian).
1599
1600 Returns:
1601
1602 A list of dictionaries containing the results for each spectrum.
1603 Each dictionary contains two values, the real and the imaginary
1604 parts when getrealimag = True, or the amplitude(absolute value)
1605 and the phase(argument) when getrealimag = False. The key for
1606 these values are 'real' and 'imag', or 'ampl' and 'phase',
1607 respectively.
1608 """
1609 if rowno is None:
1610 rowno = []
1611 if isinstance(rowno, int):
1612 rowno = [rowno]
1613 elif not (isinstance(rowno, list) or isinstance(rowno, tuple)):
1614 raise TypeError("The row number(s) must be int, list or tuple.")
1615 if len(rowno) == 0: rowno = [i for i in xrange(self.nrow())]
1616
1617 usecommonmask = True
1618
1619 if mask is None:
1620 mask = []
1621 if isinstance(mask, list) or isinstance(mask, tuple):
1622 if len(mask) == 0:
1623 mask = [[]]
1624 else:
1625 if isinstance(mask[0], bool):
1626 if len(mask) != self.nchan(self.getif(rowno[0])):
1627 raise ValueError("The spectra and the mask have "
1628 "different length.")
1629 mask = [mask]
1630 elif isinstance(mask[0], list) or isinstance(mask[0], tuple):
1631 usecommonmask = False
1632 if len(mask) != len(rowno):
1633 raise ValueError("When specifying masks for each "
1634 "spectrum, the numbers of them "
1635 "must be identical.")
1636 for i in xrange(mask):
1637 if len(mask[i]) != self.nchan(self.getif(rowno[i])):
1638 raise ValueError("The spectra and the mask have "
1639 "different length.")
1640 else:
1641 raise TypeError("The mask must be a boolean list or "
1642 "a list of boolean list.")
1643 else:
1644 raise TypeError("The mask must be a boolean list or a list of "
1645 "boolean list.")
1646
1647 res = []
1648
1649 imask = 0
1650 for whichrow in rowno:
1651 fspec = self._fft(whichrow, mask[imask], getrealimag)
1652 nspec = len(fspec)
1653
1654 i = 0
1655 v1 = []
1656 v2 = []
1657 reselem = {"real":[],"imag":[]} if getrealimag \
1658 else {"ampl":[],"phase":[]}
1659
1660 while (i < nspec):
1661 v1.append(fspec[i])
1662 v2.append(fspec[i+1])
1663 i += 2
1664
1665 if getrealimag:
1666 reselem["real"] += v1
1667 reselem["imag"] += v2
1668 else:
1669 reselem["ampl"] += v1
1670 reselem["phase"] += v2
1671
1672 res.append(reselem)
1673
1674 if not usecommonmask:
1675 imask += 1
1676
1677 return res
1678
1679 @asaplog_post_dec
1680 def create_mask(self, *args, **kwargs):
1681 """\
1682 Compute and return a mask based on [min, max] windows.
1683 The specified windows are to be INCLUDED, when the mask is
1684 applied.
1685
1686 Parameters:
1687
1688 [min, max], [min2, max2], ...
1689 Pairs of start/end points (inclusive)specifying the regions
1690 to be masked
1691
1692 invert: optional argument. If specified as True,
1693 return an inverted mask, i.e. the regions
1694 specified are EXCLUDED
1695
1696 row: create the mask using the specified row for
1697 unit conversions, default is row=0
1698 only necessary if frequency varies over rows.
1699
1700 Examples::
1701
1702 scan.set_unit('channel')
1703 # a)
1704 msk = scan.create_mask([400, 500], [800, 900])
1705 # masks everything outside 400 and 500
1706 # and 800 and 900 in the unit 'channel'
1707
1708 # b)
1709 msk = scan.create_mask([400, 500], [800, 900], invert=True)
1710 # masks the regions between 400 and 500
1711 # and 800 and 900 in the unit 'channel'
1712
1713 # c)
1714 #mask only channel 400
1715 msk = scan.create_mask([400])
1716
1717 """
1718 row = kwargs.get("row", 0)
1719 data = self._getabcissa(row)
1720 u = self._getcoordinfo()[0]
1721 if u == "":
1722 u = "channel"
1723 msg = "The current mask window unit is %s" % u
1724 i = self._check_ifs()
1725 if not i:
1726 msg += "\nThis mask is only valid for IF=%d" % (self.getif(i))
1727 asaplog.push(msg)
1728 n = len(data)
1729 msk = _n_bools(n, False)
1730 # test if args is a 'list' or a 'normal *args - UGLY!!!
1731
1732 ws = (isinstance(args[-1][-1], int)
1733 or isinstance(args[-1][-1], float)) and args or args[0]
1734 for window in ws:
1735 if len(window) == 1:
1736 window = [window[0], window[0]]
1737 if len(window) == 0 or len(window) > 2:
1738 raise ValueError("A window needs to be defined as "
1739 "[start(, end)]")
1740 if window[0] > window[1]:
1741 tmp = window[0]
1742 window[0] = window[1]
1743 window[1] = tmp
1744 for i in range(n):
1745 if data[i] >= window[0] and data[i] <= window[1]:
1746 msk[i] = True
1747 if kwargs.has_key('invert'):
1748 if kwargs.get('invert'):
1749 msk = mask_not(msk)
1750 return msk
1751
1752 def get_masklist(self, mask=None, row=0, silent=False):
1753 """\
1754 Compute and return a list of mask windows, [min, max].
1755
1756 Parameters:
1757
1758 mask: channel mask, created with create_mask.
1759
1760 row: calcutate the masklist using the specified row
1761 for unit conversions, default is row=0
1762 only necessary if frequency varies over rows.
1763
1764 Returns:
1765
1766 [min, max], [min2, max2], ...
1767 Pairs of start/end points (inclusive)specifying
1768 the masked regions
1769
1770 """
1771 if not (isinstance(mask,list) or isinstance(mask, tuple)):
1772 raise TypeError("The mask should be list or tuple.")
1773 if len(mask) <= 0:
1774 raise TypeError("The mask elements should be > 0")
1775 data = self._getabcissa(row)
1776 if len(data) != len(mask):
1777 msg = "Number of channels in scantable != number of mask elements"
1778 raise TypeError(msg)
1779 u = self._getcoordinfo()[0]
1780 if u == "":
1781 u = "channel"
1782 msg = "The current mask window unit is %s" % u
1783 i = self._check_ifs()
1784 if not i:
1785 msg += "\nThis mask is only valid for IF=%d" % (self.getif(i))
1786 if not silent:
1787 asaplog.push(msg)
1788 masklist = []
1789 ist, ien = None, None
1790 ist, ien=self.get_mask_indices(mask)
1791 if ist is not None and ien is not None:
1792 for i in xrange(len(ist)):
1793 range=[data[ist[i]],data[ien[i]]]
1794 range.sort()
1795 masklist.append([range[0],range[1]])
1796 return masklist
1797
1798 def get_mask_indices(self, mask=None):
1799 """\
1800 Compute and Return lists of mask start indices and mask end indices.
1801
1802 Parameters:
1803
1804 mask: channel mask, created with create_mask.
1805
1806 Returns:
1807
1808 List of mask start indices and that of mask end indices,
1809 i.e., [istart1,istart2,....], [iend1,iend2,....].
1810
1811 """
1812 if not (isinstance(mask,list) or isinstance(mask, tuple)):
1813 raise TypeError("The mask should be list or tuple.")
1814 if len(mask) <= 0:
1815 raise TypeError("The mask elements should be > 0")
1816 istart = []
1817 iend = []
1818 if mask[0]:
1819 istart.append(0)
1820 for i in range(len(mask)-1):
1821 if not mask[i] and mask[i+1]:
1822 istart.append(i+1)
1823 elif mask[i] and not mask[i+1]:
1824 iend.append(i)
1825 if mask[len(mask)-1]:
1826 iend.append(len(mask)-1)
1827 if len(istart) != len(iend):
1828 raise RuntimeError("Numbers of mask start != mask end.")
1829 for i in range(len(istart)):
1830 if istart[i] > iend[i]:
1831 raise RuntimeError("Mask start index > mask end index")
1832 break
1833 return istart,iend
1834
1835 @asaplog_post_dec
1836 def parse_spw_selection(self, selectstring, restfreq=None, frame=None, doppler=None):
1837 """
1838 Parse MS type spw/channel selection syntax.
1839
1840 Parameters:
1841 selectstring : A string expression of spw and channel selection.
1842 Comma-separated expressions mean different spw -
1843 channel combinations. Spws and channel selections
1844 are partitioned by a colon ':'. In a single
1845 selection expression, you can put multiple values
1846 separated by semicolons ';'. Both for spw and
1847 channel selection, allowed cases include single
1848 value, blank('') or asterisk('*') to specify all
1849 available values, two values connected with a
1850 tilde ('~') to specify an inclusive range. Unit
1851 strings for frequency or velocity can be added to
1852 the tilde-connected values. For channel selection
1853 expression, placing a '<' or a '>' is possible to
1854 specify a semi-infinite interval as well.
1855
1856 examples:
1857 '' or '*' = all spws (all channels)
1858 '<2,4~6,9' = Spws 0,1,4,5,6,9 (all channels)
1859 '3:3~45;60' = channels 3 to 45 and 60 in spw 3
1860 '0~1:2~6,8' = channels 2 to 6 in spws 0,1, and
1861 all channels in spw8
1862 '1.3~1.5GHz' = all spws whose central frequency
1863 falls in frequency range between
1864 1.3GHz and 1.5GHz.
1865 '1.3~1.5GHz:1.3~1.5GHz' = channels which fall
1866 between the specified
1867 frequency range in spws
1868 whose central frequency
1869 falls in the specified
1870 frequency range.
1871 '1:-200~250km/s' = channels that fall between the
1872 specified velocity range in
1873 spw 1.
1874 restfreq: the rest frequency.
1875 examples: '115.2712GHz', 115271201800.0
1876 frame: an optional frame type, default 'LSRK'. Valid frames are:
1877 'TOPO', 'LSRD', 'LSRK', 'BARY',
1878 'GEO', 'GALACTO', 'LGROUP', 'CMB'
1879 doppler: one of 'RADIO', 'OPTICAL', 'Z', 'BETA', 'GAMMA'
1880 Returns:
1881 A dictionary of selected (valid) spw and masklist pairs,
1882 e.g. {'0': [[50,250],[350,462]], '2': [[100,400],[550,974]]}
1883 """
1884 if not isinstance(selectstring, str):
1885 asaplog.post()
1886 asaplog.push("Expression of spw/channel selection must be a string.")
1887 asaplog.post("ERROR")
1888
1889 orig_unit = self.get_unit()
1890 self.set_unit('channel')
1891
1892 if restfreq is not None:
1893 orig_molids = self._getmolidcol_list()
1894 set_restfreq(self, restfreq)
1895
1896 orig_coord = self._getcoordinfo()
1897
1898 if frame is not None:
1899 orig_frame = orig_coord[1]
1900 self.set_freqframe(frame)
1901
1902 if doppler is not None:
1903 orig_doppler = orig_coord[2]
1904 self.set_doppler(doppler)
1905
1906 valid_ifs = self.getifnos()
1907
1908 comma_sep = selectstring.split(",")
1909 res = {}
1910
1911 for cms_elem in comma_sep:
1912 colon_sep = cms_elem.split(":")
1913
1914 if (len(colon_sep) > 2):
1915 raise RuntimeError("Invalid selection expression: more than two colons!")
1916
1917 # parse spw expression and store result in spw_list.
1918 # allowed cases include '', '*', 'a', '<a', '>a', 'a~b',
1919 # 'a~b*Hz' (where * can be '', 'k', 'M', 'G' etc.),
1920 # 'a~b*m/s' (where * can be '' or 'k') and also
1921 # several of the above expressions connected with ';'.
1922
1923 spw_list = []
1924
1925 semicolon_sep = colon_sep[0].split(";")
1926
1927 for scs_elem in semicolon_sep:
1928 scs_elem = scs_elem.strip()
1929
1930 lt_sep = scs_elem.split("<")
1931 gt_sep = scs_elem.split(">")
1932 ti_sep = scs_elem.split("~")
1933
1934 lt_sep_length = len(lt_sep)
1935 gt_sep_length = len(gt_sep)
1936 ti_sep_length = len(ti_sep)
1937
1938 len_product = lt_sep_length * gt_sep_length * ti_sep_length
1939
1940 if (len_product > 2):
1941 # '<', '>' and '~' must not coexist in a single spw expression
1942
1943 raise RuntimeError("Invalid spw selection.")
1944
1945 elif (len_product == 1):
1946 # '', '*', or single spw number.
1947
1948 if (scs_elem == "") or (scs_elem == "*"):
1949 spw_list = valid_ifs[:] # deep copy
1950
1951 else: # single number
1952 expr = int(scs_elem)
1953 spw_list.append(expr)
1954 if expr not in valid_ifs:
1955 asaplog.push("Invalid spw given. Ignored.")
1956
1957 else: # (len_product == 2)
1958 # namely, one of '<', '>' or '~' appears just once.
1959
1960 if (lt_sep_length == 2): # '<a'
1961 if is_number(lt_sep[1]):
1962 no_valid_spw = True
1963 for i in valid_ifs:
1964 if (i < float(lt_sep[1])):
1965 spw_list.append(i)
1966 no_valid_spw = False
1967
1968 if no_valid_spw:
1969 raise ValueError("Invalid spw selection ('<" + str(lt_sep[1]) + "').")
1970
1971 else:
1972 raise RuntimeError("Invalid spw selection.")
1973
1974 elif (gt_sep_length == 2): # '>a'
1975 if is_number(gt_sep[1]):
1976 no_valid_spw = True
1977 for i in valid_ifs:
1978 if (i > float(gt_sep[1])):
1979 spw_list.append(i)
1980 no_valid_spw = False
1981
1982 if no_valid_spw:
1983 raise ValueError("Invalid spw selection ('>" + str(gt_sep[1]) + "').")
1984
1985 else:
1986 raise RuntimeError("Invalid spw selection.")
1987
1988 else: # (ti_sep_length == 2) where both boundaries inclusive
1989 expr0 = ti_sep[0].strip()
1990 expr1 = ti_sep[1].strip()
1991
1992 if is_number(expr0) and is_number(expr1):
1993 # 'a~b'
1994 expr_pmin = min(float(expr0), float(expr1))
1995 expr_pmax = max(float(expr0), float(expr1))
1996 has_invalid_spw = False
1997 no_valid_spw = True
1998
1999 for i in valid_ifs:
2000 if (expr_pmin <= i) and (i <= expr_pmax):
2001 spw_list.append(i)
2002 no_valid_spw = False
2003 else:
2004 has_invalid_spw = True
2005
2006 if has_invalid_spw:
2007 msg = "Invalid spw is given. Ignored."
2008 asaplog.push(msg)
2009 asaplog.post()
2010
2011 if no_valid_spw:
2012 raise ValueError("No valid spw in range ('" + str(expr_pmin) + "~" + str(expr_pmax) + "').")
2013
2014 elif is_number(expr0) and is_frequency(expr1):
2015 # 'a~b*Hz'
2016 (expr_f0, expr_f1) = get_freq_by_string(expr0, expr1)
2017 expr_fmin = min(expr_f0, expr_f1)
2018 expr_fmax = max(expr_f0, expr_f1)
2019 no_valid_spw = True
2020
2021 for coord in self._get_coordinate_list():
2022 spw = coord['if']
2023
2024 """
2025 expr_p0 = coord['coord'].to_pixel(expr_f0)
2026 expr_p1 = coord['coord'].to_pixel(expr_f1)
2027 expr_pmin = min(expr_p0, expr_p1)
2028 expr_pmax = max(expr_p0, expr_p1)
2029
2030 pmin = 0.0
2031 pmax = float(self.nchan(spw) - 1)
2032
2033 if ((expr_pmax - pmin)*(expr_pmin - pmax) <= 0.0):
2034 spw_list.append(spw)
2035 no_valid_spw = False
2036 """
2037
2038 crd = coord['coord']
2039 fhead = crd.to_frequency(0)
2040 ftail = crd.to_frequency(self.nchan(spw) - 1)
2041 fcen = (fhead + ftail) / 2.0
2042
2043 if ((expr_fmin <= fcen) and (fcen <= expr_fmax)):
2044 spw_list.append(spw)
2045 no_valid_spw = False
2046
2047 if no_valid_spw:
2048 raise ValueError("No valid spw in range ('" + str(expr0) + "~" + str(expr1) + "').")
2049
2050 elif is_number(expr0) and is_velocity(expr1):
2051 # 'a~b*m/s'
2052 (expr_v0, expr_v1) = get_velocity_by_string(expr0, expr1)
2053 expr_vmin = min(expr_v0, expr_v1)
2054 expr_vmax = max(expr_v0, expr_v1)
2055 no_valid_spw = True
2056
2057 for coord in self._get_coordinate_list():
2058 spw = coord['if']
2059
2060 """
2061 pmin = 0.0
2062 pmax = float(self.nchan(spw) - 1)
2063
2064 vel0 = coord['coord'].to_velocity(pmin)
2065 vel1 = coord['coord'].to_velocity(pmax)
2066
2067 vmin = min(vel0, vel1)
2068 vmax = max(vel0, vel1)
2069
2070 if ((expr_vmax - vmin)*(expr_vmin - vmax) <= 0.0):
2071 spw_list.append(spw)
2072 no_valid_spw = False
2073 """
2074
2075 crd = coord['coord']
2076 fhead = crd.to_frequency(0)
2077 ftail = crd.to_frequency(self.nchan(spw) - 1)
2078 fcen = (fhead + ftail) / 2.0
2079 vcen = crd.to_velocity(crd.to_pixel(fcen))
2080
2081 if ((expr_vmin <= vcen) and (vcen <= expr_vmax)):
2082 spw_list.append(spw)
2083 no_valid_spw = False
2084
2085 if no_valid_spw:
2086 raise ValueError("No valid spw in range ('" + str(expr0) + "~" + str(expr1) + "').")
2087
2088 else:
2089 # cases such as 'aGHz~bkm/s' are not allowed now
2090 raise RuntimeError("Invalid spw selection.")
2091
2092 # check spw list and remove invalid ones.
2093 # if no valid spw left, emit ValueError.
2094 if len(spw_list) == 0:
2095 raise ValueError("No valid spw in given range.")
2096
2097 # parse channel expression and store the result in crange_list.
2098 # allowed cases include '', 'a~b', 'a*Hz~b*Hz' (where * can be
2099 # '', 'k', 'M', 'G' etc.), 'a*m/s~b*m/s' (where * can be '' or 'k')
2100 # and also several of the above expressions connected with ';'.
2101
2102 for spw in spw_list:
2103 pmin = 0.0
2104 pmax = float(self.nchan(spw) - 1)
2105
2106 molid = self._getmolidcol_list()[self.get_first_rowno_by_if(spw)]
2107
2108 if (len(colon_sep) == 1):
2109 # no expression for channel selection,
2110 # which means all channels are to be selected.
2111 crange_list = [[pmin, pmax]]
2112
2113 else: # (len(colon_sep) == 2)
2114 crange_list = []
2115
2116 found = False
2117 for i in self._get_coordinate_list():
2118 if (i['if'] == spw):
2119 coord = i['coord']
2120 found = True
2121 break
2122
2123 if found:
2124 semicolon_sep = colon_sep[1].split(";")
2125 for scs_elem in semicolon_sep:
2126 scs_elem = scs_elem.strip()
2127
2128 ti_sep = scs_elem.split("~")
2129 ti_sep_length = len(ti_sep)
2130
2131 if (ti_sep_length > 2):
2132 raise RuntimeError("Invalid channel selection.")
2133
2134 elif (ti_sep_length == 1):
2135 if (scs_elem == "") or (scs_elem == "*"):
2136 # '' and '*' for all channels
2137 crange_list = [[pmin, pmax]]
2138 break
2139 elif (is_number(scs_elem)):
2140 # single channel given
2141 crange_list.append([float(scs_elem), float(scs_elem)])
2142 else:
2143 raise RuntimeError("Invalid channel selection.")
2144
2145 else: #(ti_sep_length == 2)
2146 expr0 = ti_sep[0].strip()
2147 expr1 = ti_sep[1].strip()
2148
2149 if is_number(expr0) and is_number(expr1):
2150 # 'a~b'
2151 expr_pmin = min(float(expr0), float(expr1))
2152 expr_pmax = max(float(expr0), float(expr1))
2153
2154 elif is_number(expr0) and is_frequency(expr1):
2155 # 'a~b*Hz'
2156 (expr_f0, expr_f1) = get_freq_by_string(expr0, expr1)
2157 expr_p0 = coord.to_pixel(expr_f0)
2158 expr_p1 = coord.to_pixel(expr_f1)
2159 expr_pmin = min(expr_p0, expr_p1)
2160 expr_pmax = max(expr_p0, expr_p1)
2161
2162 elif is_number(expr0) and is_velocity(expr1):
2163 # 'a~b*m/s'
2164 restf = self.get_restfreqs()[molid][0]
2165 (expr_v0, expr_v1) = get_velocity_by_string(expr0, expr1)
2166 dppl = self.get_doppler()
2167 expr_f0 = get_frequency_by_velocity(restf, expr_v0, dppl)
2168 expr_f1 = get_frequency_by_velocity(restf, expr_v1, dppl)
2169 expr_p0 = coord.to_pixel(expr_f0)
2170 expr_p1 = coord.to_pixel(expr_f1)
2171 expr_pmin = min(expr_p0, expr_p1)
2172 expr_pmax = max(expr_p0, expr_p1)
2173
2174 else:
2175 # cases such as 'aGHz~bkm/s' are not allowed now
2176 raise RuntimeError("Invalid channel selection.")
2177
2178 cmin = max(pmin, expr_pmin)
2179 cmax = min(pmax, expr_pmax)
2180 # if the given range of channel selection has overwrap with
2181 # that of current spw, output the overwrap area.
2182 if (cmin <= cmax):
2183 cmin = float(int(cmin + 0.5))
2184 cmax = float(int(cmax + 0.5))
2185 crange_list.append([cmin, cmax])
2186
2187 if (len(crange_list) == 0):
2188 crange_list.append([])
2189
2190 if (len(crange_list[0]) > 0):
2191 if res.has_key(spw):
2192 res[spw].extend(crange_list)
2193 else:
2194 res[spw] = crange_list
2195
2196 for spw in res.keys():
2197 if spw in valid_ifs:
2198 # remove duplicated channel ranges
2199 for i in reversed(xrange(len(res[spw]))):
2200 for j in xrange(i):
2201 if ((res[spw][i][0]-res[spw][j][1])*(res[spw][i][1]-res[spw][j][0]) <= 0) or \
2202 (min(abs(res[spw][i][0]-res[spw][j][1]),abs(res[spw][j][0]-res[spw][i][1])) == 1):
2203 asaplog.post()
2204 merge_warn_mesg = "Spw " + str(spw) + ": overwrapping channel ranges are merged."
2205 asaplog.push(merge_warn_mesg)
2206 asaplog.post('WARN')
2207 res[spw][j][0] = min(res[spw][i][0], res[spw][j][0])
2208 res[spw][j][1] = max(res[spw][i][1], res[spw][j][1])
2209 res[spw].pop(i)
2210 break
2211 else:
2212 del res[spw]
2213
2214 if len(res) == 0:
2215 raise RuntimeError("No valid spw.")
2216
2217 # restore original values
2218 self.set_unit(orig_unit)
2219 if restfreq is not None:
2220 self._setmolidcol_list(orig_molids)
2221 if frame is not None:
2222 self.set_freqframe(orig_frame)
2223 if doppler is not None:
2224 self.set_doppler(orig_doppler)
2225
2226 return res
2227
2228 @asaplog_post_dec
2229 def get_first_rowno_by_if(self, ifno):
2230 found = False
2231 for irow in xrange(self.nrow()):
2232 if (self.getif(irow) == ifno):
2233 res = irow
2234 found = True
2235 break
2236
2237 if not found: raise RuntimeError("No valid spw.")
2238
2239 return res
2240
2241 @asaplog_post_dec
2242 def _get_coordinate_list(self):
2243 res = []
2244 spws = self.getifnos()
2245 for spw in spws:
2246 elem = {}
2247 elem['if'] = spw
2248 elem['coord'] = self.get_coordinate(self.get_first_rowno_by_if(spw))
2249 res.append(elem)
2250
2251 return res
2252
2253 @asaplog_post_dec
2254 def parse_maskexpr(self, maskstring):
2255 """
2256 Parse CASA type mask selection syntax (IF dependent).
2257
2258 Parameters:
2259 maskstring : A string mask selection expression.
2260 A comma separated selections mean different IF -
2261 channel combinations. IFs and channel selections
2262 are partitioned by a colon, ':'.
2263 examples:
2264 '' = all IFs (all channels)
2265 '<2,4~6,9' = IFs 0,1,4,5,6,9 (all channels)
2266 '3:3~45;60' = channels 3 to 45 and 60 in IF 3
2267 '0~1:2~6,8' = channels 2 to 6 in IFs 0,1, and
2268 all channels in IF8
2269 Returns:
2270 A dictionary of selected (valid) IF and masklist pairs,
2271 e.g. {'0': [[50,250],[350,462]], '2': [[100,400],[550,974]]}
2272 """
2273 if not isinstance(maskstring,str):
2274 asaplog.post()
2275 asaplog.push("Mask expression should be a string.")
2276 asaplog.post("ERROR")
2277
2278 valid_ifs = self.getifnos()
2279 frequnit = self.get_unit()
2280 seldict = {}
2281 if maskstring == "":
2282 maskstring = str(valid_ifs)[1:-1]
2283 ## split each selection "IF range[:CHAN range]"
2284 # split maskstring by "<spaces>,<spaces>"
2285 comma_sep = re.compile('\s*,\s*')
2286 sellist = comma_sep.split(maskstring)
2287 # separator by "<spaces>:<spaces>"
2288 collon_sep = re.compile('\s*:\s*')
2289 for currselstr in sellist:
2290 selset = collon_sep.split(currselstr)
2291 # spw and mask string (may include ~, < or >)
2292 spwmasklist = self._parse_selection(selset[0], typestr='integer',
2293 minval=min(valid_ifs),
2294 maxval=max(valid_ifs))
2295 for spwlist in spwmasklist:
2296 selspws = []
2297 for ispw in range(spwlist[0],spwlist[1]+1):
2298 # Put into the list only if ispw exists
2299 if valid_ifs.count(ispw):
2300 selspws.append(ispw)
2301 del spwmasklist, spwlist
2302
2303 # parse frequency mask list
2304 if len(selset) > 1:
2305 freqmasklist = self._parse_selection(selset[1], typestr='float',
2306 offset=0.)
2307 else:
2308 # want to select the whole spectrum
2309 freqmasklist = [None]
2310
2311 ## define a dictionary of spw - masklist combination
2312 for ispw in selspws:
2313 #print "working on", ispw
2314 spwstr = str(ispw)
2315 if len(selspws) == 0:
2316 # empty spw
2317 continue
2318 else:
2319 ## want to get min and max of the spw and
2320 ## offset to set for '<' and '>'
2321 if frequnit == 'channel':
2322 minfreq = 0
2323 maxfreq = self.nchan(ifno=ispw)
2324 offset = 0.5
2325 else:
2326 ## This is ugly part. need improvement
2327 for ifrow in xrange(self.nrow()):
2328 if self.getif(ifrow) == ispw:
2329 #print "IF",ispw,"found in row =",ifrow
2330 break
2331 freqcoord = self.get_coordinate(ifrow)
2332 freqs = self._getabcissa(ifrow)
2333 minfreq = min(freqs)
2334 maxfreq = max(freqs)
2335 if len(freqs) == 1:
2336 offset = 0.5
2337 elif frequnit.find('Hz') > 0:
2338 offset = abs(freqcoord.to_frequency(1,
2339 unit=frequnit)
2340 -freqcoord.to_frequency(0,
2341 unit=frequnit)
2342 )*0.5
2343 elif frequnit.find('m/s') > 0:
2344 offset = abs(freqcoord.to_velocity(1,
2345 unit=frequnit)
2346 -freqcoord.to_velocity(0,
2347 unit=frequnit)
2348 )*0.5
2349 else:
2350 asaplog.post()
2351 asaplog.push("Invalid frequency unit")
2352 asaplog.post("ERROR")
2353 del freqs, freqcoord, ifrow
2354 for freq in freqmasklist:
2355 selmask = freq or [minfreq, maxfreq]
2356 if selmask[0] == None:
2357 ## selection was "<freq[1]".
2358 if selmask[1] < minfreq:
2359 ## avoid adding region selection
2360 selmask = None
2361 else:
2362 selmask = [minfreq,selmask[1]-offset]
2363 elif selmask[1] == None:
2364 ## selection was ">freq[0]"
2365 if selmask[0] > maxfreq:
2366 ## avoid adding region selection
2367 selmask = None
2368 else:
2369 selmask = [selmask[0]+offset,maxfreq]
2370 if selmask:
2371 if not seldict.has_key(spwstr):
2372 # new spw selection
2373 seldict[spwstr] = []
2374 seldict[spwstr] += [selmask]
2375 del minfreq,maxfreq,offset,freq,selmask
2376 del spwstr
2377 del freqmasklist
2378 del valid_ifs
2379 if len(seldict) == 0:
2380 asaplog.post()
2381 asaplog.push("No valid selection in the mask expression: "
2382 +maskstring)
2383 asaplog.post("WARN")
2384 return None
2385 msg = "Selected masklist:\n"
2386 for sif, lmask in seldict.iteritems():
2387 msg += " IF"+sif+" - "+str(lmask)+"\n"
2388 asaplog.push(msg)
2389 return seldict
2390
2391 @asaplog_post_dec
2392 def parse_idx_selection(self, mode, selexpr):
2393 """
2394 Parse CASA type mask selection syntax of SCANNO, IFNO, POLNO,
2395 BEAMNO, and row number
2396
2397 Parameters:
2398 mode : which column to select.
2399 ['scan',|'if'|'pol'|'beam'|'row']
2400 selexpr : A comma separated selection expression.
2401 examples:
2402 '' = all (returns [])
2403 '<2,4~6,9' = indices less than 2, 4 to 6 and 9
2404 (returns [0,1,4,5,6,9])
2405 Returns:
2406 A List of selected indices
2407 """
2408 if selexpr == "":
2409 return []
2410 valid_modes = {'s': 'scan', 'i': 'if', 'p': 'pol',
2411 'b': 'beam', 'r': 'row'}
2412 smode = mode.lower()[0]
2413 if not (smode in valid_modes.keys()):
2414 msg = "Invalid mode '%s'. Valid modes are %s" %\
2415 (mode, str(valid_modes.values()))
2416 asaplog.post()
2417 asaplog.push(msg)
2418 asaplog.post("ERROR")
2419 mode = valid_modes[smode]
2420 minidx = None
2421 maxidx = None
2422 if smode == 'r':
2423 minidx = 0
2424 maxidx = self.nrow()-1
2425 else:
2426 idx = getattr(self,"get"+mode+"nos")()
2427 minidx = min(idx)
2428 maxidx = max(idx)
2429 del idx
2430 # split selexpr by "<spaces>,<spaces>"
2431 comma_sep = re.compile('\s*,\s*')
2432 sellist = comma_sep.split(selexpr)
2433 idxlist = []
2434 for currselstr in sellist:
2435 # single range (may include ~, < or >)
2436 currlist = self._parse_selection(currselstr, typestr='integer',
2437 minval=minidx,maxval=maxidx)
2438 for thelist in currlist:
2439 idxlist += range(thelist[0],thelist[1]+1)
2440 # remove duplicated elements after first ones
2441 for i in reversed(xrange(len(idxlist))):
2442 if idxlist.index(idxlist[i]) < i:
2443 idxlist.pop(i)
2444
2445 # remove elements outside range [minidx, maxidx] for smode='r'
2446 if smode == 'r':
2447 for i in reversed(xrange(len(idxlist))):
2448 if (idxlist[i] < minidx) or (idxlist[i] > maxidx):
2449 idxlist.pop(i)
2450
2451 msg = "Selected %s: %s" % (mode.upper()+"NO", str(idxlist))
2452 asaplog.push(msg)
2453 return idxlist
2454
2455 def _parse_selection(self, selstr, typestr='float', offset=0.,
2456 minval=None, maxval=None):
2457 """
2458 Parameters:
2459 selstr : The Selection string, e.g., '<3;5~7;100~103;9'
2460 typestr : The type of the values in returned list
2461 ('integer' or 'float')
2462 offset : The offset value to subtract from or add to
2463 the boundary value if the selection string
2464 includes '<' or '>' [Valid only for typestr='float']
2465 minval, maxval : The minimum/maximum values to set if the
2466 selection string includes '<' or '>'.
2467 The list element is filled with None by default.
2468 Returns:
2469 A list of min/max pair of selections.
2470 Example:
2471 _parse_selection('<3;5~7;9',typestr='int',minval=0)
2472 --> returns [[0,2],[5,7],[9,9]]
2473 _parse_selection('<3;5~7;9',typestr='float',offset=0.5,minval=0)
2474 --> returns [[0.,2.5],[5.0,7.0],[9.,9.]]
2475 """
2476 # split selstr by '<spaces>;<spaces>'
2477 semi_sep = re.compile('\s*;\s*')
2478 selgroups = semi_sep.split(selstr)
2479 sellists = []
2480 if typestr.lower().startswith('int'):
2481 formatfunc = int
2482 offset = 1
2483 else:
2484 formatfunc = float
2485
2486 for currsel in selgroups:
2487 if currsel.strip() == '*' or len(currsel.strip()) == 0:
2488 minsel = minval
2489 maxsel = maxval
2490 if currsel.find('~') > 0:
2491 # val0 <= x <= val1
2492 minsel = formatfunc(currsel.split('~')[0].strip())
2493 maxsel = formatfunc(currsel.split('~')[1].strip())
2494 elif currsel.strip().find('<=') > -1:
2495 bound = currsel.split('<=')
2496 try: # try "x <= val"
2497 minsel = minval
2498 maxsel = formatfunc(bound[1].strip())
2499 except ValueError: # now "val <= x"
2500 minsel = formatfunc(bound[0].strip())
2501 maxsel = maxval
2502 elif currsel.strip().find('>=') > -1:
2503 bound = currsel.split('>=')
2504 try: # try "x >= val"
2505 minsel = formatfunc(bound[1].strip())
2506 maxsel = maxval
2507 except ValueError: # now "val >= x"
2508 minsel = minval
2509 maxsel = formatfunc(bound[0].strip())
2510 elif currsel.strip().find('<') > -1:
2511 bound = currsel.split('<')
2512 try: # try "x < val"
2513 minsel = minval
2514 maxsel = formatfunc(bound[1].strip()) \
2515 - formatfunc(offset)
2516 except ValueError: # now "val < x"
2517 minsel = formatfunc(bound[0].strip()) \
2518 + formatfunc(offset)
2519 maxsel = maxval
2520 elif currsel.strip().find('>') > -1:
2521 bound = currsel.split('>')
2522 try: # try "x > val"
2523 minsel = formatfunc(bound[1].strip()) \
2524 + formatfunc(offset)
2525 maxsel = maxval
2526 except ValueError: # now "val > x"
2527 minsel = minval
2528 maxsel = formatfunc(bound[0].strip()) \
2529 - formatfunc(offset)
2530 else:
2531 minsel = formatfunc(currsel)
2532 maxsel = formatfunc(currsel)
2533 sellists.append([minsel,maxsel])
2534 return sellists
2535
2536# def get_restfreqs(self):
2537# """
2538# Get the restfrequency(s) stored in this scantable.
2539# The return value(s) are always of unit 'Hz'
2540# Parameters:
2541# none
2542# Returns:
2543# a list of doubles
2544# """
2545# return list(self._getrestfreqs())
2546
2547 def get_restfreqs(self, ids=None):
2548 """\
2549 Get the restfrequency(s) stored in this scantable.
2550 The return value(s) are always of unit 'Hz'
2551
2552 Parameters:
2553
2554 ids: (optional) a list of MOLECULE_ID for that restfrequency(s) to
2555 be retrieved
2556
2557 Returns:
2558
2559 dictionary containing ids and a list of doubles for each id
2560
2561 """
2562 if ids is None:
2563 rfreqs = {}
2564 idlist = self.getmolnos()
2565 for i in idlist:
2566 rfreqs[i] = list(self._getrestfreqs(i))
2567 return rfreqs
2568 else:
2569 if type(ids) == list or type(ids) == tuple:
2570 rfreqs = {}
2571 for i in ids:
2572 rfreqs[i] = list(self._getrestfreqs(i))
2573 return rfreqs
2574 else:
2575 return list(self._getrestfreqs(ids))
2576
2577 @asaplog_post_dec
2578 def set_restfreqs(self, freqs=None, unit='Hz'):
2579 """\
2580 Set or replace the restfrequency specified and
2581 if the 'freqs' argument holds a scalar,
2582 then that rest frequency will be applied to all the selected
2583 data. If the 'freqs' argument holds
2584 a vector, then it MUST be of equal or smaller length than
2585 the number of IFs (and the available restfrequencies will be
2586 replaced by this vector). In this case, *all* data have
2587 the restfrequency set per IF according
2588 to the corresponding value you give in the 'freqs' vector.
2589 E.g. 'freqs=[1e9, 2e9]' would mean IF 0 gets restfreq 1e9 and
2590 IF 1 gets restfreq 2e9.
2591
2592 You can also specify the frequencies via a linecatalog.
2593
2594 Parameters:
2595
2596 freqs: list of rest frequency values or string idenitfiers
2597
2598 unit: unit for rest frequency (default 'Hz')
2599
2600
2601 Example::
2602
2603 # set the given restfrequency for the all currently selected IFs
2604 scan.set_restfreqs(freqs=1.4e9)
2605 # set restfrequencies for the n IFs (n > 1) in the order of the
2606 # list, i.e
2607 # IF0 -> 1.4e9, IF1 -> 1.41e9, IF3 -> 1.42e9
2608 # len(list_of_restfreqs) == nIF
2609 # for nIF == 1 the following will set multiple restfrequency for
2610 # that IF
2611 scan.set_restfreqs(freqs=[1.4e9, 1.41e9, 1.42e9])
2612 # set multiple restfrequencies per IF. as a list of lists where
2613 # the outer list has nIF elements, the inner s arbitrary
2614 scan.set_restfreqs(freqs=[[1.4e9, 1.41e9], [1.67e9]])
2615
2616 *Note*:
2617
2618 To do more sophisticate Restfrequency setting, e.g. on a
2619 source and IF basis, use scantable.set_selection() before using
2620 this function::
2621
2622 # provided your scantable is called scan
2623 selection = selector()
2624 selection.set_name('ORION*')
2625 selection.set_ifs([1])
2626 scan.set_selection(selection)
2627 scan.set_restfreqs(freqs=86.6e9)
2628
2629 """
2630 varlist = vars()
2631 from asap import linecatalog
2632 # simple value
2633 if isinstance(freqs, int) or isinstance(freqs, float):
2634 self._setrestfreqs([freqs], [""], unit)
2635 # list of values
2636 elif isinstance(freqs, list) or isinstance(freqs, tuple):
2637 # list values are scalars
2638 if isinstance(freqs[-1], int) or isinstance(freqs[-1], float):
2639 if len(freqs) == 1:
2640 self._setrestfreqs(freqs, [""], unit)
2641 else:
2642 # allow the 'old' mode of setting mulitple IFs
2643 savesel = self._getselection()
2644 sel = self.get_selection()
2645 iflist = self.getifnos()
2646 if len(freqs)>len(iflist):
2647 raise ValueError("number of elements in list of list "
2648 "exeeds the current IF selections")
2649 iflist = self.getifnos()
2650 for i, fval in enumerate(freqs):
2651 sel.set_ifs(iflist[i])
2652 self._setselection(sel)
2653 self._setrestfreqs([fval], [""], unit)
2654 self._setselection(savesel)
2655
2656 # list values are dict, {'value'=, 'name'=)
2657 elif isinstance(freqs[-1], dict):
2658 values = []
2659 names = []
2660 for d in freqs:
2661 values.append(d["value"])
2662 names.append(d["name"])
2663 self._setrestfreqs(values, names, unit)
2664 elif isinstance(freqs[-1], list) or isinstance(freqs[-1], tuple):
2665 savesel = self._getselection()
2666 sel = self.get_selection()
2667 iflist = self.getifnos()
2668 if len(freqs)>len(iflist):
2669 raise ValueError("number of elements in list of list exeeds"
2670 " the current IF selections")
2671 for i, fval in enumerate(freqs):
2672 sel.set_ifs(iflist[i])
2673 self._setselection(sel)
2674 self._setrestfreqs(fval, [""], unit)
2675 self._setselection(savesel)
2676 # freqs are to be taken from a linecatalog
2677 elif isinstance(freqs, linecatalog):
2678 savesel = self._getselection()
2679 sel = self.get_selection()
2680 for i in xrange(freqs.nrow()):
2681 sel.set_ifs(iflist[i])
2682 self._setselection(sel)
2683 self._setrestfreqs([freqs.get_frequency(i)],
2684 [freqs.get_name(i)], "MHz")
2685 # ensure that we are not iterating past nIF
2686 if i == self.nif()-1: break
2687 self._setselection(savesel)
2688 else:
2689 return
2690 self._add_history("set_restfreqs", varlist)
2691
2692 @asaplog_post_dec
2693 def shift_refpix(self, delta):
2694 """\
2695 Shift the reference pixel of the Spectra Coordinate by an
2696 integer amount.
2697
2698 Parameters:
2699
2700 delta: the amount to shift by
2701
2702 *Note*:
2703
2704 Be careful using this with broadband data.
2705
2706 """
2707 varlist = vars()
2708 Scantable.shift_refpix(self, delta)
2709 s._add_history("shift_refpix", varlist)
2710
2711 @asaplog_post_dec
2712 def history(self, filename=None, nrows=-1, start=0):
2713 """\
2714 Print the history. Optionally to a file.
2715
2716 Parameters:
2717
2718 filename: The name of the file to save the history to.
2719
2720 """
2721 n = self._historylength()
2722 if nrows == -1:
2723 nrows = n
2724 if start+nrows > n:
2725 nrows = nrows-start
2726 if n > 1000 and nrows == n:
2727 nrows = 1000
2728 start = n-1000
2729 asaplog.push("Warning: History has {0} entries. Displaying last "
2730 "1000".format(n))
2731 hist = list(self._gethistory(nrows, start))
2732 out = "-"*80
2733 for h in hist:
2734 if not h.strip():
2735 continue
2736 if h.find("---") >-1:
2737 continue
2738 else:
2739 items = h.split("##")
2740 date = items[0]
2741 func = items[1]
2742 items = items[2:]
2743 out += "\n"+date+"\n"
2744 out += "Function: %s\n Parameters:" % (func)
2745 for i in items:
2746 if i == '':
2747 continue
2748 s = i.split("=")
2749 out += "\n %s = %s" % (s[0], s[1])
2750 out = "\n".join([out, "*"*80])
2751 if filename is not None:
2752 if filename is "":
2753 filename = 'scantable_history.txt'
2754 filename = os.path.expandvars(os.path.expanduser(filename))
2755 if not os.path.isdir(filename):
2756 data = open(filename, 'w')
2757 data.write(out)
2758 data.close()
2759 else:
2760 msg = "Illegal file name '%s'." % (filename)
2761 raise IOError(msg)
2762 return page(out)
2763
2764 #
2765 # Maths business
2766 #
2767 @asaplog_post_dec
2768 def average_time(self, mask=None, scanav=False, weight='tint', align=False,
2769 avmode="NONE"):
2770 """\
2771 Return the (time) weighted average of a scan. Scans will be averaged
2772 only if the source direction (RA/DEC) is within 1' otherwise
2773
2774 *Note*:
2775
2776 in channels only - align if necessary
2777
2778 Parameters:
2779
2780 mask: an optional mask (only used for 'var' and 'tsys'
2781 weighting)
2782
2783 scanav: True averages each scan separately
2784 False (default) averages all scans together,
2785
2786 weight: Weighting scheme.
2787 'none' (mean no weight)
2788 'var' (1/var(spec) weighted)
2789 'tsys' (1/Tsys**2 weighted)
2790 'tint' (integration time weighted)
2791 'tintsys' (Tint/Tsys**2)
2792 'median' ( median averaging)
2793 The default is 'tint'
2794
2795 align: align the spectra in velocity before averaging. It takes
2796 the time of the first spectrum as reference time.
2797 avmode: 'SOURCE' - also select by source name - or
2798 'NONE' (default). Not applicable for scanav=True or
2799 weight=median
2800
2801 Example::
2802
2803 # time average the scantable without using a mask
2804 newscan = scan.average_time()
2805
2806 """
2807 varlist = vars()
2808 weight = weight or 'TINT'
2809 mask = mask or ()
2810 scanav = (scanav and 'SCAN') or avmode.upper()
2811 scan = (self, )
2812
2813 if align:
2814 scan = (self.freq_align(insitu=False), )
2815 asaplog.push("Note: Alignment is don on a source-by-source basis")
2816 asaplog.push("Note: Averaging (by default) is not")
2817 # we need to set it to SOURCE averaging here
2818 s = None
2819 if weight.upper() == 'MEDIAN':
2820 s = scantable(self._math._averagechannel(scan[0], 'MEDIAN',
2821 scanav))
2822 else:
2823 s = scantable(self._math._average(scan, mask, weight.upper(),
2824 scanav))
2825 s._add_history("average_time", varlist)
2826 return s
2827
2828 @asaplog_post_dec
2829 def convert_flux(self, jyperk=None, eta=None, d=None, insitu=None):
2830 """\
2831 Return a scan where all spectra are converted to either
2832 Jansky or Kelvin depending upon the flux units of the scan table.
2833 By default the function tries to look the values up internally.
2834 If it can't find them (or if you want to over-ride), you must
2835 specify EITHER jyperk OR eta (and D which it will try to look up
2836 also if you don't set it). jyperk takes precedence if you set both.
2837
2838 Parameters:
2839
2840 jyperk: the Jy / K conversion factor
2841
2842 eta: the aperture efficiency
2843
2844 d: the geometric diameter (metres)
2845
2846 insitu: if False a new scantable is returned.
2847 Otherwise, the scaling is done in-situ
2848 The default is taken from .asaprc (False)
2849
2850 """
2851 if insitu is None: insitu = rcParams['insitu']
2852 self._math._setinsitu(insitu)
2853 varlist = vars()
2854 jyperk = jyperk or -1.0
2855 d = d or -1.0
2856 eta = eta or -1.0
2857 s = scantable(self._math._convertflux(self, d, eta, jyperk))
2858 s._add_history("convert_flux", varlist)
2859 if insitu: self._assign(s)
2860 else: return s
2861
2862 @asaplog_post_dec
2863 def gain_el(self, poly=None, filename="", method="linear", insitu=None):
2864 """\
2865 Return a scan after applying a gain-elevation correction.
2866 The correction can be made via either a polynomial or a
2867 table-based interpolation (and extrapolation if necessary).
2868 You specify polynomial coefficients, an ascii table or neither.
2869 If you specify neither, then a polynomial correction will be made
2870 with built in coefficients known for certain telescopes (an error
2871 will occur if the instrument is not known).
2872 The data and Tsys are *divided* by the scaling factors.
2873
2874 Parameters:
2875
2876 poly: Polynomial coefficients (default None) to compute a
2877 gain-elevation correction as a function of
2878 elevation (in degrees).
2879
2880 filename: The name of an ascii file holding correction factors.
2881 The first row of the ascii file must give the column
2882 names and these MUST include columns
2883 'ELEVATION' (degrees) and 'FACTOR' (multiply data
2884 by this) somewhere.
2885 The second row must give the data type of the
2886 column. Use 'R' for Real and 'I' for Integer.
2887 An example file would be
2888 (actual factors are arbitrary) :
2889
2890 TIME ELEVATION FACTOR
2891 R R R
2892 0.1 0 0.8
2893 0.2 20 0.85
2894 0.3 40 0.9
2895 0.4 60 0.85
2896 0.5 80 0.8
2897 0.6 90 0.75
2898
2899 method: Interpolation method when correcting from a table.
2900 Values are 'nearest', 'linear' (default), 'cubic'
2901 and 'spline'
2902
2903 insitu: if False a new scantable is returned.
2904 Otherwise, the scaling is done in-situ
2905 The default is taken from .asaprc (False)
2906
2907 """
2908
2909 if insitu is None: insitu = rcParams['insitu']
2910 self._math._setinsitu(insitu)
2911 varlist = vars()
2912 poly = poly or ()
2913 from os.path import expandvars
2914 filename = expandvars(filename)
2915 s = scantable(self._math._gainel(self, poly, filename, method))
2916 s._add_history("gain_el", varlist)
2917 if insitu:
2918 self._assign(s)
2919 else:
2920 return s
2921
2922 @asaplog_post_dec
2923 def freq_align(self, reftime=None, method='cubic', insitu=None):
2924 """\
2925 Return a scan where all rows have been aligned in frequency/velocity.
2926 The alignment frequency frame (e.g. LSRK) is that set by function
2927 set_freqframe.
2928
2929 Parameters:
2930
2931 reftime: reference time to align at. By default, the time of
2932 the first row of data is used.
2933
2934 method: Interpolation method for regridding the spectra.
2935 Choose from 'nearest', 'linear', 'cubic' (default)
2936 and 'spline'
2937
2938 insitu: if False a new scantable is returned.
2939 Otherwise, the scaling is done in-situ
2940 The default is taken from .asaprc (False)
2941
2942 """
2943 if insitu is None: insitu = rcParams["insitu"]
2944 oldInsitu = self._math._insitu()
2945 self._math._setinsitu(insitu)
2946 varlist = vars()
2947 reftime = reftime or ""
2948 s = scantable(self._math._freq_align(self, reftime, method))
2949 s._add_history("freq_align", varlist)
2950 self._math._setinsitu(oldInsitu)
2951 if insitu:
2952 self._assign(s)
2953 else:
2954 return s
2955
2956 @asaplog_post_dec
2957 def opacity(self, tau=None, insitu=None):
2958 """\
2959 Apply an opacity correction. The data
2960 and Tsys are multiplied by the correction factor.
2961
2962 Parameters:
2963
2964 tau: (list of) opacity from which the correction factor is
2965 exp(tau*ZD)
2966 where ZD is the zenith-distance.
2967 If a list is provided, it has to be of length nIF,
2968 nIF*nPol or 1 and in order of IF/POL, e.g.
2969 [opif0pol0, opif0pol1, opif1pol0 ...]
2970 if tau is `None` the opacities are determined from a
2971 model.
2972
2973 insitu: if False a new scantable is returned.
2974 Otherwise, the scaling is done in-situ
2975 The default is taken from .asaprc (False)
2976
2977 """
2978 if insitu is None:
2979 insitu = rcParams['insitu']
2980 self._math._setinsitu(insitu)
2981 varlist = vars()
2982 if not hasattr(tau, "__len__"):
2983 tau = [tau]
2984 s = scantable(self._math._opacity(self, tau))
2985 s._add_history("opacity", varlist)
2986 if insitu:
2987 self._assign(s)
2988 else:
2989 return s
2990
2991 @asaplog_post_dec
2992 def bin(self, width=5, insitu=None):
2993 """\
2994 Return a scan where all spectra have been binned up.
2995
2996 Parameters:
2997
2998 width: The bin width (default=5) in pixels
2999
3000 insitu: if False a new scantable is returned.
3001 Otherwise, the scaling is done in-situ
3002 The default is taken from .asaprc (False)
3003
3004 """
3005 if insitu is None:
3006 insitu = rcParams['insitu']
3007 self._math._setinsitu(insitu)
3008 varlist = vars()
3009 s = scantable(self._math._bin(self, width))
3010 s._add_history("bin", varlist)
3011 if insitu:
3012 self._assign(s)
3013 else:
3014 return s
3015
3016 @asaplog_post_dec
3017 def reshape(self, first, last, insitu=None):
3018 """Resize the band by providing first and last channel.
3019 This will cut off all channels outside [first, last].
3020 """
3021 if insitu is None:
3022 insitu = rcParams['insitu']
3023 varlist = vars()
3024 if last < 0:
3025 last = self.nchan()-1 + last
3026 s = None
3027 if insitu:
3028 s = self
3029 else:
3030 s = self.copy()
3031 s._reshape(first,last)
3032 s._add_history("reshape", varlist)
3033 if not insitu:
3034 return s
3035
3036 @asaplog_post_dec
3037 def resample(self, width=5, method='cubic', insitu=None):
3038 """\
3039 Return a scan where all spectra have been binned up.
3040
3041 Parameters:
3042
3043 width: The bin width (default=5) in pixels
3044
3045 method: Interpolation method when correcting from a table.
3046 Values are 'nearest', 'linear', 'cubic' (default)
3047 and 'spline'
3048
3049 insitu: if False a new scantable is returned.
3050 Otherwise, the scaling is done in-situ
3051 The default is taken from .asaprc (False)
3052
3053 """
3054 if insitu is None:
3055 insitu = rcParams['insitu']
3056 self._math._setinsitu(insitu)
3057 varlist = vars()
3058 s = scantable(self._math._resample(self, method, width))
3059 s._add_history("resample", varlist)
3060 if insitu:
3061 self._assign(s)
3062 else:
3063 return s
3064
3065 @asaplog_post_dec
3066 def average_pol(self, mask=None, weight='none'):
3067 """\
3068 Average the Polarisations together.
3069
3070 Parameters:
3071
3072 mask: An optional mask defining the region, where the
3073 averaging will be applied. The output will have all
3074 specified points masked.
3075
3076 weight: Weighting scheme. 'none' (default), 'var' (1/var(spec)
3077 weighted), or 'tsys' (1/Tsys**2 weighted)
3078
3079 """
3080 varlist = vars()
3081 mask = mask or ()
3082 s = scantable(self._math._averagepol(self, mask, weight.upper()))
3083 s._add_history("average_pol", varlist)
3084 return s
3085
3086 @asaplog_post_dec
3087 def average_beam(self, mask=None, weight='none'):
3088 """\
3089 Average the Beams together.
3090
3091 Parameters:
3092 mask: An optional mask defining the region, where the
3093 averaging will be applied. The output will have all
3094 specified points masked.
3095
3096 weight: Weighting scheme. 'none' (default), 'var' (1/var(spec)
3097 weighted), or 'tsys' (1/Tsys**2 weighted)
3098
3099 """
3100 varlist = vars()
3101 mask = mask or ()
3102 s = scantable(self._math._averagebeams(self, mask, weight.upper()))
3103 s._add_history("average_beam", varlist)
3104 return s
3105
3106 def parallactify(self, pflag):
3107 """\
3108 Set a flag to indicate whether this data should be treated as having
3109 been 'parallactified' (total phase == 0.0)
3110
3111 Parameters:
3112
3113 pflag: Bool indicating whether to turn this on (True) or
3114 off (False)
3115
3116 """
3117 varlist = vars()
3118 self._parallactify(pflag)
3119 self._add_history("parallactify", varlist)
3120
3121 @asaplog_post_dec
3122 def convert_pol(self, poltype=None):
3123 """\
3124 Convert the data to a different polarisation type.
3125 Note that you will need cross-polarisation terms for most conversions.
3126
3127 Parameters:
3128
3129 poltype: The new polarisation type. Valid types are:
3130 'linear', 'circular', 'stokes' and 'linpol'
3131
3132 """
3133 varlist = vars()
3134 s = scantable(self._math._convertpol(self, poltype))
3135 s._add_history("convert_pol", varlist)
3136 return s
3137
3138 @asaplog_post_dec
3139 def smooth(self, kernel="hanning", width=5.0, order=2, plot=False,
3140 insitu=None):
3141 """\
3142 Smooth the spectrum by the specified kernel (conserving flux).
3143
3144 Parameters:
3145
3146 kernel: The type of smoothing kernel. Select from
3147 'hanning' (default), 'gaussian', 'boxcar', 'rmedian'
3148 or 'poly'
3149
3150 width: The width of the kernel in pixels. For hanning this is
3151 ignored otherwise it defauls to 5 pixels.
3152 For 'gaussian' it is the Full Width Half
3153 Maximum. For 'boxcar' it is the full width.
3154 For 'rmedian' and 'poly' it is the half width.
3155
3156 order: Optional parameter for 'poly' kernel (default is 2), to
3157 specify the order of the polnomial. Ignored by all other
3158 kernels.
3159
3160 plot: plot the original and the smoothed spectra.
3161 In this each indivual fit has to be approved, by
3162 typing 'y' or 'n'
3163
3164 insitu: if False a new scantable is returned.
3165 Otherwise, the scaling is done in-situ
3166 The default is taken from .asaprc (False)
3167
3168 """
3169 if insitu is None: insitu = rcParams['insitu']
3170 self._math._setinsitu(insitu)
3171 varlist = vars()
3172
3173 if plot: orgscan = self.copy()
3174
3175 s = scantable(self._math._smooth(self, kernel.lower(), width, order))
3176 s._add_history("smooth", varlist)
3177
3178 action = 'H'
3179 if plot:
3180 from asap.asapplotter import new_asaplot
3181 theplot = new_asaplot(rcParams['plotter.gui'])
3182 from matplotlib import rc as rcp
3183 rcp('lines', linewidth=1)
3184 theplot.set_panels()
3185 ylab=s._get_ordinate_label()
3186 #theplot.palette(0,["#777777","red"])
3187 for r in xrange(s.nrow()):
3188 xsm=s._getabcissa(r)
3189 ysm=s._getspectrum(r)
3190 xorg=orgscan._getabcissa(r)
3191 yorg=orgscan._getspectrum(r)
3192 if action != "N": #skip plotting if rejecting all
3193 theplot.clear()
3194 theplot.hold()
3195 theplot.set_axes('ylabel',ylab)
3196 theplot.set_axes('xlabel',s._getabcissalabel(r))
3197 theplot.set_axes('title',s._getsourcename(r))
3198 theplot.set_line(label='Original',color="#777777")
3199 theplot.plot(xorg,yorg)
3200 theplot.set_line(label='Smoothed',color="red")
3201 theplot.plot(xsm,ysm)
3202 ### Ugly part for legend
3203 for i in [0,1]:
3204 theplot.subplots[0]['lines'].append(
3205 [theplot.subplots[0]['axes'].lines[i]]
3206 )
3207 theplot.release()
3208 ### Ugly part for legend
3209 theplot.subplots[0]['lines']=[]
3210 res = self._get_verify_action("Accept smoothing?",action)
3211 #print "IF%d, POL%d: got result = %s" %(s.getif(r),s.getpol(r),res)
3212 if r == 0: action = None
3213 #res = raw_input("Accept smoothing ([y]/n): ")
3214 if res.upper() == 'N':
3215 # reject for the current rows
3216 s._setspectrum(yorg, r)
3217 elif res.upper() == 'R':
3218 # reject all the following rows
3219 action = "N"
3220 s._setspectrum(yorg, r)
3221 elif res.upper() == 'A':
3222 # accept all the following rows
3223 break
3224 theplot.quit()
3225 del theplot
3226 del orgscan
3227
3228 if insitu: self._assign(s)
3229 else: return s
3230
3231 @asaplog_post_dec
3232 def regrid_channel(self, width=5, plot=False, insitu=None):
3233 """\
3234 Regrid the spectra by the specified channel width
3235
3236 Parameters:
3237
3238 width: The channel width (float) of regridded spectra
3239 in the current spectral unit.
3240
3241 plot: [NOT IMPLEMENTED YET]
3242 plot the original and the regridded spectra.
3243 In this each indivual fit has to be approved, by
3244 typing 'y' or 'n'
3245
3246 insitu: if False a new scantable is returned.
3247 Otherwise, the scaling is done in-situ
3248 The default is taken from .asaprc (False)
3249
3250 """
3251 if insitu is None: insitu = rcParams['insitu']
3252 varlist = vars()
3253
3254 if plot:
3255 asaplog.post()
3256 asaplog.push("Verification plot is not implemtnetd yet.")
3257 asaplog.post("WARN")
3258
3259 s = self.copy()
3260 s._regrid_specchan(width)
3261
3262 s._add_history("regrid_channel", varlist)
3263
3264# if plot:
3265# from asap.asapplotter import new_asaplot
3266# theplot = new_asaplot(rcParams['plotter.gui'])
3267# from matplotlib import rc as rcp
3268# rcp('lines', linewidth=1)
3269# theplot.set_panels()
3270# ylab=s._get_ordinate_label()
3271# #theplot.palette(0,["#777777","red"])
3272# for r in xrange(s.nrow()):
3273# xsm=s._getabcissa(r)
3274# ysm=s._getspectrum(r)
3275# xorg=orgscan._getabcissa(r)
3276# yorg=orgscan._getspectrum(r)
3277# theplot.clear()
3278# theplot.hold()
3279# theplot.set_axes('ylabel',ylab)
3280# theplot.set_axes('xlabel',s._getabcissalabel(r))
3281# theplot.set_axes('title',s._getsourcename(r))
3282# theplot.set_line(label='Original',color="#777777")
3283# theplot.plot(xorg,yorg)
3284# theplot.set_line(label='Smoothed',color="red")
3285# theplot.plot(xsm,ysm)
3286# ### Ugly part for legend
3287# for i in [0,1]:
3288# theplot.subplots[0]['lines'].append(
3289# [theplot.subplots[0]['axes'].lines[i]]
3290# )
3291# theplot.release()
3292# ### Ugly part for legend
3293# theplot.subplots[0]['lines']=[]
3294# res = raw_input("Accept smoothing ([y]/n): ")
3295# if res.upper() == 'N':
3296# s._setspectrum(yorg, r)
3297# theplot.quit()
3298# del theplot
3299# del orgscan
3300
3301 if insitu: self._assign(s)
3302 else: return s
3303
3304 @asaplog_post_dec
3305 def _parse_wn(self, wn):
3306 if isinstance(wn, list) or isinstance(wn, tuple):
3307 return wn
3308 elif isinstance(wn, int):
3309 return [ wn ]
3310 elif isinstance(wn, str):
3311 if '-' in wn: # case 'a-b' : return [a,a+1,...,b-1,b]
3312 val = wn.split('-')
3313 val = [int(val[0]), int(val[1])]
3314 val.sort()
3315 res = [i for i in xrange(val[0], val[1]+1)]
3316 elif wn[:2] == '<=' or wn[:2] == '=<': # cases '<=a','=<a' : return [0,1,...,a-1,a]
3317 val = int(wn[2:])+1
3318 res = [i for i in xrange(val)]
3319 elif wn[-2:] == '>=' or wn[-2:] == '=>': # cases 'a>=','a=>' : return [0,1,...,a-1,a]
3320 val = int(wn[:-2])+1
3321 res = [i for i in xrange(val)]
3322 elif wn[0] == '<': # case '<a' : return [0,1,...,a-2,a-1]
3323 val = int(wn[1:])
3324 res = [i for i in xrange(val)]
3325 elif wn[-1] == '>': # case 'a>' : return [0,1,...,a-2,a-1]
3326 val = int(wn[:-1])
3327 res = [i for i in xrange(val)]
3328 elif wn[:2] == '>=' or wn[:2] == '=>': # cases '>=a','=>a' : return [a,-999], which is
3329 # then interpreted in C++
3330 # side as [a,a+1,...,a_nyq]
3331 # (CAS-3759)
3332 val = int(wn[2:])
3333 res = [val, -999]
3334 #res = [i for i in xrange(val, self.nchan()/2+1)]
3335 elif wn[-2:] == '<=' or wn[-2:] == '=<': # cases 'a<=','a=<' : return [a,-999], which is
3336 # then interpreted in C++
3337 # side as [a,a+1,...,a_nyq]
3338 # (CAS-3759)
3339 val = int(wn[:-2])
3340 res = [val, -999]
3341 #res = [i for i in xrange(val, self.nchan()/2+1)]
3342 elif wn[0] == '>': # case '>a' : return [a+1,-999], which is
3343 # then interpreted in C++
3344 # side as [a+1,a+2,...,a_nyq]
3345 # (CAS-3759)
3346 val = int(wn[1:])+1
3347 res = [val, -999]
3348 #res = [i for i in xrange(val, self.nchan()/2+1)]
3349 elif wn[-1] == '<': # case 'a<' : return [a+1,-999], which is
3350 # then interpreted in C++
3351 # side as [a+1,a+2,...,a_nyq]
3352 # (CAS-3759)
3353 val = int(wn[:-1])+1
3354 res = [val, -999]
3355 #res = [i for i in xrange(val, self.nchan()/2+1)]
3356
3357 return res
3358 else:
3359 msg = 'wrong value given for addwn/rejwn'
3360 raise RuntimeError(msg)
3361
3362 @asaplog_post_dec
3363 def apply_bltable(self, insitu=None, retfitres=None, inbltable=None, outbltable=None, overwrite=None):
3364 """\
3365 Subtract baseline based on parameters written in Baseline Table.
3366
3367 Parameters:
3368 insitu: if True, baseline fitting/subtraction is done
3369 in-situ. If False, a new scantable with
3370 baseline subtracted is returned. Actually,
3371 format of the returned value depends on both
3372 insitu and retfitres (see below).
3373 The default is taken from .asaprc (False)
3374 retfitres: if True, the results of baseline fitting (i.e.,
3375 coefficients and rms) are returned.
3376 default is False.
3377 The format of the returned value of this
3378 function varies as follows:
3379 (1) in case insitu=True and retfitres=True:
3380 fitting result.
3381 (2) in case insitu=True and retfitres=False:
3382 None.
3383 (3) in case insitu=False and retfitres=True:
3384 a dictionary containing a new scantable
3385 (with baseline subtracted) and the fitting
3386 results.
3387 (4) in case insitu=False and retfitres=False:
3388 a new scantable (with baseline subtracted).
3389 inbltable: name of input baseline table. The row number of
3390 scantable and that of inbltable must be
3391 identical.
3392 outbltable: name of output baseline table where baseline
3393 parameters and fitting results recorded.
3394 default is ''(no output).
3395 overwrite: if True when an existing baseline table is
3396 specified for outbltable, overwrites it.
3397 Otherwise there is no harm.
3398 default is False.
3399 """
3400
3401 try:
3402 varlist = vars()
3403 if retfitres is None: retfitres = False
3404 if inbltable is None: raise ValueError("bltable missing.")
3405 if outbltable is None: outbltable = ''
3406 if overwrite is None: overwrite = False
3407
3408 if insitu is None: insitu = rcParams['insitu']
3409 if insitu:
3410 workscan = self
3411 else:
3412 workscan = self.copy()
3413
3414 sres = workscan._apply_bltable(inbltable,
3415 retfitres,
3416 outbltable,
3417 os.path.exists(outbltable),
3418 overwrite)
3419 if retfitres: res = parse_fitresult(sres)
3420
3421 workscan._add_history('apply_bltable', varlist)
3422
3423 if insitu:
3424 self._assign(workscan)
3425 if retfitres:
3426 return res
3427 else:
3428 return None
3429 else:
3430 if retfitres:
3431 return {'scantable': workscan, 'fitresults': res}
3432 else:
3433 return workscan
3434
3435 except RuntimeError, e:
3436 raise_fitting_failure_exception(e)
3437
3438 @asaplog_post_dec
3439 def sub_baseline(self, insitu=None, retfitres=None, blinfo=None, bltable=None, overwrite=None):
3440 """\
3441 Subtract baseline based on parameters written in the input list.
3442
3443 Parameters:
3444 insitu: if True, baseline fitting/subtraction is done
3445 in-situ. If False, a new scantable with
3446 baseline subtracted is returned. Actually,
3447 format of the returned value depends on both
3448 insitu and retfitres (see below).
3449 The default is taken from .asaprc (False)
3450 retfitres: if True, the results of baseline fitting (i.e.,
3451 coefficients and rms) are returned.
3452 default is False.
3453 The format of the returned value of this
3454 function varies as follows:
3455 (1) in case insitu=True and retfitres=True:
3456 fitting result.
3457 (2) in case insitu=True and retfitres=False:
3458 None.
3459 (3) in case insitu=False and retfitres=True:
3460 a dictionary containing a new scantable
3461 (with baseline subtracted) and the fitting
3462 results.
3463 (4) in case insitu=False and retfitres=False:
3464 a new scantable (with baseline subtracted).
3465 blinfo: baseline parameter set stored in a dictionary
3466 or a list of dictionary. Each dictionary
3467 corresponds to each spectrum and must contain
3468 the following keys and values:
3469 'row': row number,
3470 'blfunc': function name. available ones include
3471 'poly', 'chebyshev', 'cspline' and
3472 'sinusoid',
3473 'order': maximum order of polynomial. needed
3474 if blfunc='poly' or 'chebyshev',
3475 'npiece': number or piecewise polynomial.
3476 needed if blfunc='cspline',
3477 'nwave': a list of sinusoidal wave numbers.
3478 needed if blfunc='sinusoid', and
3479 'masklist': min-max windows for channel mask.
3480 the specified ranges will be used
3481 for fitting.
3482 bltable: name of output baseline table where baseline
3483 parameters and fitting results recorded.
3484 default is ''(no output).
3485 overwrite: if True when an existing baseline table is
3486 specified for bltable, overwrites it.
3487 Otherwise there is no harm.
3488 default is False.
3489
3490 Example:
3491 sub_baseline(blinfo=[{'row':0, 'blfunc':'poly', 'order':5,
3492 'masklist':[[10,350],[352,510]]},
3493 {'row':1, 'blfunc':'cspline', 'npiece':3,
3494 'masklist':[[3,16],[19,404],[407,511]]}
3495 ])
3496
3497 the first spectrum (row=0) will be fitted with polynomial
3498 of order=5 and the next one (row=1) will be fitted with cubic
3499 spline consisting of 3 pieces.
3500 """
3501
3502 try:
3503 varlist = vars()
3504 if retfitres is None: retfitres = False
3505 if blinfo is None: blinfo = []
3506 if bltable is None: bltable = ''
3507 if overwrite is None: overwrite = False
3508
3509 if insitu is None: insitu = rcParams['insitu']
3510 if insitu:
3511 workscan = self
3512 else:
3513 workscan = self.copy()
3514
3515 nrow = workscan.nrow()
3516
3517 in_blinfo = pack_blinfo(blinfo=blinfo, maxirow=nrow)
3518
3519 sres = workscan._sub_baseline(in_blinfo,
3520 retfitres,
3521 bltable,
3522 os.path.exists(bltable),
3523 overwrite)
3524 if retfitres: res = parse_fitresult(sres)
3525
3526 workscan._add_history('sub_baseline', varlist)
3527
3528 if insitu:
3529 self._assign(workscan)
3530 if retfitres:
3531 return res
3532 else:
3533 return None
3534 else:
3535 if retfitres:
3536 return {'scantable': workscan, 'fitresults': res}
3537 else:
3538 return workscan
3539
3540 except RuntimeError, e:
3541 raise_fitting_failure_exception(e)
3542
3543 @asaplog_post_dec
3544 def calc_aic(self, value=None, blfunc=None, order=None, mask=None,
3545 whichrow=None, uselinefinder=None, edge=None,
3546 threshold=None, chan_avg_limit=None):
3547 """\
3548 Calculates and returns model selection criteria for a specified
3549 baseline model and a given spectrum data.
3550 Available values include Akaike Information Criterion (AIC), the
3551 corrected Akaike Information Criterion (AICc) by Sugiura(1978),
3552 Bayesian Information Criterion (BIC) and the Generalised Cross
3553 Validation (GCV).
3554
3555 Parameters:
3556 value: name of model selection criteria to calculate.
3557 available ones include 'aic', 'aicc', 'bic' and
3558 'gcv'. default is 'aicc'.
3559 blfunc: baseline function name. available ones include
3560 'chebyshev', 'cspline' and 'sinusoid'.
3561 default is 'chebyshev'.
3562 order: parameter for basline function. actually stands for
3563 order of polynomial (order) for 'chebyshev',
3564 number of spline pieces (npiece) for 'cspline' and
3565 maximum wave number for 'sinusoid', respectively.
3566 default is 5 (which is also the default order value
3567 for [auto_]chebyshev_baseline()).
3568 mask: an optional mask. default is [].
3569 whichrow: row number. default is 0 (the first row)
3570 uselinefinder: use sd.linefinder() to flag out line regions
3571 default is True.
3572 edge: an optional number of channel to drop at
3573 the edge of spectrum. If only one value is
3574 specified, the same number will be dropped
3575 from both sides of the spectrum. Default
3576 is to keep all channels. Nested tuples
3577 represent individual edge selection for
3578 different IFs (a number of spectral channels
3579 can be different)
3580 default is (0, 0).
3581 threshold: the threshold used by line finder. It is
3582 better to keep it large as only strong lines
3583 affect the baseline solution.
3584 default is 3.
3585 chan_avg_limit: a maximum number of consequtive spectral
3586 channels to average during the search of
3587 weak and broad lines. The default is no
3588 averaging (and no search for weak lines).
3589 If such lines can affect the fitted baseline
3590 (e.g. a high order polynomial is fitted),
3591 increase this parameter (usually values up
3592 to 8 are reasonable). Most users of this
3593 method should find the default value sufficient.
3594 default is 1.
3595
3596 Example:
3597 aic = scan.calc_aic(blfunc='chebyshev', order=5, whichrow=0)
3598 """
3599
3600 try:
3601 varlist = vars()
3602
3603 if value is None: value = 'aicc'
3604 if blfunc is None: blfunc = 'chebyshev'
3605 if order is None: order = 5
3606 if mask is None: mask = []
3607 if whichrow is None: whichrow = 0
3608 if uselinefinder is None: uselinefinder = True
3609 if edge is None: edge = (0, 0)
3610 if threshold is None: threshold = 3
3611 if chan_avg_limit is None: chan_avg_limit = 1
3612
3613 return self._calc_aic(value, blfunc, order, mask,
3614 whichrow, uselinefinder, edge,
3615 threshold, chan_avg_limit)
3616
3617 except RuntimeError, e:
3618 raise_fitting_failure_exception(e)
3619
3620 @asaplog_post_dec
3621 def sinusoid_baseline(self, mask=None, applyfft=None,
3622 fftmethod=None, fftthresh=None,
3623 addwn=None, rejwn=None,
3624 insitu=None,
3625 clipthresh=None, clipniter=None,
3626 plot=None,
3627 getresidual=None,
3628 showprogress=None, minnrow=None,
3629 outlog=None,
3630 blfile=None, csvformat=None,
3631 bltable=None):
3632 """\
3633 Return a scan which has been baselined (all rows) with sinusoidal
3634 functions.
3635
3636 Parameters:
3637 mask: an optional mask
3638 applyfft: if True use some method, such as FFT, to find
3639 strongest sinusoidal components in the wavenumber
3640 domain to be used for baseline fitting.
3641 default is True.
3642 fftmethod: method to find the strong sinusoidal components.
3643 now only 'fft' is available and it is the default.
3644 fftthresh: the threshold to select wave numbers to be used for
3645 fitting from the distribution of amplitudes in the
3646 wavenumber domain.
3647 both float and string values accepted.
3648 given a float value, the unit is set to sigma.
3649 for string values, allowed formats include:
3650 'xsigma' or 'x' (= x-sigma level. e.g.,
3651 '3sigma'), or
3652 'topx' (= the x strongest ones, e.g. 'top5').
3653 default is 3.0 (unit: sigma).
3654 addwn: the additional wave numbers to be used for fitting.
3655 list or integer value is accepted to specify every
3656 wave numbers. also string value can be used in case
3657 you need to specify wave numbers in a certain range,
3658 e.g., 'a-b' (= a, a+1, a+2, ..., b-1, b),
3659 '<a' (= 0,1,...,a-2,a-1),
3660 '>=a' (= a, a+1, ... up to the maximum wave
3661 number corresponding to the Nyquist
3662 frequency for the case of FFT).
3663 default is [0].
3664 rejwn: the wave numbers NOT to be used for fitting.
3665 can be set just as addwn but has higher priority:
3666 wave numbers which are specified both in addwn
3667 and rejwn will NOT be used. default is [].
3668 insitu: if False a new scantable is returned.
3669 Otherwise, the scaling is done in-situ
3670 The default is taken from .asaprc (False)
3671 clipthresh: Clipping threshold. (default is 3.0, unit: sigma)
3672 clipniter: maximum number of iteration of 'clipthresh'-sigma
3673 clipping (default is 0)
3674 plot: *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
3675 plot the fit and the residual. In this each
3676 indivual fit has to be approved, by typing 'y'
3677 or 'n'
3678 getresidual: if False, returns best-fit values instead of
3679 residual. (default is True)
3680 showprogress: show progress status for large data.
3681 default is True.
3682 minnrow: minimum number of input spectra to show.
3683 default is 1000.
3684 outlog: Output the coefficients of the best-fit
3685 function to logger (default is False)
3686 blfile: Name of a text file in which the best-fit
3687 parameter values to be written
3688 (default is '': no file/logger output)
3689 csvformat: if True blfile is csv-formatted, default is False.
3690 bltable: name of a baseline table where fitting results
3691 (coefficients, rms, etc.) are to be written.
3692 if given, fitting results will NOT be output to
3693 scantable (insitu=True) or None will be
3694 returned (insitu=False).
3695 (default is "": no table output)
3696
3697 Example:
3698 # return a scan baselined by a combination of sinusoidal curves
3699 # having wave numbers in spectral window up to 10,
3700 # also with 3-sigma clipping, iteration up to 4 times
3701 bscan = scan.sinusoid_baseline(addwn='<=10',clipthresh=3.0,clipniter=4)
3702
3703 Note:
3704 The best-fit parameter values output in logger and/or blfile are now
3705 based on specunit of 'channel'.
3706 """
3707
3708 try:
3709 varlist = vars()
3710
3711 if insitu is None: insitu = rcParams['insitu']
3712 if insitu:
3713 workscan = self
3714 else:
3715 workscan = self.copy()
3716
3717 if mask is None: mask = []
3718 if applyfft is None: applyfft = True
3719 if fftmethod is None: fftmethod = 'fft'
3720 if fftthresh is None: fftthresh = 3.0
3721 if addwn is None: addwn = [0]
3722 if rejwn is None: rejwn = []
3723 if clipthresh is None: clipthresh = 3.0
3724 if clipniter is None: clipniter = 0
3725 if plot is None: plot = False
3726 if getresidual is None: getresidual = True
3727 if showprogress is None: showprogress = True
3728 if minnrow is None: minnrow = 1000
3729 if outlog is None: outlog = False
3730 if blfile is None: blfile = ''
3731 if csvformat is None: csvformat = False
3732 if bltable is None: bltable = ''
3733
3734 sapplyfft = 'true' if applyfft else 'false'
3735 fftinfo = ','.join([sapplyfft, fftmethod.lower(), str(fftthresh).lower()])
3736
3737 scsvformat = 'T' if csvformat else 'F'
3738
3739 #CURRENTLY, PLOT=true is UNAVAILABLE UNTIL sinusoidal fitting is implemented as a fitter method.
3740 workscan._sinusoid_baseline(mask,
3741 fftinfo,
3742 #applyfft, fftmethod.lower(),
3743 #str(fftthresh).lower(),
3744 workscan._parse_wn(addwn),
3745 workscan._parse_wn(rejwn),
3746 clipthresh, clipniter,
3747 getresidual,
3748 pack_progress_params(showprogress,
3749 minnrow),
3750 outlog, scsvformat+blfile,
3751 bltable)
3752 workscan._add_history('sinusoid_baseline', varlist)
3753
3754 if bltable == '':
3755 if insitu:
3756 self._assign(workscan)
3757 else:
3758 return workscan
3759 else:
3760 if not insitu:
3761 return None
3762
3763 except RuntimeError, e:
3764 raise_fitting_failure_exception(e)
3765
3766
3767 @asaplog_post_dec
3768 def auto_sinusoid_baseline(self, mask=None, applyfft=None,
3769 fftmethod=None, fftthresh=None,
3770 addwn=None, rejwn=None,
3771 insitu=None,
3772 clipthresh=None, clipniter=None,
3773 edge=None, threshold=None, chan_avg_limit=None,
3774 plot=None,
3775 getresidual=None,
3776 showprogress=None, minnrow=None,
3777 outlog=None,
3778 blfile=None, csvformat=None,
3779 bltable=None):
3780 """\
3781 Return a scan which has been baselined (all rows) with sinusoidal
3782 functions.
3783 Spectral lines are detected first using linefinder and masked out
3784 to avoid them affecting the baseline solution.
3785
3786 Parameters:
3787 mask: an optional mask retreived from scantable
3788 applyfft: if True use some method, such as FFT, to find
3789 strongest sinusoidal components in the wavenumber
3790 domain to be used for baseline fitting.
3791 default is True.
3792 fftmethod: method to find the strong sinusoidal components.
3793 now only 'fft' is available and it is the default.
3794 fftthresh: the threshold to select wave numbers to be used for
3795 fitting from the distribution of amplitudes in the
3796 wavenumber domain.
3797 both float and string values accepted.
3798 given a float value, the unit is set to sigma.
3799 for string values, allowed formats include:
3800 'xsigma' or 'x' (= x-sigma level. e.g.,
3801 '3sigma'), or
3802 'topx' (= the x strongest ones, e.g. 'top5').
3803 default is 3.0 (unit: sigma).
3804 addwn: the additional wave numbers to be used for fitting.
3805 list or integer value is accepted to specify every
3806 wave numbers. also string value can be used in case
3807 you need to specify wave numbers in a certain range,
3808 e.g., 'a-b' (= a, a+1, a+2, ..., b-1, b),
3809 '<a' (= 0,1,...,a-2,a-1),
3810 '>=a' (= a, a+1, ... up to the maximum wave
3811 number corresponding to the Nyquist
3812 frequency for the case of FFT).
3813 default is [0].
3814 rejwn: the wave numbers NOT to be used for fitting.
3815 can be set just as addwn but has higher priority:
3816 wave numbers which are specified both in addwn
3817 and rejwn will NOT be used. default is [].
3818 insitu: if False a new scantable is returned.
3819 Otherwise, the scaling is done in-situ
3820 The default is taken from .asaprc (False)
3821 clipthresh: Clipping threshold. (default is 3.0, unit: sigma)
3822 clipniter: maximum number of iteration of 'clipthresh'-sigma
3823 clipping (default is 0)
3824 edge: an optional number of channel to drop at
3825 the edge of spectrum. If only one value is
3826 specified, the same number will be dropped
3827 from both sides of the spectrum. Default
3828 is to keep all channels. Nested tuples
3829 represent individual edge selection for
3830 different IFs (a number of spectral channels
3831 can be different)
3832 threshold: the threshold used by line finder. It is
3833 better to keep it large as only strong lines
3834 affect the baseline solution.
3835 chan_avg_limit: a maximum number of consequtive spectral
3836 channels to average during the search of
3837 weak and broad lines. The default is no
3838 averaging (and no search for weak lines).
3839 If such lines can affect the fitted baseline
3840 (e.g. a high order polynomial is fitted),
3841 increase this parameter (usually values up
3842 to 8 are reasonable). Most users of this
3843 method should find the default value sufficient.
3844 plot: *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
3845 plot the fit and the residual. In this each
3846 indivual fit has to be approved, by typing 'y'
3847 or 'n'
3848 getresidual: if False, returns best-fit values instead of
3849 residual. (default is True)
3850 showprogress: show progress status for large data.
3851 default is True.
3852 minnrow: minimum number of input spectra to show.
3853 default is 1000.
3854 outlog: Output the coefficients of the best-fit
3855 function to logger (default is False)
3856 blfile: Name of a text file in which the best-fit
3857 parameter values to be written
3858 (default is "": no file/logger output)
3859 csvformat: if True blfile is csv-formatted, default is False.
3860 bltable: name of a baseline table where fitting results
3861 (coefficients, rms, etc.) are to be written.
3862 if given, fitting results will NOT be output to
3863 scantable (insitu=True) or None will be
3864 returned (insitu=False).
3865 (default is "": no table output)
3866
3867 Example:
3868 bscan = scan.auto_sinusoid_baseline(addwn='<=10', insitu=False)
3869
3870 Note:
3871 The best-fit parameter values output in logger and/or blfile are now
3872 based on specunit of 'channel'.
3873 """
3874
3875 try:
3876 varlist = vars()
3877
3878 if insitu is None: insitu = rcParams['insitu']
3879 if insitu:
3880 workscan = self
3881 else:
3882 workscan = self.copy()
3883
3884 if mask is None: mask = []
3885 if applyfft is None: applyfft = True
3886 if fftmethod is None: fftmethod = 'fft'
3887 if fftthresh is None: fftthresh = 3.0
3888 if addwn is None: addwn = [0]
3889 if rejwn is None: rejwn = []
3890 if clipthresh is None: clipthresh = 3.0
3891 if clipniter is None: clipniter = 0
3892 if edge is None: edge = (0,0)
3893 if threshold is None: threshold = 3
3894 if chan_avg_limit is None: chan_avg_limit = 1
3895 if plot is None: plot = False
3896 if getresidual is None: getresidual = True
3897 if showprogress is None: showprogress = True
3898 if minnrow is None: minnrow = 1000
3899 if outlog is None: outlog = False
3900 if blfile is None: blfile = ''
3901 if csvformat is None: csvformat = False
3902 if bltable is None: bltable = ''
3903
3904 sapplyfft = 'true' if applyfft else 'false'
3905 fftinfo = ','.join([sapplyfft, fftmethod.lower(), str(fftthresh).lower()])
3906
3907 scsvformat = 'T' if csvformat else 'F'
3908
3909 #CURRENTLY, PLOT=true is UNAVAILABLE UNTIL sinusoidal fitting is implemented as a fitter method.
3910 workscan._auto_sinusoid_baseline(mask,
3911 fftinfo,
3912 workscan._parse_wn(addwn),
3913 workscan._parse_wn(rejwn),
3914 clipthresh, clipniter,
3915 normalise_edge_param(edge),
3916 threshold, chan_avg_limit,
3917 getresidual,
3918 pack_progress_params(showprogress,
3919 minnrow),
3920 outlog, scsvformat+blfile, bltable)
3921 workscan._add_history("auto_sinusoid_baseline", varlist)
3922
3923 if bltable == '':
3924 if insitu:
3925 self._assign(workscan)
3926 else:
3927 return workscan
3928 else:
3929 if not insitu:
3930 return None
3931
3932 except RuntimeError, e:
3933 raise_fitting_failure_exception(e)
3934
3935 @asaplog_post_dec
3936 def cspline_baseline(self, mask=None, npiece=None, insitu=None,
3937 clipthresh=None, clipniter=None, plot=None,
3938 getresidual=None, showprogress=None, minnrow=None,
3939 outlog=None, blfile=None, csvformat=None,
3940 bltable=None):
3941 """\
3942 Return a scan which has been baselined (all rows) by cubic spline
3943 function (piecewise cubic polynomial).
3944
3945 Parameters:
3946 mask: An optional mask
3947 npiece: Number of pieces. (default is 2)
3948 insitu: If False a new scantable is returned.
3949 Otherwise, the scaling is done in-situ
3950 The default is taken from .asaprc (False)
3951 clipthresh: Clipping threshold. (default is 3.0, unit: sigma)
3952 clipniter: maximum number of iteration of 'clipthresh'-sigma
3953 clipping (default is 0)
3954 plot: *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
3955 plot the fit and the residual. In this each
3956 indivual fit has to be approved, by typing 'y'
3957 or 'n'
3958 getresidual: if False, returns best-fit values instead of
3959 residual. (default is True)
3960 showprogress: show progress status for large data.
3961 default is True.
3962 minnrow: minimum number of input spectra to show.
3963 default is 1000.
3964 outlog: Output the coefficients of the best-fit
3965 function to logger (default is False)
3966 blfile: Name of a text file in which the best-fit
3967 parameter values to be written
3968 (default is "": no file/logger output)
3969 csvformat: if True blfile is csv-formatted, default is False.
3970 bltable: name of a baseline table where fitting results
3971 (coefficients, rms, etc.) are to be written.
3972 if given, fitting results will NOT be output to
3973 scantable (insitu=True) or None will be
3974 returned (insitu=False).
3975 (default is "": no table output)
3976
3977 Example:
3978 # return a scan baselined by a cubic spline consisting of 2 pieces
3979 # (i.e., 1 internal knot),
3980 # also with 3-sigma clipping, iteration up to 4 times
3981 bscan = scan.cspline_baseline(npiece=2,clipthresh=3.0,clipniter=4)
3982
3983 Note:
3984 The best-fit parameter values output in logger and/or blfile are now
3985 based on specunit of 'channel'.
3986 """
3987
3988 try:
3989 varlist = vars()
3990
3991 if insitu is None: insitu = rcParams['insitu']
3992 if insitu:
3993 workscan = self
3994 else:
3995 workscan = self.copy()
3996
3997 if mask is None: mask = []
3998 if npiece is None: npiece = 2
3999 if clipthresh is None: clipthresh = 3.0
4000 if clipniter is None: clipniter = 0
4001 if plot is None: plot = False
4002 if getresidual is None: getresidual = True
4003 if showprogress is None: showprogress = True
4004 if minnrow is None: minnrow = 1000
4005 if outlog is None: outlog = False
4006 if blfile is None: blfile = ''
4007 if csvformat is None: csvformat = False
4008 if bltable is None: bltable = ''
4009
4010 scsvformat = 'T' if csvformat else 'F'
4011
4012 #CURRENTLY, PLOT=true UNAVAILABLE UNTIL cubic spline fitting is implemented as a fitter method.
4013 workscan._cspline_baseline(mask, npiece,
4014 clipthresh, clipniter,
4015 getresidual,
4016 pack_progress_params(showprogress,
4017 minnrow),
4018 outlog, scsvformat+blfile,
4019 bltable)
4020 workscan._add_history("cspline_baseline", varlist)
4021
4022 if bltable == '':
4023 if insitu:
4024 self._assign(workscan)
4025 else:
4026 return workscan
4027 else:
4028 if not insitu:
4029 return None
4030
4031 except RuntimeError, e:
4032 raise_fitting_failure_exception(e)
4033
4034 @asaplog_post_dec
4035 def auto_cspline_baseline(self, mask=None, npiece=None, insitu=None,
4036 clipthresh=None, clipniter=None,
4037 edge=None, threshold=None, chan_avg_limit=None,
4038 getresidual=None, plot=None,
4039 showprogress=None, minnrow=None, outlog=None,
4040 blfile=None, csvformat=None, bltable=None):
4041 """\
4042 Return a scan which has been baselined (all rows) by cubic spline
4043 function (piecewise cubic polynomial).
4044 Spectral lines are detected first using linefinder and masked out
4045 to avoid them affecting the baseline solution.
4046
4047 Parameters:
4048 mask: an optional mask retreived from scantable
4049 npiece: Number of pieces. (default is 2)
4050 insitu: if False a new scantable is returned.
4051 Otherwise, the scaling is done in-situ
4052 The default is taken from .asaprc (False)
4053 clipthresh: Clipping threshold. (default is 3.0, unit: sigma)
4054 clipniter: maximum number of iteration of 'clipthresh'-sigma
4055 clipping (default is 0)
4056 edge: an optional number of channel to drop at
4057 the edge of spectrum. If only one value is
4058 specified, the same number will be dropped
4059 from both sides of the spectrum. Default
4060 is to keep all channels. Nested tuples
4061 represent individual edge selection for
4062 different IFs (a number of spectral channels
4063 can be different)
4064 threshold: the threshold used by line finder. It is
4065 better to keep it large as only strong lines
4066 affect the baseline solution.
4067 chan_avg_limit: a maximum number of consequtive spectral
4068 channels to average during the search of
4069 weak and broad lines. The default is no
4070 averaging (and no search for weak lines).
4071 If such lines can affect the fitted baseline
4072 (e.g. a high order polynomial is fitted),
4073 increase this parameter (usually values up
4074 to 8 are reasonable). Most users of this
4075 method should find the default value sufficient.
4076 plot: *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
4077 plot the fit and the residual. In this each
4078 indivual fit has to be approved, by typing 'y'
4079 or 'n'
4080 getresidual: if False, returns best-fit values instead of
4081 residual. (default is True)
4082 showprogress: show progress status for large data.
4083 default is True.
4084 minnrow: minimum number of input spectra to show.
4085 default is 1000.
4086 outlog: Output the coefficients of the best-fit
4087 function to logger (default is False)
4088 blfile: Name of a text file in which the best-fit
4089 parameter values to be written
4090 (default is "": no file/logger output)
4091 csvformat: if True blfile is csv-formatted, default is False.
4092 bltable: name of a baseline table where fitting results
4093 (coefficients, rms, etc.) are to be written.
4094 if given, fitting results will NOT be output to
4095 scantable (insitu=True) or None will be
4096 returned (insitu=False).
4097 (default is "": no table output)
4098
4099 Example:
4100 bscan = scan.auto_cspline_baseline(npiece=3, insitu=False)
4101
4102 Note:
4103 The best-fit parameter values output in logger and/or blfile are now
4104 based on specunit of 'channel'.
4105 """
4106
4107 try:
4108 varlist = vars()
4109
4110 if insitu is None: insitu = rcParams['insitu']
4111 if insitu:
4112 workscan = self
4113 else:
4114 workscan = self.copy()
4115
4116 #if mask is None: mask = [True for i in xrange(workscan.nchan())]
4117 if mask is None: mask = []
4118 if npiece is None: npiece = 2
4119 if clipthresh is None: clipthresh = 3.0
4120 if clipniter is None: clipniter = 0
4121 if edge is None: edge = (0, 0)
4122 if threshold is None: threshold = 3
4123 if chan_avg_limit is None: chan_avg_limit = 1
4124 if plot is None: plot = False
4125 if getresidual is None: getresidual = True
4126 if showprogress is None: showprogress = True
4127 if minnrow is None: minnrow = 1000
4128 if outlog is None: outlog = False
4129 if blfile is None: blfile = ''
4130 if csvformat is None: csvformat = False
4131 if bltable is None: bltable = ''
4132
4133 scsvformat = 'T' if csvformat else 'F'
4134
4135 #CURRENTLY, PLOT=true UNAVAILABLE UNTIL cubic spline fitting is implemented as a fitter method.
4136 workscan._auto_cspline_baseline(mask, npiece,
4137 clipthresh, clipniter,
4138 normalise_edge_param(edge),
4139 threshold,
4140 chan_avg_limit, getresidual,
4141 pack_progress_params(showprogress,
4142 minnrow),
4143 outlog,
4144 scsvformat+blfile,
4145 bltable)
4146 workscan._add_history("auto_cspline_baseline", varlist)
4147
4148 if bltable == '':
4149 if insitu:
4150 self._assign(workscan)
4151 else:
4152 return workscan
4153 else:
4154 if not insitu:
4155 return None
4156
4157 except RuntimeError, e:
4158 raise_fitting_failure_exception(e)
4159
4160 @asaplog_post_dec
4161 def chebyshev_baseline(self, mask=None, order=None, insitu=None,
4162 clipthresh=None, clipniter=None, plot=None,
4163 getresidual=None, showprogress=None, minnrow=None,
4164 outlog=None, blfile=None, csvformat=None,
4165 bltable=None):
4166 """\
4167 Return a scan which has been baselined (all rows) by Chebyshev polynomials.
4168
4169 Parameters:
4170 mask: An optional mask
4171 order: the maximum order of Chebyshev polynomial (default is 5)
4172 insitu: If False a new scantable is returned.
4173 Otherwise, the scaling is done in-situ
4174 The default is taken from .asaprc (False)
4175 clipthresh: Clipping threshold. (default is 3.0, unit: sigma)
4176 clipniter: maximum number of iteration of 'clipthresh'-sigma
4177 clipping (default is 0)
4178 plot: *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
4179 plot the fit and the residual. In this each
4180 indivual fit has to be approved, by typing 'y'
4181 or 'n'
4182 getresidual: if False, returns best-fit values instead of
4183 residual. (default is True)
4184 showprogress: show progress status for large data.
4185 default is True.
4186 minnrow: minimum number of input spectra to show.
4187 default is 1000.
4188 outlog: Output the coefficients of the best-fit
4189 function to logger (default is False)
4190 blfile: Name of a text file in which the best-fit
4191 parameter values to be written
4192 (default is "": no file/logger output)
4193 csvformat: if True blfile is csv-formatted, default is False.
4194 bltable: name of a baseline table where fitting results
4195 (coefficients, rms, etc.) are to be written.
4196 if given, fitting results will NOT be output to
4197 scantable (insitu=True) or None will be
4198 returned (insitu=False).
4199 (default is "": no table output)
4200
4201 Example:
4202 # return a scan baselined by a cubic spline consisting of 2 pieces
4203 # (i.e., 1 internal knot),
4204 # also with 3-sigma clipping, iteration up to 4 times
4205 bscan = scan.cspline_baseline(npiece=2,clipthresh=3.0,clipniter=4)
4206
4207 Note:
4208 The best-fit parameter values output in logger and/or blfile are now
4209 based on specunit of 'channel'.
4210 """
4211
4212 try:
4213 varlist = vars()
4214
4215 if insitu is None: insitu = rcParams['insitu']
4216 if insitu:
4217 workscan = self
4218 else:
4219 workscan = self.copy()
4220
4221 if mask is None: mask = []
4222 if order is None: order = 5
4223 if clipthresh is None: clipthresh = 3.0
4224 if clipniter is None: clipniter = 0
4225 if plot is None: plot = False
4226 if getresidual is None: getresidual = True
4227 if showprogress is None: showprogress = True
4228 if minnrow is None: minnrow = 1000
4229 if outlog is None: outlog = False
4230 if blfile is None: blfile = ''
4231 if csvformat is None: csvformat = False
4232 if bltable is None: bltable = ''
4233
4234 scsvformat = 'T' if csvformat else 'F'
4235
4236 #CURRENTLY, PLOT=true UNAVAILABLE UNTIL cubic spline fitting is implemented as a fitter method.
4237 workscan._chebyshev_baseline(mask, order,
4238 clipthresh, clipniter,
4239 getresidual,
4240 pack_progress_params(showprogress,
4241 minnrow),
4242 outlog, scsvformat+blfile,
4243 bltable)
4244 workscan._add_history("chebyshev_baseline", varlist)
4245
4246 if bltable == '':
4247 if insitu:
4248 self._assign(workscan)
4249 else:
4250 return workscan
4251 else:
4252 if not insitu:
4253 return None
4254
4255 except RuntimeError, e:
4256 raise_fitting_failure_exception(e)
4257
4258 @asaplog_post_dec
4259 def auto_chebyshev_baseline(self, mask=None, order=None, insitu=None,
4260 clipthresh=None, clipniter=None,
4261 edge=None, threshold=None, chan_avg_limit=None,
4262 getresidual=None, plot=None,
4263 showprogress=None, minnrow=None, outlog=None,
4264 blfile=None, csvformat=None, bltable=None):
4265 """\
4266 Return a scan which has been baselined (all rows) by Chebyshev polynomials.
4267 Spectral lines are detected first using linefinder and masked out
4268 to avoid them affecting the baseline solution.
4269
4270 Parameters:
4271 mask: an optional mask retreived from scantable
4272 order: the maximum order of Chebyshev polynomial (default is 5)
4273 insitu: if False a new scantable is returned.
4274 Otherwise, the scaling is done in-situ
4275 The default is taken from .asaprc (False)
4276 clipthresh: Clipping threshold. (default is 3.0, unit: sigma)
4277 clipniter: maximum number of iteration of 'clipthresh'-sigma
4278 clipping (default is 0)
4279 edge: an optional number of channel to drop at
4280 the edge of spectrum. If only one value is
4281 specified, the same number will be dropped
4282 from both sides of the spectrum. Default
4283 is to keep all channels. Nested tuples
4284 represent individual edge selection for
4285 different IFs (a number of spectral channels
4286 can be different)
4287 threshold: the threshold used by line finder. It is
4288 better to keep it large as only strong lines
4289 affect the baseline solution.
4290 chan_avg_limit: a maximum number of consequtive spectral
4291 channels to average during the search of
4292 weak and broad lines. The default is no
4293 averaging (and no search for weak lines).
4294 If such lines can affect the fitted baseline
4295 (e.g. a high order polynomial is fitted),
4296 increase this parameter (usually values up
4297 to 8 are reasonable). Most users of this
4298 method should find the default value sufficient.
4299 plot: *** CURRENTLY UNAVAILABLE, ALWAYS FALSE ***
4300 plot the fit and the residual. In this each
4301 indivual fit has to be approved, by typing 'y'
4302 or 'n'
4303 getresidual: if False, returns best-fit values instead of
4304 residual. (default is True)
4305 showprogress: show progress status for large data.
4306 default is True.
4307 minnrow: minimum number of input spectra to show.
4308 default is 1000.
4309 outlog: Output the coefficients of the best-fit
4310 function to logger (default is False)
4311 blfile: Name of a text file in which the best-fit
4312 parameter values to be written
4313 (default is "": no file/logger output)
4314 csvformat: if True blfile is csv-formatted, default is False.
4315 bltable: name of a baseline table where fitting results
4316 (coefficients, rms, etc.) are to be written.
4317 if given, fitting results will NOT be output to
4318 scantable (insitu=True) or None will be
4319 returned (insitu=False).
4320 (default is "": no table output)
4321
4322 Example:
4323 bscan = scan.auto_cspline_baseline(npiece=3, insitu=False)
4324
4325 Note:
4326 The best-fit parameter values output in logger and/or blfile are now
4327 based on specunit of 'channel'.
4328 """
4329
4330 try:
4331 varlist = vars()
4332
4333 if insitu is None: insitu = rcParams['insitu']
4334 if insitu:
4335 workscan = self
4336 else:
4337 workscan = self.copy()
4338
4339 if mask is None: mask = []
4340 if order is None: order = 5
4341 if clipthresh is None: clipthresh = 3.0
4342 if clipniter is None: clipniter = 0
4343 if edge is None: edge = (0, 0)
4344 if threshold is None: threshold = 3
4345 if chan_avg_limit is None: chan_avg_limit = 1
4346 if plot is None: plot = False
4347 if getresidual is None: getresidual = True
4348 if showprogress is None: showprogress = True
4349 if minnrow is None: minnrow = 1000
4350 if outlog is None: outlog = False
4351 if blfile is None: blfile = ''
4352 if csvformat is None: csvformat = False
4353 if bltable is None: bltable = ''
4354
4355 scsvformat = 'T' if csvformat else 'F'
4356
4357 #CURRENTLY, PLOT=true UNAVAILABLE UNTIL cubic spline fitting is implemented as a fitter method.
4358 workscan._auto_chebyshev_baseline(mask, order,
4359 clipthresh, clipniter,
4360 normalise_edge_param(edge),
4361 threshold,
4362 chan_avg_limit, getresidual,
4363 pack_progress_params(showprogress,
4364 minnrow),
4365 outlog, scsvformat+blfile,
4366 bltable)
4367 workscan._add_history("auto_chebyshev_baseline", varlist)
4368
4369 if bltable == '':
4370 if insitu:
4371 self._assign(workscan)
4372 else:
4373 return workscan
4374 else:
4375 if not insitu:
4376 return None
4377
4378 except RuntimeError, e:
4379 raise_fitting_failure_exception(e)
4380
4381 @asaplog_post_dec
4382 def poly_baseline(self, mask=None, order=None, insitu=None,
4383 clipthresh=None, clipniter=None, plot=None,
4384 getresidual=None, showprogress=None, minnrow=None,
4385 outlog=None, blfile=None, csvformat=None,
4386 bltable=None):
4387 """\
4388 Return a scan which has been baselined (all rows) by a polynomial.
4389 Parameters:
4390 mask: an optional mask
4391 order: the order of the polynomial (default is 0)
4392 insitu: if False a new scantable is returned.
4393 Otherwise, the scaling is done in-situ
4394 The default is taken from .asaprc (False)
4395 clipthresh: Clipping threshold. (default is 3.0, unit: sigma)
4396 clipniter: maximum number of iteration of 'clipthresh'-sigma
4397 clipping (default is 0)
4398 plot: plot the fit and the residual. In this each
4399 indivual fit has to be approved, by typing 'y'
4400 or 'n'
4401 getresidual: if False, returns best-fit values instead of
4402 residual. (default is True)
4403 showprogress: show progress status for large data.
4404 default is True.
4405 minnrow: minimum number of input spectra to show.
4406 default is 1000.
4407 outlog: Output the coefficients of the best-fit
4408 function to logger (default is False)
4409 blfile: Name of a text file in which the best-fit
4410 parameter values to be written
4411 (default is "": no file/logger output)
4412 csvformat: if True blfile is csv-formatted, default is False.
4413 bltable: name of a baseline table where fitting results
4414 (coefficients, rms, etc.) are to be written.
4415 if given, fitting results will NOT be output to
4416 scantable (insitu=True) or None will be
4417 returned (insitu=False).
4418 (default is "": no table output)
4419
4420 Example:
4421 # return a scan baselined by a third order polynomial,
4422 # not using a mask
4423 bscan = scan.poly_baseline(order=3)
4424 """
4425
4426 try:
4427 varlist = vars()
4428
4429 if insitu is None:
4430 insitu = rcParams["insitu"]
4431 if insitu:
4432 workscan = self
4433 else:
4434 workscan = self.copy()
4435
4436 if mask is None: mask = []
4437 if order is None: order = 0
4438 if clipthresh is None: clipthresh = 3.0
4439 if clipniter is None: clipniter = 0
4440 if plot is None: plot = False
4441 if getresidual is None: getresidual = True
4442 if showprogress is None: showprogress = True
4443 if minnrow is None: minnrow = 1000
4444 if outlog is None: outlog = False
4445 if blfile is None: blfile = ''
4446 if csvformat is None: csvformat = False
4447 if bltable is None: bltable = ''
4448
4449 scsvformat = 'T' if csvformat else 'F'
4450
4451 if plot:
4452 outblfile = (blfile != "") and \
4453 os.path.exists(os.path.expanduser(
4454 os.path.expandvars(blfile))
4455 )
4456 if outblfile:
4457 blf = open(blfile, "a")
4458
4459 f = fitter()
4460 f.set_function(lpoly=order)
4461
4462 rows = xrange(workscan.nrow())
4463 #if len(rows) > 0: workscan._init_blinfo()
4464
4465 action = "H"
4466 for r in rows:
4467 f.x = workscan._getabcissa(r)
4468 f.y = workscan._getspectrum(r)
4469 if mask:
4470 f.mask = mask_and(mask, workscan._getmask(r)) # (CAS-1434)
4471 else: # mask=None
4472 f.mask = workscan._getmask(r)
4473
4474 f.data = None
4475 f.fit()
4476
4477 if action != "Y": # skip plotting when accepting all
4478 f.plot(residual=True)
4479 #accept_fit = raw_input("Accept fit ( [y]/n ): ")
4480 #if accept_fit.upper() == "N":
4481 # #workscan._append_blinfo(None, None, None)
4482 # continue
4483 accept_fit = self._get_verify_action("Accept fit?",action)
4484 if r == 0: action = None
4485 if accept_fit.upper() == "N":
4486 continue
4487 elif accept_fit.upper() == "R":
4488 break
4489 elif accept_fit.upper() == "A":
4490 action = "Y"
4491
4492 blpars = f.get_parameters()
4493 masklist = workscan.get_masklist(f.mask, row=r, silent=True)
4494 #workscan._append_blinfo(blpars, masklist, f.mask)
4495 workscan._setspectrum((f.fitter.getresidual()
4496 if getresidual else f.fitter.getfit()), r)
4497
4498 if outblfile:
4499 rms = workscan.get_rms(f.mask, r)
4500 dataout = \
4501 workscan.format_blparams_row(blpars["params"],
4502 blpars["fixed"],
4503 rms, str(masklist),
4504 r, True, csvformat)
4505 blf.write(dataout)
4506
4507 f._p.unmap()
4508 f._p = None
4509
4510 if outblfile:
4511 blf.close()
4512 else:
4513 workscan._poly_baseline(mask, order,
4514 clipthresh, clipniter, #
4515 getresidual,
4516 pack_progress_params(showprogress,
4517 minnrow),
4518 outlog, scsvformat+blfile,
4519 bltable) #
4520
4521 workscan._add_history("poly_baseline", varlist)
4522
4523 if insitu:
4524 self._assign(workscan)
4525 else:
4526 return workscan
4527
4528 except RuntimeError, e:
4529 raise_fitting_failure_exception(e)
4530
4531 @asaplog_post_dec
4532 def auto_poly_baseline(self, mask=None, order=None, insitu=None,
4533 clipthresh=None, clipniter=None,
4534 edge=None, threshold=None, chan_avg_limit=None,
4535 getresidual=None, plot=None,
4536 showprogress=None, minnrow=None, outlog=None,
4537 blfile=None, csvformat=None, bltable=None):
4538 """\
4539 Return a scan which has been baselined (all rows) by a polynomial.
4540 Spectral lines are detected first using linefinder and masked out
4541 to avoid them affecting the baseline solution.
4542
4543 Parameters:
4544 mask: an optional mask retreived from scantable
4545 order: the order of the polynomial (default is 0)
4546 insitu: if False a new scantable is returned.
4547 Otherwise, the scaling is done in-situ
4548 The default is taken from .asaprc (False)
4549 clipthresh: Clipping threshold. (default is 3.0, unit: sigma)
4550 clipniter: maximum number of iteration of 'clipthresh'-sigma
4551 clipping (default is 0)
4552 edge: an optional number of channel to drop at
4553 the edge of spectrum. If only one value is
4554 specified, the same number will be dropped
4555 from both sides of the spectrum. Default
4556 is to keep all channels. Nested tuples
4557 represent individual edge selection for
4558 different IFs (a number of spectral channels
4559 can be different)
4560 threshold: the threshold used by line finder. It is
4561 better to keep it large as only strong lines
4562 affect the baseline solution.
4563 chan_avg_limit: a maximum number of consequtive spectral
4564 channels to average during the search of
4565 weak and broad lines. The default is no
4566 averaging (and no search for weak lines).
4567 If such lines can affect the fitted baseline
4568 (e.g. a high order polynomial is fitted),
4569 increase this parameter (usually values up
4570 to 8 are reasonable). Most users of this
4571 method should find the default value sufficient.
4572 plot: plot the fit and the residual. In this each
4573 indivual fit has to be approved, by typing 'y'
4574 or 'n'
4575 getresidual: if False, returns best-fit values instead of
4576 residual. (default is True)
4577 showprogress: show progress status for large data.
4578 default is True.
4579 minnrow: minimum number of input spectra to show.
4580 default is 1000.
4581 outlog: Output the coefficients of the best-fit
4582 function to logger (default is False)
4583 blfile: Name of a text file in which the best-fit
4584 parameter values to be written
4585 (default is "": no file/logger output)
4586 csvformat: if True blfile is csv-formatted, default is False.
4587 bltable: name of a baseline table where fitting results
4588 (coefficients, rms, etc.) are to be written.
4589 if given, fitting results will NOT be output to
4590 scantable (insitu=True) or None will be
4591 returned (insitu=False).
4592 (default is "": no table output)
4593
4594 Example:
4595 bscan = scan.auto_poly_baseline(order=7, insitu=False)
4596 """
4597
4598 try:
4599 varlist = vars()
4600
4601 if insitu is None:
4602 insitu = rcParams['insitu']
4603 if insitu:
4604 workscan = self
4605 else:
4606 workscan = self.copy()
4607
4608 if mask is None: mask = []
4609 if order is None: order = 0
4610 if clipthresh is None: clipthresh = 3.0
4611 if clipniter is None: clipniter = 0
4612 if edge is None: edge = (0, 0)
4613 if threshold is None: threshold = 3
4614 if chan_avg_limit is None: chan_avg_limit = 1
4615 if plot is None: plot = False
4616 if getresidual is None: getresidual = True
4617 if showprogress is None: showprogress = True
4618 if minnrow is None: minnrow = 1000
4619 if outlog is None: outlog = False
4620 if blfile is None: blfile = ''
4621 if csvformat is None: csvformat = False
4622 if bltable is None: bltable = ''
4623
4624 scsvformat = 'T' if csvformat else 'F'
4625
4626 edge = normalise_edge_param(edge)
4627
4628 if plot:
4629 outblfile = (blfile != "") and \
4630 os.path.exists(os.path.expanduser(os.path.expandvars(blfile)))
4631 if outblfile: blf = open(blfile, "a")
4632
4633 from asap.asaplinefind import linefinder
4634 fl = linefinder()
4635 fl.set_options(threshold=threshold, avg_limit=chan_avg_limit)
4636 fl.set_scan(workscan)
4637
4638 f = fitter()
4639 f.set_function(lpoly=order)
4640
4641 rows = xrange(workscan.nrow())
4642 #if len(rows) > 0: workscan._init_blinfo()
4643
4644 action = "H"
4645 for r in rows:
4646 idx = 2*workscan.getif(r)
4647 if mask:
4648 msk = mask_and(mask, workscan._getmask(r)) # (CAS-1434)
4649 else: # mask=None
4650 msk = workscan._getmask(r)
4651 fl.find_lines(r, msk, edge[idx:idx+2])
4652
4653 f.x = workscan._getabcissa(r)
4654 f.y = workscan._getspectrum(r)
4655 f.mask = fl.get_mask()
4656 f.data = None
4657 f.fit()
4658
4659 if action != "Y": # skip plotting when accepting all
4660 f.plot(residual=True)
4661 #accept_fit = raw_input("Accept fit ( [y]/n ): ")
4662 accept_fit = self._get_verify_action("Accept fit?",action)
4663 if r == 0: action = None
4664 if accept_fit.upper() == "N":
4665 #workscan._append_blinfo(None, None, None)
4666 continue
4667 elif accept_fit.upper() == "R":
4668 break
4669 elif accept_fit.upper() == "A":
4670 action = "Y"
4671
4672 blpars = f.get_parameters()
4673 masklist = workscan.get_masklist(f.mask, row=r, silent=True)
4674 #workscan._append_blinfo(blpars, masklist, f.mask)
4675 workscan._setspectrum(
4676 (f.fitter.getresidual() if getresidual
4677 else f.fitter.getfit()), r
4678 )
4679
4680 if outblfile:
4681 rms = workscan.get_rms(f.mask, r)
4682 dataout = \
4683 workscan.format_blparams_row(blpars["params"],
4684 blpars["fixed"],
4685 rms, str(masklist),
4686 r, True, csvformat)
4687 blf.write(dataout)
4688
4689 f._p.unmap()
4690 f._p = None
4691
4692 if outblfile: blf.close()
4693 else:
4694 workscan._auto_poly_baseline(mask, order,
4695 clipthresh, clipniter,
4696 edge, threshold,
4697 chan_avg_limit, getresidual,
4698 pack_progress_params(showprogress,
4699 minnrow),
4700 outlog, scsvformat+blfile,
4701 bltable)
4702 workscan._add_history("auto_poly_baseline", varlist)
4703
4704 if bltable == '':
4705 if insitu:
4706 self._assign(workscan)
4707 else:
4708 return workscan
4709 else:
4710 if not insitu:
4711 return None
4712
4713 except RuntimeError, e:
4714 raise_fitting_failure_exception(e)
4715
4716 def _init_blinfo(self):
4717 """\
4718 Initialise the following three auxiliary members:
4719 blpars : parameters of the best-fit baseline,
4720 masklists : mask data (edge positions of masked channels) and
4721 actualmask : mask data (in boolean list),
4722 to keep for use later (including output to logger/text files).
4723 Used by poly_baseline() and auto_poly_baseline() in case of
4724 'plot=True'.
4725 """
4726 self.blpars = []
4727 self.masklists = []
4728 self.actualmask = []
4729 return
4730
4731 def _append_blinfo(self, data_blpars, data_masklists, data_actualmask):
4732 """\
4733 Append baseline-fitting related info to blpars, masklist and
4734 actualmask.
4735 """
4736 self.blpars.append(data_blpars)
4737 self.masklists.append(data_masklists)
4738 self.actualmask.append(data_actualmask)
4739 return
4740
4741 @asaplog_post_dec
4742 def rotate_linpolphase(self, angle):
4743 """\
4744 Rotate the phase of the complex polarization O=Q+iU correlation.
4745 This is always done in situ in the raw data. So if you call this
4746 function more than once then each call rotates the phase further.
4747
4748 Parameters:
4749
4750 angle: The angle (degrees) to rotate (add) by.
4751
4752 Example::
4753
4754 scan.rotate_linpolphase(2.3)
4755
4756 """
4757 varlist = vars()
4758 self._math._rotate_linpolphase(self, angle)
4759 self._add_history("rotate_linpolphase", varlist)
4760 return
4761
4762 @asaplog_post_dec
4763 def rotate_xyphase(self, angle):
4764 """\
4765 Rotate the phase of the XY correlation. This is always done in situ
4766 in the data. So if you call this function more than once
4767 then each call rotates the phase further.
4768
4769 Parameters:
4770
4771 angle: The angle (degrees) to rotate (add) by.
4772
4773 Example::
4774
4775 scan.rotate_xyphase(2.3)
4776
4777 """
4778 varlist = vars()
4779 self._math._rotate_xyphase(self, angle)
4780 self._add_history("rotate_xyphase", varlist)
4781 return
4782
4783 @asaplog_post_dec
4784 def swap_linears(self):
4785 """\
4786 Swap the linear polarisations XX and YY, or better the first two
4787 polarisations as this also works for ciculars.
4788 """
4789 varlist = vars()
4790 self._math._swap_linears(self)
4791 self._add_history("swap_linears", varlist)
4792 return
4793
4794 @asaplog_post_dec
4795 def invert_phase(self):
4796 """\
4797 Invert the phase of the complex polarisation
4798 """
4799 varlist = vars()
4800 self._math._invert_phase(self)
4801 self._add_history("invert_phase", varlist)
4802 return
4803
4804 @asaplog_post_dec
4805 def add(self, offset, insitu=None):
4806 """\
4807 Return a scan where all spectra have the offset added
4808
4809 Parameters:
4810
4811 offset: the offset
4812
4813 insitu: if False a new scantable is returned.
4814 Otherwise, the scaling is done in-situ
4815 The default is taken from .asaprc (False)
4816
4817 """
4818 if insitu is None: insitu = rcParams['insitu']
4819 self._math._setinsitu(insitu)
4820 varlist = vars()
4821 s = scantable(self._math._unaryop(self, offset, "ADD", False, False))
4822 s._add_history("add", varlist)
4823 if insitu:
4824 self._assign(s)
4825 else:
4826 return s
4827
4828 @asaplog_post_dec
4829 def scale(self, factor, tsys=True, insitu=None):
4830 """\
4831
4832 Return a scan where all spectra are scaled by the given 'factor'
4833
4834 Parameters:
4835
4836 factor: the scaling factor (float or 1D float list)
4837
4838 insitu: if False a new scantable is returned.
4839 Otherwise, the scaling is done in-situ
4840 The default is taken from .asaprc (False)
4841
4842 tsys: if True (default) then apply the operation to Tsys
4843 as well as the data
4844 """
4845 if insitu is None: insitu = rcParams['insitu']
4846 self._math._setinsitu(insitu)
4847 varlist = vars()
4848 s = None
4849 import numpy
4850 if isinstance(factor, list) or isinstance(factor, numpy.ndarray):
4851 if isinstance(factor[0], list) or isinstance(factor[0],
4852 numpy.ndarray):
4853 from asapmath import _array2dOp
4854 s = _array2dOp( self, factor, "MUL", tsys, insitu, True )
4855 else:
4856 s = scantable( self._math._arrayop( self, factor,
4857 "MUL", tsys, True ) )
4858 else:
4859 s = scantable(self._math._unaryop(self, factor, "MUL", tsys, True ))
4860 s._add_history("scale", varlist)
4861 if insitu:
4862 self._assign(s)
4863 else:
4864 return s
4865
4866 @preserve_selection
4867 def set_sourcetype(self, match, matchtype="pattern",
4868 sourcetype="reference"):
4869 """\
4870 Set the type of the source to be an source or reference scan
4871 using the provided pattern.
4872
4873 Parameters:
4874
4875 match: a Unix style pattern, regular expression or selector
4876
4877 matchtype: 'pattern' (default) UNIX style pattern or
4878 'regex' regular expression
4879
4880 sourcetype: the type of the source to use (source/reference)
4881
4882 """
4883 varlist = vars()
4884 stype = -1
4885 if sourcetype.lower().startswith("r") or sourcetype.lower() == "off":
4886 stype = 1
4887 elif sourcetype.lower().startswith("s") or sourcetype.lower() == "on":
4888 stype = 0
4889 else:
4890 raise ValueError("Illegal sourcetype use s(ource)/on or r(eference)/off")
4891 if matchtype.lower().startswith("p"):
4892 matchtype = "pattern"
4893 elif matchtype.lower().startswith("r"):
4894 matchtype = "regex"
4895 else:
4896 raise ValueError("Illegal matchtype, use p(attern) or r(egex)")
4897 sel = selector()
4898 if isinstance(match, selector):
4899 sel = match
4900 else:
4901 sel.set_query("SRCNAME=%s('%s')" % (matchtype, match))
4902 self.set_selection(sel)
4903 self._setsourcetype(stype)
4904 self._add_history("set_sourcetype", varlist)
4905
4906
4907 def set_sourcename(self, name):
4908 varlist = vars()
4909 self._setsourcename(name)
4910 self._add_history("set_sourcename", varlist)
4911
4912 @asaplog_post_dec
4913 @preserve_selection
4914 def auto_quotient(self, preserve=True, mode='paired', verify=False):
4915 """\
4916 This function allows to build quotients automatically.
4917 It assumes the observation to have the same number of
4918 "ons" and "offs"
4919
4920 Parameters:
4921
4922 preserve: you can preserve (default) the continuum or
4923 remove it. The equations used are
4924
4925 preserve: Output = Toff * (on/off) - Toff
4926
4927 remove: Output = Toff * (on/off) - Ton
4928
4929 mode: the on/off detection mode
4930 'paired' (default)
4931 identifies 'off' scans by the
4932 trailing '_R' (Mopra/Parkes) or
4933 '_e'/'_w' (Tid) and matches
4934 on/off pairs from the observing pattern
4935 'time'
4936 finds the closest off in time
4937
4938 .. todo:: verify argument is not implemented
4939
4940 """
4941 varlist = vars()
4942 modes = ["time", "paired"]
4943 if not mode in modes:
4944 msg = "please provide valid mode. Valid modes are %s" % (modes)
4945 raise ValueError(msg)
4946 s = None
4947 if mode.lower() == "paired":
4948 from asap._asap import srctype
4949 sel = self.get_selection()
4950 #sel.set_query("SRCTYPE==psoff")
4951 sel.set_types(srctype.psoff)
4952 self.set_selection(sel)
4953 offs = self.copy()
4954 #sel.set_query("SRCTYPE==pson")
4955 sel.set_types(srctype.pson)
4956 self.set_selection(sel)
4957 ons = self.copy()
4958 s = scantable(self._math._quotient(ons, offs, preserve))
4959 elif mode.lower() == "time":
4960 s = scantable(self._math._auto_quotient(self, mode, preserve))
4961 s._add_history("auto_quotient", varlist)
4962 return s
4963
4964 @asaplog_post_dec
4965 def mx_quotient(self, mask = None, weight='median', preserve=True):
4966 """\
4967 Form a quotient using "off" beams when observing in "MX" mode.
4968
4969 Parameters:
4970
4971 mask: an optional mask to be used when weight == 'stddev'
4972
4973 weight: How to average the off beams. Default is 'median'.
4974
4975 preserve: you can preserve (default) the continuum or
4976 remove it. The equations used are:
4977
4978 preserve: Output = Toff * (on/off) - Toff
4979
4980 remove: Output = Toff * (on/off) - Ton
4981
4982 """
4983 mask = mask or ()
4984 varlist = vars()
4985 on = scantable(self._math._mx_extract(self, 'on'))
4986 preoff = scantable(self._math._mx_extract(self, 'off'))
4987 off = preoff.average_time(mask=mask, weight=weight, scanav=False)
4988 from asapmath import quotient
4989 q = quotient(on, off, preserve)
4990 q._add_history("mx_quotient", varlist)
4991 return q
4992
4993 @asaplog_post_dec
4994 def freq_switch(self, insitu=None):
4995 """\
4996 Apply frequency switching to the data.
4997
4998 Parameters:
4999
5000 insitu: if False a new scantable is returned.
5001 Otherwise, the swictching is done in-situ
5002 The default is taken from .asaprc (False)
5003
5004 """
5005 if insitu is None: insitu = rcParams['insitu']
5006 self._math._setinsitu(insitu)
5007 varlist = vars()
5008 s = scantable(self._math._freqswitch(self))
5009 s._add_history("freq_switch", varlist)
5010 if insitu:
5011 self._assign(s)
5012 else:
5013 return s
5014
5015 @asaplog_post_dec
5016 def recalc_azel(self):
5017 """Recalculate the azimuth and elevation for each position."""
5018 varlist = vars()
5019 self._recalcazel()
5020 self._add_history("recalc_azel", varlist)
5021 return
5022
5023 @asaplog_post_dec
5024 def __add__(self, other):
5025 """
5026 implicit on all axes and on Tsys
5027 """
5028 varlist = vars()
5029 s = self.__op( other, "ADD" )
5030 s._add_history("operator +", varlist)
5031 return s
5032
5033 @asaplog_post_dec
5034 def __sub__(self, other):
5035 """
5036 implicit on all axes and on Tsys
5037 """
5038 varlist = vars()
5039 s = self.__op( other, "SUB" )
5040 s._add_history("operator -", varlist)
5041 return s
5042
5043 @asaplog_post_dec
5044 def __mul__(self, other):
5045 """
5046 implicit on all axes and on Tsys
5047 """
5048 varlist = vars()
5049 s = self.__op( other, "MUL" ) ;
5050 s._add_history("operator *", varlist)
5051 return s
5052
5053
5054 @asaplog_post_dec
5055 def __div__(self, other):
5056 """
5057 implicit on all axes and on Tsys
5058 """
5059 varlist = vars()
5060 s = self.__op( other, "DIV" )
5061 s._add_history("operator /", varlist)
5062 return s
5063
5064 @asaplog_post_dec
5065 def __op( self, other, mode ):
5066 s = None
5067 if isinstance(other, scantable):
5068 s = scantable(self._math._binaryop(self, other, mode))
5069 elif isinstance(other, float):
5070 if other == 0.0:
5071 raise ZeroDivisionError("Dividing by zero is not recommended")
5072 s = scantable(self._math._unaryop(self, other, mode, False, True))
5073 elif isinstance(other, list) or isinstance(other, numpy.ndarray):
5074 if isinstance(other[0], list) \
5075 or isinstance(other[0], numpy.ndarray):
5076 from asapmath import _array2dOp
5077 s = _array2dOp(self, other, mode, False)
5078 else:
5079 s = scantable(self._math._arrayop(self, other, mode, False, True))
5080 else:
5081 raise TypeError("Other input is not a scantable or float value")
5082 return s
5083
5084 @asaplog_post_dec
5085 def get_fit(self, row=0):
5086 """\
5087 Print or return the stored fits for a row in the scantable
5088
5089 Parameters:
5090
5091 row: the row which the fit has been applied to.
5092
5093 """
5094 if row > self.nrow():
5095 return
5096 from asap.asapfit import asapfit
5097 fit = asapfit(self._getfit(row))
5098 asaplog.push( '%s' %(fit) )
5099 return fit.as_dict()
5100
5101 @preserve_selection
5102 def flag_nans(self):
5103 """\
5104 Utility function to flag NaN values in the scantable.
5105 """
5106 import numpy
5107 basesel = self.get_selection()
5108 for i in range(self.nrow()):
5109 sel = self.get_row_selector(i)
5110 self.set_selection(basesel+sel)
5111 nans = numpy.isnan(self._getspectrum(0))
5112 if numpy.any(nans):
5113 bnans = [ bool(v) for v in nans]
5114 self.flag(bnans)
5115
5116 self.set_selection(basesel)
5117
5118 def get_row_selector(self, rowno):
5119 return selector(rows=[rowno])
5120
5121 def _add_history(self, funcname, parameters):
5122 if not rcParams['scantable.history']:
5123 return
5124 # create date
5125 sep = "##"
5126 from datetime import datetime
5127 dstr = datetime.now().strftime('%Y/%m/%d %H:%M:%S')
5128 hist = dstr+sep
5129 hist += funcname+sep#cdate+sep
5130 if parameters.has_key('self'):
5131 del parameters['self']
5132 for k, v in parameters.iteritems():
5133 if type(v) is dict:
5134 for k2, v2 in v.iteritems():
5135 hist += k2
5136 hist += "="
5137 if isinstance(v2, scantable):
5138 hist += 'scantable'
5139 elif k2 == 'mask':
5140 if isinstance(v2, list) or isinstance(v2, tuple):
5141 hist += str(self._zip_mask(v2))
5142 else:
5143 hist += str(v2)
5144 else:
5145 hist += str(v2)
5146 else:
5147 hist += k
5148 hist += "="
5149 if isinstance(v, scantable):
5150 hist += 'scantable'
5151 elif k == 'mask':
5152 if isinstance(v, list) or isinstance(v, tuple):
5153 hist += str(self._zip_mask(v))
5154 else:
5155 hist += str(v)
5156 else:
5157 hist += str(v)
5158 hist += sep
5159 hist = hist[:-2] # remove trailing '##'
5160 self._addhistory(hist)
5161
5162
5163 def _zip_mask(self, mask):
5164 mask = list(mask)
5165 i = 0
5166 segments = []
5167 while mask[i:].count(1):
5168 i += mask[i:].index(1)
5169 if mask[i:].count(0):
5170 j = i + mask[i:].index(0)
5171 else:
5172 j = len(mask)
5173 segments.append([i, j])
5174 i = j
5175 return segments
5176
5177 def _get_ordinate_label(self):
5178 fu = "("+self.get_fluxunit()+")"
5179 import re
5180 lbl = "Intensity"
5181 if re.match(".K.", fu):
5182 lbl = "Brightness Temperature "+ fu
5183 elif re.match(".Jy.", fu):
5184 lbl = "Flux density "+ fu
5185 return lbl
5186
5187 def _check_ifs(self):
5188# return len(set([self.nchan(i) for i in self.getifnos()])) == 1
5189 nchans = [self.nchan(i) for i in self.getifnos()]
5190 nchans = filter(lambda t: t > 0, nchans)
5191 return (sum(nchans)/len(nchans) == nchans[0])
5192
5193 @asaplog_post_dec
5194 def _fill(self, names, unit, average, opts={}):
5195 first = True
5196 fullnames = []
5197 for name in names:
5198 name = os.path.expandvars(name)
5199 name = os.path.expanduser(name)
5200 if not os.path.exists(name):
5201 msg = "File '%s' does not exists" % (name)
5202 raise IOError(msg)
5203 fullnames.append(name)
5204 if average:
5205 asaplog.push('Auto averaging integrations')
5206 stype = int(rcParams['scantable.storage'].lower() == 'disk')
5207 for name in fullnames:
5208 tbl = Scantable(stype)
5209 if is_ms( name ):
5210 r = msfiller( tbl )
5211 else:
5212 r = filler( tbl )
5213 msg = "Importing %s..." % (name)
5214 asaplog.push(msg, False)
5215 r.open(name, opts)
5216 rx = rcParams['scantable.reference']
5217 r.setreferenceexpr(rx)
5218 r.fill()
5219 if average:
5220 tbl = self._math._average((tbl, ), (), 'NONE', 'SCAN')
5221 if not first:
5222 tbl = self._math._merge([self, tbl])
5223 Scantable.__init__(self, tbl)
5224 r.close()
5225 del r, tbl
5226 first = False
5227 #flush log
5228 asaplog.post()
5229 if unit is not None:
5230 self.set_fluxunit(unit)
5231 if not is_casapy():
5232 self.set_freqframe(rcParams['scantable.freqframe'])
5233
5234 def _get_verify_action( self, msg, action=None ):
5235 valid_act = ['Y', 'N', 'A', 'R']
5236 if not action or not isinstance(action, str):
5237 action = raw_input("%s [Y/n/a/r] (h for help): " % msg)
5238 if action == '':
5239 return "Y"
5240 elif (action.upper()[0] in valid_act):
5241 return action.upper()[0]
5242 elif (action.upper()[0] in ['H','?']):
5243 print "Available actions of verification [Y|n|a|r]"
5244 print " Y : Yes for current data (default)"
5245 print " N : No for current data"
5246 print " A : Accept all in the following and exit from verification"
5247 print " R : Reject all in the following and exit from verification"
5248 print " H or ?: help (show this message)"
5249 return self._get_verify_action(msg)
5250 else:
5251 return 'Y'
5252
5253 def __getitem__(self, key):
5254 if key < 0:
5255 key += self.nrow()
5256 if key >= self.nrow():
5257 raise IndexError("Row index out of range.")
5258 return self._getspectrum(key)
5259
5260 def __setitem__(self, key, value):
5261 if key < 0:
5262 key += self.nrow()
5263 if key >= self.nrow():
5264 raise IndexError("Row index out of range.")
5265 if not hasattr(value, "__len__") or \
5266 len(value) > self.nchan(self.getif(key)):
5267 raise ValueError("Spectrum length doesn't match.")
5268 return self._setspectrum(value, key)
5269
5270 def __len__(self):
5271 return self.nrow()
5272
5273 def __iter__(self):
5274 for i in range(len(self)):
5275 yield self[i]
Note: See TracBrowser for help on using the repository browser.