ifw-daq  3.0.0-pre2
IFW Data Acquisition modules
cfitsio.cpp
Go to the documentation of this file.
1 /**
2  * @file
3  * @ingroup daq_ocm_libfits
4  * @copyright 2022 ESO - European Southern Observatory
5  *
6  * @brief Contains definitions for daq/fits/cfitsio.hpp
7  */
8 #include <daq/fits/cfitsio.hpp>
9 
10 #include <cassert>
11 
12 #include <fmt/format.h>
13 
14 namespace daq::fits {
15 namespace {
16 /**
17  * Reads keywords from CHDU / Current HDU
18  */
19 std::vector<LiteralKeyword> ReadCurrentHduKeywords(fitsfile* ptr) {
20  assert(ptr);
21  int status = 0;
22  int count = 0;
23  int more_space = 0;
24  if (fits_get_hdrspace(ptr, &count, &more_space, &status) != 0) {
25  throw CfitsioError(status, "Failed to get number of keywords in HDU");
26  }
27  std::vector<LiteralKeyword> keywords;
28  keywords.reserve(count);
29  for (int keynum = 1; keynum <= count; ++keynum) {
30  try {
31  char record[FLEN_CARD];
32  if (fits_read_record(ptr, keynum, &record[0], &status) != 0) {
33  throw CfitsioError(status, fmt::format("Failed to read record number {}", keynum));
34  }
35  // note: FITS returns null-terminated string
36  keywords.emplace_back(std::string_view(record));
37  } catch (...) {
38  std::throw_with_nested(CfitsioError(
39  status,
40  fmt::format("Failed to read recordd number {} as literal keyword", keynum)));
41  }
42  }
43  return keywords;
44 }
45 
46 std::string FitsErrorMessage(int status) {
47  char msg[FLEN_STATUS];
48  fits_get_errstatus(status, &msg[0]);
49  return std::string(msg);
50 }
51 
52 } // namespace
53 
54 
55 MemoryFitsFile::MemoryFitsFile() noexcept : m_memory(), m_file(nullptr, fits::DefaultClose) {
56 }
57 
58 MemoryFitsFile::MemoryFitsFile(size_t initial_buffer_size)
59  : m_memory(std::make_unique<FitsMemory>(initial_buffer_size))
60  , m_file(nullptr, fits::DefaultClose) {
61  fitsfile* ptr = nullptr;
62  int status = 0;
63  fits_create_memfile(&ptr, &m_memory->buffer, &m_memory->size, 0u, realloc, &status);
64 
65  m_file.reset(ptr);
66 }
67 
68 MemoryFitsFile::FitsMemory::FitsMemory(size_t initial_size) {
69  size = initial_size;
70  buffer = malloc(size);
71  memset(buffer, 0u, size);
72 }
73 
74 MemoryFitsFile::FitsMemory::~FitsMemory() noexcept {
75  int status = 0;
76  fits_free_memory(buffer, &status);
77 }
78 
79 CfitsioError::CfitsioError(int status, char const* message)
80  : std::runtime_error(fmt::format("{}: {}", message, FitsErrorMessage(status)).c_str())
81  , m_status(status) {
82 }
83 
84 CfitsioError::CfitsioError(int status, std::string const& message)
85  : std::runtime_error(fmt::format("{}: {}", message, FitsErrorMessage(status)).c_str())
86  , m_status(status) {
87 }
88 
89 void SelectHduNum(fitsfile* ptr, int hdu_num) {
90  assert(ptr);
91  int status = 0;
92  int type = 0;
93  (void)fits_movabs_hdu(ptr, hdu_num, &type, &status);
94  if (status != 0) {
95  throw CfitsioError(status,
96  fmt::format("HDU with number {} could not be selected", hdu_num));
97  }
98 }
99 
100 void DefaultClose(fitsfile* ptr) noexcept {
101  if (ptr == nullptr) {
102  return;
103  }
104  int status = 0;
105  fits_close_file(ptr, &status);
106 }
107 
108 int CfitsioError::GetStatus() const noexcept {
109  return m_status;
110 }
111 
112 void InitPrimaryHduEmpty(fitsfile* ptr) {
113  int status = 0;
114  int num_hdus = 0;
115  (void)fits_get_num_hdus(ptr, &num_hdus, &status);
116  if (num_hdus != 0) {
117  throw CfitsioError(HEADER_NOT_EMPTY, "Cannot safely initialize an non-empty FITS file");
118  }
119  (void)fits_create_hdu(ptr, &status);
120  if (status != 0) {
121  throw CfitsioError(status, "Failed to create HDU");
122  }
123 }
124 
125 void InitPrimaryHduNoImage(fitsfile* ptr) {
126  /*
127  SIMPLE = T / file does conform to FITS standard
128  BITPIX = 8 / number of bits per data pixel
129  NAXIS = 0 / number of data axes
130  */
131  assert(ptr);
132  int status = 0;
133  int simple = true;
134  int extend = true;
135  char bitpix = 8;
136  char naxis = 0;
137  int num_hdus = 0;
138  (void)fits_get_num_hdus(ptr, &num_hdus, &status);
139  if (num_hdus != 0) {
140  throw CfitsioError(HEADER_NOT_EMPTY, "Cannot safely initialize an non-empty FITS file");
141  }
142  (void)fits_create_hdu(ptr, &status);
143  (void)fits_write_key(
144  ptr, TLOGICAL, "SIMPLE", &simple, "file does conform to FITS standard", &status);
145  (void)fits_write_key(ptr, TBYTE, "BITPIX", &bitpix, "number of bits per data pixel", &status);
146  (void)fits_write_key(
147  ptr, TBYTE, "NAXIS", &naxis, "file provides primary HDU keywords only", &status);
148  (void)fits_write_key(
149  ptr, TLOGICAL, "EXTEND", &extend, "FITS Extension may be present", &status);
150  if (status != 0) {
151  char err_text[31]; // returns maximum 30 characters
152  fits_get_errstatus(status, &err_text[0]);
153  err_text[30] = '\0';
154  throw CfitsioError(status, fmt::format("failed to initialize primary HDU: {}", err_text));
155  }
156 }
157 
158 UniqueFitsFile CreateEmpty(char const* filename) {
159  fitsfile* ptr = nullptr;
160  int status = 0;
161  fits_create_diskfile(&ptr, const_cast<char*>(filename), &status);
162  if (status != 0 || ptr == nullptr) {
163  throw CfitsioError(status, fmt::format("failed to create FITS file '{}'", filename));
164  }
165  return UniqueFitsFile(ptr, DefaultClose);
166 }
167 
168 UniqueFitsFile Open(char const* filename, OpenMode mode) {
169  fitsfile* ptr = nullptr;
170  int status = 0;
171  fits_open_diskfile(&ptr,
172  const_cast<char*>(filename),
173  mode == OpenMode::ReadWrite ? READWRITE : READONLY,
174  &status);
175  if (status != 0 || ptr == nullptr) {
176  throw CfitsioError(status, fmt::format("failed to open FITS file '{}'", filename));
177  }
178  return UniqueFitsFile(ptr, DefaultClose);
179 }
180 
181 std::vector<LiteralKeyword> ReadKeywords(fitsfile* ptr, int hdu_num) {
182  assert(ptr);
183  SelectHduNum(ptr, hdu_num);
184  return ReadCurrentHduKeywords(ptr);
185 }
186 
187 std::vector<LiteralKeyword>
188 ReadKeywords(fitsfile* ptr, HduType type, std::string_view name, std::optional<int> version) {
189  assert(ptr);
190  int status = 0;
191  // Makes sure string is null-terminated
192  std::string null_terminated_name(name);
193  (void)fits_movnam_hdu(ptr,
194  static_cast<int>(type),
195  const_cast<char*>(null_terminated_name.c_str()),
196  version.value_or(0),
197  &status);
198  if (status != 0) {
199  throw CfitsioError(
200  status,
201  fmt::format("HDU with type {}, name '{}' and version {} could not be selected",
202  type,
203  name,
204  version.value_or(0)));
205  }
206  return ReadCurrentHduKeywords(ptr);
207 }
208 
209 void DeleteAllKeywords(fitsfile* ptr, int hdu_num) {
210  assert(ptr);
211  SelectHduNum(ptr, hdu_num);
212 
213  int status = 0;
214  int num_current_keys = 0;
215  // Reserve space for keys
216  // If FITS file has not yet been closed for the first time it returns -1 for more_keys (likely
217  // because it's still virtual).
218  // We don't yet do anything, but could issue warning.
219  fits_get_hdrspace(ptr, &num_current_keys, nullptr, &status);
220  for (int keynum = num_current_keys; keynum > 0; --keynum) {
221  fits_delete_record(ptr, keynum, &status);
222  if (status != 0) {
223  throw CfitsioError(status,
224  fmt::format("failed to delete keyword record index {}", keynum));
225  }
226  }
227 }
228 
229 void MakeHduSpace(fitsfile* ptr, int hdu_num, size_t additional_keys) {
230  assert(ptr);
231  SelectHduNum(ptr, hdu_num);
232 
233  int status = 0;
234  int num_current_keys = 0;
235  int more_keys = additional_keys;
236 
237  // Reserve space for keys
238  // If FITS file has not yet been closed for the first time it returns -1 for more_keys (likely
239  // because it's still virtual).
240  // We don't yet do anything, but could issue warning.
241  fits_get_hdrspace(ptr, &num_current_keys, &more_keys, &status);
242  if (status != 0) {
243  throw CfitsioError(status, fmt::format("Failed to update header space"));
244  }
245 }
246 
247 void WriteKeywords(fitsfile* ptr,
248  int hdu_num,
249  std::vector<LiteralKeyword> const& keywords,
250  std::optional<ssize_t>* remaining_size) {
251  assert(ptr);
252  SelectHduNum(ptr, hdu_num);
253  std::string null_terminated_record;
254  null_terminated_record.reserve(FLEN_CARD);
255  int status = 0;
256  int num_current_keys = 0;
257  int more_keys = keywords.size();
258  // Reserve space for keys
259  // If FITS file has not yet been closed for the first time it returns -1 for more_keys (likely
260  // because it's still virtual).
261  // We don't yet do anything, but could issue warning.
262  fits_get_hdrspace(ptr, &num_current_keys, &more_keys, &status);
263  if (status != 0) {
264  throw CfitsioError(status, fmt::format("Failed to get header space"));
265  }
266 
267  if (remaining_size != nullptr && more_keys != -1) {
268  *remaining_size = more_keys - keywords.size();
269  }
270 
271  for (auto const& kw : keywords) {
272  null_terminated_record.assign(kw.GetRecord());
273  // note: Unclear it fits requires null terminated record or not...
274  fits_write_record(ptr, null_terminated_record.c_str(), &status);
275  if (status != 0) {
276  throw CfitsioError(
277  status,
278  fmt::format("Failed to write keyword record: \"{}\"", null_terminated_record));
279  }
280  }
281 }
282 
283 void WriteChecksum(fitsfile* ptr, int hdu_num) {
284  assert(ptr);
285  SelectHduNum(ptr, hdu_num);
286  int status = 0;
287  fits_write_chksum(ptr, &status);
288  if (status != 0) {
289  throw CfitsioError(
290  status, fmt::format("Failed to write checksum keywords to HDU number {}", hdu_num));
291  }
292 }
293 
294 } // namespace daq::fits
Contains functions and data structures related to cfitsio.
Represents errors from cfitsio.
Definition: cfitsio.hpp:90
int GetStatus() const noexcept
Definition: cfitsio.cpp:108
CfitsioError(int status, char const *message)
Definition: cfitsio.cpp:79
MemoryFitsFile() noexcept
Creates empty file.
Definition: cfitsio.cpp:55
string version
Definition: conf.py:90
void SelectHduNum(fitsfile *ptr, int hdu_num)
Select current HDU number.
Definition: cfitsio.cpp:89
UniqueFitsFile Open(char const *filename, OpenMode mode)
Open file.
Definition: cfitsio.cpp:168
std::unique_ptr< fitsfile, void(*)(fitsfile *) noexcept > UniqueFitsFile
Defines unique ownership type to cfitsio fitsfile.
Definition: cfitsio.hpp:33
void WriteKeywords(fitsfile *ptr, int hdu_num, std::vector< LiteralKeyword > const &keywords, std::optional< ssize_t > *remaining_size)
Write keywords to HDU identified by number hdu_num.
Definition: cfitsio.cpp:247
void MakeHduSpace(fitsfile *ptr, int hdu_num, size_t additional_keys)
Definition: cfitsio.cpp:229
void WriteChecksum(fitsfile *ptr, int hdu_num)
Write or update checksum keywords DATASUM and CHECKSUM to HDU specified by hdu_num.
Definition: cfitsio.cpp:283
void DefaultClose(fitsfile *ptr) noexcept
Default close function that is used by UniqueFitsFile as a deleter.
Definition: cfitsio.cpp:100
void DeleteAllKeywords(fitsfile *ptr, int hdu_num)
Delete all keywords from HDU.
Definition: cfitsio.cpp:209
void InitPrimaryHduNoImage(fitsfile *ptr)
Initializes an empty FITS file with a primary HDU.
Definition: cfitsio.cpp:125
std::vector< LiteralKeyword > ReadKeywords(fitsfile *ptr, int hdu_num)
Read keywords from HDU identifed by absolute position hdu_num.
Definition: cfitsio.cpp:181
UniqueFitsFile CreateEmpty(char const *filename)
Creates empty FITS file using fits_create_file and returns a pointer with a deleter that will close t...
Definition: cfitsio.cpp:158
void InitPrimaryHduEmpty(fitsfile *ptr)
Initializes an empty FITS file with an empty primary HDU (no keywords)
Definition: cfitsio.cpp:112