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

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