RTC Toolkit 5.1.0
Loading...
Searching...
No Matches
fitsDataRecorder.ipp
Go to the documentation of this file.
1
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};
88
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 char err_text[30];
148 fits_get_errstatus(status, err_text);
149 std::string buffer;
150 char fits_buffer[80];
151 while (fits_read_errmsg(fits_buffer)) {
152 buffer += fits_buffer;
153 }
155 fmt::format("{} {}: {}", message, err_text, 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)
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}
173
174template <class... T>
175void FitsRecorder<T...>::Open(const std::filesystem::path& file) {
176 m_row_index = 1;
178 int status{0};
179 fits_create_diskfile(&filepointer, file.c_str(), &status);
180 ThrowOnBadStatus(status, "Error in fits_open_diskfile");
181 m_fits_handle.reset(filepointer);
182 std::vector<std::string> column_names{}, column_units{}, column_forms{};
183 column_names.reserve(sizeof...(T));
184 column_units.reserve(sizeof...(T));
185 column_forms.reserve(sizeof...(T));
186 for (auto column : this->m_column_description) {
187 column_names.push_back(std::string{column.name});
188 column_units.push_back(std::string{column.unit});
189 }
190 GetFITSTFormWithLength<T...>(column_forms, this->m_column_length);
191 auto iter = DataRecorder<T...>::m_disabled_fields.rbegin();
192 for (int i = sizeof...(T) - 1; iter != DataRecorder<T...>::m_disabled_fields.rend(); iter++) {
193 if (*iter) {
194 column_names.erase(std::next(column_names.begin(), i));
195 column_units.erase(std::next(column_units.begin(), i));
196 column_forms.erase(std::next(column_forms.begin(), i));
197 }
198 i--;
199 }
200 // fits expects char*
201 std::vector<char*> column_names_fits;
202 std::vector<char*> column_units_fits;
203 std::vector<char*> column_forms_fits;
204 column_names_fits.reserve(column_names.size());
205 column_units_fits.reserve(column_units.size());
206 column_forms_fits.reserve(column_forms.size());
207 for (auto& column_name : column_names) {
208 column_names_fits.push_back(column_name.data());
209 }
210 for (auto& column_unit : column_units) {
211 column_units_fits.push_back(column_unit.data());
212 }
213 for (auto& column_form : column_forms) {
214 column_forms_fits.push_back(column_form.data());
215 }
216 fits_create_tbl(m_fits_handle.get(),
218 0,
219 column_names_fits.size(),
220 column_names_fits.data(),
221 column_forms_fits.data(),
222 column_units_fits.data(),
223 "RECORDING",
224 &status);
225
226 ThrowOnBadStatus(status, "error in fits_create_tbl");
227}
228
229template <class... T>
231 if (m_fits_handle != nullptr) {
232 CII_THROW(RtctkException, "FitsRecorder: Setting ColumnLength after Open not allowed");
233 }
234 if (sizeof...(T) <= column) {
235 CII_THROW(InvalidArgumentException, "column index out of bounds");
236 }
237 if (length >= 4294967296) {
239 fmt::format("length({}) must be less than 2^32", length));
240 }
241 m_column_length[column] = length;
242}
243
244template <class... T>
245void FitsRecorder<T...>::SetColumnLength(const std::tuple<T...>& data) {
246 auto set_column_lengths = [this](const auto& first, const auto&... args) {
247 long column = 0;
248 auto set_column_length = [this, &column](const auto& x) {
249 // dynamically sized types
250 if constexpr (IS_DYNAMIC_SPAN_TYPE<std::decay_t<decltype(x)>> or
251 IS_VECTOR_TYPE<std::decay_t<decltype(x)>> or
252 IS_MATRIX_BUFFER_TYPE<std::decay_t<decltype(x)>>) {
253 this->SetColumnLength(column, x.size());
254 }
255 column++;
256 };
258 (set_column_length(args), ...);
259 };
260 std::apply(set_column_lengths, data);
261}
262
263// template <class... T>
264// void FitsRecorder<T...>::Write(const gsl::span<const std::tuple<T...>>) {
265
266// }
267
268// template <class... T>
269// void FitsRecorder<T...>::Write(const std::tuple<T...>& data) {
270// this->Write(gsl::span(&data, 1));
271// }
272
273template <class... T>
274void FitsRecorder<T...>::Write(const std::tuple<T...>& data) {
275 auto write = [this](const auto& first, const auto&... args) {
276 long fits_column = 1;
277 long element = 0;
278 auto write_elem = [this, &fits_column, &element](const auto& x) {
279 if (this->m_disabled_fields[element]) {
280 element += 1;
281 return;
282 }
283 int status{0};
284 // dynamically sized types
285 if constexpr (IS_DYNAMIC_SPAN_TYPE<std::decay_t<decltype(x)>> or
286 IS_VECTOR_TYPE<std::decay_t<decltype(x)>> or
287 IS_MATRIX_BUFFER_TYPE<std::decay_t<decltype(x)>>) {
288 if (not m_column_length[element].has_value()) {
290 fmt::format("dynamic sized type given, but length not given for {}",
291 this->m_column_description[element].name));
292 } else if (x.size() != m_column_length[element]) {
294 "data size does not match configured length");
295 }
296 std::vector<typename std::decay_t<decltype(x)>::value_type> copy(
297 x.data(), x.data() + m_column_length[element].value());
298 fits_write_col(m_fits_handle.get(),
299 FitsDataTypeValue<typename decltype(copy)::value_type>::DTYPE,
301 m_row_index,
302 1,
303 copy.size(),
304 copy.data(),
305 &status);
306 ThrowOnBadStatus(status, "Error writing to binary table");
307 // This case is for writing statically sized types
308 } else if constexpr (IS_STD_ARRAY_TYPE<std::decay_t<decltype(x)>> or
309 IS_STATIC_SPAN_TYPE<std::decay_t<decltype(x)>>) {
310 std::vector<typename std::decay_t<decltype(x)>::value_type> copy(
311 x.data(), x.data() + x.size());
312 fits_write_col(m_fits_handle.get(),
313 FitsDataTypeValue<typename decltype(copy)::value_type>::DTYPE,
315 m_row_index,
316 1,
317 copy.size(),
318 copy.data(),
319 &status);
320 ThrowOnBadStatus(status, "Error writing to binary table");
321 // This is for writing strings
322 } else if constexpr (std::is_same_v<std::decay_t<decltype(x)>, std::string>) {
323 std::string copy{x};
324 fits_write_col(m_fits_handle.get(),
325 TSTRING,
327 m_row_index,
328 1,
329 1,
330 copy.data(),
331 &status);
332 ThrowOnBadStatus(status, "Error writing to binary table");
333 // for scalar values
334 } else {
335 // need to copy since cfitsio wants a non-const type
336 std::decay_t<decltype(x)> copy{x};
337 fits_write_col(m_fits_handle.get(),
338 FitsDataTypeValue<decltype(copy)>::DTYPE,
340 m_row_index,
341 1,
342 1,
343 &copy,
344 &status);
345 ThrowOnBadStatus(status, "Error writing to binary table");
346 }
347 fits_column += 1;
348 element += 1;
349 };
351 (write_elem(args), ...);
352 };
353 std::apply(write, data);
354 m_row_index += 1;
355}
356
357template <class... T>
359 m_fits_handle.reset(nullptr);
360}
361
362template <class... T>
364 int status{0};
365 fits_close_file(pointer, &status);
366 ThrowOnBadStatus(status, "Error closing file");
367}
368
369} // namespace rtctk::componentFramework
This is an abstract class that can be used to implement an OutputStage for a Recording Unit.
Definition dataRecorder.hpp:29
Definition fitsDataRecorder.hpp:71
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:175
void SetColumnLength(size_t column, size_t length)
Set the exact length of a column.
Definition fitsDataRecorder.ipp:230
void Write(const std::tuple< T... > &data) override
Write a single row of data.
Definition fitsDataRecorder.ipp:274
void Close() override
Close the currently open file.
Definition fitsDataRecorder.ipp:358
Thrown if an argument passed to a method was invalid.
Definition exceptions.hpp:300
The RtctkException class is the base class for all Rtctk exceptions.
Definition exceptions.hpp:211
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
elt::mal::future< std::string > InjectReqRepEvent(StateMachineEngine &engine)
Definition malEventInjector.hpp:23
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.