source: branches/MarsWobble/mcore/ofits.h@ 18168

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