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