source: trunk/Mars/mcore/ofits.h@ 17767

Last change on this file since 17767 was 17761, checked in by tbretz, 11 years ago
Added an semi-automatic setting of PACKAGE_NAME, PACKAGE_VERSION and REVISION
File size: 34.5 KB
Line 
1#ifndef MARS_ofits
2#define MARS_ofits
3
4#include <string>
5#include <string.h>
6#include <algorithm>
7#include <sstream>
8#include <iostream>
9#include <fstream>
10#include <iomanip>
11#include <vector>
12#include <algorithm>
13#include <stdexcept>
14
15#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
16
17#ifndef PACKAGE_NAME
18#define PACKAGE_NAME "n/a"
19#endif
20
21#ifndef PACKAGE_VERSION
22#define PACKAGE_VERSION "n/a"
23#endif
24
25#ifndef REVISION
26#define REVISION "n/a"
27#endif
28
29#ifdef __CINT__
30#define off_t size_t
31#endif
32
33#ifndef __MARS__
34#ifndef gLog
35#define gLog std::cerr
36#define ___err___ ""
37#define ___warn___ ""
38#define ___all___ ""
39#endif
40#else
41#include "MLog.h"
42#include "MLogManip.h"
43#define ___err___ err
44#define ___warn___ warn
45#define ___all___ all
46#endif
47
48#include "FITS.h"
49#include "checksum.h"
50
51// Sloppy: allow / <--- left
52// allow all characters (see specs for what is possible)
53
54// units: m kg s rad sr K A mol cd Hz J W V N Pa C Ohm S F Wb T Hlm lx
55
56class ofits : public std::ostream
57{
58protected:
59 std::filebuf fFilebuf;
60
61public:
62 struct Key
63 {
64 std::string key;
65 bool delim;
66 std::string value;
67 std::string comment;
68 std::string fitsString;
69
70 off_t offset; // offset in file
71
72 bool changed; // For closing the file
73
74 Key(const std::string &k="") : key(k), delim(false), fitsString(""), offset(0), changed(true) { }
75
76 std::string Trim(const std::string &str)
77 {
78 // Trim Both leading and trailing spaces
79 const size_t first = str.find_first_not_of(' '); // Find the first character position after excluding leading blank spaces
80 const size_t last = str.find_last_not_of(' '); // Find the first character position from reverse af
81
82 // if all spaces or empty return an empty string
83 if (std::string::npos==first || std::string::npos==last)
84 return std::string();
85
86 return str.substr(first, last-first+1);
87 }
88
89 bool FormatKey()
90 {
91 key = Trim(key);
92 if (key.empty())
93 {
94#ifdef __EXCEPTIONS
95 throw std::runtime_error("Key name empty.");
96#else
97 gLog << ___err___ << "ERROR - Key name empty." << std::endl;
98 return false;
99#endif
100 }
101 if (key.size()>8)
102 {
103 std::ostringstream sout;
104 sout << "Key '" << key << "' exceeds 8 bytes.";
105#ifdef __EXCEPTIONS
106 throw std::runtime_error(sout.str());
107#else
108 gLog << ___err___ << "ERROR - " << sout.str() << std::endl;
109 return false;
110#endif
111 }
112
113 //transform(key.begin(), key.end(), key.begin(), toupper);
114#if GCC_VERSION < 40603
115 for (std::string::const_iterator c=key.begin(); c<key.end(); c++)
116#else
117 for (std::string::const_iterator c=key.cbegin(); c<key.cend(); c++)
118#endif
119 if ((*c<'A' || *c>'Z') && (*c<'0' || *c>'9') && *c!='-' && *c!='_')
120 {
121 std::ostringstream sout;
122 sout << "Invalid character '" << *c << "' found in key '" << key << "'";
123#ifdef __EXCEPTIONS
124 throw std::runtime_error(sout.str());
125#else
126 gLog << ___err___ << "ERROR - " << sout.str() << std::endl;
127 return false;
128#endif
129 }
130
131 return true;
132 }
133
134 bool FormatComment()
135 {
136 comment = Trim(comment);
137
138#if GCC_VERSION < 40603
139 for (std::string::const_iterator c=key.begin(); c<key.end(); c++)
140#else
141 for (std::string::const_iterator c=key.cbegin(); c<key.cend(); c++)
142#endif
143 if (*c<32 || *c>126)
144 {
145 std::ostringstream sout;
146 sout << "Invalid character '" << *c << "' [" << int(*c) << "] found in comment '" << comment << "'";
147#ifdef __EXCEPTIONS
148 throw std::runtime_error(sout.str());
149#else
150 gLog << ___err___ << "ERROR - " << sout.str() << std::endl;
151 return false;
152#endif
153 }
154
155 return true;
156 }
157
158 bool check(bool trim=false)
159 {
160 if (!FormatKey())
161 return false;
162
163 if (!FormatComment())
164 return false;
165
166 size_t sz = CalcSize();
167 if (sz<=80)
168 return true;
169
170 if (!trim)
171 {
172 std::ostringstream sout;
173 sout << "Size " << sz << " of entry for key '" << key << "' exceeds 80 characters.";
174#ifdef __EXCEPTIONS
175 throw std::runtime_error(sout.str());
176#else
177 gLog << ___err___ << "ERROR - " << sout.str() << std::endl;
178#endif
179 return false;
180 }
181
182 //looks like something went wrong. Maybe entry is too long ?
183 //try to remove the comment
184 comment = "";
185
186 sz = CalcSize();
187 if (sz<=80)
188 {
189#ifndef __EXCEPTIONS
190 std::ostringstream sout;
191 sout << "Size " << sz << " of entry for key '" << key << "' exceeds 80 characters... removed comment.";
192 gLog << ___warn___ << "WARNING - " << sout.str() << std::endl;
193#endif
194 return true;
195 }
196
197 std::ostringstream sout;
198 sout << "Size " << sz << " of entry for key '" << key << "' exceeds 80 characters even without comment.";
199#ifdef __EXCEPTIONS
200 throw std::runtime_error(sout.str());
201#else
202 gLog << ___err___ << "ERROR - " << sout.str() << std::endl;
203 return false;
204#endif
205 }
206
207 size_t CalcSize() const
208 {
209 if (!delim)
210 return 10+comment.size();
211
212 return 10 + (value.size()<20?20:value.size()) + 3 + comment.size();
213 }
214
215 std::string Compile()
216 {
217
218 if (fitsString != "")
219 return fitsString;
220
221 std::ostringstream sout;
222 sout << std::left << std::setw(8) << key;
223
224 if (!delim)
225 {
226 sout << " " << comment;
227 return sout.str();
228 }
229
230 sout << "= ";
231 sout << (!value.empty() && value[0]=='\''?std::left:std::right);
232 sout << std::setw(20) << value << std::left;
233
234 if (!comment.empty())
235 sout << " / " << comment;
236
237 return sout.str();
238 }
239
240 Checksum checksum;
241
242 void Out(std::ostream &fout)
243 {
244 if (!changed)
245 return;
246
247 std::string str = Compile();
248 str.insert(str.end(), 80-str.size(), ' ');
249
250 if (offset==0)
251 offset = fout.tellp();
252
253 //cout << "Write[" << offset << "]: " << key << "/" << value << endl;
254
255 fout.seekp(offset);
256 fout << str;
257
258 checksum.reset();
259 checksum.add(str.c_str(), 80);
260
261 changed = false;
262 }
263 /*
264 void Out(ostream &out)
265 {
266 std::string str = Compile();
267
268 str.insert(str.end(), 80-str.size(), ' ');
269
270 out << str;
271 changed = false;
272 }*/
273 };
274
275private:
276 std::vector<Key> fKeys;
277
278 std::vector<Key>::iterator findkey(const std::string &key)
279 {
280 for (auto it=fKeys.begin(); it!=fKeys.end(); it++)
281 if (key==it->key)
282 return it;
283
284 return fKeys.end();
285 }
286
287 bool Set(const std::string &key="", bool delim=false, const std::string &value="", const std::string &comment="")
288 {
289 // If no delimit add the row no matter if it alread exists
290 if (delim)
291 {
292 // if the row already exists: update it
293 auto it = findkey(key);
294 if (it!=fKeys.end())
295 {
296 it->value = value;
297 it->changed = true;
298 return true;
299 }
300 }
301
302 if (fTable.num_rows>0)
303 {
304 std::ostringstream sout;
305 sout << "No new header key can be defined, rows were already written to the file... ignoring new key '" << key << "'";
306#ifdef __EXCEPTIONS
307 throw std::runtime_error(sout.str());
308#else
309 gLog << ___err___ << "ERROR - " << sout.str() << std::endl;
310 return false;
311#endif
312 }
313
314 Key entry;
315
316 entry.key = key;
317 entry.delim = delim;
318 entry.value = value;
319 entry.comment = comment;
320 entry.offset = 0;
321 entry.changed = true;
322
323 if (!entry.check(fCommentTrimming))
324 return false;
325
326 fKeys.push_back(entry);
327 return true;
328 }
329
330protected:
331 struct Table
332 {
333 off_t offset;
334
335 size_t bytes_per_row;
336 size_t num_rows;
337 size_t num_cols;
338
339 struct Column
340 {
341 std::string name;
342 size_t offset;
343 size_t num;
344 size_t size;
345 char type;
346 };
347
348 std::vector<Column> cols;
349
350 Table() : offset(0), bytes_per_row(0), num_rows(0), num_cols(0)
351 {
352 }
353 };
354
355
356 Table fTable;
357
358 std::vector<char> fOutputBuffer;
359
360 std::vector<Table::Column>::const_iterator findcol(const std::string &name)
361 {
362 for (auto it=fTable.cols.cbegin(); it!=fTable.cols.cend(); it++)
363 if (name==it->name)
364 return it;
365
366 return fTable.cols.cend();
367 }
368
369 Checksum fDataSum;
370 Checksum fHeaderSum;
371
372 bool fCommentTrimming;
373 bool fManualExtName;
374
375public:
376 ofits()
377 : std::ostream(), fFilebuf(), fCommentTrimming(false), fManualExtName(false)
378 {
379 init(&fFilebuf);
380 }
381
382 ofits(const char *fname)
383 : std::ostream(), fFilebuf(), fCommentTrimming(false), fManualExtName(false)
384 {
385 init(&fFilebuf);
386 open(fname);
387 }
388
389#ifdef __GXX_EXPERIMENTAL_CXX0X__
390 ofits(const std::string &fname)
391 : std::ostream(), fCommentTrimming(false), fManualExtName(false)
392 {
393 init(&fFilebuf);
394 open(fname);
395 }
396#endif
397
398 virtual ~ofits()
399 {
400 if (is_open())
401 close();
402 }
403/*
404 filebuf *rdbuf() const
405 {
406 return const_cast<filebuf*>(&fFilebuf);
407 }
408*/
409 bool is_open()
410 {
411 return fFilebuf.is_open();
412 }
413
414 bool is_open() const
415 {
416 return fFilebuf.is_open();
417 }
418
419 virtual void open(const char *filename, bool addEXTNAMEKey=true)
420 {
421 fDataSum = 0;
422 fHeaderSum = 0;
423
424 fTable = Table();
425 fKeys.clear();
426
427 SetStr("XTENSION", "BINTABLE", "binary table extension");
428 SetInt("BITPIX", 8, "8-bit bytes");
429 SetInt("NAXIS", 2, "2-dimensional binary table");
430 SetInt("NAXIS1", 0, "width of table in bytes");
431 SetInt("NAXIS2", 0, "number of rows in table");
432 SetInt("PCOUNT", 0, "size of special data area");
433 SetInt("GCOUNT", 1, "one data group (required keyword)");
434 SetInt("TFIELDS", 0, "number of fields in each row");
435 if (addEXTNAMEKey)
436 SetStr("EXTNAME", "", "name of extension table");
437 else
438 fManualExtName = true;
439 SetStr("CHECKSUM", "0000000000000000", "Checksum for the whole HDU");
440 SetStr("DATASUM", " 0", "Checksum for the data block");
441
442 if (!fFilebuf.open(filename, ios_base::out|ios_base::trunc))
443 setstate(ios_base::failbit);
444 else
445 clear();
446 }
447
448 virtual void open(const std::string &filename, bool addEXTNAMEKey=true)
449 {
450 open(filename.c_str(), addEXTNAMEKey);
451 }
452
453 void AllowCommentsTrimming(bool allow)
454 {
455 fCommentTrimming = allow;
456 }
457 //Etienne: required to enable 1 to 1 reconstruction of files
458 bool SetKeyComment(const std::string& key, const std::string& comment)
459 {
460 auto it = findkey(key);
461 if (it==fKeys.end())
462 return false;
463 it->comment = comment;
464 it->changed = true;
465 return true;
466 }
467 bool SetKeyFromFitsString(const std::string& fitsString)
468 {
469 if (fTable.num_rows>0)
470 {
471 std::ostringstream sout;
472 sout << "No new header key can be defined, rows were already written to the file... ignoring new key '" << fitsString << "'";
473#ifdef __EXCEPTIONS
474 throw std::runtime_error(sout.str());
475#else
476 gLog << ___err___ << "ERROR - " << sout.str() << std::endl;
477 return false;
478#endif
479 }
480
481 Key entry;
482 entry.fitsString = fitsString;
483 entry.changed = true;
484 fKeys.push_back(entry);
485 return true;
486 }
487 bool SetRaw(const std::string &key, const std::string &val, const std::string &comment)
488 {
489 return Set(key, true, val, comment);
490 }
491
492 bool SetBool(const std::string &key, bool b, const std::string &comment="")
493 {
494 return Set(key, true, b?"T":"F", comment);
495 }
496
497 bool AddEmpty(const std::string &key, const std::string &comment="")
498 {
499 return Set(key, true, "", comment);
500 }
501
502 bool SetStr(const std::string &key, std::string s, const std::string &comment="")
503 {
504 for (uint i=0; i<s.length(); i++)
505 if (s[i]=='\'')
506 s.insert(i++, "\'");
507
508 return Set(key, true, "'"+s+"'", comment);
509 }
510
511 bool SetInt(const std::string &key, int64_t i, const std::string &comment="")
512 {
513 std::ostringstream sout;
514 sout << i;
515
516 return Set(key, true, sout.str(), comment);
517 }
518
519 bool SetFloat(const std::string &key, double f, int p, const std::string &comment="")
520 {
521 std::ostringstream sout;
522
523 if (p<0)
524 sout << std::setprecision(-p) << fixed;
525 if (p>0)
526 sout << std::setprecision(p);
527 if (p==0)
528 sout << std::setprecision(f>1e-100 && f<1e100 ? 15 : 14);
529
530 sout << f;
531
532 std::string str = sout.str();
533
534 replace(str.begin(), str.end(), 'e', 'E');
535
536 if (str.find_first_of('E')==std::string::npos && str.find_first_of('.')==std::string::npos)
537 str += ".";
538
539 return Set(key, true, str, comment);
540 }
541
542 bool SetFloat(const std::string &key, double f, const std::string &comment="")
543 {
544 return SetFloat(key, f, 0, comment);
545 }
546
547 bool SetHex(const std::string &key, uint64_t i, const std::string &comment="")
548 {
549 std::ostringstream sout;
550 sout << std::hex << "0x" << i;
551 return SetStr(key, sout.str(), comment);
552 }
553
554 bool AddComment(const std::string &comment)
555 {
556 return Set("COMMENT", false, "", comment);
557 }
558
559 bool AddHistory(const std::string &comment)
560 {
561 return Set("HISTORY", false, "", comment);
562 }
563
564 void End()
565 {
566 Set("END");
567 while (fKeys.size()%36!=0)
568 fKeys.emplace_back();
569 }
570
571 std::string CommentFromType(char type)
572 {
573 std::string comment;
574
575 switch (type)
576 {
577 case 'L': comment = "[1-byte BOOL]"; break;
578 case 'A': comment = "[1-byte CHAR]"; break;
579 case 'B': comment = "[1-byte BOOL]"; break;
580 case 'I': comment = "[2-byte INT]"; break;
581 case 'J': comment = "[4-byte INT]"; break;
582 case 'K': comment = "[8-byte INT]"; break;
583 case 'E': comment = "[4-byte FLOAT]"; break;
584 case 'D': comment = "[8-byte FLOAT]"; break;
585 case 'Q': comment = "[var. Length]"; break;
586 }
587
588 return comment;
589 }
590
591 uint32_t SizeFromType(char type)
592 {
593 size_t size = 0;
594
595 switch (type)
596 {
597 case 'L': size = 1; break;
598 case 'A': size = 1; break;
599 case 'B': size = 1; break;
600 case 'I': size = 2; break;
601 case 'J': size = 4; break;
602 case 'K': size = 8; break;
603 case 'E': size = 4; break;
604 case 'D': size = 8; break;
605 case 'Q': size = 16; break;
606 }
607
608 return size;
609 }
610 //ETIENNE to be able to restore the file 1 to 1, I must restore the header keys myself
611 virtual bool AddColumn(uint32_t cnt, char typechar, const std::string &name, const std::string &unit, const std::string &comment="", bool addHeaderKeys=true)
612 {
613 if (tellp()<0)
614 {
615 std::ostringstream sout;
616 sout << "File not open... ignoring column '" << name << "'";
617#ifdef __EXCEPTIONS
618 throw std::runtime_error(sout.str());
619#else
620 gLog << ___err___ << "ERROR - " << sout.str() << std::endl;
621 return false;
622#endif
623 }
624
625 if (tellp()>0)
626 {
627 std::ostringstream sout;
628 sout << "Header already written, no new column can be defined... ignoring column '" << name << "'";
629#ifdef __EXCEPTIONS
630 throw std::runtime_error(sout.str());
631#else
632 gLog << ___err___ << "ERROR - " << sout.str() << std::endl;
633 return false;
634#endif
635 }
636
637 if (findcol(name)!=fTable.cols.cend())
638 {
639 std::ostringstream sout;
640 sout << "A column with the name '" << name << "' already exists.";
641#ifdef __EXCEPTIONS
642 throw std::runtime_error(sout.str());
643#else
644 gLog << ___err___ << "ERROR - " << sout.str() << std::endl;
645 return false;
646#endif
647 }
648
649 typechar = toupper(typechar);
650
651 static const std::string allow("LABIJKEDQ");
652#if GCC_VERSION < 40603
653 if (std::find(allow.begin(), allow.end(), typechar)==allow.end())
654#else
655 if (std::find(allow.cbegin(), allow.cend(), typechar)==allow.end())
656#endif
657 {
658 std::ostringstream sout;
659 sout << "Column type '" << typechar << "' not supported.";
660#ifdef __EXCEPTIONS
661 throw std::runtime_error(sout.str());
662#else
663 gLog << ___err___ << "ERROR - " << sout.str() << std::endl;
664 return false;
665#endif
666 }
667
668 std::ostringstream type;
669 type << cnt;
670 if (typechar=='Q')
671 type << "QB";
672 else
673 type << typechar;
674
675 fTable.num_cols++;
676
677 if (addHeaderKeys)
678 {
679#if GCC_VERSION < 40603
680 const std::string nc = std::to_string((long long int)(fTable.num_cols));
681#else
682 const std::string nc = std::to_string(fTable.num_cols);
683#endif
684 SetStr("TFORM"+nc, type.str(), "format of "+name+" "+CommentFromType(typechar));
685 SetStr("TTYPE"+nc, name, comment);
686 if (!unit.empty())
687 SetStr("TUNIT"+nc, unit, "unit of "+name);
688 }
689 size_t size = SizeFromType(typechar);
690
691 Table::Column col;
692
693 col.name = name;
694 col.type = typechar;
695 col.num = cnt;
696 col.size = size;
697 col.offset = fTable.bytes_per_row;
698
699 fTable.cols.push_back(col);
700
701 fTable.bytes_per_row += size*cnt;
702
703 // Align to four bytes
704 fOutputBuffer.resize(fTable.bytes_per_row + 4-fTable.bytes_per_row%4);
705
706 return true;
707 }
708
709 virtual bool AddColumn(const FITS::Compression&, uint32_t cnt, char typechar, const std::string& name, const std::string& unit, const std::string& comment="", bool addHeaderKeys=true)
710 {
711 return AddColumn(cnt, typechar, name, unit, comment, addHeaderKeys);
712 }
713
714 bool AddColumnShort(uint32_t cnt, const std::string &name, const std::string &unit="", const std::string &comment="")
715 { return AddColumn(cnt, 'I', name, unit, comment); }
716 bool AddColumnInt(uint32_t cnt, const std::string &name, const std::string &unit="", const std::string &comment="")
717 { return AddColumn(cnt, 'J', name, unit, comment); }
718 bool AddColumnLong(uint32_t cnt, const std::string &name, const std::string &unit="", const std::string &comment="")
719 { return AddColumn(cnt, 'K', name, unit, comment); }
720 bool AddColumnFloat(uint32_t cnt, const std::string &name, const std::string &unit="", const std::string &comment="")
721 { return AddColumn(cnt, 'E', name, unit, comment); }
722 bool AddColumnDouble(uint32_t cnt, const std::string &name, const std::string &unit="", const std::string &comment="")
723 { return AddColumn(cnt, 'D', name, unit, comment); }
724 bool AddColumnChar(uint32_t cnt, const std::string &name, const std::string &unit="", const std::string &comment="")
725 { return AddColumn(cnt, 'A', name, unit, comment); }
726 bool AddColumnByte(uint32_t cnt, const std::string &name, const std::string &unit="", const std::string &comment="")
727 { return AddColumn(cnt, 'B', name, unit, comment); }
728 bool AddColumnBool(uint32_t cnt, const std::string &name, const std::string &unit="", const std::string &comment="")
729 { return AddColumn(cnt, 'L', name, unit, comment); }
730
731 bool AddColumnShort(const std::string &name, const std::string &unit="", const std::string &comment="")
732 { return AddColumn(1, 'I', name, unit, comment); }
733 bool AddColumnInt(const std::string &name, const std::string &unit="", const std::string &comment="")
734 { return AddColumn(1, 'J', name, unit, comment); }
735 bool AddColumnLong(const std::string &name, const std::string &unit="", const std::string &comment="")
736 { return AddColumn(1, 'K', name, unit, comment); }
737 bool AddColumnFloat(const std::string &name, const std::string &unit="", const std::string &comment="")
738 { return AddColumn(1, 'E', name, unit, comment); }
739 bool AddColumnDouble(const std::string &name, const std::string &unit="", const std::string &comment="")
740 { return AddColumn(1, 'D', name, unit, comment); }
741 bool AddColumnChar(const std::string &name, const std::string &unit="", const std::string &comment="")
742 { return AddColumn(1, 'A', name, unit, comment); }
743 bool AddColumnByte(const std::string &name, const std::string &unit="", const std::string &comment="")
744 { return AddColumn(1, 'B', name, unit, comment); }
745 bool AddColumnBool(const std::string &name, const std::string &unit="", const std::string &comment="")
746 { return AddColumn(1, 'L', name, unit, comment); }
747
748 bool AddColumnShort(const FITS::Compression &comp, uint32_t cnt, const std::string &name, const std::string &unit="", const std::string &comment="")
749 { return AddColumn(comp, cnt, 'I', name, unit, comment); }
750 bool AddColumnInt(const FITS::Compression &comp, uint32_t cnt, const std::string &name, const std::string &unit="", const std::string &comment="")
751 { return AddColumn(comp, cnt, 'J', name, unit, comment); }
752 bool AddColumnLong(const FITS::Compression &comp, uint32_t cnt, const std::string &name, const std::string &unit="", const std::string &comment="")
753 { return AddColumn(comp, cnt, 'K', name, unit, comment); }
754 bool AddColumnFloat(const FITS::Compression &comp, uint32_t cnt, const std::string &name, const std::string &unit="", const std::string &comment="")
755 { return AddColumn(comp, cnt, 'E', name, unit, comment); }
756 bool AddColumnDouble(const FITS::Compression &comp, uint32_t cnt, const std::string &name, const std::string &unit="", const std::string &comment="")
757 { return AddColumn(comp, cnt, 'D', name, unit, comment); }
758 bool AddColumnChar(const FITS::Compression &comp, uint32_t cnt, const std::string &name, const std::string &unit="", const std::string &comment="")
759 { return AddColumn(comp, cnt, 'A', name, unit, comment); }
760 bool AddColumnByte(const FITS::Compression &comp, uint32_t cnt, const std::string &name, const std::string &unit="", const std::string &comment="")
761 { return AddColumn(comp, cnt, 'B', name, unit, comment); }
762 bool AddColumnBool(const FITS::Compression &comp, uint32_t cnt, const std::string &name, const std::string &unit="", const std::string &comment="")
763 { return AddColumn(comp, cnt, 'L', name, unit, comment); }
764
765 bool AddColumnShort(const FITS::Compression &comp, const std::string &name, const std::string &unit="", const std::string &comment="")
766 { return AddColumn(comp, 1, 'I', name, unit, comment); }
767 bool AddColumnInt(const FITS::Compression &comp, const std::string &name, const std::string &unit="", const std::string &comment="")
768 { return AddColumn(comp, 1, 'J', name, unit, comment); }
769 bool AddColumnLong(const FITS::Compression &comp, const std::string &name, const std::string &unit="", const std::string &comment="")
770 { return AddColumn(comp, 1, 'K', name, unit, comment); }
771 bool AddColumnFloat(const FITS::Compression &comp, const std::string &name, const std::string &unit="", const std::string &comment="")
772 { return AddColumn(comp, 1, 'E', name, unit, comment); }
773 bool AddColumnDouble(const FITS::Compression &comp, const std::string &name, const std::string &unit="", const std::string &comment="")
774 { return AddColumn(comp, 1, 'D', name, unit, comment); }
775 bool AddColumnChar(const FITS::Compression &comp, const std::string &name, const std::string &unit="", const std::string &comment="")
776 { return AddColumn(comp, 1, 'A', name, unit, comment); }
777 bool AddColumnByte(const FITS::Compression &comp, const std::string &name, const std::string &unit="", const std::string &comment="")
778 { return AddColumn(comp, 1, 'B', name, unit, comment); }
779 bool AddColumnBool(const FITS::Compression &comp, const std::string &name, const std::string &unit="", const std::string &comment="")
780 { return AddColumn(comp, 1, 'L', name, unit, comment); }
781
782 /*
783 bool AddKey(string key, double d, const std::string &comment)
784 {
785 ostringstream out;
786 out << d;
787
788 std::string s = out.str();
789
790 replace(s.begin(), s.end(), "e", "E");
791
792 return AddKey(key, s, comment);
793 }*/
794
795
796 Checksum WriteHeader(std::ostream &fout)
797 {
798 Checksum sum;
799 uint32_t count=0;
800 for (auto it=fKeys.begin(); it!=fKeys.end(); it++)
801 {
802 it->Out(fout);
803 sum += it->checksum;
804 count++;
805 }
806 fout.flush();
807
808 return sum;
809 }
810
811 Checksum WriteHeader()
812 {
813 return WriteHeader(*this);
814 }
815
816 void FlushHeader()
817 {
818 const off_t pos = tellp();
819 WriteHeader();
820 seekp(pos);
821 }
822
823 Checksum WriteFitsHeader()
824 {
825 ofits h;
826
827 h.SetBool("SIMPLE", true, "file does conform to FITS standard");
828 h.SetInt ("BITPIX", 8, "number of bits per data pixel");
829 h.SetInt ("NAXIS", 0, "number of data axes");
830 h.SetBool("EXTEND", true, "FITS dataset may contain extensions");
831 h.SetStr ("CHECKSUM","0000000000000000", "Checksum for the whole HDU");
832 h.SetStr ("DATASUM", " 0", "Checksum for the data block");
833 h.AddComment("FITS (Flexible Image Transport System) format is defined in 'Astronomy");
834 h.AddComment("and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H");
835 h.End();
836
837 const Checksum sum = h.WriteHeader(*this);
838
839 h.SetStr("CHECKSUM", sum.str());
840
841 const size_t offset = tellp();
842 h.WriteHeader(*this);
843 seekp(offset);
844
845 return sum;
846 }
847
848 virtual bool WriteDrsOffsetsTable ()
849 {
850 return true;
851 }
852
853 virtual bool WriteCatalog()
854 {
855 return true;
856 }
857
858 virtual bool WriteTableHeader(const char *name="DATA")
859 {
860 if (tellp()>0)
861 {
862#ifdef __EXCEPTIONS
863 throw std::runtime_error("File not empty anymore.");
864#else
865 gLog << ___err___ << "ERROR - File not empty anymore." << std::endl;
866 return false;
867#endif
868 }
869
870 fHeaderSum = WriteFitsHeader();
871
872 WriteDrsOffsetsTable();
873
874 if (!fManualExtName)
875 SetStr("EXTNAME", name);
876 SetInt("NAXIS1", fTable.bytes_per_row);
877 SetInt("TFIELDS", fTable.cols.size());
878
879 End();
880
881 WriteHeader();
882
883 WriteCatalog();
884
885 return good();
886 }
887
888 template<size_t N>
889 void revcpy(char *dest, const char *src, int num)
890 {
891 const char *pend = src + num*N;
892 for (const char *ptr = src; ptr<pend; ptr+=N, dest+=N)
893 std::reverse_copy(ptr, ptr+N, dest);
894 }
895
896 virtual uint32_t GetBytesPerRow() const { return fTable.bytes_per_row; }
897
898 virtual bool WriteRow(const void *ptr, size_t cnt, bool byte_swap=true)
899 {
900 // FIXME: Make sure that header was already written
901 // or write header now!
902 if (cnt!=fTable.bytes_per_row)
903 {
904 std::ostringstream sout;
905 sout << "WriteRow - Size " << cnt << " does not match expected size " << fTable.bytes_per_row;
906#ifdef __EXCEPTIONS
907 throw std::runtime_error(sout.str());
908#else
909 gLog << ___err___ << "ERROR - " << sout.str() << std::endl;
910 return false;
911#endif
912 }
913
914 // For the checksum we need everything to be correctly aligned
915 const uint8_t offset = fTable.offset%4;
916
917 char *buffer = fOutputBuffer.data() + offset;
918
919 auto ib = fOutputBuffer.begin();
920 auto ie = fOutputBuffer.rbegin();
921 *ib++ = 0;
922 *ib++ = 0;
923 *ib++ = 0;
924 *ib = 0;
925
926 *ie++ = 0;
927 *ie++ = 0;
928 *ie++ = 0;
929 *ie = 0;
930
931 if (!byte_swap)
932 memcpy(buffer, ptr, cnt);
933 else
934 {
935 for (auto it=fTable.cols.cbegin(); it!=fTable.cols.cend(); it++)
936 {
937 const char *src = reinterpret_cast<const char*>(ptr) + it->offset;
938 char *dest = buffer + it->offset;
939
940 // Let the compiler do some optimization by
941 // knowing the we only have 1, 2, 4 and 8
942 switch (it->size)
943 {
944 case 1: memcpy (dest, src, it->num*it->size); break;
945 case 2: revcpy<2>(dest, src, it->num); break;
946 case 4: revcpy<4>(dest, src, it->num); break;
947 case 8: revcpy<8>(dest, src, it->num); break;
948 }
949 }
950 }
951
952 write(buffer, cnt);
953 fDataSum.add(fOutputBuffer);
954
955 fTable.num_rows++;
956 fTable.offset += cnt;
957 return good();
958 }
959
960 template<typename N>
961 bool WriteRow(const std::vector<N> &vec)
962 {
963 return WriteRow(vec.data(), vec.size()*sizeof(N));
964 }
965
966 // Flushes the number of rows to the header on disk
967 virtual void FlushNumRows()
968 {
969 SetInt("NAXIS2", fTable.num_rows);
970 FlushHeader();
971 }
972
973 size_t GetNumRows() const { return fTable.num_rows; }
974
975 void AlignTo2880Bytes()
976 {
977 if (tellp()%(80*36)>0)
978 {
979 std::vector<char> filler(80*36-tellp()%(80*36));
980 write(filler.data(), filler.size());
981 }
982 }
983
984 Checksum UpdateHeaderChecksum()
985 {
986 std::ostringstream dataSumStr;
987 dataSumStr << fDataSum.val();
988 SetStr("DATASUM", dataSumStr.str());
989
990 const Checksum sum = WriteHeader();
991
992 //sum += headersum;
993
994 SetStr("CHECKSUM", (sum+fDataSum).str());
995
996 return WriteHeader();
997 }
998 virtual bool close()
999 {
1000 if (tellp()<0)
1001 return false;
1002
1003 AlignTo2880Bytes();
1004
1005 // We don't have to jump back to the end of the file
1006 SetInt("NAXIS2", fTable.num_rows);
1007
1008 const Checksum chk = UpdateHeaderChecksum();
1009
1010 if (!fFilebuf.close())
1011 setstate(ios_base::failbit);
1012
1013 if ((chk+fDataSum).valid())
1014 return true;
1015
1016 std::ostringstream sout;
1017 sout << "Checksum (" << std::hex << chk.val() << ") invalid.";
1018#ifdef __EXCEPTIONS
1019 throw std::runtime_error(sout.str());
1020#else
1021 gLog << ___err___ << "ERROR - " << sout.str() << std::endl;
1022 return false;
1023#endif
1024 }
1025
1026 std::pair<std::string, int> GetChecksumData()
1027 {
1028 std::string datasum;
1029 std::string checksum;
1030 //cannot directly use the Get methods, because they are only in fits.h
1031 for (std::vector<Key>::const_iterator it=fKeys.cbegin(); it!= fKeys.cend(); it++)
1032 {
1033 if (it->key == "CHECKSUM") checksum = it->value;
1034 if (it->key == "DATASUM") datasum = it->value;
1035 }
1036 if (checksum[0] == '\'')
1037 checksum = checksum.substr(1,checksum.size()-2);
1038 if (datasum[0] == '\'')
1039 datasum = datasum.substr(1, datasum.size()-2);
1040 return std::make_pair(checksum, atoi(datasum.c_str()));
1041 }
1042
1043 void SetDefaultKeys()
1044 {
1045 SetStr("TELESCOP", "FACT", "Telescope that acquired this data");
1046 SetStr("CREATOR", typeid(*this).name(), "Class that wrote this file");
1047 SetFloat("EXTREL", 1.0, "Release Number");
1048 SetStr("COMPILED", __DATE__" " __TIME__, "Compile time");
1049 SetStr("ORIGIN", "FACT", "Institution that wrote the file");
1050 SetStr("TIMESYS", "UTC", "Time system");
1051 SetStr("TIMEUNIT", "d", "Time given in days w.r.t. to MJDREF");
1052 SetInt("MJDREF", 40587, "MJD to UNIX time (seconds since 1970/1/1)");
1053 SetStr("PACKAGE", PACKAGE_NAME, "Package name");
1054 SetStr("VERSION", PACKAGE_VERSION, "Package description");
1055 SetStr("REVISION", REVISION, "SVN revision");
1056
1057 const time_t t0 = time(NULL);
1058 const struct tm *tmp1 = gmtime(&t0);
1059
1060 std::string str(19, '\0');
1061 if (tmp1 && strftime(const_cast<char*>(str.data()), 20, "%Y-%m-%dT%H:%M:%S", tmp1))
1062 SetStr("DATE", str, "File creation date");
1063 }
1064};
1065
1066#if 0
1067#include "fits.h"
1068
1069int main()
1070{
1071 using namespace std;
1072
1073 ofits h2("delme.fits");
1074
1075 h2.SetInt("KEY1", 1, "comment 1");
1076 h2.AddColumnInt(2, "testcol1", "counts", "My comment");
1077 h2.AddColumnInt("testcol2", "counts", "My comment");
1078 //h2.AddColumnInt("testcol2", "counts", "My comment");
1079 h2.SetInt("KEY2", 2, "comment 2");
1080
1081 /*
1082 AddFloat("X0", 0.000123456, "number of fields in each row");
1083 AddFloat("X1", 0, "number of fields in each row");
1084 AddFloat("X2", 12345, "number of fields in each row");
1085 AddFloat("X3", 123456.67890, "number of fields in each row");
1086 AddFloat("X4", 1234567890123456789.12345678901234567890, "number of fields in each row");
1087 AddFloat("X5", 1234567890.1234567890e20, "number of fields in each row");
1088 AddFloat("X6", 1234567890.1234567890e-20, "number of fields in each row");
1089 AddFloat("XB", 1234567890.1234567890e-111, "number of fields in each row");
1090 AddFloat("X7", 1e-5, "number of fields in each row");
1091 AddFloat("X8", 1e-6, "number of fields in each row");
1092 //AddStr("12345678", "123456789012345678", "12345678901234567890123456789012345678901234567");
1093 */
1094 // -
1095
1096 h2.WriteTableHeader("TABLE_NAME");
1097
1098 for (int i=0; i<10; i++)
1099 {
1100 int j[3] = { i+10, i*10, i*100 };
1101 h2.WriteRow(j, 3*sizeof(i));
1102 }
1103
1104 //h2.AddColumnInt("testcol2", "counts", "My comment");
1105 //h2.SetInt("KEY3", 2, "comment 2");
1106 h2.SetInt("KEY2", 2, "comment 2xxx");
1107 h2.SetInt("KEY1", 11);
1108
1109 h2.close();
1110
1111 cout << "---" << endl;
1112
1113 fits f("delme.fits");
1114 if (!f)
1115 throw std::runtime_error("xxx");
1116
1117 cout << "Header is valid: " << f.IsHeaderOk() << endl;
1118
1119 while (f.GetNextRow());
1120
1121 cout << "File is valid: " << f.IsFileOk() << endl;
1122
1123 cout << "---" << endl;
1124
1125 return 0;
1126}
1127#endif
1128
1129#endif
Note: See TracBrowser for help on using the repository browser.