source: tags/asap-4.0.0/python/scantable.py@ 2354

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

Fixed another instance of inserting a new method argument. THIS NEEDS TO GO TO THE END!

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