source: trunk/src/SDLineFinder.cc @ 344

Last change on this file since 344 was 344, checked in by vor010, 19 years ago

An algorithm to detect line wings, which are
below detection threshold, has been added

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.4 KB
Line 
1//#---------------------------------------------------------------------------
2//# SDLineFinder.cc: A class for automated spectral line search
3//#--------------------------------------------------------------------------
4//# Copyright (C) 2004
5//# ATNF
6//#
7//# This program is free software; you can redistribute it and/or modify it
8//# under the terms of the GNU General Public License as published by the Free
9//# Software Foundation; either version 2 of the License, or (at your option)
10//# any later version.
11//#
12//# This program is distributed in the hope that it will be useful, but
13//# WITHOUT ANY WARRANTY; without even the implied warranty of
14//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
15//# Public License for more details.
16//#
17//# You should have received a copy of the GNU General Public License along
18//# with this program; if not, write to the Free Software Foundation, Inc.,
19//# 675 Massachusetts Ave, Cambridge, MA 02139, USA.
20//#
21//# Correspondence concerning this software should be addressed as follows:
22//#        Internet email: Malte.Marquarding@csiro.au
23//#        Postal address: Malte Marquarding,
24//#                        Australia Telescope National Facility,
25//#                        P.O. Box 76,
26//#                        Epping, NSW, 2121,
27//#                        AUSTRALIA
28//#
29//# $Id:
30//#---------------------------------------------------------------------------
31
32
33// ASAP
34#include "SDLineFinder.h"
35
36// STL
37#include <functional>
38#include <algorithm>
39#include <iostream>
40
41using namespace asap;
42using namespace casa;
43using namespace std;
44using namespace boost::python;
45
46namespace asap {
47
48///////////////////////////////////////////////////////////////////////////////
49//
50// IStatHolder - an abstract class to collect statistics from the running
51//               mean calculator, if necessary.
52//               We define it here, because it is used in LFRunningMean and
53//               SDLineFinder only
54//
55
56struct IStatHolder  {
57   // This function is called for each spectral channel processed by
58   // the running mean calculator. The order of channel numbers may be
59   // arbitrary
60   // ch - a number of channel, corresponding to (approximately) the centre
61   //      of the running box
62   // box_nchan - number of channels in the box
63   //
64   virtual void accumulate(int ch, Float sum, Float sum2, int box_nchan)
65                      throw(AipsError) = 0;                   
66};
67
68//
69///////////////////////////////////////////////////////////////////////////////
70
71///////////////////////////////////////////////////////////////////////////////
72//
73// SignAccumulator - a simple class to deal with running mean statistics:
74//                   it stores the sign of the value-mean only
75//
76class SignAccumulator : public IStatHolder {
77   Vector<Int>  sign;                // either +1, -1 or 0
78   const Vector<Float>   &spectrum;  // a reference to the spectrum
79                                     // to calculate the sign   
80public:
81   // all channels >=nchan are ignored
82   SignAccumulator(uInt nchan, const Vector<Float> &in_spectrum);
83   
84   // This function is called for each spectral channel processed by
85   // the running mean calculator. The order of channel numbers may be
86   // arbitrary
87   // ch - a number of channel, corresponding to (approximately) the centre
88   //      of the running box
89   // box_nchan - number of channels in the box
90   //
91   virtual void accumulate(int ch, Float sum, Float, int box_nchan)
92                      throw(AipsError);
93
94   // access to the sign
95   const Vector<Int>& getSigns() const throw();
96};
97
98//
99///////////////////////////////////////////////////////////////////////////////
100
101///////////////////////////////////////////////////////////////////////////////
102//
103// LFRunningMean - a running mean algorithm for line detection
104//
105//
106
107// An auxiliary class implementing one pass of the line search algorithm,
108// which uses a running mean. We define this class here because it is
109// used in SDLineFinder only. The incapsulation of this code into a separate
110// class will provide a possibility to add new algorithms with minor changes
111class LFRunningMean {
112   // The input data to work with. Use reference symantics to avoid
113   // an unnecessary copying   
114   const casa::Vector<casa::Float>  &spectrum; // a buffer for the spectrum
115   const casa::Vector<casa::Bool>   &mask; // associated mask
116   const std::pair<int,int>         &edge; // start and stop+1 channels
117                                           // to work with
118   
119   // statistics for running mean filtering
120   casa::Float sum;       // sum of fluxes
121   casa::Float sumsq;     // sum of squares of fluxes
122   int box_chan_cntr;     // actual number of channels in the box
123   int max_box_nchan;     // maximum allowed number of channels in the box
124                          // (calculated from boxsize and actual spectrum size)
125
126   // temporary line edge channels and flag, which is True if the line
127   // was detected in the previous channels.
128   std::pair<int,int> cur_line;
129   casa::Bool is_detected_before;
130   int  min_nchan;                         // A minimum number of consequtive
131                                           // channels, which should satisfy
132                                           // the detection criterion, to be
133                                           // a detection
134   casa::Float threshold;                  // detection threshold - the
135                                           // minimal signal to noise ratio
136public:
137   // set up the object with the references to actual data
138   // as well as the detection criterion (min_nchan and threshold, see above)
139   // and the number of channels in the running box
140   LFRunningMean(const casa::Vector<casa::Float>  &in_spectrum,
141                 const casa::Vector<casa::Bool>   &in_mask,
142                 const std::pair<int,int>         &in_edge,
143                 int in_max_box_nchan,
144                 int in_min_nchan = 3,
145                 casa::Float in_threshold = 5);
146                 
147   // replace the detection criterion
148   void setCriterion(int in_min_nchan, casa::Float in_threshold) throw();
149
150   // find spectral lines and add them into list
151   // if statholder is not NULL, the accumulate function of it will be
152   // called for each channel to save statistics
153   void findLines(std::list<pair<int,int> > &lines,
154                  IStatHolder* statholder = NULL) throw(casa::AipsError);
155   
156protected:
157   // supplementary function to control running mean calculations.
158   // It adds a specified channel to the running mean box and
159   // removes (ch-maxboxnchan+1)'th channel from there
160   // Channels, for which the mask is false or index is beyond the
161   // allowed range, are ignored
162   void advanceRunningBox(int ch) throw(casa::AipsError);
163   
164
165   // test a channel against current running mean & rms
166   // if channel specified is masked out or beyond the allowed indexes,
167   // false is returned
168   casa::Bool testChannel(int ch) const
169                     throw(std::exception, casa::AipsError);
170
171   // process a channel: update curline and is_detected before and
172   // add a new line to the list, if necessary using processCurLine()
173   void processChannel(std::list<pair<int,int> > &lines,
174                       int ch) throw(casa::AipsError);
175
176   // process the interval of channels stored in curline
177   // if it satisfies the criterion, add this interval as a new line
178   void processCurLine(std::list<pair<int,int> > &lines) throw(casa::AipsError);
179
180};
181
182//
183///////////////////////////////////////////////////////////////////////////////
184} // namespace asap
185
186///////////////////////////////////////////////////////////////////////////////
187//
188// SignAccumulator - a simple class to deal with running mean statistics:
189//                   it stores the sign of the value-mean only
190//
191
192// all channels >=nchan are ignored
193SignAccumulator::SignAccumulator(uInt nchan,
194                   const Vector<Float> &in_spectrum) : sign(nchan,0),
195                   spectrum(in_spectrum) {}
196
197
198// This function is called for each spectral channel processed by
199// the running mean calculator. The order of channel numbers may be
200// arbitrary
201// ch - a number of channel, corresponding to (approximately) the centre
202//      of the running box
203// box_nchan - number of channels in the box
204//
205void SignAccumulator::accumulate(int ch, Float sum, Float, int box_nchan)
206                   throw(AipsError)
207{
208   if (ch>=sign.nelements()) return;
209   DebugAssert(ch>=0,AipsError);
210   DebugAssert(ch<=spectrum.nelements(), AipsError);
211   if (box_nchan) {
212       Float buf=spectrum[ch]-sum/Float(box_nchan);
213       if (buf>0) sign[ch]=1;
214       else if (buf<0) sign[ch]=-1;
215       else sign[ch]=0;
216   } else sign[ch]=0;   
217}
218
219// access to the sign
220const Vector<Int>& SignAccumulator::getSigns() const throw()
221{
222   return sign;
223}
224
225
226//
227///////////////////////////////////////////////////////////////////////////////
228
229
230///////////////////////////////////////////////////////////////////////////////
231//
232// LFRunningMean - a running mean algorithm for line detection
233//
234//
235
236// set up the object with the references to actual data
237// as well as the detection criterion (min_nchan and threshold, see above)
238// and the number of channels in the running box
239LFRunningMean::LFRunningMean(const casa::Vector<casa::Float>  &in_spectrum,
240                             const casa::Vector<casa::Bool>   &in_mask,
241                             const std::pair<int,int>         &in_edge,
242                             int in_max_box_nchan,
243                             int in_min_nchan, casa::Float in_threshold) :
244        spectrum(in_spectrum), mask(in_mask), edge(in_edge),
245        max_box_nchan(in_max_box_nchan),
246        min_nchan(in_min_nchan),threshold(in_threshold) {}
247
248// replace the detection criterion
249void LFRunningMean::setCriterion(int in_min_nchan, casa::Float in_threshold)
250                                 throw()
251{
252  min_nchan=in_min_nchan;
253  threshold=in_threshold;
254}
255
256
257// supplementary function to control running mean calculations.
258// It adds a specified channel to the running mean box and
259// removes (ch-max_box_nchan+1)'th channel from there
260// Channels, for which the mask is false or index is beyond the
261// allowed range, are ignored
262void LFRunningMean::advanceRunningBox(int ch) throw(AipsError)
263{
264  if (ch>=edge.first && ch<edge.second)
265      if (mask[ch]) { // ch is a valid channel
266          ++box_chan_cntr;
267          sum+=spectrum[ch];
268          sumsq+=square(spectrum[ch]);         
269      }
270  int ch2remove=ch-max_box_nchan;
271  if (ch2remove>=edge.first && ch2remove<edge.second)
272      if (mask[ch2remove]) { // ch2remove is a valid channel
273          --box_chan_cntr;
274          sum-=spectrum[ch2remove];
275          sumsq-=square(spectrum[ch2remove]); 
276      }
277}
278
279// test a channel against current running mean & rms
280// if channel specified is masked out or beyond the allowed indexes,
281// false is returned
282Bool LFRunningMean::testChannel(int ch) const throw(exception, AipsError)
283{
284  if (ch<edge.first || ch>=edge.second) return False;
285  if (!mask[ch]) return False;
286  DebugAssert(box_chan_cntr, AipsError);
287  Float mean=sum/Float(box_chan_cntr);
288  Float variance=sqrt(sumsq/Float(box_chan_cntr)-square(mean));
289  /*
290  if (ch>3900 && ch<4100)
291    cout<<"Tested "<<ch<<" mean="<<mean<<" variance="<<variance<<" sp-mean="<<spectrum[ch]-mean<<endl;
292  */
293  return fabs(spectrum[ch]-mean)>=threshold*variance;
294}
295
296// process a channel: update cur_line and is_detected before and
297// add a new line to the list, if necessary
298void LFRunningMean::processChannel(std::list<pair<int,int> > &lines,
299                                   int ch) throw(casa::AipsError)
300{
301  try {
302       if (testChannel(ch)) {
303           if (is_detected_before)
304               cur_line.second=ch+1;
305           else {
306               is_detected_before=True;
307               cur_line.first=ch;
308               cur_line.second=ch+1;
309           }
310       } else processCurLine(lines);   
311  }
312  catch (const AipsError &ae) {
313      throw;
314  } 
315  catch (const exception &ex) {
316      throw AipsError(String("SDLineFinder::processChannel - STL error: ")+ex.what());
317  }
318}
319
320// process the interval of channels stored in cur_line
321// if it satisfies the criterion, add this interval as a new line
322void LFRunningMean::processCurLine(std::list<pair<int,int> > &lines)
323                                   throw(casa::AipsError)
324{
325  try {
326       if (is_detected_before) {                     
327           if (cur_line.second-cur_line.first>min_nchan) {
328               // it was a detection. We need to change the list
329               Bool add_new_line=False;
330               if (lines.size()) {
331                   for (int i=lines.back().second;i<cur_line.first;++i)
332                        if (mask[i]) { // one valid channel in between
333                                //  means that we deal with a separate line
334                            add_new_line=True;
335                            break;
336                        }
337               } else add_new_line=True;
338               if (add_new_line)
339                   lines.push_back(cur_line);
340               else lines.back().second=cur_line.second;                   
341           }
342           is_detected_before=False;
343       }     
344  }
345  catch (const AipsError &ae) {
346      throw;
347  } 
348  catch (const exception &ex) {
349      throw AipsError(String("SDLineFinder::processCurLine - STL error: ")+ex.what());
350  }
351}
352
353// find spectral lines and add them into list
354void LFRunningMean::findLines(std::list<pair<int,int> > &lines,
355                              IStatHolder* statholder)
356                        throw(casa::AipsError)
357{
358  const int minboxnchan=4;
359
360  // fill statistics for initial box
361  box_chan_cntr=0; // no channels are currently in the box
362  sum=0;           // initialize statistics
363  sumsq=0;
364  int initial_box_ch=edge.first;
365  for (;initial_box_ch<edge.second && box_chan_cntr<max_box_nchan;
366        ++initial_box_ch)
367       advanceRunningBox(initial_box_ch);
368   
369  if (initial_box_ch==edge.second)       
370      throw AipsError("LFRunningMean::findLines - too much channels are masked");
371
372  // actual search algorithm
373  is_detected_before=False;
374
375  if (statholder!=NULL)
376      for (int n=0;n<initial_box_ch-max_box_nchan/2;++n)
377           statholder->accumulate(n,sum,sumsq,box_chan_cntr);
378
379  if (box_chan_cntr>=minboxnchan)
380      // there is a minimum amount of data. We can search in the
381      // half of the initial box   
382      for (int n=edge.first;n<initial_box_ch-max_box_nchan/2;++n)
383           processChannel(lines,n);               
384 
385  // now the box can be moved. n+max_box_nchan/2 is a new index which haven't
386  // yet been included in the running mean.
387  for (int n=initial_box_ch-max_box_nchan/2;n<edge.second;++n) {
388      advanceRunningBox(n+max_box_nchan/2); // update running mean & variance
389      if (statholder!=NULL)
390          statholder->accumulate(n,sum,sumsq,box_chan_cntr);
391      if (box_chan_cntr>=minboxnchan) // have enough data to process
392          processChannel(lines,n);
393      else processCurLine(lines); // just finish what was accumulated before
394  }
395  if (statholder!=NULL)
396      for (int n=edge.second;n<spectrum.nelements();++n)
397           statholder->accumulate(n,sum,sumsq,box_chan_cntr);
398}
399
400//
401///////////////////////////////////////////////////////////////////////////////
402
403///////////////////////////////////////////////////////////////////////////////
404//
405// SDLineFinder::IntersectsWith  -  An auxiliary object function to test
406// whether two lines have a non-void intersection
407//
408
409
410// line1 - range of the first line: start channel and stop+1
411SDLineFinder::IntersectsWith::IntersectsWith(const std::pair<int,int> &in_line1) :
412                          line1(in_line1) {}
413
414
415// return true if line2 intersects with line1 with at least one
416// common channel, and false otherwise
417// line2 - range of the second line: start channel and stop+1
418bool SDLineFinder::IntersectsWith::operator()(const std::pair<int,int> &line2)
419                          const throw()
420{
421  if (line2.second<line1.first) return false; // line2 is at lower channels
422  if (line2.first>line1.second) return false; // line2 is at upper channels
423  return true; // line2 has an intersection or is adjacent to line1
424}
425
426//
427///////////////////////////////////////////////////////////////////////////////
428
429///////////////////////////////////////////////////////////////////////////////
430//
431// SDLineFinder::BuildUnion - An auxiliary object function to build a union
432// of several lines to account for a possibility of merging the nearby lines
433//
434
435// set an initial line (can be a first line in the sequence)
436SDLineFinder::BuildUnion::BuildUnion(const std::pair<int,int> &line1) :
437                             temp_line(line1) {}
438
439// update temp_line with a union of temp_line and new_line
440// provided there is no gap between the lines
441void SDLineFinder::BuildUnion::operator()(const std::pair<int,int> &new_line)
442                                   throw()
443{
444  if (new_line.first<temp_line.first) temp_line.first=new_line.first;
445  if (new_line.second>temp_line.second) temp_line.second=new_line.second;
446}
447
448// return the result (temp_line)
449const std::pair<int,int>& SDLineFinder::BuildUnion::result() const throw()
450{
451  return temp_line;
452}
453
454//
455///////////////////////////////////////////////////////////////////////////////
456
457///////////////////////////////////////////////////////////////////////////////
458//
459// SDLineFinder::LaterThan - An auxiliary object function to test whether a
460// specified line is at lower spectral channels (to preserve the order in
461// the line list)
462//
463
464// setup the line to compare with
465SDLineFinder::LaterThan::LaterThan(const std::pair<int,int> &in_line1) :
466                         line1(in_line1) {}
467
468// return true if line2 should be placed later than line1
469// in the ordered list (so, it is at greater channel numbers)
470bool SDLineFinder::LaterThan::operator()(const std::pair<int,int> &line2)
471                          const throw()
472{
473  if (line2.second<line1.first) return false; // line2 is at lower channels
474  if (line2.first>line1.second) return true; // line2 is at upper channels
475 
476  // line2 intersects with line1. We should have no such situation in
477  // practice
478  return line2.first>line1.first;
479}
480
481//
482///////////////////////////////////////////////////////////////////////////////
483
484
485///////////////////////////////////////////////////////////////////////////////
486//
487// SDLineFinder  -  a class for automated spectral line search
488//
489//
490
491SDLineFinder::SDLineFinder() throw() : edge(0,0)
492{
493  // detection threshold - the minimal signal to noise ratio
494  threshold=3.; // 3 sigma is a default
495  box_size=1./16.; // default box size for running mean calculations is
496                  // 1/16 of the whole spectrum
497  // A minimum number of consequtive channels, which should satisfy
498  // the detection criterion, to be a detection
499  min_nchan=3;     // default is 3 channels
500}
501
502SDLineFinder::~SDLineFinder() throw(AipsError) {}
503
504// set scan to work with (in_scan parameter), associated mask (in_mask
505// parameter) and the edge channel rejection (in_edge parameter)
506//   if in_edge has zero length, all channels chosen by mask will be used
507//   if in_edge has one element only, it represents the number of
508//      channels to drop from both sides of the spectrum
509//   in_edge is introduced for convinience, although all functionality
510//   can be achieved using a spectrum mask only   
511void SDLineFinder::setScan(const SDMemTableWrapper &in_scan,
512               const std::vector<bool> &in_mask,
513               const boost::python::tuple &in_edge) throw(AipsError)
514{
515  try {
516       scan=in_scan.getCP();
517       AlwaysAssert(!scan.null(),AipsError);
518       if (scan->nRow()!=1)
519           throw AipsError("SDLineFinder::setScan - in_scan contains more than 1 row."
520                           "Choose one first.");       
521       mask=in_mask;
522       if (mask.nelements()!=scan->nChan())
523           throw AipsError("SDLineFinder::setScan - in_scan and in_mask have different"
524                           "number of spectral channels.");
525
526       // number of elements in the in_edge tuple
527       int n=extract<int>(in_edge.attr("__len__")());
528       if (n>2 || n<0)
529           throw AipsError("SDLineFinder::setScan - the length of the in_edge parameter"
530                           "should not exceed 2");
531       if (!n) {
532           // all spectrum, no rejection
533           edge.first=0;
534           edge.second=scan->nChan();
535       } else {
536           edge.first=extract<int>(in_edge.attr("__getitem__")(0));
537           if (edge.first<0)
538               throw AipsError("SDLineFinder::setScan - the in_edge parameter has a negative"
539                                "number of channels to drop");
540           if (edge.first>=scan->nChan())
541               throw AipsError("SDLineFinder::setScan - all channels are rejected by the in_edge parameter");
542           if (n==2) {
543               edge.second=extract<int>(in_edge.attr("__getitem__")(1));
544               if (edge.second<0)
545                   throw AipsError("SDLineFinder::setScan - the in_edge parameter has a negative"
546                                   "number of channels to drop");
547               edge.second=scan->nChan()-edge.second;
548           } else edge.second=scan->nChan()-edge.first;
549           if (edge.second<0 || (edge.second+edge.first)>scan->nChan())
550               throw AipsError("SDLineFinder::setScan - all channels are rejected by the in_edge parameter");
551       }       
552  }
553  catch(const AipsError &ae) {
554       // setScan is unsuccessfull, reset scan/mask/edge
555       scan=CountedConstPtr<SDMemTable>(); // null pointer
556       mask.resize(0);
557       edge=pair<int,int>(0,0);
558       throw;
559  }
560}
561
562// search for spectral lines. Number of lines found is returned
563int SDLineFinder::findLines() throw(casa::AipsError)
564{
565  const int minboxnchan=4;
566  if (scan.null())
567      throw AipsError("SDLineFinder::findLines - a scan should be set first,"
568                      " use set_scan");
569  DebugAssert(mask.nelements()==scan->nChan(), AipsError);
570  int max_box_nchan=int(scan->nChan()*box_size); // number of channels in running
571                                                 // box
572  if (max_box_nchan<2)
573      throw AipsError("SDLineFinder::findLines - box_size is too small");
574
575  scan->getSpectrum(spectrum);
576
577  lines.resize(0); // search from the scratch
578  Vector<Bool> temp_mask(mask);
579 
580  while (true) {
581     // line find algorithm
582     LFRunningMean lfalg(spectrum,temp_mask,edge,max_box_nchan,min_nchan,
583                         threshold);
584     SignAccumulator sacc(spectrum.nelements(),spectrum);
585
586     // a buffer for new lines found at this iteration
587     std::list<pair<int,int> > new_lines;
588     
589     lfalg.findLines(new_lines,&sacc);
590     if (!new_lines.size()) break; // nothing new
591
592     searchForWings(new_lines, sacc.getSigns());
593
594     // update the list (lines) merging intervals, if necessary
595     addNewSearchResult(new_lines,lines);
596     // get a new mask
597     temp_mask=getMask();     
598  }
599  return int(lines.size());
600}
601
602
603// get the mask to mask out all lines that have been found (default)
604// if invert=true, only channels belong to lines will be unmasked
605// Note: all channels originally masked by the input mask (in_mask
606//       in setScan) or dropped out by the edge parameter (in_edge
607//       in setScan) are still excluded regardless on the invert option
608std::vector<bool> SDLineFinder::getMask(bool invert)
609                                        const throw(casa::AipsError)
610{
611  try {
612       if (scan.null())
613           throw AipsError("SDLineFinder::getMask - a scan should be set first,"
614                      " use set_scan followed by find_lines");
615       DebugAssert(mask.nelements()==scan->nChan(), AipsError);
616       /*
617       if (!lines.size())
618           throw AipsError("SDLineFinder::getMask - one have to search for "
619                           "lines first, use find_lines");
620       */                         
621       std::vector<bool> res_mask(mask.nelements());
622       // iterator through lines
623       std::list<std::pair<int,int> >::const_iterator cli=lines.begin();
624       for (int ch=0;ch<res_mask.size();++ch)
625            if (ch<edge.first || ch>=edge.second) res_mask[ch]=false;
626            else if (!mask[ch]) res_mask[ch]=false;
627            else {           
628                    res_mask[ch]=!invert; // no line by default
629                    if (cli==lines.end()) continue;
630                    if (ch>=cli->first && ch<cli->second)
631                        res_mask[ch]=invert; // this is a line
632                    if (ch>=cli->second)
633                        ++cli; // next line in the list
634                 }
635       
636       return res_mask;
637  }
638  catch (const AipsError &ae) {
639      throw;
640  } 
641  catch (const exception &ex) {
642      throw AipsError(String("SDLineFinder::getMask - STL error: ")+ex.what());
643  }
644}
645
646// get range for all lines found. If defunits is true (default), the
647// same units as used in the scan will be returned (e.g. velocity
648// instead of channels). If defunits is false, channels will be returned
649std::vector<int> SDLineFinder::getLineRanges(bool defunits)
650                             const throw(casa::AipsError)
651{
652  try {
653       if (scan.null())
654           throw AipsError("SDLineFinder::getLineRanges - a scan should be set first,"
655                      " use set_scan followed by find_lines");
656       DebugAssert(mask.nelements()==scan->nChan(), AipsError);
657       
658       if (!lines.size())
659           throw AipsError("SDLineFinder::getLineRanges - one have to search for "
660                           "lines first, use find_lines");
661                           
662       // temporary
663       if (defunits)
664           throw AipsError("SDLineFinder::getLineRanges - sorry, defunits=true have not "
665                           "yet been implemented");
666       //
667       std::vector<int> res(2*lines.size());
668       // iterator through lines & result
669       std::list<std::pair<int,int> >::const_iterator cli=lines.begin();
670       std::vector<int>::iterator ri=res.begin();
671       for (;cli!=lines.end() && ri!=res.end();++cli,++ri) {           
672            *ri=cli->first;
673            if (++ri!=res.end())
674                *ri=cli->second-1;         
675       }
676       return res;
677  }
678  catch (const AipsError &ae) {
679      throw;
680  } 
681  catch (const exception &ex) {
682      throw AipsError(String("SDLineFinder::getLineRanges - STL error: ")+ex.what());
683  }
684}
685
686// concatenate two lists preserving the order. If two lines appear to
687// be adjacent, they are joined into the new one
688void SDLineFinder::addNewSearchResult(const std::list<pair<int, int> > &newlines,
689                         std::list<std::pair<int, int> > &lines_list)
690                        throw(AipsError)
691{
692  try {
693      for (std::list<pair<int,int> >::const_iterator cli=newlines.begin();
694           cli!=newlines.end();++cli) {
695           
696           // the first item, which has a non-void intersection or touches
697           // the new line
698           std::list<pair<int,int> >::iterator pos_beg=find_if(lines_list.begin(),
699                          lines_list.end(), IntersectsWith(*cli));           
700           // the last such item         
701           std::list<pair<int,int> >::iterator pos_end=find_if(pos_beg,
702                          lines_list.end(), not1(IntersectsWith(*cli)));
703
704           // extract all lines which intersect or touch a new one into
705           // a temporary buffer. This may invalidate the iterators
706           // line_buffer may be empty, if no lines intersects with a new
707           // one.
708           std::list<pair<int,int> > lines_buffer;
709           lines_buffer.splice(lines_buffer.end(),lines_list, pos_beg, pos_end);
710
711           // build a union of all intersecting lines
712           pair<int,int> union_line=for_each(lines_buffer.begin(),
713                   lines_buffer.end(),BuildUnion(*cli)).result();
714           
715           // search for a right place for the new line (union_line) and add
716           std::list<pair<int,int> >::iterator pos2insert=find_if(lines_list.begin(),
717                          lines_list.end(), LaterThan(union_line));
718           lines_list.insert(pos2insert,union_line);
719      }
720  }
721  catch (const AipsError &ae) {
722      throw;
723  } 
724  catch (const exception &ex) {
725      throw AipsError(String("SDLineFinder::addNewSearchResult - STL error: ")+ex.what());
726  }
727}
728
729// extend all line ranges to the point where a value stored in the
730// specified vector changes (e.g. value-mean change its sign)
731// This operation is necessary to include line wings, which are below
732// the detection threshold. If lines becomes adjacent, they are
733// merged together. Any masked channel stops the extension
734void SDLineFinder::searchForWings(std::list<std::pair<int, int> > &newlines,
735           const casa::Vector<casa::Int> &signs) throw(casa::AipsError)
736{
737  try {
738      for (std::list<pair<int,int> >::iterator li=newlines.begin();
739           li!=newlines.end();++li) {
740           // update the left hand side
741           for (int n=li->first-1;n>=edge.first;--n) {
742                if (!mask[n]) break;
743                if (signs[n]==signs[li->first] && signs[li->first])
744                    li->first=n;
745                else break;   
746           }
747           // update the right hand side
748           for (int n=li->second;n<edge.second;++n) {
749                if (!mask[n]) break;
750                if (signs[n]==signs[li->second-1] && signs[li->second-1])
751                    li->second=n;
752                else break;   
753           }
754      }
755      // need to search for possible mergers.
756      std::list<std::pair<int, int> >  result_buffer;
757      addNewSearchResult(newlines,result_buffer);
758      newlines.clear();
759      newlines.splice(newlines.end(),result_buffer);
760  }
761  catch (const AipsError &ae) {
762      throw;
763  } 
764  catch (const exception &ex) {
765      throw AipsError(String("SDLineFinder::extendLines - STL error: ")+ex.what());
766  }
767}
Note: See TracBrowser for help on using the repository browser.