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