source: trunk/src/STLineFinder.cpp@ 2069

Last change on this file since 2069 was 2012, checked in by WataruKawasaki, 14 years ago

New Development: Yes

JIRA Issue: Yes (CAS-2373, CAS-2620)

Ready for Test: Yes

Interface Changes: Yes

What Interface Changed: For Scantable::polyBaseline(), parameters and return value have been changed.

Test Programs:

Put in Release Notes: Yes

Module(s): sdbaseline, sd.linefinder

Description: (1) CAS-2373-related:

(1.1) Modified Scantable::polyBaseline() to have the row-based loop inside.

Now it fits and subtracts baseline for all rows and also output info
about the fitting result to logger and text file, while in the
previous version this method just did baseline fitting/subtraction
for one row only and had to be put inside a row-based loop at the
python side ("poly_baseline()" in scantable.py) and result output had
also to be controlled at the python side. Using a test data from NRO
45m telescope (348,000 rows, 512 channels), the processing time of
scantable.poly_baseline() has reduced from 130 minutes to 5-6 minutes.

(1.2) For accelerating the another polynomial fitting function, namely

scantable.auto_poly_baseline(), added a method
Scantable::autoPolyBaseline(). This basically does the same thing
with Scantable::polyBaseline(), but uses linefinfer also to
automatically flag the line regions.

(1.3) To make linefinder usable in Scantable class, added a method

linefinder.setdata(). This makes it possible to apply linefinder
for a float-list data given as a parameter, without setting scantable,
while it was indispensable to set scantable to use linefinger previously.

(2) CAS-2620-related:

Added Scantable::cubicSplineBaseline() and autoCubicSplineBaseline() for
fit baseline using the cubic spline function. Parameters include npiece
(number of polynomial pieces), clipthresh (clipping threshold), and
clipniter (maximum iteration number).



  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 52.6 KB
