casa  $Rev:20696$
 All Classes Namespaces Files Functions Variables
asaplinefind.py
Go to the documentation of this file.
00001 import _asap
00002 
00003 class linefinder:
00004     """
00005     The class for automated spectral line search in ASAP.
00006 
00007     Example:
00008        fl=linefinder()
00009        fl.set_scan(sc)
00010        fl.set_options(threshold=3)
00011        nlines=fl.find_lines(edge=(50,0))
00012        if nlines!=0:
00013           print "Found ",nlines," spectral lines"
00014           print fl.get_ranges(False)
00015        else:
00016           print "No lines found!"
00017        sc2=sc.poly_baseline(fl.get_mask(),7)
00018 
00019     The algorithm involves a simple threshold criterion. The line is
00020     considered to be detected if a specified number of consequtive
00021     channels (default is 3) is brighter (with respect to the current baseline
00022     estimate) than the threshold times the noise level. This criterion is
00023     applied in the iterative procedure updating baseline estimate and trying
00024     reduced spectral resolutions to detect broad lines as well. The off-line
00025     noise level is determined at each iteration as an average of 80% of the
00026     lowest variances across the spectrum (i.e. histogram equalization is
00027     used to avoid missing weak lines if strong ones are present). For
00028     bad baseline shapes it is recommended to increase the threshold and
00029     possibly switch the averaging option off (see set_options) to
00030     detect strong lines only, fit a high order baseline and repeat the line
00031     search.
00032 
00033     """
00034 
00035     def __init__(self):
00036         """
00037         Create a line finder object.
00038         """
00039         self.finder = _asap.linefinder()
00040         return
00041 
00042     def set_options(self,threshold=1.7320508075688772,min_nchan=3,
00043         avg_limit=8,box_size=0.2,noise_box='all',noise_stat='mean80'):
00044         """
00045         Set the parameters of the algorithm
00046         Parameters:
00047              threshold    a single channel S/N ratio above which the
00048                           channel is considered to be a detection
00049                           Default is sqrt(3), which together with
00050                           min_nchan=3 gives a 3-sigma criterion
00051              min_nchan    a minimal number of consequtive channels,
00052                           which should satisfy a threshold criterion to
00053                           be a detection. Default is 3.
00054              avg_limit    A number of consequtive channels not greater than
00055                           this parameter can be averaged to search for
00056                           broad lines. Default is 8.
00057              box_size     A running mean/median box size specified as a fraction
00058                           of the total spectrum length. Default is 1/5
00059              noise_box    Area of the spectrum used to estimate noise stats
00060                           Both string values and numbers are allowed
00061                           Allowed string values:
00062                              'all' use all the spectrum (default)
00063                              'box' noise box is the same as running mean/median
00064                                    box
00065                           Numeric values are defined as a fraction from the
00066                           spectrum size. Values should be positive.
00067                           (noise_box == box_size has the same effect as
00068                            noise_box = 'box')
00069              noise_stat   Statistics used to estimate noise, allowed values:
00070                               'mean80' use the 80% of the lowest deviations
00071                                        in the noise box (default)
00072                               'median' median of deviations in the noise box
00073 
00074         Note:  For bad baselines threshold should be increased,
00075                and avg_limit decreased (or even switched off completely by
00076                setting this parameter to 1) to avoid detecting baseline
00077                undulations instead of real lines.
00078         """
00079         if noise_stat.lower() not in ["mean80",'median']:
00080            raise RuntimeError, "noise_stat argument in linefinder.set_options can only be mean80 or median"
00081         nStat = (noise_stat.lower() == "median")
00082         nBox = -1.
00083         if isinstance(noise_box,str):
00084            if noise_box.lower() not in ['all','box']:
00085               raise RuntimeError, "string-valued noise_box in linefinder.set_options can only be all or box"
00086            if noise_box.lower() == 'box':
00087               nBox = box_size
00088         else:
00089            nBox = float(noise_box)
00090         self.finder.setoptions(threshold,min_nchan,avg_limit,box_size,nBox,nStat)
00091         return
00092 
00093     def set_scan(self, scan):
00094         """
00095         Set the 'data' (scantable) to work with.
00096         Parameters:
00097              scan:    a scantable
00098         """
00099         if not scan:
00100            raise RuntimeError, 'Please give a correct scan'
00101         self.finder.setscan(scan)
00102 
00103     def set_data(self, spectrum):
00104         """
00105         Set the 'data' (spectrum) to work with
00106         Parameters: a method to allow linefinder work without setting scantable
00107         for the purpose of using linefinder inside some method in scantable
00108         class. (Dec 22, 2010 by W.Kawasaki)
00109         """
00110         if isinstance(spectrum, list) or isinstance(spectrum, tuple):
00111             if not isinstance(spectrum[0], float):
00112                 raise RuntimeError, "Parameter 'spectrum' has to be a list or a tuple of float"
00113         else:
00114             raise RuntimeError, "Parameter 'spectrum' has to be a list or a tuple of float"
00115         self.finder.setdata(spectrum)
00116         
00117     def find_lines(self,nRow=0,mask=[],edge=(0,0)):
00118         """
00119         Search for spectral lines in the scan assigned in set_scan.
00120         Parameters:
00121              nRow:       a row in the scantable to work with
00122              mask:       an optional mask (e.g. retreived from scantable)
00123              edge:       an optional number of channels to drop at
00124                          the edge of the spectrum. If only one value is
00125                          specified, the same number will be dropped from
00126                          both sides of the spectrum. Default is to keep
00127                          all channels
00128         A number of lines found will be returned
00129         """
00130         if isinstance(edge,int):
00131            edge=(edge,)
00132 
00133         from asap import _is_sequence_or_number as _is_valid
00134 
00135         if not _is_valid(edge, int):
00136            raise RuntimeError, "Parameter 'edge' has to be an integer or \
00137            a pair of integers specified as a tuple"
00138 
00139         if len(edge)>2:
00140            raise RuntimeError, "The edge parameter should have two \
00141            or less elements"
00142         return self.finder.findlines(mask,list(edge),nRow)
00143     def get_mask(self,invert=False):
00144         """
00145         Get the mask to mask out all lines that have been found (default)
00146 
00147         Parameters:
00148               invert  if True, only channels belong to lines will be unmasked
00149 
00150         Note: all channels originally masked by the input mask or
00151               dropped out by the edge parameter will still be excluded
00152               regardless on the invert option
00153         """
00154         return self.finder.getmask(invert)
00155     def get_ranges(self,defunits=True):
00156         """
00157         Get ranges (start and end channels or velocities) for all spectral
00158         lines found.
00159 
00160         Parameters:
00161               defunits  if True (default), the range will use the same units
00162                         as set for the scan (e.g. LSR velocity)
00163                         if False, the range will be expressed in channels
00164         """
00165         if (defunits):
00166             return self.finder.getlineranges()
00167         else:
00168             return self.finder.getlinerangesinchannels()