source: trunk/src/fitsHeader.cc @ 947

Last change on this file since 947 was 947, checked in by MatthewWhiting, 12 years ago

First lot of changes addressing the spectral WCS issue #146. This removes the large bit of code in wcsIO that tries to analyse the spectral type and convert to standard
forms, and instead just makes use of what is in the FITS file. There are a few additions to both FitsHeader? and Detection to keep track of the spectral type (for the
purposes of writing out the results), and to how the spectral columns are named (this now makes use of the four-letter ctype code, or "S-type" code).

File size: 9.8 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->spectralType  = h.spectralType;
86    this->spectralDescription = h.spectralDescription;
87    this->originalFluxUnits = h.originalFluxUnits;
88    this->fluxUnits     = h.fluxUnits;
89    this->intFluxUnits  = h.intFluxUnits;
90    this->itsBeam       = h.itsBeam;
91    this->blankKeyword  = h.blankKeyword;
92    this->bzeroKeyword  = h.bzeroKeyword;
93    this->bscaleKeyword = h.bscaleKeyword;
94    this->scale         = h.scale;
95    this->offset        = h.offset;
96    this->power         = h.power;
97    return *this;
98  }
99
100  void FitsHeader::setWCS(struct wcsprm *w)
101  {
102    ///  A function that assigns the wcs parameters, and runs
103    ///   wcsset to set it up correctly.
104    ///  Performs a check to see if the WCS is good (by looking at
105    ///   the lng and lat wcsprm parameters), and sets the wcsIsGood
106    ///   flag accordingly.
107    /// \param w A WCSLIB wcsprm struct with the correct parameters.
108
109    wcscopy(true, w, this->wcs);
110    wcsset(this->wcs);
111    if( (w->lng!=-1) && (w->lat!=-1) ) this->wcsIsGood = true;
112  }
113
114  struct wcsprm *FitsHeader::getWCS()
115  {
116    ///  A function that returns a properly initilized wcsprm object
117    ///  corresponding to the WCS.
118
119    struct wcsprm *wNew = (struct wcsprm *)calloc(1,sizeof(struct wcsprm));
120    wNew->flag=-1;
121    wcsini(true, this->wcs->naxis, wNew);
122    wcscopy(true, this->wcs, wNew);
123    wcsset(wNew);
124    return wNew;
125  }
126
127  int FitsHeader::wcsToPix(const double *world, double *pix)
128  {     
129    return wcsToPixSingle(this->wcs, world, pix); 
130  }
131  int FitsHeader::wcsToPix(const double *world, double *pix, const int npts)
132  {
133    return wcsToPixMulti(this->wcs, world, pix, npts); 
134  }
135  int FitsHeader::pixToWCS(const double *pix, double *world)
136  {   
137    return pixToWCSSingle(this->wcs, pix, world); 
138  }
139  int FitsHeader::pixToWCS(const double *pix, double *world, const int npts)
140  {
141    return pixToWCSMulti(this->wcs, pix,world, npts); 
142  }
143
144
145  double FitsHeader::pixToVel(double &x, double &y, double &z)
146  {
147    double vel;
148    if(this->wcsIsGood){
149      double *pix   = new double[3];
150      double *world = new double[3];
151      pix[0] = x; pix[1] = y; pix[2] = z;
152      pixToWCSSingle(this->wcs,pix,world);
153      vel = this->specToVel(world[2]);
154      delete [] pix;
155      delete [] world;
156    }
157    else vel = z;
158    return vel;
159  }
160
161  double* FitsHeader::pixToVel(double &x, double &y, double *zarray, int size)
162  {
163    double *newzarray = new double[size];
164    if(this->wcsIsGood){
165      double *pix   = new double[size*3];
166      for(int i=0;i<size;i++){
167        pix[3*i]   = x;
168        pix[3*i+1] = y;
169        pix[3*i+2] = zarray[i];
170      }
171      double *world = new double[size*3];
172      pixToWCSMulti(this->wcs,pix,world,size);
173      delete [] pix;
174      for(int i=0;i<size;i++) newzarray[i] = this->specToVel(world[3*i+2]);
175      delete [] world;
176    }
177    else{
178      for(int i=0;i<size;i++) newzarray[i] = zarray[i];
179    }
180    return newzarray;
181  }
182
183  double FitsHeader::specToVel(const double &coord)
184  {
185    double vel;
186    if(power==1.0) vel =  coord*this->scale + this->offset;
187    else vel = pow( (coord*this->scale + this->offset), this->power);
188    return vel;
189  }
190
191  double FitsHeader::velToSpec(const float &velocity)
192  {
193    //   return velToCoord(this->wcs,velocity,this->spectralUnits);};
194    return (pow(velocity, 1./this->power) - this->offset) / this->scale;
195  }
196
197  std::string FitsHeader::getIAUName(double ra, double dec)
198  {
199    if(std::string(this->wcs->lngtyp)=="RA")
200      return getIAUNameEQ(ra, dec, this->wcs->equinox);
201    else
202      return getIAUNameGAL(ra, dec);
203  }
204
205  void FitsHeader::fixUnits(Param &par)
206  {
207    ///  Put the units for the FITS header into some sort of standard form.
208    ///
209    ///  We first get the desired spectral units from the Parameter set,
210    ///  and then transform the spectral units of the wcsprm struct to
211    ///  those units. If this doesn't work, we leave them as they are. If
212    ///  they are blank, we make them SPC and give an error message --
213    ///  this should hopefully NOT happen.
214    ///
215    ///  We also work out the units for the integrated flux. If there are
216    ///  three axes, we just append the spectral units to the flux units
217    ///  (removing "/beam" if it is present). If there are just two, we
218    ///  simply keep it the same, removing the "/beam".
219    ///
220    ///  \param par The parameter set telling us what the desired
221    ///             spectral units are.
222
223    // define spectral units from the param set
224    this->spectralUnits = par.getSpectralUnits();
225    double sc=1.;
226    double of=0.;
227    double po=1.;
228    //   if((this->wcsIsGood) && (this->naxis>2)){
229    if(this->wcsIsGood){
230      int status = wcsunits( this->wcs->cunit[this->wcs->spec],
231                             (char *)this->spectralUnits.c_str(),
232                             &sc, &of, &po);
233      if(status > 0){
234        std::stringstream errmsg;
235        errmsg << "WCSUNITS Error, Code = " << status
236               << ": " << wcsunits_errmsg[status] << "\n";
237        if(status == 10)
238          errmsg << "Tried to get conversion from \""
239                 << this->wcs->cunit[this->wcs->spec] << "\" to \""
240                 << this->spectralUnits.c_str() << "\".\n";
241        this->spectralUnits = this->wcs->cunit[this->wcs->spec];
242        if(this->spectralUnits==""){
243          errmsg << "Spectral units not specified. "
244                 << "For data presentation, we will use dummy units of \"SPC\"."
245                 << "\n"
246                 << "Please report this occurence -- it should not happen now! "
247                 << "In the meantime, you may want to set the CUNIT"
248                 << this->wcs->spec + 1 <<" keyword to make this work.\n";
249          this->spectralUnits = "SPC";
250        }
251        DUCHAMPERROR("fixUnits", errmsg.str());
252      }
253      // else{
254      //        std::cerr << "Converted " << this->wcs->cunit[this->wcs->spec] << " to " << this->spectralUnits << " and got offset="<<of << ", scale="<<sc <<", and power="<<po <<"\n";
255      // }
256    }
257    this->scale = sc;
258    this->offset= of;
259    this->power = po;
260
261    this->setIntFluxUnits();
262
263  }
264
265  bool FitsHeader::needBeamSize()
266  {
267    ///  A function that tells you whether the beam correction is
268    ///  needed. It checks to see whether the flux units string ends in
269    ///  "/beam" (in which case the beam size etc is needed and
270    ///  integrated fluxes need to be corrected). If we don't have any beam
271    ///  information, this will return false.
272    ///  /return True if FitsHeader::fluxUnits ends in "/beam". False
273    ///  otherwise.
274
275    int size = this->fluxUnits.size();
276    if(this->itsBeam.origin()==EMPTY) return false; // we have no beam to correct with!
277    else if(size<6) return false;
278    else {
279      std::string tailOfFluxUnits = makelower(this->fluxUnits.substr(size-5,size));
280      return (tailOfFluxUnits == "/beam");
281    }
282  }
283
284  void FitsHeader::setIntFluxUnits()
285  {
286
287    /// Work out the integrated flux units, based on the spectral
288    /// units. If flux is per beam, trim the /beam from the flux
289    /// units. Then, if as long as the image is 3D, multiply by the
290    /// spectral units.
291   
292    if(this->needBeamSize())
293      this->intFluxUnits = this->fluxUnits.substr(0,this->fluxUnits.size()-5);
294    else
295      this->intFluxUnits = this->fluxUnits;
296
297    if(!this->flag2D) this->intFluxUnits += std::string(" ") + this->spectralUnits;
298
299  }
300
301
302
303}
Note: See TracBrowser for help on using the repository browser.