Line data Source code
1 : //# Spectral2Estimate.cc: Member templates for SpectralEstimate 2 : //# Copyright (C) 2001,2002,2003,2004 3 : //# Associated Universities, Inc. Washington DC, USA. 4 : //# 5 : //# This library is free software; you can redistribute it and/or modify it 6 : //# under the terms of the GNU Library General Public License as published by 7 : //# the Free Software Foundation; either version 2 of the License, or (at your 8 : //# option) any later version. 9 : //# 10 : //# This library is distributed in the hope that it will be useful, but WITHOUT 11 : //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 : //# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 13 : //# License for more details. 14 : //# 15 : //# You should have received a copy of the GNU Library General Public License 16 : //# along with this library; if not, write to the Free Software Foundation, 17 : //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. 18 : //# 19 : //# Correspondence concerning AIPS++ should be addressed as follows: 20 : //# Internet email: aips2-request@nrao.edu. 21 : //# Postal address: AIPS++ Project Office 22 : //# National Radio Astronomy Observatory 23 : //# 520 Edgemont Road 24 : //# Charlottesville, VA 22903-2475 USA 25 : //# 26 : //# $Id: Spectral2Estimate.tcc 19935 2007-02-27 05:07:40Z Malte.Marquarding $ 27 : 28 : //# Includes 29 : #include <components/SpectralComponents/SpectralEstimate.h> 30 : 31 : #include <casacore/casa/BasicMath/Math.h> 32 : #include <casacore/casa/BasicSL/Constants.h> 33 : #include <casacore/casa/Utilities/Assert.h> 34 : #include <components/SpectralComponents/CompiledSpectralElement.h> 35 : #include <components/SpectralComponents/GaussianSpectralElement.h> 36 : #include <components/SpectralComponents/PolynomialSpectralElement.h> 37 : 38 : namespace casa { //#Begin namespace casa 39 : 40 : //# Member templates 41 : template <class MT> 42 0 : const SpectralList &SpectralEstimate::estimate(const casacore::Vector<MT> &prof, 43 : casacore::Vector<MT> *der) { 44 0 : if (prof.nelements() != lprof_p) { 45 0 : delete [] deriv_p; deriv_p = 0; lprof_p = 0; 46 0 : lprof_p = prof.nelements(); 47 0 : deriv_p = new casacore::Double[lprof_p]; 48 : }; 49 : // Check if signal in window 50 0 : if (!window(prof)) return slist_p; 51 : // Limit window 52 0 : windowEnd_p = casacore::min(windowEnd_p+q_p , casacore::Int(lprof_p)); 53 0 : windowLow_p = casacore::max(windowLow_p-q_p , 0 ); 54 : // Get the second derivatives 55 0 : findc2(prof); 56 : // Next for debugging 57 0 : if (der) { 58 0 : for (casacore::uInt i=0; i<lprof_p; i++) (*der)[i] = deriv_p[i]; 59 : }; 60 : // Find the estimates (sorted) 61 0 : findga(prof); 62 : // cout << slist_p << endl; 63 0 : return slist_p; 64 : } 65 : 66 : template <class MT> 67 0 : const SpectralList& SpectralEstimate::estimate(const casacore::Vector<MT>& x, 68 : const casacore::Vector<MT>& y) 69 : { 70 0 : if (x.nelements() != y.nelements()) { 71 0 : throw(casacore::AipsError("Abcissa and ordinate vectors must be the same length")); 72 : } 73 0 : if (x.nelements()==1) { 74 0 : throw(casacore::AipsError("Not enough elements in vectors")); 75 : } 76 : // Get pixel-based estimate (into slist_p) 77 0 : estimate(y); 78 : // Convert 79 0 : for (casacore::uInt i=0; i<slist_p.nelements(); i++) { 80 0 : if (slist_p[i]->getType() != SpectralElement::GAUSSIAN) { 81 0 : throw casacore::AipsError("Non-gaussian spectral types cannot be estimated"); 82 : } 83 0 : const GaussianSpectralElement elIn = *dynamic_cast<const GaussianSpectralElement *>(slist_p[i]); 84 0 : GaussianSpectralElement elOut = convertElement (x, elIn); 85 0 : slist_p.set(elOut, i); 86 : } 87 0 : return slist_p; 88 : } 89 : 90 : 91 : template <class MT> 92 0 : casacore::uInt SpectralEstimate::window(const casacore::Vector<MT> &prof) { 93 0 : windowLow_p =0; 94 0 : windowEnd_p = 0; 95 0 : if (!useWindow_p || rms_p <= 0.0 || lprof_p == 0) { 96 0 : if (regionEnd_p) { 97 0 : windowLow_p = casacore::min(casacore::max(0,regionLow_p),casacore::Int(lprof_p)); 98 0 : windowEnd_p = casacore::min(regionEnd_p, casacore::Int(lprof_p)); 99 0 : } else windowEnd_p = lprof_p; 100 0 : return windowEnd_p-windowLow_p; 101 : }; 102 : // Total flux in profile and max position 103 0 : casacore::Double flux(0.0); 104 0 : casacore::Double pmax(prof(0)); 105 0 : casacore::uInt imax(0); 106 0 : for (casacore::Int i=windowLow_p; i<windowEnd_p; i++) { 107 0 : if (prof(i)>pmax) { 108 0 : pmax = prof(i); 109 0 : imax = i; 110 : }; 111 0 : flux += prof(i); 112 : }; 113 : // No data 114 0 : if (pmax < cutoff_p) return 0; 115 : // Window boundaries; new/old base and centre; width 116 0 : casacore::Int width(-1); 117 0 : casacore::Int nw(0); 118 0 : casacore::Double bnew(flux), bold; 119 0 : casacore::Double cnew(imax), cold; 120 0 : do { 121 0 : width++; 122 0 : cold = cnew; 123 0 : bold = bnew; 124 0 : windowLow_p = casacore::max(0, casacore::Int(cold-width+0.5)); 125 0 : windowEnd_p = casacore::min(casacore::Int(lprof_p), casacore::Int(cold+width+1.5)); 126 : // flux and first moment in window 127 0 : casacore::Double s(0); 128 0 : casacore::Double c(0); 129 0 : for (casacore::Int i=windowLow_p; i<windowEnd_p; i++) { 130 0 : s += prof(i); 131 0 : c += i*prof(i); 132 : }; 133 0 : bnew = flux-s; 134 0 : nw = lprof_p-windowEnd_p+windowLow_p; 135 0 : if (s != 0.0) { 136 0 : cnew = c/s; 137 0 : if (cnew < 0 || cnew >= lprof_p) cnew = cold; 138 : }; 139 0 : } while (abs(bnew-bold) > rms_p && nw); 140 0 : return windowEnd_p-windowLow_p; 141 : } 142 : 143 : template <class MT> 144 0 : void SpectralEstimate::findc2(const casacore::Vector<MT> &prof) { 145 0 : for (casacore::Int i=windowLow_p; i<windowEnd_p; i++) { 146 : // Moments 147 0 : casacore::Double m0(0.0); 148 0 : casacore::Double m2(0.0); 149 0 : for (casacore::Int j = -q_p; j <= q_p; j++) { 150 0 : casacore::Int k = i+j; 151 0 : if (k >= 0 && k<casacore::Int(lprof_p)) { 152 : // add to moments 153 0 : m0 += prof(k); 154 0 : m2 += prof(k)*j*j; 155 : }; 156 : }; 157 : // get the derivative 158 0 : deriv_p[i] = a_p*(m2-b_p*m0); 159 : }; 160 0 : } 161 : 162 : template <class MT> 163 0 : void SpectralEstimate::findga(const casacore::Vector<MT> &prof) { 164 0 : casacore::Int i(windowLow_p-1); 165 : // Window on Gaussian 166 0 : casacore::Int iclo(windowLow_p); 167 0 : casacore::Int ichi(windowLow_p); 168 : // Peak counter 169 0 : casacore::Int nmax = 0; 170 0 : GaussianSpectralElement tspel; 171 0 : while (++i < windowEnd_p) { 172 0 : if (deriv_p[i] > 0.0) { 173 : // At edge? 174 0 : if (i > windowLow_p && i < windowEnd_p-1) { 175 : // Peak in 2nd derivative 176 0 : if (deriv_p[i-1] < deriv_p[i] && deriv_p[i+1] < deriv_p[i]) nmax++; 177 : // At start 178 0 : } else if (i == windowLow_p && deriv_p[i+1] < deriv_p[i]) nmax++; 179 : // At end of window 180 0 : else if (i == windowEnd_p-1 && deriv_p[i-1] < deriv_p[i]) nmax++; 181 : }; 182 0 : switch (nmax) { 183 : // Search for next peak 184 0 : case 1: 185 0 : break; 186 : // Found a Gaussian 187 0 : case 2: { 188 : // Some moments 189 0 : casacore::Double m0m(0); 190 0 : casacore::Double m0(0); 191 0 : casacore::Double m1(0); 192 0 : casacore::Double m2(0); 193 0 : ichi = i; 194 : // Do Schwarz' calculation 195 0 : casacore::Double b = deriv_p[iclo]; 196 0 : casacore::Double a = (deriv_p[ichi] - b) / (ichi-iclo); 197 0 : for (casacore::Int ic=iclo; ic<=ichi; ic++) { 198 0 : m0m += casacore::min(deriv_p[ic], 0.0); 199 0 : casacore::Double wi = deriv_p[ic] - a*(ic-iclo) - b; 200 0 : m0 += wi; 201 0 : m1 += wi*ic; 202 0 : m2 += wi*ic*ic; 203 : }; 204 : // determinant 205 0 : casacore::Double det = m2*m0 - m1*m1; 206 0 : if (det > 0.0 && fabs(m0m) > FLT_EPSILON) { 207 0 : casacore::Double xm = m1/m0; 208 0 : casacore::Double sg = 1.69*sqrt(det) / fabs(m0); 209 : // Width above critical? 210 0 : if (sg > sigmin_p) { 211 0 : casacore::Int is = casacore::Int(1.73*sg+0.5); 212 0 : casacore::Int im = casacore::Int(xm+0.5); 213 0 : casacore::Double yl(0); 214 0 : if ((im-is) >= 0) yl = prof(im-is); 215 0 : casacore::Double yh(0); 216 0 : if ((im + is) <= casacore::Int(lprof_p-1)) yh = prof(im+is); 217 0 : casacore::Double ym = prof(im); 218 : // modified by dmehringer 2012apr03 to deal with 0 denominator 219 : // 0.0/0.0 produces NaN on Linux but 0 on OSX 220 0 : casacore::Double pg = (ym-0.5*(yh+yl)); 221 0 : if (pg != 0) { 222 0 : casacore::Double denom = (1.0-exp(-0.5*(is*is)/sg/sg)); 223 0 : if (denom == 0) { 224 0 : throw casacore::AipsError("Bailing because division by zero is undefined"); 225 : } 226 0 : pg /= denom; 227 : } 228 : // end dmehring mods 229 0 : pg = casacore::min(pg, ym); 230 : // cout << "pg " << pg << " cutoff " << cutoff_p << endl; 231 : // Above critical level? Add to list 232 0 : if (pg > cutoff_p) { 233 : // cout << pg << " " << xm << " " << sg << endl; 234 0 : tspel.setAmpl(pg); 235 0 : tspel.setCenter(xm); 236 0 : tspel.setSigma(sg); 237 0 : slist_p.insert(tspel); 238 : }; 239 : }; 240 : }; 241 : // Next gaussian 242 0 : iclo = ichi; 243 0 : nmax--; 244 0 : break; 245 : } 246 0 : default: 247 0 : iclo = i+1; 248 0 : break; 249 : }; 250 : }; 251 0 : } 252 : 253 : template <class MT> 254 0 : GaussianSpectralElement SpectralEstimate::convertElement (const casacore::Vector<MT>& x, 255 : const GaussianSpectralElement& el) const 256 : { 257 0 : GaussianSpectralElement elOut = el; 258 0 : const casacore::Int& idxMax = x.nelements()-1; 259 : 260 : // Get current (pars are amp, center, width as the SpectralElement 261 : // will always be a Gaussian) 262 : 263 0 : casacore::Vector<casacore::Double> par, err; 264 0 : el.get(par); 265 0 : el.getError(err); 266 : 267 : // Center 268 : 269 0 : casacore::Int cenIdx = casacore::Int(par[1]); 270 : 271 : // Get the x-increment, local to the center, as best we can from 272 : // the abcissa vector. The following algorithm assumes the X 273 : // vector is monotonic 274 : 275 : casacore::Double incX; 276 0 : if (cenIdx-1<0) { 277 0 : incX = x[1] - x[0]; 278 0 : } else if (cenIdx+1>idxMax) { 279 0 : incX = x[idxMax] - x[idxMax-1]; 280 : } else { 281 0 : incX = 0.5 * (x(cenIdx+1) - x(cenIdx-1)); 282 : } 283 : // 284 0 : if (cenIdx<0) { 285 0 : par[1] = incX*par[1] + x[0]; // Extrapolate from x[0] 286 0 : } else if (cenIdx>idxMax) { 287 0 : par[1] = incX*(par[1]-idxMax) + x[idxMax]; // Extrapolate from x[idxMax] 288 : } else { 289 0 : casacore::Double dIdx = par[1] - cenIdx; 290 0 : par[1] = x[cenIdx] + dIdx*incX; // Interpolate 291 : } 292 0 : err[1] = abs(err[1] * incX); 293 : 294 : // Width 295 : 296 0 : par[2] = abs(par[2] * incX); 297 0 : err[2] = abs(err[2] * incX); 298 : 299 0 : elOut.set(par); 300 0 : elOut.setError(err); 301 0 : return elOut; 302 : } 303 : 304 : 305 : } //# End namespace casa