RTC Toolkit 6.0.0-pre2
Loading...
Searching...
No Matches
fitsDataRecorder.ipp
Go to the documentation of this file.
1
12
13// Note this is a template implementation file and should not be included directly.
14// The typical header protection macro is not added to avoid it showing up in Doxygen API
15// documentation.
16#pragma once
17
18#include <ciiException.hpp>
19#include <fitsio.h>
20#include <longnam.h>
21
26
27#include <complex>
28#include <cstddef>
29#include <filesystem>
30#include <fmt/core.h>
31#include <fmt/format.h>
32#include <functional>
33#include <limits>
34#include <memory>
35#include <type_traits>
36#include <vector>
37
38namespace {
39template <class T>
40struct FitsDataTypeValue {
41 static_assert(true, "invalid data for fits writer");
42};
43
44template <>
45struct FitsDataTypeValue<unsigned char> {
46 static constexpr int DTYPE = TBYTE;
47};
48
49template <>
50struct FitsDataTypeValue<signed char> {
51 static constexpr int DTYPE = TSBYTE;
52};
53
54template <>
55struct FitsDataTypeValue<bool> {
56 static constexpr int DTYPE = TLOGICAL;
57};
58
59// The following check will prevent the code from building and producing a binary that silently
60// corrupts the data. If ever the code needs to be built on a platform where the size of the boolean
61// type is different from char, the implementation will need to be modified to correctly handle
62// this.
63static_assert(sizeof(bool) == sizeof(char),
64 "Cfitsio uses 'char' for TLOGICAL internally. Therefore the sizes of 'bool' and "
65 "'char' must match for this implementation to work.");
66
67// not supported by write_cols
68// template <>
69// struct FitsDataTypeValue<std::string> {
70// static constexpr int DTYPE = TSTRING;
71// };
72
73// not supported by write_cols
74// template <>
75// struct FitsDataTypeValue<char*> {
76// static constexpr int DTYPE = TSTRING;
77// };
78
79template <>
80struct FitsDataTypeValue<unsigned short> {
81 static constexpr int DTYPE = TUSHORT;
82};
83
84template <>
85struct FitsDataTypeValue<signed short> {
86 static constexpr int DTYPE = TSHORT;
87};
89template <>
90struct FitsDataTypeValue<unsigned int> {
91 static constexpr int DTYPE = TUINT;
92};
93
94template <>
95struct FitsDataTypeValue<int> {
96 static constexpr int DTYPE = TINT;
97};
98
99template <>
100struct FitsDataTypeValue<unsigned long> {
101 static constexpr int DTYPE = TULONG;
102};
103
104template <>
105struct FitsDataTypeValue<long> {
106 static constexpr int DTYPE = TLONG;
107};
108
109template <>
110struct FitsDataTypeValue<float> {
111 static constexpr int DTYPE = TFLOAT;
112};
113
114template <>
115struct FitsDataTypeValue<unsigned long long> {
116 static constexpr int DTYPE = TULONGLONG;
117};
118
119template <>
120struct FitsDataTypeValue<long long> {
121 static constexpr int DTYPE = TLONGLONG;
122};
123
124template <>
125struct FitsDataTypeValue<double> {
126 static constexpr int DTYPE = TDOUBLE;
127};
128
129template <>
130struct FitsDataTypeValue<std::complex<float>> {
131 static constexpr int DTYPE = TCOMPLEX;
132};
133
134template <>
135struct FitsDataTypeValue<std::complex<double>> {
136 static constexpr int DTYPE = TDBLCOMPLEX;
137};
138
139template <typename data>
140void TableWriterHelper(fitsfile* fptr, int colnum, long firstrow, std::vector<data>& input);
141
142template <typename data>
143void TableWriterHelper(fitsfile* fptr, int colnum, long firstrow, std::vector<data>& input);
144
145void ThrowOnBadStatus(int status, const std::string& message) {
146 if (status) {
147 std::array<char, 80> err_text;
148 fits_get_errstatus(status, err_text.data());
149 std::string buffer;
150 std::array<char, 80> fits_buffer;
151 while (fits_read_errmsg(fits_buffer.data())) {
152 buffer += fits_buffer.data();
153 }
155 fmt::format("{} {}: {}", message, err_text.data(), buffer));
156 }
157}
158
159} // namespace
160
162
163template <class... T>
165 const std::array<bool, sizeof...(T)>& disabled,
166 const std::string& extension_name)
167 : DataRecorder<T...>(columns, disabled)
168 , m_fits_handle(nullptr, &CloseFits)
169 , m_extension_name(extension_name)
170 , m_row_index(1)
171 , m_column_length{(FitsColumnFormat<T>::COUNT)...} {
172 // Initialize cfitsio before any other cfitsio functions are called. This must be called
173 // before using cfitsio in a multithreaded environment. Calling it multiple times is safe.
174 fits_init_cfitsio();
175}
176
177template <class... T>
178void FitsRecorder<T...>::Open(const std::filesystem::path& file) {
179 m_row_index = 1;
180 fitsfile* filepointer;
181 int status{0};
182 fits_create_diskfile(&filepointer, file.c_str(), &status);
183 ThrowOnBadStatus(status, "Error in fits_open_diskfile");
184 m_fits_handle.reset(filepointer);
185 std::vector<std::string> column_names{}, column_units{}, column_forms{};
186 column_names.reserve(sizeof...(T));
187 column_units.reserve(sizeof...(T));
188 column_forms.reserve(sizeof...(T));
189 for (auto column : this->m_column_description) {
190 column_names.push_back(std::string{column.name});
191 column_units.push_back(std::string{column.unit});
192 }
193 GetFITSTFormWithLength<T...>(column_forms, this->m_column_length);
194 auto iter = DataRecorder<T...>::m_disabled_fields.rbegin();
195 for (int i = sizeof...(T) - 1; iter != DataRecorder<T...>::m_disabled_fields.rend(); iter++) {
196 if (*iter) {
197 column_names.erase(std::next(column_names.begin(), i));
198 column_units.erase(std::next(column_units.begin(), i));
199 column_forms.erase(std::next(column_forms.begin(), i));
200 }
201 i--;
202 }
203 // fits expects char*
204 std::vector<char*> column_names_fits;
205 std::vector<char*> column_units_fits;
206 std::vector<char*> column_forms_fits;
207 column_names_fits.reserve(column_names.size());
208 column_units_fits.reserve(column_units.size());
209 column_forms_fits.reserve(column_forms.size());
210 for (auto& column_name : column_names) {
211 column_names_fits.push_back(column_name.data());
212 }
213 for (auto& column_unit : column_units) {
214 column_units_fits.push_back(column_unit.data());
215 }
216 for (auto& column_form : column_forms) {
217 column_forms_fits.push_back(column_form.data());
218 }
219 fits_create_tbl(m_fits_handle.get(),
220 BINARY_TBL,
221 0,
222 column_names_fits.size(),
223 column_names_fits.data(),
224 column_forms_fits.data(),
225 column_units_fits.data(),
226 "RECORDING",
227 &status);
228
229 ThrowOnBadStatus(status, "error in fits_create_tbl");
230}
231
232template <class... T>
233void FitsRecorder<T...>::SetColumnLength(size_t column, size_t length) {
234 if (m_fits_handle != nullptr) {
235 CII_THROW(RtctkException, "FitsRecorder: Setting ColumnLength after Open not allowed");
236 }
237 if (sizeof...(T) <= column) {
238 CII_THROW(InvalidArgumentException, "column index out of bounds");
239 }
240 if (length >= 4294967296) {
241 CII_THROW(InvalidArgumentException,
242 fmt::format("length({}) must be less than 2^32", length));
243 }
244 m_column_length[column] = length;
245}
246
247template <class... T>
248void FitsRecorder<T...>::SetColumnLength(const std::tuple<T...>& data) {
249 auto set_column_lengths = [this](const auto& first, const auto&... args) {
250 long column = 0;
251 auto set_column_length = [this, &column](const auto& x) {
252 // dynamically sized types
253 if constexpr (IS_DYNAMIC_SPAN_TYPE<std::decay_t<decltype(x)>> or
254 IS_VECTOR_TYPE<std::decay_t<decltype(x)>> or
255 IS_MATRIX_BUFFER_TYPE<std::decay_t<decltype(x)>>) {
256 this->SetColumnLength(column, x.size());
257 }
258 column++;
259 };
260 set_column_length(first);
261 (set_column_length(args), ...);
262 };
263 std::apply(set_column_lengths, data);
264}
265
266// template <class... T>
267// void FitsRecorder<T...>::Write(const gsl::span<const std::tuple<T...>>) {
268
269// }
270
271// template <class... T>
272// void FitsRecorder<T...>::Write(const std::tuple<T...>& data) {
273// this->Write(gsl::span(&data, 1));
274// }
275
276template <class... T>
277void FitsRecorder<T...>::Write(const std::tuple<T...>& data) {
278 auto write = [this](const auto& first, const auto&... args) {
279 long fits_column = 1;
280 long element = 0;
281 auto write_elem = [this, &fits_column, &element](const auto& x) {
282 if (this->m_disabled_fields[element]) {
283 element += 1;
284 return;
285 }
286 int status{0};
287 // dynamically sized types
288 if constexpr (IS_DYNAMIC_SPAN_TYPE<std::decay_t<decltype(x)>> or
289 IS_VECTOR_TYPE<std::decay_t<decltype(x)>> or
290 IS_MATRIX_BUFFER_TYPE<std::decay_t<decltype(x)>>) {
291 if (not m_column_length[element].has_value()) {
292 CII_THROW(InvalidArgumentException,
293 fmt::format("dynamic sized type given, but length not given for {}",
294 this->m_column_description[element].name));
295 } else if (x.size() != m_column_length[element]) {
296 CII_THROW(InvalidArgumentException,
297 "data size does not match configured length");
298 }
299 std::vector<typename std::decay_t<decltype(x)>::value_type> copy(
300 x.data(), x.data() + m_column_length[element].value());
301 fits_write_col(m_fits_handle.get(),
302 FitsDataTypeValue<typename decltype(copy)::value_type>::DTYPE,
303 fits_column,
304 m_row_index,
305 1,
306 copy.size(),
307 copy.data(),
308 &status);
309 ThrowOnBadStatus(status, "Error writing to binary table");
310 // This case is for writing statically sized types
311 } else if constexpr (IS_STD_ARRAY_TYPE<std::decay_t<decltype(x)>> or
312 IS_STATIC_SPAN_TYPE<std::decay_t<decltype(x)>>) {
313 std::vector<typename std::decay_t<decltype(x)>::value_type> copy(
314 x.data(), x.data() + x.size());
315 fits_write_col(m_fits_handle.get(),
316 FitsDataTypeValue<typename decltype(copy)::value_type>::DTYPE,
317 fits_column,
318 m_row_index,
319 1,
320 copy.size(),
321 copy.data(),
322 &status);
323 ThrowOnBadStatus(status, "Error writing to binary table");
324 // This is for writing strings
325 } else if constexpr (std::is_same_v<std::decay_t<decltype(x)>, std::string>) {
326 std::string copy{x};
327 char* char_string = copy.data();
328 fits_write_col(m_fits_handle.get(),
329 TSTRING,
330 fits_column,
331 m_row_index,
332 1,
333 1,
334 &char_string,
335 &status);
336 ThrowOnBadStatus(status, "Error writing to binary table");
337 // for scalar values
338 } else {
339 // need to copy since cfitsio wants a non-const type
340 std::decay_t<decltype(x)> copy{x};
341 fits_write_col(m_fits_handle.get(),
342 FitsDataTypeValue<decltype(copy)>::DTYPE,
343 fits_column,
344 m_row_index,
345 1,
346 1,
347 &copy,
348 &status);
349 ThrowOnBadStatus(status, "Error writing to binary table");
350 }
351 fits_column += 1;
352 element += 1;
353 };
354 write_elem(first);
355 (write_elem(args), ...);
356 };
357 std::apply(write, data);
358 m_row_index += 1;
359}
360
361template <class... T>
363 m_fits_handle.reset(nullptr);
364}
365
366template <class... T>
367void FitsRecorder<T...>::CloseFits(fitsfile* pointer) {
368 int status{0};
369 fits_close_file(pointer, &status);
370 ThrowOnBadStatus(status, "Error closing file");
371}
372
373} // namespace rtctk::componentFramework
DisabledFields m_disabled_fields
Definition dataRecorder.hpp:109
const ColumnDescription m_column_description
Definition dataRecorder.hpp:110
const std::array< ColumnMetaData, sizeof...(T)> ColumnDescription
Definition dataRecorder.hpp:49
DataRecorder(const ColumnDescription &columns, const DisabledFields &disable={})
Definition dataRecorder.hpp:57
FitsRecorder(const ColumnDescription &columns, const std::array< bool, sizeof...(T)> &disable={}, const std::string &extension_name="recording")
create a new recorder
Definition fitsDataRecorder.ipp:164
void Open(const std::filesystem::path &file) override
Open a file.
Definition fitsDataRecorder.ipp:178
void SetColumnLength(size_t column, size_t length)
Set the exact length of a column.
Definition fitsDataRecorder.ipp:233
void Write(const std::tuple< T... > &data) override
Write a single row of data.
Definition fitsDataRecorder.ipp:277
void Close() override
Close the currently open file.
Definition fitsDataRecorder.ipp:362
Thrown if an argument passed to a method was invalid.
Definition exceptions.hpp:302
The RtctkException class is the base class for all Rtctk exceptions.
Definition exceptions.hpp:213
fitscolumnFormat is used to get a fits type from a C++ type.
Definition commandReplier.cpp:22
constexpr bool IS_STATIC_SPAN_TYPE
Is true if the type is a gsl::span type with fixed size.
Definition typeTraits.hpp:179
void GetFITSTFormWithLength(std::vector< std::string > &input, const gsl::span< std::optional< size_t >, sizeof...(Args)+1 > sizes)
Get a vactor of TFORM strings for a list of types using the custom sizes provided.
Definition fitsColumnFormat.ipp:158
constexpr bool IS_STD_ARRAY_TYPE
Is true if the type is a std::array<U> of some type U.
Definition typeTraits.hpp:149
constexpr bool IS_DYNAMIC_SPAN_TYPE
Is true if the type is a gsl::span type with dynamic size.
Definition typeTraits.hpp:209
constexpr bool IS_VECTOR_TYPE
Is true if the type is a std::vector<U> of some type U.
Definition typeTraits.hpp:47
constexpr bool IS_MATRIX_BUFFER_TYPE
Is true if the type is a MatrixBuffer<U> of some type U.
Definition typeTraits.hpp:77
Definition ddsSub.hpp:156
FitsRecorder allows to write ColumnData to into fits files in a specified directory.
Utility functions for use in data recording.
Type that stores type information for a FITS TFORM string.
Definition fitsColumnFormat.hpp:31
Provides useful mechanisms to test various type traits.