source: trunk/src/Detection/detection.cc @ 191

Last change on this file since 191 was 191, checked in by Matthew Whiting, 18 years ago

Added the ability to report the peak S/N value for each detection, both in the text output and in the information given above the spectral plots.

File size: 12.9 KB
Line 
1#include <iostream>
2#include <iomanip>
3#include <vector>
4#include <string>
5#include <wcs.h>
6#include <math.h>
7#include <param.hh>
8#include <Utils/utils.hh>
9#include <Detection/detection.hh>
10
11using std::setw;
12using std::setprecision;
13using std::endl;
14
15std::ostream& operator<< ( std::ostream& theStream, Voxel& vox)
16{
17  /**
18   * << operator for Voxel class
19   * A convenient way of printing the coordinate
20   *  and flux values of a voxel.
21   */ 
22
23  theStream << setw(4) << vox.itsX ;
24  theStream << " " << setw(4) << vox.itsY;
25  theStream << " " << setw(4) << vox.itsZ;
26  theStream << setprecision(4);
27  theStream << "  " << vox.itsF;
28
29}
30//--------------------------------------------------------------------
31
32void Detection::calcParams()
33{
34  /**
35   * Detection::calcParams()
36   *  A function that calculates centroid positions,
37   *   minima & maxima of coordinates,
38   *   and total & peak fluxes for a detection.
39   */
40
41  this->xcentre = 0;
42  this->ycentre = 0;
43  this->zcentre = 0;
44  this->totalFlux = 0;
45  this->peakFlux = 0;
46  this->xmin = 0;
47  this->xmax = 0;
48  this->ymin = 0;
49  this->ymax = 0;
50  this->zmin = 0;
51  this->zmax = 0;
52  if(this->pix.size()>0){
53    this->peakFlux = this->pix[0].itsF;
54    for(int ctr=0;ctr<this->pix.size();ctr++){
55      this->xcentre   += this->pix[ctr].itsX;
56      this->ycentre   += this->pix[ctr].itsY;
57      this->zcentre   += this->pix[ctr].itsZ;
58      this->totalFlux += this->pix[ctr].itsF;
59      if((ctr==0)||(this->pix[ctr].itsX<this->xmin))
60        this->xmin = this->pix[ctr].itsX;
61      if((ctr==0)||(this->pix[ctr].itsX>this->xmax))
62        this->xmax = this->pix[ctr].itsX;
63      if((ctr==0)||(this->pix[ctr].itsY<this->ymin))
64        this->ymin = this->pix[ctr].itsY;
65      if((ctr==0)||(this->pix[ctr].itsY>this->ymax))
66        this->ymax = this->pix[ctr].itsY;
67      if((ctr==0)||(this->pix[ctr].itsZ<this->zmin))
68        this->zmin = this->pix[ctr].itsZ;
69      if((ctr==0)||(this->pix[ctr].itsZ>this->zmax))
70        this->zmax = this->pix[ctr].itsZ;
71      if(this->negativeSource){
72        // if negative feature, peakFlux is most negative flux
73        if((ctr==0)||(this->pix[ctr].itsF < this->peakFlux)){
74          this->peakFlux = this->pix[ctr].itsF;
75          this->xpeak = this->pix[ctr].itsX;
76          this->ypeak = this->pix[ctr].itsY;
77          this->zpeak = this->pix[ctr].itsZ;
78        }
79      }
80      else{
81        // otherwise, it's a regular detection,
82        //   and peakFlux is most positive flux
83        if((ctr==0)||(this->pix[ctr].itsF > this->peakFlux)){
84          this->peakFlux = this->pix[ctr].itsF;
85          this->xpeak = this->pix[ctr].itsX;
86          this->ypeak = this->pix[ctr].itsY;
87          this->zpeak = this->pix[ctr].itsZ;
88        }
89      }
90    }
91    this->xcentre /= this->pix.size();
92    this->ycentre /= this->pix.size();
93    this->zcentre /= this->pix.size();
94  }
95}
96//--------------------------------------------------------------------
97
98void Detection::calcWCSparams(FitsHeader &head)
99{
100  /**
101   * Detection::calcWCSparams(FitsHeader &)
102   *  Use the input wcs to calculate the position and velocity
103   *    information for the Detection.
104   *  Quantities calculated:
105   *     RA: ra [deg], ra (string), ra width.
106   *     Dec: dec [deg], dec (string), dec width.
107   *     Vel: vel [km/s], min & max vel, vel width.
108   *     Other: coord type for all three axes, nuRest,
109   *            name (IAU-style, in equatorial or Galactic)
110   *     Uses getIntegFlux to calculate the integrated flux in (say) [Jy km/s]
111   */
112
113  if(head.isWCS()){
114
115    this->calcParams(); // make sure this is up to date.
116
117    double *pixcrd = new double[15];
118    double *world  = new double[15];
119    /*
120      define a five-point array in 3D:
121      (x,y,z), (x,y,z1), (x,y,z2), (x1,y1,z), (x2,y2,z)
122      [note: x = central point, x1 = minimum x, x2 = maximum x etc.]
123      and convert to world coordinates.   
124    */
125    pixcrd[0]  = pixcrd[3] = pixcrd[6] = this->xcentre;
126    pixcrd[9]  = this->xmin-0.5;
127    pixcrd[12] = this->xmax+0.5;
128    pixcrd[1]  = pixcrd[4] = pixcrd[7] = this->ycentre;
129    pixcrd[10] = this->ymin-0.5;
130    pixcrd[13] = this->ymax+0.5;
131    pixcrd[2] = pixcrd[11] = pixcrd[14] = this->zcentre;
132    pixcrd[5] = this->zmin;
133    pixcrd[8] = this->zmax;
134    int flag = head.pixToWCS(pixcrd, world, 5);
135    delete [] pixcrd;
136
137    // world now has the WCS coords for the five points
138    //    -- use this to work out WCS params
139 
140    this->lngtype = head.getWCS()->lngtyp;
141    this->lattype = head.getWCS()->lattyp;
142    this->specUnits = head.getSpectralUnits();
143    this->fluxUnits = head.getFluxUnits();
144    // if fluxUnits are eg. Jy/beam, make intFluxUnits = Jy km/s
145    this->intFluxUnits = head.getIntFluxUnits();
146    this->ra   = world[0];
147    this->dec  = world[1];
148    this->raS  = decToDMS(this->ra, this->lngtype);
149    this->decS = decToDMS(this->dec,this->lattype);
150    this->raWidth   = angularSeparation(world[9],world[1],world[12],world[1]) * 60.;
151    this->decWidth  = angularSeparation(world[0],world[10],world[0],world[13]) * 60.;
152    this->name = head.getIAUName(this->ra, this->dec);
153    this->vel    = head.specToVel(world[2]);
154    this->velMin = head.specToVel(world[5]);
155    this->velMax = head.specToVel(world[8]);
156    this->velWidth = fabs(this->velMax - this->velMin);
157
158    this->getIntegFlux(head);
159   
160    this->flagWCS = true;
161
162    delete [] world;
163
164  }
165}
166//--------------------------------------------------------------------
167
168float Detection::getIntegFlux(FitsHeader &head)
169{
170  /**
171   * Detection::getIntegFlux(FitsHeader)
172   *  Uses the input wcs to calculate the velocity-integrated flux,
173   *   putting velocity in units of km/s.
174   *  Integrates over full spatial and velocity range as given
175   *   by the extrema calculated by calcWCSparams.
176   *  If the flux units end in "/beam" (eg. Jy/beam), then the
177   *   flux is corrected by the beam size (in pixels).
178   */
179
180  // include one pixel either side in each direction
181  int xsize = (this->xmax-this->xmin+3);
182  int ysize = (this->ymax-this->ymin+3);
183  int zsize = (this->zmax-this->zmin+3);
184  vector <bool> isObj(xsize*ysize*zsize,false);
185  vector <float> fluxArray(xsize*ysize*zsize,0.);
186  // work out which pixels are object pixels
187  for(int p=0;p<this->pix.size();p++){
188    int pos = (this->pix[p].getX()-this->xmin+1)
189      + (this->pix[p].getY()-this->ymin+1)*xsize
190      + (this->pix[p].getZ()-this->zmin+1)*xsize*ysize;
191    fluxArray[pos] = this->pix[p].getF();
192    isObj[pos] = true;
193  }
194 
195  // work out the WCS coords for each pixel
196  double *world  = new double[xsize*ysize*zsize];
197  double x,y,z;
198  for(int i=0;i<xsize*ysize*zsize;i++){
199    x = double( this->xmin -1 + i%xsize );
200    y = double( this->ymin -1 + (i/xsize)%ysize );
201    z = double( this->zmin -1 + i/(xsize*ysize) );
202    world[i] = head.pixToVel(x,y,z);
203  }
204
205  this->intFlux = 0.;
206  for(int pix=0; pix<xsize*ysize; pix++){ // loop over each spatial pixel.
207    for(int z=0; z<zsize; z++){
208      int pos =  z*xsize*ysize + pix;
209      if(isObj[pos]){ // if it's an object pixel...
210        float deltaVel;
211        if(z==0)
212          deltaVel = (world[pos+xsize*ysize] - world[pos]);
213        else if(z==(zsize-1))
214          deltaVel = (world[pos] - world[pos-xsize*ysize]);
215        else
216          deltaVel = (world[pos+xsize*ysize] - world[pos-xsize*ysize]) / 2.;
217        this->intFlux += fluxArray[pos] * fabs(deltaVel);
218      }
219    }
220  }
221
222  // correct for the beam size if the flux units string ends in "/beam"
223  int size = this->fluxUnits.size();
224  string tailOfFluxUnits = this->fluxUnits.substr(size-5,size);
225  if(tailOfFluxUnits == "/beam") this->intFlux /= head.getBeamSize();
226
227  delete [] world;
228}
229//--------------------------------------------------------------------
230
231void Detection::addAnObject(Detection &toAdd)
232{
233  /**
234   * Detection::addAnObject(Detection &)
235   *  Combines two objects by adding all the pixels of the argument
236   *   to the instigator.
237   *  All pixel & flux parameters are recalculated (so that
238   *   calcParams does not need to be called a second time),
239   *   but WCS parameters are not.
240   *  If the instigator is empty (pix.size()==0) then we just make it
241   *   equal to the argument, and call calcParams to initialise the
242   *   necessary parameters
243   */
244
245  int size = this->pix.size();
246  if(size==0){
247    *this = toAdd;
248    this->calcParams();
249  }
250  else if(size>0){
251
252    this->xcentre *= size;
253    this->ycentre *= size;
254    this->zcentre *= size;
255 
256    for(int ctr=0;ctr<toAdd.getSize();ctr++){
257      long x  = toAdd.getX(ctr);
258      long y  = toAdd.getY(ctr);
259      long z  = toAdd.getZ(ctr);
260      float f = toAdd.getF(ctr);
261      bool isNewPix = true;
262      int ctr2 = 0;
263      // For each pixel in the new object, test to see if it already
264      //  appears in the object
265      while( isNewPix && (ctr2<this->pix.size()) ){
266        isNewPix = isNewPix && (( this->pix[ctr2].itsX != x ) ||
267                                ( this->pix[ctr2].itsY != y ) ||
268                                ( this->pix[ctr2].itsZ != z ) );
269        ctr2++;
270      }
271      if(isNewPix){
272        // If the pixel is new, add it to the object and
273        //   re-calculate the parameters.
274        this->pix.push_back(toAdd.getPixel(ctr));
275        this->xcentre += x;
276        this->ycentre += y;
277        this->zcentre += z;
278        this->totalFlux += f;
279        if     (x < this->xmin) this->xmin = x;
280        else if(x > this->xmax) this->xmax = x;
281        if     (y < this->ymin) this->ymin = y;
282        else if(y > this->ymax) this->ymax = y;
283        if     (z < this->zmin) this->zmin = z;
284        else if(z > this->zmax) this->zmax = z;
285        if(f > this->peakFlux) this->peakFlux = f;
286      }
287    }
288    size = this->pix.size();
289    this->xcentre /= size;
290    this->ycentre /= size;
291    this->zcentre /= size;
292
293  }
294}
295//--------------------------------------------------------------------
296
297void Detection::addOffsets(Param &par)
298{
299  this->xSubOffset = par.getXOffset();
300  this->ySubOffset = par.getYOffset();
301  this->zSubOffset = par.getZOffset();
302}
303//--------------------------------------------------------------------
304
305bool Detection::hasEnoughChannels(int minNumber)
306{
307  /**
308   *  bool hasEnoughChannels(int)
309   *    A function to determine if the Detection has enough
310   *    contiguous channels to meet the minimum requirement
311   *    given as the argument.
312   *    Needs to have at least one occurence of minNumber consecutive
313   *    channels present to return true. Otherwise returns false.
314   */
315
316  // Original requirement -- based on total span
317// int numChannels = this->getZmax() - this->getZmin() + 1;
318
319// Alternative -- number of distinct channels detected
320//   int numChannels = 0;
321//   this->SortByZ();
322//   if(this->getSize()>0) numChannels++;
323//   for(int i=1;i<this->getSize();i++)
324//     if(this->getZ(i)>this->getZ(i-1)) numChannels++; 
325//   return (numChannels < minNumber);
326
327// Preferred method -- need a set of minNumber consecutive channels present.
328  this->SortByZ();
329  int numChannels = 0;
330  bool result = false;
331  if(this->getSize()>0) numChannels++;
332  for(int i=1;i<this->getSize();i++) {
333    if( (this->getZ(i) - this->getZ(i-1)) == 1) numChannels++;
334    else if( (this->getZ(i) - this->getZ(i-1)) >= 2) numChannels = 1;
335
336    if( numChannels >= minNumber) result = true;
337  }
338  return result;
339 
340}
341//--------------------------------------------------------------------
342
343int Detection::getSpatialSize()
344{
345  /**
346   *  int getSpatialSize()
347   *    A function that returns the number of distinct spatial
348   *     pixels in a Detection.
349   */
350
351  vector<Pixel> spatialPix;
352  Pixel newpix;
353  bool addThisOne;
354  newpix.setXY(this->getX(0),this->getY(0));
355  spatialPix.push_back(newpix);
356  for(int i=1;i<this->pix.size();i++){
357    newpix.setXY(this->getX(i),this->getY(i));
358    addThisOne = true;
359    for(int j=0;(j<spatialPix.size())&&addThisOne;j++) {
360      // do whole list or until addThisOne=false
361      addThisOne = ( (newpix.getX()!=spatialPix[j].getX()) ||
362                     (newpix.getY()!=spatialPix[j].getY())   );
363      // ie. if one of X or Y is different, addThisOne is true.
364    }
365    if(addThisOne) spatialPix.push_back(newpix);
366  }
367  return spatialPix.size();
368}
369//--------------------------------------------------------------------
370
371std::ostream& operator<< ( std::ostream& theStream, Detection& obj)
372{
373  /**
374   * << operator for Detection class
375   *  A convenient way of printing the coordinate & flux
376   *  values for each pixel in the Detection
377   *   --> use as front end to the << operator for Voxels.
378   */ 
379
380  for(int i=0;i<obj.pix.size();i++) theStream << obj.pix[i] << endl;
381  theStream<<"---"<<endl;
382}
383//--------------------------------------------------------------------
384
385Detection combineObjects(Detection &first, Detection &second)
386{
387  // make the new object
388  int *ctr = new int;
389  Detection *newObject = new Detection;
390  for(*ctr=0;(*ctr)<first.getSize();(*ctr)++){
391    newObject->addPixel(first.getPixel(*ctr));
392  }
393  for(*ctr=0;(*ctr)<second.getSize();(*ctr)++){
394    newObject->addPixel(second.getPixel(*ctr));
395  }
396  delete ctr;
397  newObject->calcParams();
398  return *newObject;
399}
400//--------------------------------------------------------------------
401
402vector <Detection> combineLists(vector <Detection> &first, vector <Detection> &second)
403{
404  // make the new object
405  vector <Detection> newList(first.size()+second.size());
406  for(int i=0;i<first.size();i++) newList[i] = first[i];
407  for(int i=0;i<second.size();i++) newList[i+first.size()] = second[i];
408 
409  return newList;
410}
411
Note: See TracBrowser for help on using the repository browser.