source: trunk/src/fitsHeader.cc @ 789

Last change on this file since 789 was 789, checked in by MatthewWhiting, 13 years ago

And fixing #110 - the thing that needed fixing was for the FitsHeader::needBeamSize() function - this now checks to see if the beam is empty, and so does not do the correction. Also the integrated flux units are now only changed if the beam is not EMPTY.

File size: 10.3 KB
Line 
1// -----------------------------------------------------------------------
2// fitsHeader.cc: Information about the FITS file's header.
3// -----------------------------------------------------------------------
4// Copyright (C) 2006, Matthew Whiting, ATNF
5//
6// This program is free software; you can redistribute it and/or modify it
7// under the terms of the GNU General Public License as published by the
8// Free Software Foundation; either version 2 of the License, or (at your
9// option) any later version.
10//
11// Duchamp is distributed in the hope that it will be useful, but WITHOUT
12// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14// for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with Duchamp; if not, write to the Free Software Foundation,
18// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
19//
20// Correspondence concerning Duchamp may be directed to:
21//    Internet email: Matthew.Whiting [at] atnf.csiro.au
22//    Postal address: Dr. Matthew Whiting
23//                    Australia Telescope National Facility, CSIRO
24//                    PO Box 76
25//                    Epping NSW 1710
26//                    AUSTRALIA
27// -----------------------------------------------------------------------
28#include <iostream>
29#include <sstream>
30#include <string>
31#include <stdlib.h>
32#include <wcslib/wcs.h>
33#include <wcslib/wcsunits.h>
34#include <duchamp/duchamp.hh>
35#include <duchamp/param.hh>
36#include <duchamp/fitsHeader.hh>
37#include <duchamp/Utils/utils.hh>
38
39namespace duchamp
40{
41
42  FitsHeader::FitsHeader()
43  {
44    this->wcs = (struct wcsprm *)calloc(1,sizeof(struct wcsprm));
45    this->wcs->flag=-1;
46    wcsini(true, 3, this->wcs);
47    this->wcsIsGood = false;
48    this->nwcs = 0;
49    this->flag2D=false;
50    this->spectralUnits="";
51    this->spectralDescription=duchamp::duchampSpectralDescription[FREQUENCY];
52    this->originalFluxUnits="counts";
53    this->fluxUnits="counts";
54    this->intFluxUnits="counts";
55    this->scale=1.;
56    this->offset=0.;
57    this->power=1.;
58    this->itsBeam.empty();
59  }
60
61  FitsHeader::~FitsHeader()
62  {
63    /// @details Uses the WCSLIB function wcsvfree to clear the wcsprm struct.
64    wcsvfree(&nwcs,&wcs);
65  }
66
67  FitsHeader::FitsHeader(const FitsHeader& h)
68  {
69    operator=(h);
70  }
71
72  FitsHeader& FitsHeader::operator= (const FitsHeader& h)
73  {
74    if(this == &h) return *this;
75    this->wcs = (struct wcsprm *)calloc(1,sizeof(struct wcsprm));
76    this->wcs->flag     = -1;
77    wcsini(true, h.wcs->naxis, this->wcs);
78    wcscopy(true, h.wcs, this->wcs);
79    wcsset(this->wcs);
80    this->nwcs          = h.nwcs;
81    this->wcsIsGood     = h.wcsIsGood;
82    this->naxis         = h.naxis;
83    this->flag2D        = h.flag2D;
84    this->spectralUnits = h.spectralUnits;
85    this->spectralDescription = h.spectralDescription;
86    this->originalFluxUnits = h.originalFluxUnits;
87    this->fluxUnits     = h.fluxUnits;
88    this->intFluxUnits  = h.intFluxUnits;
89    this->beamSize      = h.beamSize;
90    this->bmajKeyword   = h.bmajKeyword;
91    this->bminKeyword   = h.bminKeyword;
92    this->bpaKeyword    = h.bpaKeyword;
93    this->itsBeam       = h.itsBeam;
94    this->blankKeyword  = h.blankKeyword;
95    this->bzeroKeyword  = h.bzeroKeyword;
96    this->bscaleKeyword = h.bscaleKeyword;
97    this->scale         = h.scale;
98    this->offset        = h.offset;
99    this->power         = h.power;
100    return *this;
101  }
102
103  void FitsHeader::setWCS(struct wcsprm *w)
104  {
105    ///  A function that assigns the wcs parameters, and runs
106    ///   wcsset to set it up correctly.
107    ///  Performs a check to see if the WCS is good (by looking at
108    ///   the lng and lat wcsprm parameters), and sets the wcsIsGood
109    ///   flag accordingly.
110    /// \param w A WCSLIB wcsprm struct with the correct parameters.
111
112    wcscopy(true, w, this->wcs);
113    wcsset(this->wcs);
114    if( (w->lng!=-1) && (w->lat!=-1) ) this->wcsIsGood = true;
115  }
116
117  struct wcsprm *FitsHeader::getWCS()
118  {
119    ///  A function that returns a properly initilized wcsprm object
120    ///  corresponding to the WCS.
121
122    struct wcsprm *wNew = (struct wcsprm *)calloc(1,sizeof(struct wcsprm));
123    wNew->flag=-1;
124    wcsini(true, this->wcs->naxis, wNew);
125    wcscopy(true, this->wcs, wNew);
126    wcsset(wNew);
127    return wNew;
128  }
129
130  int FitsHeader::wcsToPix(const double *world, double *pix)
131  {     
132    return wcsToPixSingle(this->wcs, world, pix); 
133  }
134  int FitsHeader::wcsToPix(const double *world, double *pix, const int npts)
135  {
136    return wcsToPixMulti(this->wcs, world, pix, npts); 
137  }
138  int FitsHeader::pixToWCS(const double *pix, double *world)
139  {   
140    return pixToWCSSingle(this->wcs, pix, world); 
141  }
142  int FitsHeader::pixToWCS(const double *pix, double *world, const int npts)
143  {
144    return pixToWCSMulti(this->wcs, pix,world, npts); 
145  }
146
147
148  double FitsHeader::pixToVel(double &x, double &y, double &z)
149  {
150    double vel;
151    if(this->wcsIsGood){
152      double *pix   = new double[3];
153      double *world = new double[3];
154      pix[0] = x; pix[1] = y; pix[2] = z;
155      pixToWCSSingle(this->wcs,pix,world);
156      vel = this->specToVel(world[2]);
157      delete [] pix;
158      delete [] world;
159    }
160    else vel = z;
161    return vel;
162  }
163
164  double* FitsHeader::pixToVel(double &x, double &y, double *zarray, int size)
165  {
166    double *newzarray = new double[size];
167    if(this->wcsIsGood){
168      double *pix   = new double[size*3];
169      for(int i=0;i<size;i++){
170        pix[3*i]   = x;
171        pix[3*i+1] = y;
172        pix[3*i+2] = zarray[i];
173      }
174      double *world = new double[size*3];
175      pixToWCSMulti(this->wcs,pix,world,size);
176      delete [] pix;
177      for(int i=0;i<size;i++) newzarray[i] = this->specToVel(world[3*i+2]);
178      delete [] world;
179    }
180    else{
181      for(int i=0;i<size;i++) newzarray[i] = zarray[i];
182    }
183    return newzarray;
184  }
185
186  double FitsHeader::specToVel(const double &coord)
187  {
188    double vel;
189    if(power==1.0) vel =  coord*this->scale + this->offset;
190    else vel = pow( (coord*this->scale + this->offset), this->power);
191    return vel;
192  }
193
194  double FitsHeader::velToSpec(const float &velocity)
195  {
196    //   return velToCoord(this->wcs,velocity,this->spectralUnits);};
197    return (pow(velocity, 1./this->power) - this->offset) / this->scale;
198  }
199
200  std::string FitsHeader::getIAUName(double ra, double dec)
201  {
202    if(std::string(this->wcs->lngtyp)=="RA")
203      return getIAUNameEQ(ra, dec, this->wcs->equinox);
204    else
205      return getIAUNameGAL(ra, dec);
206  }
207
208  bool FitsHeader::needBeamSize()
209  {
210    ///  A function that tells you whether the beam correction is
211    ///  needed. It checks to see whether the flux units string ends in
212    ///  "/beam" (in which case the beam size etc is needed and
213    ///  integrated fluxes need to be corrected). If we don't have any beam
214    ///  information, this will return false.
215    ///  /return True if FitsHeader::fluxUnits ends in "/beam". False
216    ///  otherwise.
217
218    int size = this->fluxUnits.size();
219    if(this->itsBeam.origin()==EMPTY) return false; // we have no beam to correct with!
220    else if(size<6) return false;
221    else {
222      std::string tailOfFluxUnits = makelower(this->fluxUnits.substr(size-5,size));
223      return (tailOfFluxUnits == "/beam");
224    }
225  }
226
227  void FitsHeader::fixUnits(Param &par)
228  {
229    ///  Put the units for the FITS header into some sort of standard form.
230    ///
231    ///  We first get the desired spectral units from the Parameter set,
232    ///  and then transform the spectral units of the wcsprm struct to
233    ///  those units. If this doesn't work, we leave them as they are. If
234    ///  they are blank, we make them SPC and give an error message --
235    ///  this should hopefully NOT happen.
236    ///
237    ///  We also work out the units for the integrated flux. If there are
238    ///  three axes, we just append the spectral units to the flux units
239    ///  (removing "/beam" if it is present). If there are just two, we
240    ///  simply keep it the same, removing the "/beam".
241    ///
242    ///  \param par The parameter set telling us what the desired
243    ///             spectral units are.
244
245    // define spectral units from the param set
246    this->spectralUnits = par.getSpectralUnits();
247    double sc=1.;
248    double of=0.;
249    double po=1.;
250    //   if((this->wcsIsGood) && (this->naxis>2)){
251    if(this->wcsIsGood){
252      int status = wcsunits( this->wcs->cunit[this->wcs->spec],
253                             (char *)this->spectralUnits.c_str(),
254                             &sc, &of, &po);
255      if(status > 0){
256        std::stringstream errmsg;
257        errmsg << "WCSUNITS Error, Code = " << status
258               << ": " << wcsunits_errmsg[status] << "\n";
259        if(status == 10)
260          errmsg << "Tried to get conversion from \""
261                 << this->wcs->cunit[this->wcs->spec] << "\" to \""
262                 << this->spectralUnits.c_str() << "\".\n";
263        this->spectralUnits = this->wcs->cunit[this->wcs->spec];
264        if(this->spectralUnits==""){
265          errmsg << "Spectral units not specified. "
266                 << "For data presentation, we will use dummy units of \"SPC\"."
267                 << "\n"
268                 << "Please report this occurence -- it should not happen now! "
269                 << "In the meantime, you may want to set the CUNIT"
270                 << this->wcs->spec + 1 <<" keyword to make this work.\n";
271          this->spectralUnits = "SPC";
272        }
273        duchampError("fixUnits", errmsg.str());
274      }
275      // else{
276      //        std::cerr << "Converted " << this->wcs->cunit[this->wcs->spec] << " to " << this->spectralUnits << " and got offset="<<of << ", scale="<<sc <<", and power="<<po <<"\n";
277      // }
278    }
279    this->scale = sc;
280    this->offset= of;
281    this->power = po;
282
283    this->setIntFluxUnits();
284
285  }
286
287  void FitsHeader::setIntFluxUnits()
288  {
289
290    /// Work out the integrated flux units, based on the spectral
291    /// units. If flux is per beam, trim the /beam from the flux
292    /// units. Then, if as long as the image is 3D, multiply by the
293    /// spectral units.
294
295    if(this->fluxUnits.size()>5 && this->itsBeam.origin()!=EMPTY){
296   
297      if(makelower(this->fluxUnits.substr(this->fluxUnits.size()-5,
298                                          this->fluxUnits.size()   )) == "/beam"){
299        this->intFluxUnits = this->fluxUnits.substr(0,this->fluxUnits.size()-5);
300      }
301      else this->intFluxUnits = this->fluxUnits;
302
303    }
304    else this->intFluxUnits = this->fluxUnits;
305
306    if(!this->flag2D) this->intFluxUnits += std::string(" ") + this->spectralUnits;
307
308  }
309
310  float getBeamArea(float fwhmMaj, float fwhmMin)
311  {
312
313    return M_PI * (fwhmMaj/2.) * (fwhmMin/2.) / M_LN2;
314
315  }
316
317
318}
Note: See TracBrowser for help on using the repository browser.