mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
Loading...
Searching...
No Matches
fitsFile_test.cpp
Go to the documentation of this file.
1/** \file fitsFile_test.cpp
2 */
3#include "../../../catch2/catch.hpp"
4
5using namespace Catch::Matchers;
6
7#include "../../../../include/ioutils/fits/fitsFile.hpp"
8using namespace mx::fits;
9
10#include "../../../../include/improc/eigenImage.hpp"
11#include "../../../../include/improc/eigenCube.hpp"
12
13namespace mx
14{
15namespace unitTest
16{
17namespace fitsTest
18{
19namespace fitsFileTest
20{
21
22// Test harnass for fitsFile
23template <typename dataT>
24class fitsFile_test : public fitsFile<dataT>
25{
26 public:
27 typedef fitsFile<dataT>::pixarrT pixarrTT;
28
29 // Interface to protected calcPixarrs
30 void calcPixarrs( pixarrTT &pixarrs )
31 {
33 }
34
35 // Interface to set protected axis dimensions
36 void setnax( int naxis, long naxes0, long naxes1, long naxes2 )
37 {
39
40 if( naxis > 0 )
41 {
42 fitsFile<dataT>::m_naxes[0] = naxes0;
43 }
44
45 if( naxis > 1 )
46 {
47 fitsFile<dataT>::m_naxes[1] = naxes1;
48 }
49
50 if( naxis > 2 )
51 {
52 fitsFile<dataT>::m_naxes[2] = naxes2;
53 }
54 }
55
56 mx::error_t writeTestFrame( const std::string &fname, int n )
57 {
58 mx::improc::eigenImage<float> im( 1024, 1024 );
59
60 for( int cc = 0; cc < im.cols(); ++cc )
61 {
62 for( int rr = 0; rr < im.rows(); ++rr )
63 {
64 im( rr, cc ) = cc * im.rows() + rr;
65 }
66 }
67
68 fitsHeader fh;
69 fh.append( std::format( "FLOAT{}", n ), static_cast<float>( 1.1 ), "testing 1.1" );
70 fh.append( std::format( "INT{}", n ), static_cast<int>( 2 ), "testing 1.1" );
71
72 return this->write( std::format( "{}{}.fits", fname, n ), im, fh );
73 }
74};
75
76/// Calculating subimage sizes
77/** Verify calculation of subimage sizes
78 *
79 * \ingroup fitsFile_unit_tests
80 */
81TEST_CASE( "Calculating subimage sizes", "[ioutils::fits::fitsFile]" )
82{
83// document what protected members are tested here
84#ifdef MXLIB_DOXYGEN_PROTECTED_REF
87 ff.calcPixarrs( pixarrs );
88 ff.naxis();
89 ff.naxes( 0 );
90 ff.setReadSize( 10, 9, 7, 10 ) ff.setCubeReadSize( 5, 3 );
91#endif
92
93 SECTION( "a 1D image to read" )
94 {
95 SECTION( "reading the whole image with default setup" )
96 {
97 fitsFile_test<float> fft;
98
99 fft.setnax( 1, 64, 0, 0 );
100
101 REQUIRE( fft.naxis() == 1 );
102 REQUIRE( fft.naxes( 0 ) == 64 );
103 REQUIRE( fft.naxes( 1 ) == -1 );
104
105 fitsFile_test<float>::pixarrTT pixarrs;
106 fft.calcPixarrs( pixarrs );
107
108 REQUIRE( pixarrs.fpix[0] == 1 );
109 REQUIRE( pixarrs.lpix[0] == 64 );
110 REQUIRE( pixarrs.inc[0] == 1 );
111 }
112
113 SECTION( "reading a subimage" )
114 {
115 fitsFile_test<float> fft;
116
117 fft.setnax( 1, 64, 0, 0 );
118
119 REQUIRE( fft.naxis() == 1 );
120 REQUIRE( fft.naxes( 0 ) == 64 );
121 REQUIRE( fft.naxes( 1 ) == -1 );
122
123 fft.setReadSize( 10, 0, 7, 0 );
124
125 fitsFile_test<float>::pixarrTT pixarrs;
126 fft.calcPixarrs( pixarrs );
127
128 REQUIRE( pixarrs.fpix[0] == 11 );
129 REQUIRE( pixarrs.lpix[0] == 17 );
130 REQUIRE( pixarrs.inc[0] == 1 );
131 }
132 }
133
134 SECTION( "a 2D image to read" )
135 {
136 SECTION( "reading the whole image with default setup" )
137 {
138 fitsFile_test<float> fft;
139
140 fft.setnax( 2, 64, 64, 0 );
141
142 REQUIRE( fft.naxis() == 2 );
143 REQUIRE( fft.naxes( 0 ) == 64 );
144 REQUIRE( fft.naxes( 1 ) == 64 );
145 REQUIRE( fft.naxes( 2 ) == -1 );
146
147 fitsFile_test<float>::pixarrTT pixarrs;
148 fft.calcPixarrs( pixarrs );
149
150 REQUIRE( pixarrs.fpix[0] == 1 );
151 REQUIRE( pixarrs.fpix[1] == 1 );
152 REQUIRE( pixarrs.lpix[0] == 64 );
153 REQUIRE( pixarrs.lpix[1] == 64 );
154 REQUIRE( pixarrs.inc[0] == 1 );
155 REQUIRE( pixarrs.inc[1] == 1 );
156 }
157
158 SECTION( "reading a subimage" )
159 {
160 fitsFile_test<float> fft;
161
162 fft.setnax( 2, 64, 64, 0 );
163
164 REQUIRE( fft.naxis() == 2 );
165 REQUIRE( fft.naxes( 0 ) == 64 );
166 REQUIRE( fft.naxes( 1 ) == 64 );
167 REQUIRE( fft.naxes( 2 ) == -1 );
168
169 fft.setReadSize( 10, 9, 7, 10 );
170
171 fitsFile_test<float>::pixarrTT pixarrs;
172 fft.calcPixarrs( pixarrs );
173
174 REQUIRE( pixarrs.fpix[0] == 11 );
175 REQUIRE( pixarrs.fpix[1] == 10 );
176 REQUIRE( pixarrs.lpix[0] == 17 );
177 REQUIRE( pixarrs.lpix[1] == 19 );
178 REQUIRE( pixarrs.inc[0] == 1 );
179 REQUIRE( pixarrs.inc[1] == 1 );
180 }
181 }
182
183 SECTION( "a 3D image to read" )
184 {
185 WHEN( "reading the whole image with default setup" )
186 {
187 fitsFile_test<float> fft;
188
189 fft.setnax( 3, 64, 64, 64 );
190
191 REQUIRE( fft.naxis() == 3 );
192 REQUIRE( fft.naxes( 0 ) == 64 );
193 REQUIRE( fft.naxes( 1 ) == 64 );
194 REQUIRE( fft.naxes( 2 ) == 64 );
195 REQUIRE( fft.naxes( 3 ) == -1 );
196
197 fitsFile_test<float>::pixarrTT pixarrs;
198 fft.calcPixarrs( pixarrs );
199
200 REQUIRE( pixarrs.fpix[0] == 1 );
201 REQUIRE( pixarrs.fpix[1] == 1 );
202 REQUIRE( pixarrs.fpix[2] == 1 );
203
204 REQUIRE( pixarrs.lpix[0] == 64 );
205 REQUIRE( pixarrs.lpix[1] == 64 );
206 REQUIRE( pixarrs.lpix[2] == 64 );
207 REQUIRE( pixarrs.inc[0] == 1 );
208 REQUIRE( pixarrs.inc[1] == 1 );
209 REQUIRE( pixarrs.inc[2] == 1 );
210 }
211
212 SECTION( "reading a subimage" )
213 {
214 fitsFile_test<float> fft;
215
216 fft.setnax( 3, 64, 64, 64 );
217
218 REQUIRE( fft.naxis() == 3 );
219 REQUIRE( fft.naxes( 0 ) == 64 );
220 REQUIRE( fft.naxes( 1 ) == 64 );
221 REQUIRE( fft.naxes( 2 ) == 64 );
222 REQUIRE( fft.naxes( 3 ) == -1 );
223
224 fft.setReadSize( 10, 9, 7, 10 );
225 fft.setCubeReadSize( 5, 3 );
226
227 fitsFile_test<float>::pixarrTT pixarrs;
228 fft.calcPixarrs( pixarrs );
229
230 REQUIRE( pixarrs.fpix[0] == 11 );
231 REQUIRE( pixarrs.fpix[1] == 10 );
232 REQUIRE( pixarrs.fpix[2] == 6 );
233 REQUIRE( pixarrs.lpix[0] == 17 );
234 REQUIRE( pixarrs.lpix[1] == 19 );
235 REQUIRE( pixarrs.lpix[2] == 8 );
236 REQUIRE( pixarrs.inc[0] == 1 );
237 REQUIRE( pixarrs.inc[1] == 1 );
238 REQUIRE( pixarrs.inc[2] == 1 );
239 }
240 }
241}
242
243/// Basic writing and reading
244/**
245 * \ingroup fitsFile_unit_tests
246 */
247TEST_CASE( "Basic writing and reading", "[ioutils::fits::fitsFile]" )
248{
249 // clang-format off
250 // document what fitsFile_test is doing
251 #ifdef MXLIB_DOXYGEN_PROTECTED_REF
253 fitsHeader fh;
254 fh.append( "dummy", 2, "dummy" );
256 ff.write( "dummy", im, fh );
257 #endif
258 // clang-format on
259
260 SECTION( "write a frame and read it back in" )
261 {
262 fitsFile_test<float> fft;
263
264 REQUIRE( fft.writeTestFrame( "/tmp/fitsFile_test", 1 ) == mx::error_t::noerror );
265
267 fitsHeader fh;
269
270 REQUIRE( ff.read( im, fh, "/tmp/fitsFile_test1.fits" ) == mx::error_t::noerror );
271
272 REQUIRE_THAT( fh["FLOAT1"].Float(), WithinAbs( 1.1, 1e-5 ) );
273 REQUIRE( fh["INT1"].Int() == 2 );
274 REQUIRE( im.rows() == 1024 );
275 REQUIRE( im.cols() == 1024 );
276 }
277}
278
279/// Cube writing and reading
280/**
281 * \ingroup fitsFile_unit_tests
282 */
283TEST_CASE( "Cube writing and reading", "[ioutils::fits::fitsFile]" )
284{
285 // clang-format off
286 // document what fitsFile_test is doing
287 #ifdef MXLIB_DOXYGEN_PROTECTED_REF
289 fitsHeader fh;
290 fh.append( "dummy", 2, "dummy" );
292 ff.write( "dummy", im, fh );
293 #endif
294 // clang-format on
295
296 SECTION( "write 5 frames and read them back in as a cube" )
297 {
298 fitsFile_test<float> fft;
299
300 REQUIRE( fft.writeTestFrame( "/tmp/fitsFile_test", 1 ) == mx::error_t::noerror );
301 REQUIRE( fft.writeTestFrame( "/tmp/fitsFile_test", 2 ) == mx::error_t::noerror );
302 REQUIRE( fft.writeTestFrame( "/tmp/fitsFile_test", 3 ) == mx::error_t::noerror );
303 REQUIRE( fft.writeTestFrame( "/tmp/fitsFile_test", 4 ) == mx::error_t::noerror );
304 REQUIRE( fft.writeTestFrame( "/tmp/fitsFile_test", 5 ) == mx::error_t::noerror );
305
306 std::vector<std::string> flist({"/tmp/fitsFile_test1.fits","/tmp/fitsFile_test2.fits",
307 "/tmp/fitsFile_test3.fits","/tmp/fitsFile_test4.fits","/tmp/fitsFile_test5.fits"});
308
311
312 REQUIRE( ff.read( imc, flist ) == mx::error_t::noerror );
313
314 REQUIRE( imc.rows() == 1024 );
315 REQUIRE( imc.cols() == 1024 );
316 REQUIRE( imc.planes() == 5 );
317 }
318}
319
320/// Reading headers
321/**
322 *
323 * \ingroup fitsFile_unit_tests
324 */
325TEST_CASE( "Reading headers", "[ioutils::fits::fitsFile]" )
326{
327// document what fitsFile_test is doing
328#ifdef MXLIB_DOXYGEN_PROTECTED_REF
330 fitsHeader fh;
331 fh.append( "dummy", 2, "dummy" );
333 ff.write( "dummy", im, fh );
334#endif
335
336 fitsFile_test<float> fft;
337
338 std::vector<std::string> fnames(
339 { "/tmp/fitsFile_test1.fits", "/tmp/fitsFile_test2.fits", "/tmp/fitsFile_test3.fits" } );
340 REQUIRE( fft.writeTestFrame( "/tmp/fitsFile_test", 1 ) == mx::error_t::noerror );
341 REQUIRE( fft.writeTestFrame( "/tmp/fitsFile_test", 2 ) == mx::error_t::noerror );
342 REQUIRE( fft.writeTestFrame( "/tmp/fitsFile_test", 3 ) == mx::error_t::noerror );
343
344 SECTION( "write three frames and read their headers as a vector, not allocated" )
345 {
346
348 std::vector<mx::fits::fitsHeader<mx::verbose::d>> fh;
350
351 REQUIRE( ff.readHeader( fh, fnames ) == mx::error_t::noerror );
352 REQUIRE( fh.size() == fnames.size() );
353
354 REQUIRE_THAT( fh[0]["FLOAT1"].Float(), WithinAbs( 1.1, 1e-5 ) );
355 REQUIRE( fh[0]["INT1"].Int() == 2 );
356 REQUIRE_THAT( fh[1]["FLOAT2"].Float(), WithinAbs( 1.1, 1e-5 ) );
357 REQUIRE( fh[1]["INT2"].Int() == 2 );
358 REQUIRE_THAT( fh[2]["FLOAT3"].Float(), WithinAbs( 1.1, 1e-5 ) );
359 REQUIRE( fh[2]["INT3"].Int() == 2 );
360 }
361
362 SECTION( "write three frames and read their headers as a vector, allocated" )
363 {
364
366 std::vector<mx::fits::fitsHeader<mx::verbose::d>> fh;
368
369 fh.resize( fnames.size() );
370 REQUIRE( ff.readHeader( fh, fnames ) == mx::error_t::noerror );
371
372 mx::error_t errc;
373 errc = mx::error_t::error;
374 REQUIRE_THAT( fh[0]["FLOAT1"].Float( &errc ), WithinAbs( 1.1, 1e-5 ) );
375 REQUIRE( errc == mx::error_t::noerror );
376 errc = mx::error_t::error;
377 REQUIRE( fh[0]["INT1"].Int( &errc ) == 2 );
378 REQUIRE( errc == mx::error_t::noerror );
379 errc = mx::error_t::error;
380 REQUIRE_THAT( fh[1]["FLOAT2"].Float( &errc ), WithinAbs( 1.1, 1e-5 ) );
381 REQUIRE( errc == mx::error_t::noerror );
382 errc = mx::error_t::error;
383 REQUIRE( fh[1]["INT2"].Int( &errc ) == 2 );
384 REQUIRE( errc == mx::error_t::noerror );
385 errc = mx::error_t::error;
386 REQUIRE_THAT( fh[2]["FLOAT3"].Float( &errc ), WithinAbs( 1.1, 1e-5 ) );
387 REQUIRE( errc == mx::error_t::noerror );
388 errc = mx::error_t::error;
389 REQUIRE( fh[2]["INT3"].Int( &errc ) == 2 );
390 REQUIRE( errc == mx::error_t::noerror );
391 }
392
393 SECTION( "write three frames and read their headers as a vector, allocated to wrong size" )
394 {
396 std::vector<mx::fits::fitsHeader<mx::verbose::d>> fh;
398
399 fh.resize( fnames.size() + 1 );
400 REQUIRE( ff.readHeader( fh, fnames ) == mx::error_t::invalidarg );
401 }
402
403 SECTION( "write three frames and read their headers as a vector, extracting only 1 keyword" )
404 {
405
407 std::vector<mx::fits::fitsHeader<mx::verbose::d>> fh;
409
410 fh.resize( fnames.size() );
411 fh[0].append( "FLOAT1" );
412 fh[1].append( "FLOAT2" );
413 fh[2].append( "FLOAT3" );
414
415 REQUIRE( ff.readHeader( fh, fnames ) == mx::error_t::noerror );
416
417 mx::error_t errc;
418
419 REQUIRE_THAT( fh[0]["FLOAT1"].Float( &errc ), WithinAbs( 1.1, 1e-5 ) );
420 REQUIRE( errc == mx::error_t::noerror );
421 REQUIRE( fh[0]["FLOAT1"].valueGood() == true );
422
423 REQUIRE( fh[0]["INT1"].Int( &errc ) == 0 );
424 REQUIRE( errc == mx::error_t::invalidarg );
425 REQUIRE( fh[0]["INT1"].valueGood() == false );
426
427 REQUIRE_THAT( fh[1]["FLOAT2"].Float( &errc ), WithinAbs( 1.1, 1e-5 ) );
428 REQUIRE( errc == mx::error_t::noerror );
429 REQUIRE( fh[1]["FLOAT2"].valueGood() == true );
430
431 REQUIRE( fh[1]["INT2"].Int( &errc ) == 0 );
432 REQUIRE( errc == mx::error_t::invalidarg );
433 REQUIRE( fh[1]["INT2"].valueGood() == false );
434
435 REQUIRE_THAT( fh[2]["FLOAT3"].Float( &errc ), WithinAbs( 1.1, 1e-5 ) );
436 REQUIRE( errc == mx::error_t::noerror );
437 REQUIRE( fh[2]["FLOAT3"].valueGood() == true );
438
439 REQUIRE( fh[2]["INT3"].Int( &errc ) == 0 );
440 REQUIRE( errc == mx::error_t::invalidarg );
441 REQUIRE( fh[2]["INT3"].valueGood() == false );
442 }
443}
444
445} // namespace fitsFileTest
446} // namespace fitsTest
447} // namespace unitTest
448} // namespace mx
Class to manage interactions with a FITS file.
Definition fitsFile.hpp:53
int naxis()
Get the current value of m_naxis.
Definition fitsFile.hpp:749
void setCubeReadSize()
Set to read all frames from a cube.
long naxes(int dim)
Get the current value of m_naxes for the specified dimension.
Definition fitsFile.hpp:755
void setReadSize()
Set to read all the pixels in the file.
error_t readHeader(fitsHeader< verboseT > &head)
Read the header from the fits file.
error_t calcPixarrs(pixarrT &pixarr)
Fill in the read-size arrays for reading a subset (always used)
Definition fitsFile.hpp:915
error_t read(dataT *data)
Read the contents of the FITS file into an array.
Definition fitsFile.hpp:992
error_t write(const dataT *im, int d1, int d2, int d3, fitsHeader< verboseT > *head)
Write the contents of a raw array to the FITS file.
Class to manage a FITS file metadata header and provide fast access to the cards by keyword.
error_t append(const fitsHeaderCard< verboseT > &card)
Append a fitsHeaderCard to the end of the header.
An image cube with an Eigen-like API.
Definition eigenCube.hpp:30
Eigen::Array< scalarT, -1, -1 > eigenImage
Definition of the eigenImage type, which is an alias for Eigen::Array.
error_t
The mxlib error codes.
Definition error_t.hpp:26
@ noerror
No error has occurred.
@ invalidarg
An argument was invalid.
@ error
A general error has occurred.
TEST_CASE("Calculating subimage sizes", "[ioutils::fits::fitsFile]")
Calculating subimage sizes.
The mxlib c++ namespace.
Definition mxlib.hpp:37