source: trunk/src/SDLineFinder.cc@ 351

Last change on this file since 351 was 351, checked in by vor010, 20 years ago

SDLineFinder - interface of auxiliary classes
has been changed to simplify experiments with algorithms. Functionality is
the same as before

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.5 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#include <fstream>
41
42using namespace asap;
43using namespace casa;
44using namespace std;
45using namespace boost::python;
46
47namespace asap {
48
49///////////////////////////////////////////////////////////////////////////////
50//
51// IStatHolder - an abstract class to collect statistics from the running
52// mean calculator, if necessary.
53// We define it here, because it is used in LFRunningMean and
54// SDLineFinder only
55//
56
57struct IStatHolder {
58 // This function is called for each spectral channel processed by
59 // the running mean calculator. The order of channel numbers may be
60 // arbitrary
61 // ch - a number of channel, corresponding to (approximately) the centre
62 // of the running box
63 // box_nchan - number of channels in the box
64 //
65 virtual void accumulate(int ch, Float sum, Float sum2, int box_nchan)
66 throw(AipsError) = 0;
67};
68
69//
70///////////////////////////////////////////////////////////////////////////////
71
72///////////////////////////////////////////////////////////////////////////////
73//
74// SignAccumulator - a simple class to deal with running mean statistics:
75// it stores the sign of the value-mean only
76//
77class SignAccumulator : public IStatHolder {
78 Vector<Int> sign; // either +1, -1 or 0
79 const Vector<Float> &spectrum; // a reference to the spectrum
80 // to calculate the sign
81public:
82 // all channels >=nchan are ignored
83 SignAccumulator(uInt nchan, const Vector<Float> &in_spectrum);
84
85 // This function is called for each spectral channel processed by
86 // the running mean calculator. The order of channel numbers may be
87 // arbitrary
88 // ch - a number of channel, corresponding to (approximately) the centre
89 // of the running box
90 // box_nchan - number of channels in the box
91 //
92 virtual void accumulate(int ch, Float sum, Float, int box_nchan)
93 throw(AipsError);
94
95 // access to the sign
96 const Vector<Int>& getSigns() const throw();
97};
98
99//
100///////////////////////////////////////////////////////////////////////////////
101
102///////////////////////////////////////////////////////////////////////////////
103//
104// RunningBox - a running box calculator. This class implements
105// interations over the specified spectrum and calculates
106// running box filter statistics.
107//
108
109class RunningBox {
110 // The input data to work with. Use reference symantics to avoid
111 // an unnecessary copying
112 const casa::Vector<casa::Float> &spectrum; // a buffer for the spectrum
113 const casa::Vector<casa::Bool> &mask; // associated mask
114 const std::pair<int,int> &edge; // start and stop+1 channels
115 // to work with
116
117 // statistics for running box filtering
118 casa::Float sumf; // sum of fluxes
119 casa::Float sumf2; // sum of squares of fluxes
120 casa::Float sumch; // sum of channel numbers (for linear fit)
121 casa::Float sumch2; // sum of squares of channel numbers (for linear fit)
122 casa::Float sumfch; // sum of flux*(channel number) (for linear fit)
123
124 int box_chan_cntr; // actual number of channels in the box
125 int max_box_nchan; // maximum allowed number of channels in the box
126 // (calculated from boxsize and actual spectrum size)
127 // cache for derivative statistics
128 mutable casa::Bool need2recalculate; // if true, values of the statistics
129 // below are invalid
130 mutable casa::Float linmean; // a value of the linear fit to the
131 // points in the running box
132 mutable casa::Float linvariance; // the same for variance
133 int cur_channel; // the number of the current channel
134 int start_advance; // number of channel from which the box can
135 // be moved (the middle of the box, if there is no
136 // masking)
137public:
138 // set up the object with the references to actual data
139 // as well as the number of channels in the running box
140 RunningBox(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) throw(AipsError);
144
145 // access to the statistics
146 const casa::Float& getLinMean() const throw(AipsError);
147 const casa::Float& getLinVariance() const throw(AipsError);
148 const casa::Float aboveMean() const throw(AipsError);
149 int getChannel() const throw();
150
151 // actual number of channels in the box (max_box_nchan, if no channels
152 // are masked)
153 int getNumberOfBoxPoints() const throw();
154
155 // next channel
156 void next() throw(AipsError);
157
158 // checking whether there are still elements
159 casa::Bool haveMore() const throw();
160
161 // go to start
162 void rewind() throw(AipsError);
163
164protected:
165 // supplementary function to control running mean calculations.
166 // It adds a specified channel to the running mean box and
167 // removes (ch-maxboxnchan+1)'th channel from there
168 // Channels, for which the mask is false or index is beyond the
169 // allowed range, are ignored
170 void advanceRunningBox(int ch) throw(casa::AipsError);
171
172 // calculate derivative statistics. This function is const, because
173 // it updates the cache only
174 void updateDerivativeStatistics() const throw(AipsError);
175};
176
177//
178///////////////////////////////////////////////////////////////////////////////
179
180///////////////////////////////////////////////////////////////////////////////
181//
182// LFAboveThreshold An algorithm for line detection using running box
183// statistics. Line is detected if it is above the
184// specified threshold at the specified number of
185// consequtive channels. Prefix LF stands for Line Finder
186//
187class LFAboveThreshold {
188 // temporary line edge channels and flag, which is True if the line
189 // was detected in the previous channels.
190 std::pair<int,int> cur_line;
191 casa::Bool is_detected_before;
192 int min_nchan; // A minimum number of consequtive
193 // channels, which should satisfy
194 // the detection criterion, to be
195 // a detection
196 casa::Float threshold; // detection threshold - the
197 // minimal signal to noise ratio
198 std::list<pair<int,int> > &lines; // list where detections are saved
199 // (pair: start and stop+1 channel)
200 RunningBox *running_box; // running box filter
201public:
202
203 // set up the detection criterion
204 LFAboveThreshold(std::list<pair<int,int> > &in_lines,
205 int in_min_nchan = 3,
206 casa::Float in_threshold = 5) throw();
207 virtual ~LFAboveThreshold() throw();
208
209 // replace the detection criterion
210 void setCriterion(int in_min_nchan, casa::Float in_threshold) throw();
211
212 // find spectral lines and add them into list
213 // if statholder is not NULL, the accumulate function of it will be
214 // called for each channel to save statistics
215 // spectrum, mask and edge - reference to the data
216 // max_box_nchan - number of channels in the running box
217 void findLines(const casa::Vector<casa::Float> &spectrum,
218 const casa::Vector<casa::Bool> &mask,
219 const std::pair<int,int> &edge,
220 int max_box_nchan,
221 IStatHolder* statholder = NULL) throw(casa::AipsError);
222
223protected:
224
225 // process a channel: update curline and is_detected before and
226 // add a new line to the list, if necessary using processCurLine()
227 // detect=true indicates that the current channel satisfies the criterion
228 void processChannel(Bool detect, const casa::Vector<casa::Bool> &mask)
229 throw(casa::AipsError);
230
231 // process the interval of channels stored in curline
232 // if it satisfies the criterion, add this interval as a new line
233 void processCurLine(const casa::Vector<casa::Bool> &mask)
234 throw(casa::AipsError);
235};
236
237//
238///////////////////////////////////////////////////////////////////////////////
239
240} // namespace asap
241
242///////////////////////////////////////////////////////////////////////////////
243//
244// SignAccumulator - a simple class to deal with running mean statistics:
245// it stores the sign of the value-mean only
246//
247
248// all channels >=nchan are ignored
249SignAccumulator::SignAccumulator(uInt nchan,
250 const Vector<Float> &in_spectrum) : sign(nchan,0),
251 spectrum(in_spectrum) {}
252
253
254// This function is called for each spectral channel processed by
255// the running mean calculator. The order of channel numbers may be
256// arbitrary
257// ch - a number of channel, corresponding to (approximately) the centre
258// of the running box
259// box_nchan - number of channels in the box
260//
261void SignAccumulator::accumulate(int ch, Float sum, Float sum2, int box_nchan)
262 throw(AipsError)
263{
264 if (ch>=sign.nelements()) return;
265 DebugAssert(ch>=0,AipsError);
266 DebugAssert(ch<=spectrum.nelements(), AipsError);
267 if (box_nchan) {
268 Float buf=spectrum[ch]-sum; ///Float(box_nchan);
269 if (buf>0) sign[ch]=1;
270 else if (buf<0) sign[ch]=-1;
271 else sign[ch]=0;
272 } else sign[ch]=0;
273}
274
275// access to the sign
276const Vector<Int>& SignAccumulator::getSigns() const throw()
277{
278 return sign;
279}
280
281
282//
283///////////////////////////////////////////////////////////////////////////////
284
285///////////////////////////////////////////////////////////////////////////////
286//
287// RunningBox - a running box calculator. This class implements
288// interations over the specified spectrum and calculates
289// running box filter statistics.
290//
291
292// set up the object with the references to actual data
293// and the number of channels in the running box
294RunningBox::RunningBox(const casa::Vector<casa::Float> &in_spectrum,
295 const casa::Vector<casa::Bool> &in_mask,
296 const std::pair<int,int> &in_edge,
297 int in_max_box_nchan) throw(AipsError) :
298 spectrum(in_spectrum), mask(in_mask), edge(in_edge),
299 max_box_nchan(in_max_box_nchan)
300{
301 rewind();
302}
303
304void RunningBox::rewind() throw(AipsError) {
305 // fill statistics for initial box
306 box_chan_cntr=0; // no channels are currently in the box
307 sumf=0.; // initialize statistics
308 sumf2=0.;
309 sumch=0.;
310 sumch2=0.;
311 sumfch=0.;
312 int initial_box_ch=edge.first;
313 for (;initial_box_ch<edge.second && box_chan_cntr<max_box_nchan;
314 ++initial_box_ch)
315 advanceRunningBox(initial_box_ch);
316
317 if (initial_box_ch==edge.second)
318 throw AipsError("RunningBox::rewind - too much channels are masked");
319
320 cur_channel=edge.first;
321 start_advance=initial_box_ch-max_box_nchan/2;
322}
323
324// access to the statistics
325const casa::Float& RunningBox::getLinMean() const throw(AipsError)
326{
327 DebugAssert(cur_channel<edge.second, AipsError);
328 if (need2recalculate) updateDerivativeStatistics();
329 return linmean;
330}
331
332const casa::Float& RunningBox::getLinVariance() const throw(AipsError)
333{
334 DebugAssert(cur_channel<edge.second, AipsError);
335 if (need2recalculate) updateDerivativeStatistics();
336 return linvariance;
337}
338
339const casa::Float RunningBox::aboveMean() const throw(AipsError)
340{
341 DebugAssert(cur_channel<edge.second, AipsError);
342 if (need2recalculate) updateDerivativeStatistics();
343 return spectrum[cur_channel]-linmean;
344}
345
346int RunningBox::getChannel() const throw()
347{
348 return cur_channel;
349}
350
351// actual number of channels in the box (max_box_nchan, if no channels
352// are masked)
353int RunningBox::getNumberOfBoxPoints() const throw()
354{
355 return box_chan_cntr;
356}
357
358// supplementary function to control running mean calculations.
359// It adds a specified channel to the running mean box and
360// removes (ch-max_box_nchan+1)'th channel from there
361// Channels, for which the mask is false or index is beyond the
362// allowed range, are ignored
363void RunningBox::advanceRunningBox(int ch) throw(AipsError)
364{
365 if (ch>=edge.first && ch<edge.second)
366 if (mask[ch]) { // ch is a valid channel
367 ++box_chan_cntr;
368 sumf+=spectrum[ch];
369 sumf2+=square(spectrum[ch]);
370 sumch+=Float(ch);
371 sumch2+=square(Float(ch));
372 sumfch+=spectrum[ch]*Float(ch);
373 need2recalculate=True;
374 }
375 int ch2remove=ch-max_box_nchan;
376 if (ch2remove>=edge.first && ch2remove<edge.second)
377 if (mask[ch2remove]) { // ch2remove is a valid channel
378 --box_chan_cntr;
379 sumf-=spectrum[ch2remove];
380 sumf2-=square(spectrum[ch2remove]);
381 sumch-=Float(ch2remove);
382 sumch2-=square(Float(ch2remove));
383 sumfch-=spectrum[ch2remove]*Float(ch2remove);
384 need2recalculate=True;
385 }
386}
387
388// next channel
389void RunningBox::next() throw(AipsError)
390{
391 AlwaysAssert(cur_channel<edge.second,AipsError);
392 ++cur_channel;
393 if (cur_channel+max_box_nchan/2<edge.second && cur_channel>=start_advance)
394 advanceRunningBox(cur_channel+max_box_nchan/2); // update statistics
395}
396
397// checking whether there are still elements
398casa::Bool RunningBox::haveMore() const throw()
399{
400 return cur_channel<edge.second;
401}
402
403// calculate derivative statistics. This function is const, because
404// it updates the cache only
405void RunningBox::updateDerivativeStatistics() const throw(AipsError)
406{
407 AlwaysAssert(box_chan_cntr, AipsError);
408
409 Float mean=sumf/Float(box_chan_cntr);
410
411 // linear LSF formulae
412 Float meanch=sumch/Float(box_chan_cntr);
413 Float meanch2=sumch2/Float(box_chan_cntr);
414 if (meanch==meanch2 || box_chan_cntr<3) {
415 // vertical line in the spectrum, can't calculate linmean and linvariance
416 linmean=0.;
417 linvariance=0.;
418 } else {
419 Float coeff=(sumfch/Float(box_chan_cntr)-meanch*mean)/
420 (meanch2-square(meanch));
421 linmean=coeff*(Float(cur_channel)-meanch)+mean;
422 linvariance=sqrt(sumf2/Float(box_chan_cntr)-square(mean)-
423 square(coeff)*(meanch2-square(meanch)));
424 }
425 need2recalculate=False;
426}
427
428
429//
430///////////////////////////////////////////////////////////////////////////////
431
432///////////////////////////////////////////////////////////////////////////////
433//
434// LFAboveThreshold - a running mean algorithm for line detection
435//
436//
437
438
439// set up the detection criterion
440LFAboveThreshold::LFAboveThreshold(std::list<pair<int,int> > &in_lines,
441 int in_min_nchan,
442 casa::Float in_threshold) throw() :
443 min_nchan(in_min_nchan), threshold(in_threshold),
444 lines(in_lines), running_box(NULL) {}
445
446LFAboveThreshold::~LFAboveThreshold() throw()
447{
448 if (running_box!=NULL) delete running_box;
449}
450
451// replace the detection criterion
452void LFAboveThreshold::setCriterion(int in_min_nchan, casa::Float in_threshold)
453 throw()
454{
455 min_nchan=in_min_nchan;
456 threshold=in_threshold;
457}
458
459
460// process a channel: update cur_line and is_detected before and
461// add a new line to the list, if necessary
462void LFAboveThreshold::processChannel(Bool detect,
463 const casa::Vector<casa::Bool> &mask) throw(casa::AipsError)
464{
465 try {
466 if (detect) {
467 if (is_detected_before)
468 cur_line.second=running_box->getChannel()+1;
469 else {
470 is_detected_before=True;
471 cur_line.first=running_box->getChannel();
472 cur_line.second=running_box->getChannel()+1;
473 }
474 } else processCurLine(mask);
475 }
476 catch (const AipsError &ae) {
477 throw;
478 }
479 catch (const exception &ex) {
480 throw AipsError(String("LFAboveThreshold::processChannel - STL error: ")+ex.what());
481 }
482}
483
484// process the interval of channels stored in cur_line
485// if it satisfies the criterion, add this interval as a new line
486void LFAboveThreshold::processCurLine(const casa::Vector<casa::Bool> &mask)
487 throw(casa::AipsError)
488{
489 try {
490 if (is_detected_before) {
491 if (cur_line.second-cur_line.first>min_nchan) {
492 // it was a detection. We need to change the list
493 Bool add_new_line=False;
494 if (lines.size()) {
495 for (int i=lines.back().second;i<cur_line.first;++i)
496 if (mask[i]) { // one valid channel in between
497 // means that we deal with a separate line
498 add_new_line=True;
499 break;
500 }
501 } else add_new_line=True;
502 if (add_new_line)
503 lines.push_back(cur_line);
504 else lines.back().second=cur_line.second;
505 }
506 is_detected_before=False;
507 }
508 }
509 catch (const AipsError &ae) {
510 throw;
511 }
512 catch (const exception &ex) {
513 throw AipsError(String("LFAboveThreshold::processCurLine - STL error: ")+ex.what());
514 }
515}
516
517// find spectral lines and add them into list
518void LFAboveThreshold::findLines(const casa::Vector<casa::Float> &spectrum,
519 const casa::Vector<casa::Bool> &mask,
520 const std::pair<int,int> &edge,
521 int max_box_nchan,
522 IStatHolder* statholder)
523 throw(casa::AipsError)
524{
525 const int minboxnchan=4;
526 try {
527
528 if (running_box!=NULL) delete running_box;
529 running_box=new RunningBox(spectrum,mask,edge,max_box_nchan);
530
531 // actual search algorithm
532 is_detected_before=False;
533
534 for (;running_box->haveMore();running_box->next()) {
535 const int ch=running_box->getChannel();
536 if (running_box->getNumberOfBoxPoints()>=minboxnchan)
537 processChannel(mask[ch] && (fabs(spectrum[ch]-
538 running_box->getLinMean()) >=
539 threshold*running_box->getLinVariance()), mask);
540 else processCurLine(mask); // just finish what was accumulated before
541 if (statholder!=NULL)
542 statholder->accumulate(running_box->getChannel(),
543 running_box->getLinMean(),
544 running_box->getLinVariance(),
545 running_box->getNumberOfBoxPoints());
546 }
547 }
548 catch (const AipsError &ae) {
549 throw;
550 }
551 catch (const exception &ex) {
552 throw AipsError(String("LFAboveThreshold::findLines - STL error: ")+ex.what());
553 }
554}
555
556//
557///////////////////////////////////////////////////////////////////////////////
558
559///////////////////////////////////////////////////////////////////////////////
560//
561// SDLineFinder::IntersectsWith - An auxiliary object function to test
562// whether two lines have a non-void intersection
563//
564
565
566// line1 - range of the first line: start channel and stop+1
567SDLineFinder::IntersectsWith::IntersectsWith(const std::pair<int,int> &in_line1) :
568 line1(in_line1) {}
569
570
571// return true if line2 intersects with line1 with at least one
572// common channel, and false otherwise
573// line2 - range of the second line: start channel and stop+1
574bool SDLineFinder::IntersectsWith::operator()(const std::pair<int,int> &line2)
575 const throw()
576{
577 if (line2.second<line1.first) return false; // line2 is at lower channels
578 if (line2.first>line1.second) return false; // line2 is at upper channels
579 return true; // line2 has an intersection or is adjacent to line1
580}
581
582//
583///////////////////////////////////////////////////////////////////////////////
584
585///////////////////////////////////////////////////////////////////////////////
586//
587// SDLineFinder::BuildUnion - An auxiliary object function to build a union
588// of several lines to account for a possibility of merging the nearby lines
589//
590
591// set an initial line (can be a first line in the sequence)
592SDLineFinder::BuildUnion::BuildUnion(const std::pair<int,int> &line1) :
593 temp_line(line1) {}
594
595// update temp_line with a union of temp_line and new_line
596// provided there is no gap between the lines
597void SDLineFinder::BuildUnion::operator()(const std::pair<int,int> &new_line)
598 throw()
599{
600 if (new_line.first<temp_line.first) temp_line.first=new_line.first;
601 if (new_line.second>temp_line.second) temp_line.second=new_line.second;
602}
603
604// return the result (temp_line)
605const std::pair<int,int>& SDLineFinder::BuildUnion::result() const throw()
606{
607 return temp_line;
608}
609
610//
611///////////////////////////////////////////////////////////////////////////////
612
613///////////////////////////////////////////////////////////////////////////////
614//
615// SDLineFinder::LaterThan - An auxiliary object function to test whether a
616// specified line is at lower spectral channels (to preserve the order in
617// the line list)
618//
619
620// setup the line to compare with
621SDLineFinder::LaterThan::LaterThan(const std::pair<int,int> &in_line1) :
622 line1(in_line1) {}
623
624// return true if line2 should be placed later than line1
625// in the ordered list (so, it is at greater channel numbers)
626bool SDLineFinder::LaterThan::operator()(const std::pair<int,int> &line2)
627 const throw()
628{
629 if (line2.second<line1.first) return false; // line2 is at lower channels
630 if (line2.first>line1.second) return true; // line2 is at upper channels
631
632 // line2 intersects with line1. We should have no such situation in
633 // practice
634 return line2.first>line1.first;
635}
636
637//
638///////////////////////////////////////////////////////////////////////////////
639
640
641///////////////////////////////////////////////////////////////////////////////
642//
643// SDLineFinder - a class for automated spectral line search
644//
645//
646
647SDLineFinder::SDLineFinder() throw() : edge(0,0)
648{
649 // detection threshold - the minimal signal to noise ratio
650 threshold=3.; // 3 sigma is a default
651 box_size=1./5.; // default box size for running mean calculations is
652 // 1/5 of the whole spectrum
653 // A minimum number of consequtive channels, which should satisfy
654 // the detection criterion, to be a detection
655 min_nchan=3; // default is 3 channels
656}
657
658SDLineFinder::~SDLineFinder() throw(AipsError) {}
659
660// set scan to work with (in_scan parameter), associated mask (in_mask
661// parameter) and the edge channel rejection (in_edge parameter)
662// if in_edge has zero length, all channels chosen by mask will be used
663// if in_edge has one element only, it represents the number of
664// channels to drop from both sides of the spectrum
665// in_edge is introduced for convinience, although all functionality
666// can be achieved using a spectrum mask only
667void SDLineFinder::setScan(const SDMemTableWrapper &in_scan,
668 const std::vector<bool> &in_mask,
669 const boost::python::tuple &in_edge) throw(AipsError)
670{
671 try {
672 scan=in_scan.getCP();
673 AlwaysAssert(!scan.null(),AipsError);
674 if (scan->nRow()!=1)
675 throw AipsError("SDLineFinder::setScan - in_scan contains more than 1 row."
676 "Choose one first.");
677 mask=in_mask;
678 if (mask.nelements()!=scan->nChan())
679 throw AipsError("SDLineFinder::setScan - in_scan and in_mask have different"
680 "number of spectral channels.");
681
682 // number of elements in the in_edge tuple
683 int n=extract<int>(in_edge.attr("__len__")());
684 if (n>2 || n<0)
685 throw AipsError("SDLineFinder::setScan - the length of the in_edge parameter"
686 "should not exceed 2");
687 if (!n) {
688 // all spectrum, no rejection
689 edge.first=0;
690 edge.second=scan->nChan();
691 } else {
692 edge.first=extract<int>(in_edge.attr("__getitem__")(0));
693 if (edge.first<0)
694 throw AipsError("SDLineFinder::setScan - the in_edge parameter has a negative"
695 "number of channels to drop");
696 if (edge.first>=scan->nChan())
697 throw AipsError("SDLineFinder::setScan - all channels are rejected by the in_edge parameter");
698 if (n==2) {
699 edge.second=extract<int>(in_edge.attr("__getitem__")(1));
700 if (edge.second<0)
701 throw AipsError("SDLineFinder::setScan - the in_edge parameter has a negative"
702 "number of channels to drop");
703 edge.second=scan->nChan()-edge.second;
704 } else edge.second=scan->nChan()-edge.first;
705 if (edge.second<0 || (edge.second+edge.first)>scan->nChan())
706 throw AipsError("SDLineFinder::setScan - all channels are rejected by the in_edge parameter");
707 }
708 }
709 catch(const AipsError &ae) {
710 // setScan is unsuccessfull, reset scan/mask/edge
711 scan=CountedConstPtr<SDMemTable>(); // null pointer
712 mask.resize(0);
713 edge=pair<int,int>(0,0);
714 throw;
715 }
716}
717
718// search for spectral lines. Number of lines found is returned
719int SDLineFinder::findLines() throw(casa::AipsError)
720{
721 const int minboxnchan=4;
722 if (scan.null())
723 throw AipsError("SDLineFinder::findLines - a scan should be set first,"
724 " use set_scan");
725 DebugAssert(mask.nelements()==scan->nChan(), AipsError);
726 int max_box_nchan=int(scan->nChan()*box_size); // number of channels in running
727 // box
728 if (max_box_nchan<2)
729 throw AipsError("SDLineFinder::findLines - box_size is too small");
730
731 scan->getSpectrum(spectrum);
732
733 lines.resize(0); // search from the scratch
734 Vector<Bool> temp_mask(mask);
735
736 Bool first_pass=True;
737 while (true) {
738 // a buffer for new lines found at this iteration
739 std::list<pair<int,int> > new_lines;
740
741 // line find algorithm
742 LFAboveThreshold lfalg(new_lines,min_nchan, threshold);
743
744 SignAccumulator sacc(spectrum.nelements(),spectrum);
745
746
747 try {
748 lfalg.findLines(spectrum,temp_mask,edge,max_box_nchan,&sacc);
749 }
750 catch(const AipsError &ae) {
751 if (first_pass) throw;
752 break; // nothing new
753 }
754 first_pass=False;
755 if (!new_lines.size()) break; // nothing new
756
757 searchForWings(new_lines, sacc.getSigns());
758
759 // update the list (lines) merging intervals, if necessary
760 addNewSearchResult(new_lines,lines);
761 // get a new mask
762 temp_mask=getMask();
763 }
764 return int(lines.size());
765}
766
767
768// get the mask to mask out all lines that have been found (default)
769// if invert=true, only channels belong to lines will be unmasked
770// Note: all channels originally masked by the input mask (in_mask
771// in setScan) or dropped out by the edge parameter (in_edge
772// in setScan) are still excluded regardless on the invert option
773std::vector<bool> SDLineFinder::getMask(bool invert)
774 const throw(casa::AipsError)
775{
776 try {
777 if (scan.null())
778 throw AipsError("SDLineFinder::getMask - a scan should be set first,"
779 " use set_scan followed by find_lines");
780 DebugAssert(mask.nelements()==scan->nChan(), AipsError);
781 /*
782 if (!lines.size())
783 throw AipsError("SDLineFinder::getMask - one have to search for "
784 "lines first, use find_lines");
785 */
786 std::vector<bool> res_mask(mask.nelements());
787 // iterator through lines
788 std::list<std::pair<int,int> >::const_iterator cli=lines.begin();
789 for (int ch=0;ch<res_mask.size();++ch)
790 if (ch<edge.first || ch>=edge.second) res_mask[ch]=false;
791 else if (!mask[ch]) res_mask[ch]=false;
792 else {
793 res_mask[ch]=!invert; // no line by default
794 if (cli==lines.end()) continue;
795 if (ch>=cli->first && ch<cli->second)
796 res_mask[ch]=invert; // this is a line
797 if (ch>=cli->second)
798 ++cli; // next line in the list
799 }
800
801 return res_mask;
802 }
803 catch (const AipsError &ae) {
804 throw;
805 }
806 catch (const exception &ex) {
807 throw AipsError(String("SDLineFinder::getMask - STL error: ")+ex.what());
808 }
809}
810
811// get range for all lines found. If defunits is true (default), the
812// same units as used in the scan will be returned (e.g. velocity
813// instead of channels). If defunits is false, channels will be returned
814std::vector<int> SDLineFinder::getLineRanges(bool defunits)
815 const throw(casa::AipsError)
816{
817 try {
818 if (scan.null())
819 throw AipsError("SDLineFinder::getLineRanges - a scan should be set first,"
820 " use set_scan followed by find_lines");
821 DebugAssert(mask.nelements()==scan->nChan(), AipsError);
822
823 if (!lines.size())
824 throw AipsError("SDLineFinder::getLineRanges - one have to search for "
825 "lines first, use find_lines");
826
827 // temporary
828 if (defunits)
829 throw AipsError("SDLineFinder::getLineRanges - sorry, defunits=true have not "
830 "yet been implemented");
831 //
832 std::vector<int> res(2*lines.size());
833 // iterator through lines & result
834 std::list<std::pair<int,int> >::const_iterator cli=lines.begin();
835 std::vector<int>::iterator ri=res.begin();
836 for (;cli!=lines.end() && ri!=res.end();++cli,++ri) {
837 *ri=cli->first;
838 if (++ri!=res.end())
839 *ri=cli->second-1;
840 }
841 return res;
842 }
843 catch (const AipsError &ae) {
844 throw;
845 }
846 catch (const exception &ex) {
847 throw AipsError(String("SDLineFinder::getLineRanges - STL error: ")+ex.what());
848 }
849}
850
851// concatenate two lists preserving the order. If two lines appear to
852// be adjacent, they are joined into the new one
853void SDLineFinder::addNewSearchResult(const std::list<pair<int, int> > &newlines,
854 std::list<std::pair<int, int> > &lines_list)
855 throw(AipsError)
856{
857 try {
858 for (std::list<pair<int,int> >::const_iterator cli=newlines.begin();
859 cli!=newlines.end();++cli) {
860
861 // the first item, which has a non-void intersection or touches
862 // the new line
863 std::list<pair<int,int> >::iterator pos_beg=find_if(lines_list.begin(),
864 lines_list.end(), IntersectsWith(*cli));
865 // the last such item
866 std::list<pair<int,int> >::iterator pos_end=find_if(pos_beg,
867 lines_list.end(), not1(IntersectsWith(*cli)));
868
869 // extract all lines which intersect or touch a new one into
870 // a temporary buffer. This may invalidate the iterators
871 // line_buffer may be empty, if no lines intersects with a new
872 // one.
873 std::list<pair<int,int> > lines_buffer;
874 lines_buffer.splice(lines_buffer.end(),lines_list, pos_beg, pos_end);
875
876 // build a union of all intersecting lines
877 pair<int,int> union_line=for_each(lines_buffer.begin(),
878 lines_buffer.end(),BuildUnion(*cli)).result();
879
880 // search for a right place for the new line (union_line) and add
881 std::list<pair<int,int> >::iterator pos2insert=find_if(lines_list.begin(),
882 lines_list.end(), LaterThan(union_line));
883 lines_list.insert(pos2insert,union_line);
884 }
885 }
886 catch (const AipsError &ae) {
887 throw;
888 }
889 catch (const exception &ex) {
890 throw AipsError(String("SDLineFinder::addNewSearchResult - STL error: ")+ex.what());
891 }
892}
893
894// extend all line ranges to the point where a value stored in the
895// specified vector changes (e.g. value-mean change its sign)
896// This operation is necessary to include line wings, which are below
897// the detection threshold. If lines becomes adjacent, they are
898// merged together. Any masked channel stops the extension
899void SDLineFinder::searchForWings(std::list<std::pair<int, int> > &newlines,
900 const casa::Vector<casa::Int> &signs) throw(casa::AipsError)
901{
902 try {
903 for (std::list<pair<int,int> >::iterator li=newlines.begin();
904 li!=newlines.end();++li) {
905 // update the left hand side
906 for (int n=li->first-1;n>=edge.first;--n) {
907 if (!mask[n]) break;
908 if (signs[n]==signs[li->first] && signs[li->first])
909 li->first=n;
910 else break;
911 }
912 // update the right hand side
913 for (int n=li->second;n<edge.second;++n) {
914 if (!mask[n]) break;
915 if (signs[n]==signs[li->second-1] && signs[li->second-1])
916 li->second=n;
917 else break;
918 }
919 }
920 // need to search for possible mergers.
921 std::list<std::pair<int, int> > result_buffer;
922 addNewSearchResult(newlines,result_buffer);
923 newlines.clear();
924 newlines.splice(newlines.end(),result_buffer);
925 }
926 catch (const AipsError &ae) {
927 throw;
928 }
929 catch (const exception &ex) {
930 throw AipsError(String("SDLineFinder::extendLines - STL error: ")+ex.what());
931 }
932}
Note: See TracBrowser for help on using the repository browser.