Line 
1//#---------------------------------------------------------------------------
2//# STLineFinder.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: STLineFinder.cpp 2012 2011-02-25 05:51:50Z WataruKawasaki $
30//#---------------------------------------------------------------------------
31
32
33// ASAP
34#include "STLineFinder.h"
35#include "STFitter.h"
36#include "IndexedCompare.h"
37
38// STL
39#include <functional>
40#include <algorithm>
41#include <iostream>
42#include <fstream>
43
44using namespace asap;
45using namespace casa;
46using namespace std;
47
48namespace asap {
49
50///////////////////////////////////////////////////////////////////////////////
51//
52// RunningBox - a running box calculator. This class implements
53// iterations over the specified spectrum and calculates
54// running box filter statistics.
55//
56
57class RunningBox {
58 // The input data to work with. Use reference symantics to avoid
59 // an unnecessary copying
60 const casa::Vector<casa::Float> &spectrum; // a buffer for the spectrum
61 const casa::Vector<casa::Bool> &mask; // associated mask
62 const std::pair<int,int> &edge; // start and stop+1 channels
63 // to work with
64
65 // statistics for running box filtering
66 casa::Float sumf; // sum of fluxes
67 casa::Float sumf2; // sum of squares of fluxes
68 casa::Float sumch; // sum of channel numbers (for linear fit)
69 casa::Float sumch2; // sum of squares of channel numbers (for linear fit)
70 casa::Float sumfch; // sum of flux*(channel number) (for linear fit)
71
72 int box_chan_cntr; // actual number of channels in the box
73 int max_box_nchan; // maximum allowed number of channels in the box
74 // (calculated from boxsize and actual spectrum size)
75 // cache for derivative statistics
76 mutable casa::Bool need2recalculate; // if true, values of the statistics
77 // below are invalid
78 mutable casa::Float linmean; // a value of the linear fit to the
79 // points in the running box
80 mutable casa::Float linvariance; // the same for variance
81 int cur_channel; // the number of the current channel
82 int start_advance; // number of channel from which the box can
83 // be moved (the middle of the box, if there is no
84 // masking)
85public:
86 // set up the object with the references to actual data
87 // as well as the number of channels in the running box
88 RunningBox(const casa::Vector<casa::Float> &in_spectrum,
89 const casa::Vector<casa::Bool> &in_mask,
90 const std::pair<int,int> &in_edge,
91 int in_max_box_nchan) throw(AipsError);
92
93 // access to the statistics
94 const casa::Float& getLinMean() const throw(AipsError);
95 const casa::Float& getLinVariance() const throw(AipsError);
96 const casa::Float aboveMean() const throw(AipsError);
97 int getChannel() const throw();
98
99 // actual number of channels in the box (max_box_nchan, if no channels
100 // are masked)
101 int getNumberOfBoxPoints() const throw();
102
103 // next channel
104 void next() throw(AipsError);
105
106 // checking whether there are still elements
107 casa::Bool haveMore() const throw();
108
109 // go to start
110 void rewind() throw(AipsError);
111
112protected:
113 // supplementary function to control running mean/median calculations.
114 // It adds a specified channel to the running box and
115 // removes (ch-maxboxnchan+1)'th channel from there
116 // Channels, for which the mask is false or index is beyond the
117 // allowed range, are ignored
118 void advanceRunningBox(int ch) throw(casa::AipsError);
119
120 // calculate derivative statistics. This function is const, because
121 // it updates the cache only
122 void updateDerivativeStatistics() const throw(AipsError);
123};
124
125//
126///////////////////////////////////////////////////////////////////////////////
127
128///////////////////////////////////////////////////////////////////////////////
129//
130// LFAboveThreshold An algorithm for line detection using running box
131// statistics. Line is detected if it is above the
132// specified threshold at the specified number of
133// consequtive channels. Prefix LF stands for Line Finder
134//
135class LFAboveThreshold : protected LFLineListOperations {
136 // temporary line edge channels and flag, which is True if the line
137 // was detected in the previous channels.
138 std::pair<int,int> cur_line;
139 casa::Bool is_detected_before;
140 int min_nchan; // A minimum number of consequtive
141 // channels, which should satisfy
142 // the detection criterion, to be
143 // a detection
144 casa::Float threshold; // detection threshold - the
145 // minimal signal to noise ratio
146 std::list<pair<int,int> > &lines; // list where detections are saved
147 // (pair: start and stop+1 channel)
148 RunningBox *running_box; // running box filter
149 casa::Vector<Int> signs; // An array to store the signs of
150 // the value - current mean
151 // (used to search wings)
152 casa::Int last_sign; // a sign (+1, -1 or 0) of the
153 // last point of the detected line
154 //
155 bool itsUseMedian; // true if median statistics is used
156 // to determine the noise level, otherwise
157 // it is the mean of the lowest 80% of deviations
158 // (default)
159 int itsNoiseSampleSize; // sample size used to estimate the noise statistics
160 // Negative value means the whole spectrum is used (default)
161public:
162
163 // set up the detection criterion
164 LFAboveThreshold(std::list<pair<int,int> > &in_lines,
165 int in_min_nchan = 3,
166 casa::Float in_threshold = 5,
167 bool use_median = false,
168 int noise_sample_size = -1) throw();
169 virtual ~LFAboveThreshold() throw();
170
171 // replace the detection criterion
172 void setCriterion(int in_min_nchan, casa::Float in_threshold) throw();
173
174 // return the array with signs of the value-current mean
175 // An element is +1 if value>mean, -1 if less, 0 if equal.
176 // This array is updated each time the findLines method is called and
177 // is used to search the line wings
178 const casa::Vector<Int>& getSigns() const throw();
179
180 // find spectral lines and add them into list
181 // if statholder is not NULL, the accumulate function of it will be
182 // called for each channel to save statistics
183 // spectrum, mask and edge - reference to the data
184 // max_box_nchan - number of channels in the running box
185 void findLines(const casa::Vector<casa::Float> &spectrum,
186 const casa::Vector<casa::Bool> &mask,
187 const std::pair<int,int> &edge,
188 int max_box_nchan) throw(casa::AipsError);
189
190protected:
191
192 // process a channel: update curline and is_detected before and
193 // add a new line to the list, if necessary using processCurLine()
194 // detect=true indicates that the current channel satisfies the criterion
195 void processChannel(Bool detect, const casa::Vector<casa::Bool> &mask)
196 throw(casa::AipsError);
197
198 // process the interval of channels stored in curline
199 // if it satisfies the criterion, add this interval as a new line
200 void processCurLine(const casa::Vector<casa::Bool> &mask)
201 throw(casa::AipsError);
202
203 // get the sign of runningBox->aboveMean(). The RunningBox pointer
204 // should be defined
205 casa::Int getAboveMeanSign() const throw();
206};
207
208//
209///////////////////////////////////////////////////////////////////////////////
210
211///////////////////////////////////////////////////////////////////////////////
212//
213// LFNoiseEstimator a helper class designed to estimate off-line variance
214// using statistics depending on the distribution of
215// values (e.g. like a median)
216//
217// Two statistics are supported: median and an average of
218// 80% of smallest values.
219//
220
221struct LFNoiseEstimator {
222 // construct an object
223 // size - maximum sample size. After a size number of elements is processed
224 // any new samples would cause the algorithm to drop the oldest samples in the
225 // buffer.
226 explicit LFNoiseEstimator(size_t size);
227
228 // add a new sample
229 // in - the new value
230 void add(float in);
231
232 // median of the distribution
233 float median() const;
234
235 // mean of lowest 80% of the samples
236 float meanLowest80Percent() const;
237
238 // return true if the buffer is full (i.e. statistics are representative)
239 inline bool filledToCapacity() const { return itsBufferFull;}
240
241protected:
242 // update cache of sorted indices
243 // (it is assumed that itsSampleNumber points to the newly
244 // replaced element)
245 void updateSortedCache() const;
246
247 // build sorted cache from the scratch
248 void buildSortedCache() const;
249
250 // number of samples accumulated so far
251 // (can be less than the buffer size)
252 size_t numberOfSamples() const;
253
254 // this helper method builds the cache if
255 // necessary using one of the methods
256 void fillCacheIfNecessary() const;
257
258private:
259 // buffer with samples (unsorted)
260 std::vector<float> itsVariances;
261 // current sample number (<=itsVariances.size())
262 size_t itsSampleNumber;
263 // true, if the buffer all values in the sample buffer are used
264 bool itsBufferFull;
265 // cached indices into vector of samples
266 mutable std::vector<size_t> itsSortedIndices;
267 // true if any of the statistics have been obtained at least
268 // once. This flag allows to implement a more efficient way of
269 // calculating statistics, if they are needed at once and not
270 // after each addition of a new element
271 mutable bool itsStatisticsAccessed;
272};
273
274//
275///////////////////////////////////////////////////////////////////////////////
276
277
278} // namespace asap
279
280///////////////////////////////////////////////////////////////////////////////
281//
282// LFNoiseEstimator a helper class designed to estimate off-line variance
283// using statistics depending on the distribution of
284// values (e.g. like a median)
285//
286// Two statistics are supported: median and an average of
287// 80% of smallest values.
288//
289
290// construct an object
291// size - maximum sample size. After a size number of elements is processed
292// any new samples would cause the algorithm to drop the oldest samples in the
293// buffer.
294LFNoiseEstimator::LFNoiseEstimator(size_t size) : itsVariances(size),
295 itsSampleNumber(0), itsBufferFull(false), itsSortedIndices(size),
296 itsStatisticsAccessed(false)
297{
298 AlwaysAssert(size>0,AipsError);
299}
300
301
302// add a new sample
303// in - the new value
304void LFNoiseEstimator::add(float in)
305{
306 if (isnan(in)) {
307 // normally it shouldn't happen
308 return;
309 }
310 itsVariances[itsSampleNumber] = in;
311
312 if (itsStatisticsAccessed) {
313 // only do element by element addition if on-the-fly
314 // statistics are needed
315 updateSortedCache();
316 }
317
318 // advance itsSampleNumber now
319 ++itsSampleNumber;
320 if (itsSampleNumber == itsVariances.size()) {
321 itsSampleNumber = 0;
322 itsBufferFull = true;
323 }
324 AlwaysAssert(itsSampleNumber<itsVariances.size(),AipsError);
325}
326
327// number of samples accumulated so far
328// (can be less than the buffer size)
329size_t LFNoiseEstimator::numberOfSamples() const
330{
331 // the number of samples accumulated so far may be less than the
332 // buffer size
333 const size_t nSamples = itsBufferFull ? itsVariances.size(): itsSampleNumber;
334 AlwaysAssert( (nSamples > 0) && (nSamples <= itsVariances.size()), AipsError);
335 return nSamples;
336}
337
338// this helper method builds the cache if
339// necessary using one of the methods
340void LFNoiseEstimator::fillCacheIfNecessary() const
341{
342 if (!itsStatisticsAccessed) {
343 if ((itsSampleNumber!=0) || itsBufferFull) {
344 // build the whole cache efficiently
345 buildSortedCache();
346 } else {
347 updateSortedCache();
348 }
349 itsStatisticsAccessed = true;
350 } // otherwise, it is updated in 'add' using on-the-fly method
351}
352
353// median of the distribution
354float LFNoiseEstimator::median() const
355{
356 fillCacheIfNecessary();
357 // the number of samples accumulated so far may be less than the
358 // buffer size
359 const size_t nSamples = numberOfSamples();
360 const size_t medSample = nSamples / 2;
361 AlwaysAssert(medSample < itsSortedIndices.size(), AipsError);
362 return itsVariances[itsSortedIndices[medSample]];
363}
364
365// mean of lowest 80% of the samples
366float LFNoiseEstimator::meanLowest80Percent() const
367{
368 fillCacheIfNecessary();
369 // the number of samples accumulated so far may be less than the
370 // buffer size
371 const size_t nSamples = numberOfSamples();
372 float result = 0;
373 size_t numpt=size_t(0.8*nSamples);
374 if (!numpt) {
375 numpt=nSamples; // no much else left,
376 // although it is very inaccurate
377 }
378 AlwaysAssert( (numpt > 0) && (numpt<itsSortedIndices.size()), AipsError);
379 for (size_t ch=0; ch<numpt; ++ch) {
380 result += itsVariances[itsSortedIndices[ch]];
381 }
382 result /= float(numpt);
383 return result;
384}
385
386// update cache of sorted indices
387// (it is assumed that itsSampleNumber points to the newly
388// replaced element)
389void LFNoiseEstimator::updateSortedCache() const
390{
391 // the number of samples accumulated so far may be less than the
392 // buffer size
393 const size_t nSamples = numberOfSamples();
394
395 if (itsBufferFull) {
396 // first find the index of the element which is being replaced
397 size_t index = nSamples;
398 for (size_t i=0; i<nSamples; ++i) {
399 AlwaysAssert(i < itsSortedIndices.size(), AipsError);
400 if (itsSortedIndices[i] == itsSampleNumber) {
401 index = i;
402 break;
403 }
404 }
405 AlwaysAssert( index < nSamples, AipsError);
406
407 const vector<size_t>::iterator indStart = itsSortedIndices.begin();
408 // merge this element with preceeding block first
409 if (index != 0) {
410 // merge indices on the basis of variances
411 inplace_merge(indStart,indStart+index,indStart+index+1,
412 indexedCompare<size_t>(itsVariances.begin()));
413 }
414 // merge with the following block
415 if (index + 1 != nSamples) {
416 // merge indices on the basis of variances
417 inplace_merge(indStart,indStart+index+1,indStart+nSamples,
418 indexedCompare<size_t>(itsVariances.begin()));
419 }
420 } else {
421 // itsSampleNumber is the index of the new element
422 AlwaysAssert(itsSampleNumber < itsSortedIndices.size(), AipsError);
423 itsSortedIndices[itsSampleNumber] = itsSampleNumber;
424 if (itsSampleNumber >= 1) {
425 // we have to place this new sample in
426 const vector<size_t>::iterator indStart = itsSortedIndices.begin();
427 // merge indices on the basis of variances
428 inplace_merge(indStart,indStart+itsSampleNumber,indStart+itsSampleNumber+1,
429 indexedCompare<size_t>(itsVariances.begin()));
430 }
431 }
432}
433
434// build sorted cache from the scratch
435void LFNoiseEstimator::buildSortedCache() const
436{
437 // the number of samples accumulated so far may be less than the
438 // buffer size
439 const size_t nSamples = numberOfSamples();
440 AlwaysAssert(nSamples <= itsSortedIndices.size(), AipsError);
441 for (size_t i=0; i<nSamples; ++i) {
442 itsSortedIndices[i]=i;
443 }
444
445 // sort indices, but check the array of variances
446 const vector<size_t>::iterator indStart = itsSortedIndices.begin();
447 stable_sort(indStart,indStart+nSamples, indexedCompare<size_t>(itsVariances.begin()));
448}
449
450//
451///////////////////////////////////////////////////////////////////////////////
452
453///////////////////////////////////////////////////////////////////////////////
454//
455// RunningBox - a running box calculator. This class implements
456// interations over the specified spectrum and calculates
457// running box filter statistics.
458//
459
460// set up the object with the references to actual data
461// and the number of channels in the running box
462RunningBox::RunningBox(const casa::Vector<casa::Float> &in_spectrum,
463 const casa::Vector<casa::Bool> &in_mask,
464 const std::pair<int,int> &in_edge,
465 int in_max_box_nchan) throw(AipsError) :
466 spectrum(in_spectrum), mask(in_mask), edge(in_edge),
467 max_box_nchan(in_max_box_nchan)
468{
469 rewind();
470}
471
472void RunningBox::rewind() throw(AipsError) {
473 // fill statistics for initial box
474 box_chan_cntr=0; // no channels are currently in the box
475 sumf=0.; // initialize statistics
476 sumf2=0.;
477 sumch=0.;
478 sumch2=0.;
479 sumfch=0.;
480 int initial_box_ch=edge.first;
481 for (;initial_box_ch<edge.second && box_chan_cntr<max_box_nchan;
482 ++initial_box_ch)
483 advanceRunningBox(initial_box_ch);
484
485 if (initial_box_ch==edge.second)
486 throw AipsError("RunningBox::rewind - too much channels are masked");
487
488 cur_channel=edge.first;
489 start_advance=initial_box_ch-max_box_nchan/2;
490}
491
492// access to the statistics
493const casa::Float& RunningBox::getLinMean() const throw(AipsError)
494{
495 DebugAssert(cur_channel<edge.second, AipsError);
496 if (need2recalculate) updateDerivativeStatistics();
497 return linmean;
498}
499
500const casa::Float& RunningBox::getLinVariance() const throw(AipsError)
501{
502 DebugAssert(cur_channel<edge.second, AipsError);
503 if (need2recalculate) updateDerivativeStatistics();
504 return linvariance;
505}
506
507const casa::Float RunningBox::aboveMean() const throw(AipsError)
508{
509 DebugAssert(cur_channel<edge.second, AipsError);
510 if (need2recalculate) updateDerivativeStatistics();
511 return spectrum[cur_channel]-linmean;
512}
513
514int RunningBox::getChannel() const throw()
515{
516 return cur_channel;
517}
518
519// actual number of channels in the box (max_box_nchan, if no channels
520// are masked)
521int RunningBox::getNumberOfBoxPoints() const throw()
522{
523 return box_chan_cntr;
524}
525
526// supplementary function to control running mean/median calculations.
527// It adds a specified channel to the running box and
528// removes (ch-max_box_nchan+1)'th channel from there
529// Channels, for which the mask is false or index is beyond the
530// allowed range, are ignored
531void RunningBox::advanceRunningBox(int ch) throw(AipsError)
532{
533 if (ch>=edge.first && ch<edge.second)
534 if (mask[ch]) { // ch is a valid channel
535 ++box_chan_cntr;
536 sumf+=spectrum[ch];
537 sumf2+=square(spectrum[ch]);
538 sumch+=Float(ch);
539 sumch2+=square(Float(ch));
540 sumfch+=spectrum[ch]*Float(ch);
541 need2recalculate=True;
542 }
543 int ch2remove=ch-max_box_nchan;
544 if (ch2remove>=edge.first && ch2remove<edge.second)
545 if (mask[ch2remove]) { // ch2remove is a valid channel
546 --box_chan_cntr;
547 sumf-=spectrum[ch2remove];
548 sumf2-=square(spectrum[ch2remove]);
549 sumch-=Float(ch2remove);
550 sumch2-=square(Float(ch2remove));
551 sumfch-=spectrum[ch2remove]*Float(ch2remove);
552 need2recalculate=True;
553 }
554}
555
556// next channel
557void RunningBox::next() throw(AipsError)
558{
559 AlwaysAssert(cur_channel<edge.second,AipsError);
560 ++cur_channel;
561 if (cur_channel+max_box_nchan/2<edge.second && cur_channel>=start_advance)
562 advanceRunningBox(cur_channel+max_box_nchan/2); // update statistics
563}
564
565// checking whether there are still elements
566casa::Bool RunningBox::haveMore() const throw()
567{
568 return cur_channel<edge.second;
569}
570
571// calculate derivative statistics. This function is const, because
572// it updates the cache only
573void RunningBox::updateDerivativeStatistics() const throw(AipsError)
574{
575 AlwaysAssert(box_chan_cntr, AipsError);
576
577 Float mean=sumf/Float(box_chan_cntr);
578
579 // linear LSF formulae
580 Float meanch=sumch/Float(box_chan_cntr);
581 Float meanch2=sumch2/Float(box_chan_cntr);
582 if (meanch==meanch2 || box_chan_cntr<3) {
583 // vertical line in the spectrum, can't calculate linmean and linvariance
584 linmean=0.;
585 linvariance=0.;
586 } else {
587 Float coeff=(sumfch/Float(box_chan_cntr)-meanch*mean)/
588 (meanch2-square(meanch));
589 linmean=coeff*(Float(cur_channel)-meanch)+mean;
590 linvariance=sumf2/Float(box_chan_cntr)-square(mean)-
591 square(coeff)*(meanch2-square(meanch));
592 if (linvariance<0.) {
593 // this shouldn't happen normally, but could be due to round-off error
594 linvariance = 0;
595 } else {
596 linvariance = sqrt(linvariance);
597 }
598 }
599 need2recalculate=False;
600}
601
602
603//
604///////////////////////////////////////////////////////////////////////////////
605
606///////////////////////////////////////////////////////////////////////////////
607//
608// LFAboveThreshold - a running mean/median algorithm for line detection
609//
610//
611
612
613// set up the detection criterion
614LFAboveThreshold::LFAboveThreshold(std::list<pair<int,int> > &in_lines,
615 int in_min_nchan,
616 casa::Float in_threshold,
617 bool use_median,
618 int noise_sample_size) throw() :
619 min_nchan(in_min_nchan), threshold(in_threshold),
620 lines(in_lines), running_box(NULL), itsUseMedian(use_median),
621 itsNoiseSampleSize(noise_sample_size) {}
622
623LFAboveThreshold::~LFAboveThreshold() throw()
624{
625 if (running_box!=NULL) delete running_box;
626}
627
628// replace the detection criterion
629void LFAboveThreshold::setCriterion(int in_min_nchan, casa::Float in_threshold)
630 throw()
631{
632 min_nchan=in_min_nchan;
633 threshold=in_threshold;
634}
635
636// get the sign of runningBox->aboveMean(). The RunningBox pointer
637// should be defined
638casa::Int LFAboveThreshold::getAboveMeanSign() const throw()
639{
640 const Float buf=running_box->aboveMean();
641 if (buf>0) return 1;
642 if (buf<0) return -1;
643 return 0;
644}
645
646
647// process a channel: update cur_line and is_detected before and
648// add a new line to the list, if necessary
649void LFAboveThreshold::processChannel(Bool detect,
650 const casa::Vector<casa::Bool> &mask) throw(casa::AipsError)
651{
652 try {
653 if (is_detected_before) {
654 // we have to check that the current detection has the
655 // same sign of running_box->aboveMean
656 // otherwise it could be a spurious detection
657 if (last_sign && last_sign!=getAboveMeanSign())
658 detect=False;
659 }
660 if (detect) {
661 last_sign=getAboveMeanSign();
662 if (is_detected_before)
663 cur_line.second=running_box->getChannel()+1;
664 else {
665 is_detected_before=True;
666 cur_line.first=running_box->getChannel();
667 cur_line.second=running_box->getChannel()+1;
668 }
669 } else processCurLine(mask);
670 }
671 catch (const AipsError &ae) {
672 throw;
673 }
674 catch (const exception &ex) {
675 throw AipsError(String("LFAboveThreshold::processChannel - STL error: ")+ex.what());
676 }
677}
678
679// process the interval of channels stored in cur_line
680// if it satisfies the criterion, add this interval as a new line
681void LFAboveThreshold::processCurLine(const casa::Vector<casa::Bool> &mask)
682 throw(casa::AipsError)
683{
684 try {
685 if (is_detected_before) {
686 if (cur_line.second-cur_line.first>=min_nchan) {
687 // it was a detection. We need to change the list
688 Bool add_new_line=False;
689 if (lines.size()) {
690 for (int i=lines.back().second;i<cur_line.first;++i)
691 if (mask[i]) { // one valid channel in between
692 // means that we deal with a separate line
693 add_new_line=True;
694 break;
695 }
696 } else add_new_line=True;
697 if (add_new_line)
698 lines.push_back(cur_line);
699 else lines.back().second=cur_line.second;
700 }
701 is_detected_before=False;
702 }
703 }
704 catch (const AipsError &ae) {
705 throw;
706 }
707 catch (const exception &ex) {
708 throw AipsError(String("LFAboveThreshold::processCurLine - STL error: ")+ex.what());
709 }
710}
711
712// return the array with signs of the value-current mean
713// An element is +1 if value>mean, -1 if less, 0 if equal.
714// This array is updated each time the findLines method is called and
715// is used to search the line wings
716const casa::Vector<Int>& LFAboveThreshold::getSigns() const throw()
717{
718 return signs;
719}
720
721// find spectral lines and add them into list
722void LFAboveThreshold::findLines(const casa::Vector<casa::Float> &spectrum,
723 const casa::Vector<casa::Bool> &mask,
724 const std::pair<int,int> &edge,
725 int max_box_nchan)
726 throw(casa::AipsError)
727{
728 const int minboxnchan=4;
729 try {
730
731 if (running_box!=NULL) delete running_box;
732 running_box=new RunningBox(spectrum,mask,edge,max_box_nchan);
733
734 // determine the off-line variance first
735 // an assumption made: lines occupy a small part of the spectrum
736
737 const size_t noiseSampleSize = itsNoiseSampleSize<0 ? size_t(edge.second-edge.first) :
738 std::min(size_t(itsNoiseSampleSize), size_t(edge.second-edge.first));
739 DebugAssert(noiseSampleSize,AipsError);
740 const bool globalNoise = (size_t(edge.second - edge.first) == noiseSampleSize);
741 LFNoiseEstimator ne(noiseSampleSize);
742
743 for (;running_box->haveMore();running_box->next()) {
744 ne.add(running_box->getLinVariance());
745 if (ne.filledToCapacity()) {
746 break;
747 }
748 }
749
750 Float offline_variance = -1; // just a flag that it is unset
751
752 if (globalNoise) {
753 offline_variance = itsUseMedian ? ne.median() : ne.meanLowest80Percent();
754 }
755
756 // actual search algorithm
757 is_detected_before=False;
758
759 // initiate the signs array
760 signs.resize(spectrum.nelements());
761 signs=Vector<Int>(spectrum.nelements(),0);
762
763 //ofstream os("dbg.dat");
764 for (running_box->rewind();running_box->haveMore();
765 running_box->next()) {
766 const int ch=running_box->getChannel();
767 if (!globalNoise) {
768 // add a next point for a local noise estimate
769 ne.add(running_box->getLinVariance());
770 }
771 if (running_box->getNumberOfBoxPoints()>=minboxnchan) {
772 if (!globalNoise) {
773 offline_variance = itsUseMedian ? ne.median() : ne.meanLowest80Percent();
774 }
775 AlwaysAssert(offline_variance>0.,AipsError);
776 processChannel(mask[ch] && (fabs(running_box->aboveMean()) >=
777 threshold*offline_variance), mask);
778 } else processCurLine(mask); // just finish what was accumulated before
779
780 signs[ch]=getAboveMeanSign();
781 //os<<ch<<" "<<spectrum[ch]<<" "<<fabs(running_box->aboveMean())<<" "<<
782 //threshold*offline_variance<<endl;
783 }
784 if (lines.size())
785 searchForWings(lines,signs,mask,edge);
786 }
787 catch (const AipsError &ae) {
788 throw;
789 }
790 catch (const exception &ex) {
791 throw AipsError(String("LFAboveThreshold::findLines - STL error: ")+ex.what());
792 }
793}
794
795//
796///////////////////////////////////////////////////////////////////////////////
797
798///////////////////////////////////////////////////////////////////////////////
799//
800// LFLineListOperations::IntersectsWith - An auxiliary object function
801// to test whether two lines have a non-void intersection
802//
803
804
805// line1 - range of the first line: start channel and stop+1
806LFLineListOperations::IntersectsWith::IntersectsWith(const std::pair<int,int> &in_line1) :
807 line1(in_line1) {}
808
809
810// return true if line2 intersects with line1 with at least one
811// common channel, and false otherwise
812// line2 - range of the second line: start channel and stop+1
813bool LFLineListOperations::IntersectsWith::operator()(const std::pair<int,int> &line2)
814 const throw()
815{
816 if (line2.second<line1.first) return false; // line2 is at lower channels
817 if (line2.first>line1.second) return false; // line2 is at upper channels
818 return true; // line2 has an intersection or is adjacent to line1
819}
820
821//
822///////////////////////////////////////////////////////////////////////////////
823
824///////////////////////////////////////////////////////////////////////////////
825//
826// LFLineListOperations::BuildUnion - An auxiliary object function to build a union
827// of several lines to account for a possibility of merging the nearby lines
828//
829
830// set an initial line (can be a first line in the sequence)
831LFLineListOperations::BuildUnion::BuildUnion(const std::pair<int,int> &line1) :
832 temp_line(line1) {}
833
834// update temp_line with a union of temp_line and new_line
835// provided there is no gap between the lines
836void LFLineListOperations::BuildUnion::operator()(const std::pair<int,int> &new_line)
837 throw()
838{
839 if (new_line.first<temp_line.first) temp_line.first=new_line.first;
840 if (new_line.second>temp_line.second) temp_line.second=new_line.second;
841}
842
843// return the result (temp_line)
844const std::pair<int,int>& LFLineListOperations::BuildUnion::result() const throw()
845{
846 return temp_line;
847}
848
849//
850///////////////////////////////////////////////////////////////////////////////
851
852///////////////////////////////////////////////////////////////////////////////
853//
854// LFLineListOperations::LaterThan - An auxiliary object function to test whether a
855// specified line is at lower spectral channels (to preserve the order in
856// the line list)
857//
858
859// setup the line to compare with
860LFLineListOperations::LaterThan::LaterThan(const std::pair<int,int> &in_line1) :
861 line1(in_line1) {}
862
863// return true if line2 should be placed later than line1
864// in the ordered list (so, it is at greater channel numbers)
865bool LFLineListOperations::LaterThan::operator()(const std::pair<int,int> &line2)
866 const throw()
867{
868 if (line2.second<line1.first) return false; // line2 is at lower channels
869 if (line2.first>line1.second) return true; // line2 is at upper channels
870
871 // line2 intersects with line1. We should have no such situation in
872 // practice
873 return line2.first>line1.first;
874}
875
876//
877///////////////////////////////////////////////////////////////////////////////
878
879
880///////////////////////////////////////////////////////////////////////////////
881//
882// STLineFinder - a class for automated spectral line search
883//
884//
885
886STLineFinder::STLineFinder() throw() : edge(0,0)
887{
888 setOptions();
889}
890
891// set the parameters controlling algorithm
892// in_threshold a single channel threshold default is sqrt(3), which
893// means together with 3 minimum channels at least 3 sigma
894// detection criterion
895// For bad baseline shape, in_threshold may need to be
896// increased
897// in_min_nchan minimum number of channels above the threshold to report
898// a detection, default is 3
899// in_avg_limit perform the averaging of no more than in_avg_limit
900// adjacent channels to search for broad lines
901// Default is 8, but for a bad baseline shape this
902// parameter should be decreased (may be even down to a
903// minimum of 1 to disable this option) to avoid
904// confusing of baseline undulations with a real line.
905// Setting a very large value doesn't usually provide
906// valid detections.
907// in_box_size the box size for running mean/median calculation. Default is
908// 1./5. of the whole spectrum size
909// in_noise_box the box size for off-line noise estimation (if working with
910// local noise. Negative value means use global noise estimate
911// Default is -1 (i.e. estimate using the whole spectrum)
912// in_median true if median statistics is used as opposed to average of
913// the lowest 80% of deviations (default)
914void STLineFinder::setOptions(const casa::Float &in_threshold,
915 const casa::Int &in_min_nchan,
916 const casa::Int &in_avg_limit,
917 const casa::Float &in_box_size,
918 const casa::Float &in_noise_box,
919 const casa::Bool &in_median) throw()
920{
921 threshold=in_threshold;
922 min_nchan=in_min_nchan;
923 avg_limit=in_avg_limit;
924 box_size=in_box_size;
925 itsNoiseBox = in_noise_box;
926 itsUseMedian = in_median;
927
928 useScantable = true;
929}
930
931STLineFinder::~STLineFinder() throw(AipsError) {}
932
933// set scan to work with (in_scan parameter)
934void STLineFinder::setScan(const ScantableWrapper &in_scan) throw(AipsError)
935{
936 scan=in_scan.getCP();
937 AlwaysAssert(!scan.null(),AipsError);
938}
939
940// set spectrum data to work with. this is a method to allow linefinder work
941// without setting scantable for the purpose of using linefinder inside some
942// method in scantable class. (Dec 22, 2010 by W.Kawasaki)
943void STLineFinder::setData(const std::vector<float> &in_spectrum)
944{
945 spectrum = Vector<Float>(in_spectrum);
946 useScantable = false;
947}
948
949// search for spectral lines. Number of lines found is returned
950// in_edge and in_mask control channel rejection for a given row
951// if in_edge has zero length, all channels chosen by mask will be used
952// if in_edge has one element only, it represents the number of
953// channels to drop from both sides of the spectrum
954// in_edge is introduced for convinience, although all functionality
955// can be achieved using a spectrum mask only
956int STLineFinder::findLines(const std::vector<bool> &in_mask,
957 const std::vector<int> &in_edge,
958 const casa::uInt &whichRow) throw(casa::AipsError)
959{
960 if (useScantable && scan.null())
961 throw AipsError("STLineFinder::findLines - a scan should be set first,"
962 " use set_scan");
963
964 uInt nchan = useScantable ? scan->nchan(scan->getIF(whichRow)) : spectrum.nelements();
965 // set up mask and edge rejection
966 // no mask given...
967 if (in_mask.size() == 0) {
968 mask = Vector<Bool>(nchan,True);
969 } else {
970 // use provided mask
971 mask=Vector<Bool>(in_mask);
972 }
973 if (mask.nelements()!=nchan)
974 throw AipsError("STLineFinder::findLines - in_scan and in_mask, or in_spectrum "
975 "and in_mask have different number of spectral channels.");
976
977 // taking flagged channels into account
978 if (useScantable) {
979 vector<bool> flaggedChannels = scan->getMask(whichRow);
980 if (flaggedChannels.size()) {
981 // there is a mask set for this row
982 if (flaggedChannels.size() != mask.nelements()) {
983 throw AipsError("STLineFinder::findLines - internal inconsistency: number of "
984 "mask elements do not match the number of channels");
985 }
986 for (size_t ch = 0; ch<mask.nelements(); ++ch) {
987 mask[ch] &= flaggedChannels[ch];
988 }
989 }
990 }
991
992 // number of elements in in_edge
993 if (in_edge.size()>2)
994 throw AipsError("STLineFinder::findLines - the length of the in_edge parameter"
995 "should not exceed 2");
996 if (!in_edge.size()) {
997 // all spectra, no rejection
998 edge.first=0;
999 edge.second=nchan;
1000 } else {
1001 edge.first=in_edge[0];
1002 if (edge.first<0)
1003 throw AipsError("STLineFinder::findLines - the in_edge parameter has a negative"
1004 "number of channels to drop");
1005 if (edge.first>=int(nchan))
1006 throw AipsError("STLineFinder::findLines - all channels are rejected by the in_edge parameter");
1007 if (in_edge.size()==2) {
1008 edge.second=in_edge[1];
1009 if (edge.second<0)
1010 throw AipsError("STLineFinder::findLines - the in_edge parameter has a negative"
1011 "number of channels to drop");
1012 edge.second=nchan-edge.second;
1013 } else edge.second=nchan-edge.first;
1014 if (edge.second<0 || (edge.first>=edge.second))
1015 throw AipsError("STLineFinder::findLines - all channels are rejected by the in_edge parameter");
1016 }
1017
1018 //
1019 int max_box_nchan=int(nchan*box_size); // number of channels in running
1020 // box
1021 if (max_box_nchan<2)
1022 throw AipsError("STLineFinder::findLines - box_size is too small");
1023
1024 // number of elements in the sample for noise estimate
1025 const int noise_box = itsNoiseBox<0 ? -1 : int(nchan * itsNoiseBox);
1026
1027 if ((noise_box!= -1) and (noise_box<2))
1028 throw AipsError("STLineFinder::findLines - noise_box is supposed to be at least 2 elements");
1029
1030 if (useScantable) {
1031 spectrum.resize();
1032 spectrum = Vector<Float>(scan->getSpectrum(whichRow));
1033 }
1034
1035 lines.resize(0); // search from the scratch
1036 last_row_used=whichRow;
1037 Vector<Bool> temp_mask(mask);
1038
1039 Bool first_pass=True;
1040 Int avg_factor=1; // this number of adjacent channels is averaged together
1041 // the total number of the channels is not altered
1042 // instead, min_nchan is also scaled
1043 // it helps to search for broad lines
1044 Vector<Int> signs; // a buffer for signs of the value - mean quantity
1045 // see LFAboveThreshold for details
1046 // We need only signs resulted from last iteration
1047 // because all previous values may be corrupted by the
1048 // presence of spectral lines
1049 while (true) {
1050 // a buffer for new lines found at this iteration
1051 std::list<pair<int,int> > new_lines;
1052
1053 try {
1054 // line find algorithm
1055 LFAboveThreshold lfalg(new_lines,avg_factor*min_nchan, threshold, itsUseMedian,noise_box);
1056 lfalg.findLines(spectrum,temp_mask,edge,max_box_nchan);
1057 signs.resize(lfalg.getSigns().nelements());
1058 signs=lfalg.getSigns();
1059 first_pass=False;
1060 if (!new_lines.size())
1061 throw AipsError("spurious"); // nothing new - use the same
1062 // code as for a real exception
1063 }
1064 catch(const AipsError &ae) {
1065 if (first_pass) throw;
1066 // nothing new - proceed to the next step of averaging, if any
1067 // (to search for broad lines)
1068 if (avg_factor>=avg_limit) break; // averaging up to avg_limit
1069 // adjacent channels,
1070 // stop after that
1071 avg_factor*=2; // twice as more averaging
1072 subtractBaseline(temp_mask,9);
1073 averageAdjacentChannels(temp_mask,avg_factor);
1074 continue;
1075 }
1076 keepStrongestOnly(temp_mask,new_lines,max_box_nchan);
1077 // update the list (lines) merging intervals, if necessary
1078 addNewSearchResult(new_lines,lines);
1079 // get a new mask
1080 temp_mask=getMask();
1081 }
1082
1083 // an additional search for wings because in the presence of very strong
1084 // lines temporary mean used at each iteration will be higher than
1085 // the true mean
1086
1087 if (lines.size())
1088 LFLineListOperations::searchForWings(lines,signs,mask,edge);
1089
1090 return int(lines.size());
1091}
1092
1093// auxiliary function to fit and subtract a polynomial from the current
1094// spectrum. It uses the Fitter class. This action is required before
1095// reducing the spectral resolution if the baseline shape is bad
1096void STLineFinder::subtractBaseline(const casa::Vector<casa::Bool> &temp_mask,
1097 const casa::Int &order) throw(casa::AipsError)
1098{
1099 AlwaysAssert(spectrum.nelements(),AipsError);
1100 // use the fact that temp_mask excludes channels rejected at the edge
1101 Fitter sdf;
1102 std::vector<float> absc(spectrum.nelements());
1103 for (unsigned int i=0;i<absc.size();++i)
1104 absc[i]=float(i)/float(spectrum.nelements());
1105 std::vector<float> spec;
1106 spectrum.tovector(spec);
1107 std::vector<bool> std_mask;
1108 temp_mask.tovector(std_mask);
1109 sdf.setData(absc,spec,std_mask);
1110 sdf.setExpression("poly",order);
1111 if (!sdf.fit()) return; // fit failed, use old spectrum
1112 spectrum=casa::Vector<casa::Float>(sdf.getResidual());
1113}
1114
1115// auxiliary function to average adjacent channels and update the mask
1116// if at least one channel involved in summation is masked, all
1117// output channels will be masked. This function works with the
1118// spectrum and edge fields of this class, but updates the mask
1119// array specified, rather than the field of this class
1120// boxsize - a number of adjacent channels to average
1121void STLineFinder::averageAdjacentChannels(casa::Vector<casa::Bool> &mask2update,
1122 const casa::Int &boxsize)
1123 throw(casa::AipsError)
1124{
1125 DebugAssert(mask2update.nelements()==spectrum.nelements(), AipsError);
1126 DebugAssert(boxsize!=0,AipsError);
1127
1128 for (int n=edge.first;n<edge.second;n+=boxsize) {
1129 DebugAssert(n<spectrum.nelements(),AipsError);
1130 int nboxch=0; // number of channels currently in the box
1131 Float mean=0; // buffer for mean calculations
1132 for (int k=n;k<n+boxsize && k<edge.second;++k)
1133 if (mask2update[k]) { // k is a valid channel
1134 mean+=spectrum[k];
1135 ++nboxch;
1136 }
1137 if (nboxch<boxsize) // mask these channels
1138 for (int k=n;k<n+boxsize && k<edge.second;++k)
1139 mask2update[k]=False;
1140 else {
1141 mean/=Float(boxsize);
1142 for (int k=n;k<n+boxsize && k<edge.second;++k)
1143 spectrum[k]=mean;
1144 }
1145 }
1146}
1147
1148
1149// get the mask to mask out all lines that have been found (default)
1150// if invert=true, only channels belong to lines will be unmasked
1151// Note: all channels originally masked by the input mask (in_mask
1152// in setScan) or dropped out by the edge parameter (in_edge
1153// in setScan) are still excluded regardless on the invert option
1154std::vector<bool> STLineFinder::getMask(bool invert)
1155 const throw(casa::AipsError)
1156{
1157 try {
1158 if (useScantable) {
1159 if (scan.null())
1160 throw AipsError("STLineFinder::getMask - a scan should be set first,"
1161 " use set_scan followed by find_lines");
1162 DebugAssert(mask.nelements()==scan->getChannels(last_row_used), AipsError);
1163 }
1164 /*
1165 if (!lines.size())
1166 throw AipsError("STLineFinder::getMask - one have to search for "
1167 "lines first, use find_lines");
1168 */
1169 std::vector<bool> res_mask(mask.nelements());
1170 // iterator through lines
1171 std::list<std::pair<int,int> >::const_iterator cli=lines.begin();
1172 for (int ch=0;ch<int(res_mask.size());++ch) {
1173 if (ch<edge.first || ch>=edge.second) res_mask[ch]=false;
1174 else if (!mask[ch]) res_mask[ch]=false;
1175 else {
1176 res_mask[ch]=!invert; // no line by default
1177 if (cli!=lines.end())
1178 if (ch>=cli->first && ch<cli->second)
1179 res_mask[ch]=invert; // this is a line
1180 }
1181 if (cli!=lines.end())
1182 if (ch>=cli->second)
1183 ++cli; // next line in the list
1184 }
1185 return res_mask;
1186 }
1187 catch (const AipsError &ae) {
1188 throw;
1189 }
1190 catch (const exception &ex) {
1191 throw AipsError(String("STLineFinder::getMask - STL error: ")+ex.what());
1192 }
1193}
1194
1195// get range for all lines found. The same units as used in the scan
1196// will be returned (e.g. velocity instead of channels).
1197std::vector<double> STLineFinder::getLineRanges()
1198 const throw(casa::AipsError)
1199{
1200 std::vector<double> vel;
1201 if (useScantable) {
1202 // convert to required abscissa units
1203 vel = scan->getAbcissa(last_row_used);
1204 } else {
1205 for (int i = 0; i < spectrum.nelements(); ++i)
1206 vel.push_back((double)i);
1207 }
1208 std::vector<int> ranges=getLineRangesInChannels();
1209 std::vector<double> res(ranges.size());
1210
1211 std::vector<int>::const_iterator cri=ranges.begin();
1212 std::vector<double>::iterator outi=res.begin();
1213 for (;cri!=ranges.end() && outi!=res.end();++cri,++outi)
1214 if (uInt(*cri)>=vel.size())
1215 throw AipsError("STLineFinder::getLineRanges - getAbcissa provided less channels than reqired");
1216 else *outi=vel[*cri];
1217 return res;
1218}
1219
1220// The same as getLineRanges, but channels are always used to specify
1221// the range
1222std::vector<int> STLineFinder::getLineRangesInChannels()
1223 const throw(casa::AipsError)
1224{
1225 try {
1226 if (useScantable) {
1227 if (scan.null())
1228 throw AipsError("STLineFinder::getLineRangesInChannels - a scan should be set first,"
1229 " use set_scan followed by find_lines");
1230 DebugAssert(mask.nelements()==scan->getChannels(last_row_used), AipsError);
1231 }
1232
1233 if (!lines.size())
1234 throw AipsError("STLineFinder::getLineRangesInChannels - one have to search for "
1235 "lines first, use find_lines");
1236
1237 std::vector<int> res(2*lines.size());
1238 // iterator through lines & result
1239 std::list<std::pair<int,int> >::const_iterator cli = lines.begin();
1240 std::vector<int>::iterator ri = res.begin();
1241 for (; cli != lines.end() && ri != res.end(); ++cli,++ri) {
1242 *ri = cli->first;
1243 if (++ri != res.end())
1244 *ri = cli->second - 1;
1245 }
1246 return res;
1247 } catch (const AipsError &ae) {
1248 throw;
1249 } catch (const exception &ex) {
1250 throw AipsError(String("STLineFinder::getLineRanges - STL error: ") + ex.what());
1251 }
1252}
1253
1254
1255
1256// an auxiliary function to remove all lines from the list, except the
1257// strongest one (by absolute value). If the lines removed are real,
1258// they will be find again at the next iteration. This approach
1259// increases the number of iterations required, but is able to remove
1260// spurious detections likely to occur near strong lines.
1261// Later a better criterion may be implemented, e.g.
1262// taking into consideration the brightness of different lines. Now
1263// use the simplest solution
1264// temp_mask - mask to work with (may be different from original mask as
1265// the lines previously found may be masked)
1266// lines2update - a list of lines to work with
1267// nothing will be done if it is empty
1268// max_box_nchan - channels in the running box for baseline filtering
1269void STLineFinder::keepStrongestOnly(const casa::Vector<casa::Bool> &temp_mask,
1270 std::list<std::pair<int, int> > &lines2update,
1271 int max_box_nchan)
1272 throw (casa::AipsError)
1273{
1274 try {
1275 if (!lines2update.size()) return; // ignore an empty list
1276
1277 // current line
1278 std::list<std::pair<int,int> >::iterator li=lines2update.begin();
1279 // strongest line
1280 std::list<std::pair<int,int> >::iterator strongli=lines2update.begin();
1281 // the flux (absolute value) of the strongest line
1282 Float peak_flux=-1; // negative value - a flag showing uninitialized
1283 // value
1284 // the algorithm below relies on the list being ordered
1285 Float tmp_flux=-1; // a temporary peak
1286 for (RunningBox running_box(spectrum,temp_mask,edge,max_box_nchan);
1287 running_box.haveMore(); running_box.next()) {
1288
1289 if (li==lines2update.end()) break; // no more lines
1290 const int ch=running_box.getChannel();
1291 if (ch>=li->first && ch<li->second)
1292 if (temp_mask[ch] && tmp_flux<fabs(running_box.aboveMean()))
1293 tmp_flux=fabs(running_box.aboveMean());
1294 if (ch==li->second-1) {
1295 if (peak_flux<tmp_flux) { // if peak_flux=-1, this condition
1296 peak_flux=tmp_flux; // will be satisfied
1297 strongli=li;
1298 }
1299 ++li;
1300 tmp_flux=-1;
1301 }
1302 }
1303 std::list<std::pair<int,int> > res;
1304 res.splice(res.end(),lines2update,strongli);
1305 lines2update.clear();
1306 lines2update.splice(lines2update.end(),res);
1307 }
1308 catch (const AipsError &ae) {
1309 throw;
1310 }
1311 catch (const exception &ex) {
1312 throw AipsError(String("STLineFinder::keepStrongestOnly - STL error: ")+ex.what());
1313 }
1314
1315}
1316
1317//
1318///////////////////////////////////////////////////////////////////////////////
1319
1320
1321///////////////////////////////////////////////////////////////////////////////
1322//
1323// LFLineListOperations - a class incapsulating operations with line lists
1324// The LF prefix stands for Line Finder
1325//
1326
1327// concatenate two lists preserving the order. If two lines appear to
1328// be adjacent, they are joined into the new one
1329void LFLineListOperations::addNewSearchResult(const std::list<pair<int, int> > &newlines,
1330 std::list<std::pair<int, int> > &lines_list)
1331 throw(AipsError)
1332{
1333 try {
1334 for (std::list<pair<int,int> >::const_iterator cli=newlines.begin();
1335 cli!=newlines.end();++cli) {
1336
1337 // the first item, which has a non-void intersection or touches
1338 // the new line
1339 std::list<pair<int,int> >::iterator pos_beg=find_if(lines_list.begin(),
1340 lines_list.end(), IntersectsWith(*cli));
1341 // the last such item
1342 std::list<pair<int,int> >::iterator pos_end=find_if(pos_beg,
1343 lines_list.end(), not1(IntersectsWith(*cli)));
1344
1345 // extract all lines which intersect or touch a new one into
1346 // a temporary buffer. This may invalidate the iterators
1347 // line_buffer may be empty, if no lines intersects with a new
1348 // one.
1349 std::list<pair<int,int> > lines_buffer;
1350 lines_buffer.splice(lines_buffer.end(),lines_list, pos_beg, pos_end);
1351
1352 // build a union of all intersecting lines
1353 pair<int,int> union_line=for_each(lines_buffer.begin(),
1354 lines_buffer.end(),BuildUnion(*cli)).result();
1355
1356 // search for a right place for the new line (union_line) and add
1357 std::list<pair<int,int> >::iterator pos2insert=find_if(lines_list.begin(),
1358 lines_list.end(), LaterThan(union_line));
1359 lines_list.insert(pos2insert,union_line);
1360 }
1361 }
1362 catch (const AipsError &ae) {
1363 throw;
1364 }
1365 catch (const exception &ex) {
1366 throw AipsError(String("LFLineListOperations::addNewSearchResult - STL error: ")+ex.what());
1367 }
1368}
1369
1370// extend all line ranges to the point where a value stored in the
1371// specified vector changes (e.g. value-mean change its sign)
1372// This operation is necessary to include line wings, which are below
1373// the detection threshold. If lines becomes adjacent, they are
1374// merged together. Any masked channel stops the extension
1375void LFLineListOperations::searchForWings(std::list<std::pair<int, int> > &newlines,
1376 const casa::Vector<casa::Int> &signs,
1377 const casa::Vector<casa::Bool> &mask,
1378 const std::pair<int,int> &edge) throw(casa::AipsError)
1379{
1380 try {
1381 for (std::list<pair<int,int> >::iterator li=newlines.begin();
1382 li!=newlines.end();++li) {
1383 // update the left hand side
1384 for (int n=li->first-1;n>=edge.first;--n) {
1385 if (!mask[n]) break;
1386 if (signs[n]==signs[li->first] && signs[li->first])
1387 li->first=n;
1388 else break;
1389 }
1390 // update the right hand side
1391 for (int n=li->second;n<edge.second;++n) {
1392 if (!mask[n]) break;
1393 if (signs[n]==signs[li->second-1] && signs[li->second-1])
1394 li->second=n;
1395 else break;
1396 }
1397 }
1398 // need to search for possible mergers.
1399 std::list<std::pair<int, int> > result_buffer;
1400 addNewSearchResult(newlines,result_buffer);
1401 newlines.clear();
1402 newlines.splice(newlines.end(),result_buffer);
1403 }
1404 catch (const AipsError &ae) {
1405 throw;
1406 }
1407 catch (const exception &ex) {
1408 throw AipsError(String("LFLineListOperations::extendLines - STL error: ")+ex.what());
1409 }
1410}
1411
1412//
1413///////////////////////////////////////////////////////////////////////////////
Note: See TracBrowser for help on using the repository browser.