LSSTApplications  11.0-13-gbb96280,12.1+18,12.1+7,12.1-1-g14f38d3+72,12.1-1-g16c0db7+5,12.1-1-g5961e7a+84,12.1-1-ge22e12b+23,12.1-11-g06625e2+4,12.1-11-g0d7f63b+4,12.1-19-gd507bfc,12.1-2-g7dda0ab+38,12.1-2-gc0bc6ab+81,12.1-21-g6ffe579+2,12.1-21-gbdb6c2a+4,12.1-24-g941c398+5,12.1-3-g57f6835+7,12.1-3-gf0736f3,12.1-37-g3ddd237,12.1-4-gf46015e+5,12.1-5-g06c326c+20,12.1-5-g648ee80+3,12.1-5-gc2189d7+4,12.1-6-ga608fc0+1,12.1-7-g3349e2a+5,12.1-7-gfd75620+9,12.1-9-g577b946+5,12.1-9-gc4df26a+10
LSSTDataManagementBasePackage
simpleFits.cc
Go to the documentation of this file.
1 /*
2  * LSST Data Management System
3  * Copyright 2008, 2009, 2010 LSST Corporation.
4  *
5  * This product includes software developed by the
6  * LSST Project (http://www.lsst.org/).
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the LSST License Statement and
19  * the GNU General Public License along with this program. If not,
20  * see <http://www.lsstcorp.org/LegalNotices/>.
21  */
22 
29 #ifndef DOXYGEN // Doxygen doesn't like includes inside namespaces
30 namespace posix { // here so no-one includes them first outside namespace posix {}
31 # include <unistd.h>
32 # include <fcntl.h>
33 }
34 #endif
35 using namespace posix;
36 #include <cstdint>
37 #include <cstdlib>
38 #include <cstdio>
39 #include <cstring>
40 #include <cassert>
41 #include <cctype>
42 
43 #include "lsst/pex/exceptions.h"
44 #include "boost/any.hpp"
45 #include "lsst/afw/fits.h"
46 
47 #include "simpleFits.h"
48 
49 namespace image = lsst::afw::image;
51 
52 #define FITS_SIZE 2880
53 
55 class Card {
56 public:
57  Card(const std::string &name, bool val, const char *commnt = ""
58  ) : keyword(name), value(val), comment(commnt) { }
59  Card(const std::string &name, int val, const char *commnt = ""
60  ) : keyword(name), value(val), comment(commnt) { }
61  Card(const std::string &name, double val, const char *commnt = ""
62  ) : keyword(name), value(val), comment(commnt) { }
63  Card(const std::string &name, float val, const char *commnt = ""
64  ) : keyword(name), value(val), comment(commnt) { }
65  Card(const std::string &name, const std::string &val, const char *commnt = ""
66  ) : keyword(name), value(val), comment(commnt) { }
67  Card(const std::string &name, const char *val, const char *commnt = ""
68  ) : keyword(name), value(std::string(val)), comment(commnt) { }
69 
70  ~Card() {}
71 
72  int write(int fd, int ncard, char *record) const;
73 
74  std::string keyword;
75  boost::any value;
76  std::string comment;
77 };
78 
79 /*****************************************************************************/
80 /*
81  * Write a Card
82  */
83 int Card::write(int fd,
84  int ncard,
85  char *record
86  ) const {
87  char *card = &record[80*ncard];
88 
89  if (value.type() == typeid(std::string)) {
90  const char *str = boost::any_cast<std::string>(value).c_str();
91  if (keyword == "" ||
92  keyword == "COMMENT" || keyword == "END" || keyword == "HISTORY") {
93  sprintf(card, "%-8.8s%-72s", keyword.c_str(), str);
94  } else {
95  sprintf(card,"%-8.8s= '%s' %c%-*s",
96  keyword.c_str(), str,
97  (comment == "" ? ' ' : '/'),
98  (int)(80 - 14 - strlen(str)), comment.c_str());
99  }
100  } else {
101  sprintf(card, "%-8.8s= ", keyword.c_str());
102  card += 10;
103  if (value.type() == typeid(bool)) {
104  sprintf(card, "%20s", boost::any_cast<bool>(value) ? "T" : "F");
105  } else if (value.type() == typeid(int)) {
106  sprintf(card, "%20d", boost::any_cast<int>(value));
107  } else if (value.type() == typeid(double)) {
108  sprintf(card, "%20.10f", boost::any_cast<double>(value));
109  } else if (value.type() == typeid(float)) {
110  sprintf(card, "%20.7f", boost::any_cast<float>(value));
111  }
112  card += 20;
113  sprintf(card, " %c%-48s", (comment == "" ? ' ' : '/'), comment.c_str());
114  }
115 /*
116  * Write record if full
117  */
118  if (++ncard == 36) {
119  if (posix::write(fd, record, FITS_SIZE) != FITS_SIZE) {
120  throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError, "Cannot write header record");
121  }
122  ncard = 0;
123  }
124 
125  return ncard;
126 }
128 
129 /*****************************************************************************/
130 /*
131  * Utilities
132  *
133  * Flip high-order bit so as to write unsigned short to FITS. Grrr.
134  */
135 namespace {
136  void flip_high_bit(char *arr, // array that needs bits swapped
137  const int n) { // number of bytes in arr
138  if (n%2 != 0) {
139  throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError,
140  (boost::format("Attempt to bit flip odd number of bytes: %d") % n).str());
141  }
142 
143  unsigned short* uarr = reinterpret_cast<unsigned short *>(arr);
144  for(unsigned short *end = uarr + n/2; uarr < end; ++uarr) {
145  *uarr ^= 0x8000;
146  }
147  }
148 }
149 
150 /*
151  * Byte swap ABABAB -> BABABAB in place
152  */
153 namespace {
154  void swap_2(char *arr, // array to swap
155  const int n) { // number of bytes
156  if (n%2 != 0) {
157  throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError,
158  (boost::format("Attempt to byte swap odd number of bytes: %d") % n).str());
159  }
160 
161  for(char *end = arr + n;arr < end;arr += 2) {
162  char t = arr[0];
163  arr[0] = arr[1];
164  arr[1] = t;
165  }
166  }
167  /*
168  * Byte swap ABCDABCD -> DCBADCBA in place (e.g. sun <--> vax)
169  */
170  void swap_4(char *arr, // array to swap
171  const int n) { // number of bytes
172  if (n%4 != 0) {
173  throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError,
174  (boost::format("Attempt to byte swap non-multiple of 4 bytes: %d") % n).str());
175  }
176 
177  for(char *end = arr + n;arr < end;arr += 4) {
178  char t = arr[0];
179  arr[0] = arr[3];
180  arr[3] = t;
181  t = arr[1];
182  arr[1] = arr[2];
183  arr[2] = t;
184  }
185  }
186 
187  /*
188  * Byte swap ABCDEFGH -> HGFEDCBA in place (e.g. sun <--> vax)
189  */
190  void swap_8(char *arr, // array to swap
191  const int n) { // number of bytes
192  if (n%8 != 0) {
193  throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError,
194  (boost::format("Attempt to byte swap non-multiple of 8 bytes: %d") % n).str());
195  }
196 
197  for(char *end = arr + n;arr < end;arr += 8) {
198  char t = arr[0];
199  arr[0] = arr[7];
200  arr[7] = t;
201  t = arr[1];
202  arr[1] = arr[6];
203  arr[6] = t;
204  t = arr[2];
205  arr[2] = arr[5];
206  arr[5] = t;
207  t = arr[3];
208  arr[3] = arr[4];
209  arr[4] = t;
210  }
211  }
212 
213 /*****************************************************************************/
214 
215  int write_fits_hdr(int fd,
216  int bitpix,
217  int naxis,
218  int *naxes,
219  std::list<Card>& cards, /* extra header cards */
220  int primary) /* is this the primary HDU? */
221  {
222  int i;
223  char record[FITS_SIZE + 1]; /* write buffer */
224 
225  int ncard = 0;
226  if (primary) {
227  Card card("SIMPLE", true);
228  ncard = card.write(fd, ncard, record);
229  } else {
230  Card card("XTENSION", "IMAGE");
231  ncard = card.write(fd, ncard, record);
232  }
233 
234  {
235  Card card("BITPIX", bitpix);
236  ncard = card.write(fd, ncard, record);
237  }
238  {
239  Card card("NAXIS", naxis);
240  ncard = card.write(fd, ncard, record);
241  }
242  for(i = 0; i < naxis; i++) {
243  char key[] = "NAXIS.";
244  sprintf(key, "NAXIS%d", i + 1);
245  Card card(key, naxes[i]);
246  ncard = card.write(fd, ncard, record);
247  }
248  if (primary) {
249  Card card("EXTEND", true, "There may be extensions");
250  ncard = card.write(fd,ncard,record);
251  }
252 /*
253  * Write extra header cards
254  */
255  for (std::list<Card>::const_iterator card = cards.begin(); card != cards.end(); card++) {
256  ncard = card->write(fd,ncard,record);
257  }
258 
259  {
260  Card card("END", "");
261  ncard = card.write(fd,ncard,record);
262  }
263  while(ncard != 0) {
264  Card card("", "");
265  ncard = card.write(fd,ncard,record);
266  }
267 
268  return 0;
269  }
270 
271 /*
272  * Pad out to a FITS record boundary
273  */
274  void pad_to_fits_record(int fd, // output file descriptor
275  int npixel, // number of pixels already written to HDU
276  int bitpix // bitpix for this datatype
277  ) {
278  const int bytes_per_pixel = (bitpix > 0 ? bitpix : -bitpix)/8;
279  int nbyte = npixel*bytes_per_pixel;
280 
281  if (nbyte%FITS_SIZE != 0) {
282  char record[FITS_SIZE + 1]; /* write buffer */
283 
284  nbyte = FITS_SIZE - nbyte%FITS_SIZE;
285  memset(record, ' ', nbyte);
286  if (write(fd, record, nbyte) != nbyte) {
287  throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError,
288  "error padding file to multiple of fits block size");
289  }
290  }
291  }
292 
293  int write_fits_data(int fd,
294  int bitpix,
295  char *begin,
296  char *end
297  ) {
298  const int bytes_per_pixel = (bitpix > 0 ? bitpix : -bitpix)/8;
299  int swap_bytes = 0; // the default
300 #if defined(LSST_LITTLE_ENDIAN) // we'll need to byte swap FITS
301  if (bytes_per_pixel > 1) {
302  swap_bytes = 1;
303  }
304 #endif
305 
306  char *buff = NULL; // I/O buffer
307  bool allocated = false; // do I need to free it?
308  if (swap_bytes || bitpix == 16) {
309  buff = new char[FITS_SIZE*bytes_per_pixel];
310  allocated = true;
311  }
312 
313  int nbyte = end - begin;
314  int nwrite = (nbyte > FITS_SIZE) ? FITS_SIZE : nbyte;
315  for (char *ptr = begin; ptr != end; nbyte -= nwrite, ptr += nwrite) {
316  if (end - ptr < nwrite) {
317  nwrite = end - ptr;
318  }
319 
320  if (swap_bytes) {
321  memcpy(buff, ptr, nwrite);
322  if (bitpix == 16) { // flip high-order bit
323  flip_high_bit(buff, nwrite);
324  }
325 
326  if (bytes_per_pixel == 2) {
327  swap_2(buff, nwrite);
328  } else if (bytes_per_pixel == 4) {
329  swap_4(buff, nwrite);
330  } else if (bytes_per_pixel == 8) {
331  swap_8(buff, nwrite);
332  } else {
333  fprintf(stderr,"You cannot get here\n");
334  abort();
335  }
336  } else {
337  if (bitpix == 16) { // flip high-order bit
338  memcpy(buff, ptr, nwrite);
339  flip_high_bit(buff, nwrite);
340  } else {
341  buff = ptr;
342  }
343  }
344 
345  if (write(fd, buff, nwrite) != nwrite) {
346  perror("Error writing image: ");
347  break;
348  }
349  }
350 
351  if (allocated) {
352  delete buff;
353  }
354 
355  return (nbyte == 0 ? 0 : -1);
356  }
357 }
358 
359 namespace lsst { namespace afw { namespace display {
360 
361 template<typename ImageT>
362 void writeBasicFits(int fd, // file descriptor to write to
363  ImageT const& data, // The data to write
364  image::Wcs const* Wcs, // which Wcs to use for pixel
365  char const *title // title to write to DS9
366  ) {
367  /*
368  * Allocate cards for FITS headers
369  */
370  std::list<Card> cards;
371  /*
372  * What sort if image is it?
373  */
374  int bitpix = lsst::afw::fits::getBitPix<typename ImageT::Pixel>();
375  if (bitpix == 20) { // cfitsio for "Unsigned short"
376  cards.push_back(Card("BZERO", 32768.0, ""));
377  cards.push_back(Card("BSCALE", 1.0, ""));
378  bitpix = 16;
379  } else if (bitpix == 0) {
380  throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError, "Unsupported image type");
381  }
382  /*
383  * Generate WcsA, pixel coordinates, allowing for X0 and Y0
384  */
385  std::string wcsName = "A";
386  cards.push_back(Card(str(boost::format("CRVAL1%s") % wcsName),
387  data.getX0(), "(output) Column pixel of Reference Pixel"));
388  cards.push_back(Card(str(boost::format("CRVAL2%s") % wcsName),
389  data.getY0(), "(output) Row pixel of Reference Pixel"));
390  cards.push_back(Card(str(boost::format("CRPIX1%s") % wcsName), 1.0,
391  "Column Pixel Coordinate of Reference"));
392  cards.push_back(Card(str(boost::format("CRPIX2%s") % wcsName), 1.0, "Row Pixel Coordinate of Reference"));
393  cards.push_back(Card(str(boost::format("CTYPE1%s") % wcsName), "LINEAR", "Type of projection"));
394  cards.push_back(Card(str(boost::format("CTYPE1%s") % wcsName), "LINEAR", "Type of projection"));
395  cards.push_back(Card(str(boost::format("CUNIT1%s") % wcsName), "PIXEL", "Column unit"));
396  cards.push_back(Card(str(boost::format("CUNIT2%s") % wcsName), "PIXEL", "Row unit"));
397  /*
398  * Now WcsB, so that pixel (0,0) is correctly labelled (but ignoring XY0)
399  */
400  wcsName = "B";
401  cards.push_back(Card(str(boost::format("CRVAL1%s") % wcsName), 0,
402  "(output) Column pixel of Reference Pixel"));
403  cards.push_back(Card(str(boost::format("CRVAL2%s") % wcsName), 0,
404  "(output) Row pixel of Reference Pixel"));
405  cards.push_back(Card(str(boost::format("CRPIX1%s") % wcsName), 1.0,
406  "Column Pixel Coordinate of Reference"));
407  cards.push_back(Card(str(boost::format("CRPIX2%s") % wcsName), 1.0, "Row Pixel Coordinate of Reference"));
408  cards.push_back(Card(str(boost::format("CTYPE1%s") % wcsName), "LINEAR", "Type of projection"));
409  cards.push_back(Card(str(boost::format("CTYPE1%s") % wcsName), "LINEAR", "Type of projection"));
410  cards.push_back(Card(str(boost::format("CUNIT1%s") % wcsName), "PIXEL", "Column unit"));
411  cards.push_back(Card(str(boost::format("CUNIT2%s") % wcsName), "PIXEL", "Row unit"));
412 
413  if (title) {
414  cards.push_back(Card("OBJECT", title, "Image being displayed"));
415  }
416  /*
417  * Was there something else?
418  */
419  if (Wcs != NULL) {
420  typedef std::vector<std::string> NameList;
421 
422  image::Wcs::Ptr newWcs = Wcs->clone(); //Create a copy
423  newWcs->shiftReferencePixel(-data.getX0(), -data.getY0());
424 
425  lsst::daf::base::PropertySet::Ptr metadata = newWcs->getFitsMetadata();
426 
427  NameList paramNames = metadata->paramNames();
428 
429  for (NameList::const_iterator i = paramNames.begin(), end = paramNames.end(); i != end; ++i) {
430  if (*i == "SIMPLE" ||
431  *i == "BITPIX" ||
432  *i == "NAXIS" ||
433  *i == "NAXIS1" ||
434  *i == "NAXIS2" ||
435  *i == "XTENSION" ||
436  *i == "PCOUNT" ||
437  *i == "GCOUNT"
438  ) {
439  continue;
440  }
441  std::type_info const &type = metadata->typeOf(*i);
442  if (type == typeid(bool)) {
443  cards.push_back(Card(*i, metadata->get<bool>(*i)));
444  } else if (type == typeid(int)) {
445  cards.push_back(Card(*i, metadata->get<int>(*i)));
446  } else if (type == typeid(float)) {
447  cards.push_back(Card(*i, metadata->get<float>(*i)));
448  } else if (type == typeid(double)) {
449  cards.push_back(Card(*i, metadata->get<double>(*i)));
450  } else {
451  cards.push_back(Card(*i, metadata->get<std::string>(*i)));
452  }
453  }
454  }
455  /*
456  * Basic FITS stuff
457  */
458  const int naxis = 2; // == NAXIS
459  int naxes[naxis]; /* values of NAXIS1 etc */
460  naxes[0] = data.getWidth();
461  naxes[1] = data.getHeight();
462 
463  write_fits_hdr(fd, bitpix, naxis, naxes, cards, 1);
464  for (int y = 0; y != data.getHeight(); ++y) {
465  if (write_fits_data(fd, bitpix, (char *)(data.row_begin(y)), (char *)(data.row_end(y))) < 0){
466  throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError,
467  (boost::format("Error writing data for row %d") % y).str());
468  }
469  }
470 
471  pad_to_fits_record(fd, data.getWidth()*data.getHeight(), bitpix);
472 }
473 
474 /******************************************************************************/
475 
476 template<typename ImageT>
477 void writeBasicFits(std::string const& filename, // file to write, or "| cmd"
478  ImageT const& data, // The data to write
479  image::Wcs const* Wcs, // which Wcs to use for pixel
480  char const* title // title to write to DS9
481  ) {
482  int fd;
483  if ((filename.c_str())[0] == '|') { // a command
484  const char *cmd = filename.c_str() + 1;
485  while (isspace(*cmd)) {
486  cmd++;
487  }
488 
489  fd = fileno(popen(cmd, "w"));
490  } else {
491  fd = creat(filename.c_str(), 777);
492  }
493 
494  if (fd < 0) {
495  throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError,
496  (boost::format("Cannot open \"%s\"") % filename).str());
497  }
498 
499  try {
500  writeBasicFits(fd, data, Wcs, title);
502  (void)close(fd);
503  throw;
504  }
505 
506  (void)close(fd);
507 }
508 
510 #define INSTANTIATE(IMAGET) \
511  template void writeBasicFits(int, IMAGET const&, image::Wcs const *, char const *); \
512  template void writeBasicFits(std::string const&, IMAGET const&, image::Wcs const *, char const *)
513 
514 #define INSTANTIATE_IMAGE(T) INSTANTIATE(lsst::afw::image::Image<T>)
515 #define INSTANTIATE_MASK(T) INSTANTIATE(lsst::afw::image::Mask<T>)
516 
517 INSTANTIATE_IMAGE(std::uint16_t);
518 INSTANTIATE_IMAGE(int);
519 INSTANTIATE_IMAGE(float);
520 INSTANTIATE_IMAGE(double);
521 INSTANTIATE_IMAGE(std::uint64_t);
522 
523 INSTANTIATE_MASK(std::uint16_t);
525 
526 }}}
int y
table::Key< std::string > name
Definition: ApCorrMap.cc:71
void writeBasicFits(int fd, ImageT const &data, image::Wcs const *Wcs, char const *title)
Definition: simpleFits.cc:362
Include files required for standard LSST Exception handling.
Definitions to write a FITS image.
#define INSTANTIATE_IMAGE(IMAGE)
Definition: ImagePca.cc:125
Implementation of the WCS standard for a any projection.
Definition: Wcs.h:107
virtual Ptr clone(void) const
Definition: Wcs.cc:566
std::shared_ptr< Wcs > Ptr
Definition: Wcs.h:113
table::Key< table::Array< Kernel::Pixel > > image
Definition: FixedKernel.cc:117
lsst::daf::base::PropertySet PropertySet
Definition: Wcs.cc:59
bool any(CoordinateExpr< N > const &expr)
Return true if any elements are true.
metadata import lsst afw display as afwDisplay n
Utilities for working with FITS files.
#define LSST_EXCEPT(type,...)
Create an exception with a given type and message and optionally other arguments (dependent on the ty...
Definition: Exception.h:46
#define FITS_SIZE
Definition: simpleFits.cc:52
std::shared_ptr< PropertySet > Ptr
Definition: PropertySet.h:85
ImageT val
Definition: CR.cc:159