source: trunk/Mars/mcore/fits.h@ 12918

Last change on this file since 12918 was 12845, checked in by tbretz, 13 years ago
Implemented 'A' as 8-bit characters; in SetPtrAddress: return an existing pointer if set already.
File size: 22.9 KB
Line 
1#ifndef MARS_fits
2#define MARS_fits
3
4#include <stdint.h>
5
6#include <map>
7#include <string>
8#include <sstream>
9#include <algorithm>
10
11#ifdef __EXCEPTIONS
12#include <stdexcept>
13#endif
14
15#ifdef __CINT__
16#define off_t size_t
17#else
18#include <unordered_map>
19#endif
20
21#ifndef __MARS__
22#include <vector>
23#include <iomanip>
24#include <iostream>
25#define gLog cerr
26#define ___err___ ""
27#define ___all___ ""
28#else
29#include "MLog.h"
30#include "MLogManip.h"
31#define ___err___ err
32#define ___all___ all
33#endif
34
35#ifdef HAVE_ZLIB
36#include "izstream.h"
37#else
38#include <fstream>
39#define izstream ifstream
40#warning Support for zipped FITS files disabled.
41#endif
42
43namespace std
44{
45
46class fits : public izstream
47{
48public:
49 struct Entry
50 {
51 char type;
52 string value;
53 string comment;
54
55 template<typename T>
56 T Get() const
57 {
58 T t;
59
60 istringstream str(value);
61 str >> t;
62
63 return t;
64 }
65 };
66
67 struct Table
68 {
69 off_t offset;
70
71 string name;
72 size_t bytes_per_row;
73 size_t num_rows;
74 size_t num_cols;
75
76 struct Column
77 {
78 size_t offset;
79 size_t num;
80 size_t size;
81 char type;
82 string unit;
83 };
84
85 typedef map<string, Entry> Keys;
86 typedef map<string, Column> Columns;
87
88 Columns cols;
89 Keys keys;
90
91 string Trim(const string &str, char c=' ') const
92 {
93 // Trim Both leading and trailing spaces
94 const size_t pstart = str.find_first_not_of(c); // Find the first character position after excluding leading blank spaces
95 const size_t pend = str.find_last_not_of(c); // Find the first character position from reverse af
96
97 // if all spaces or empty return an empty string
98 if (string::npos==pstart || string::npos==pend)
99 return string();
100
101 return str.substr(pstart, pend-pstart+1);
102 }
103
104 bool Check(const string &key, char type, const string &value="") const
105 {
106 const Keys::const_iterator it = keys.find(key);
107 if (it==keys.end())
108 {
109 ostringstream str;
110 str << "Key '" << key << "' not found.";
111#ifdef __EXCEPTIONS
112 throw runtime_error(str.str());
113#else
114 gLog << ___err___ << "ERROR - " << str.str() << endl;
115 return false;
116#endif
117 }
118
119 if (it->second.type!=type)
120 {
121 ostringstream str;
122 str << "Wrong type for key '" << key << "': expected " << type << ", found " << it->second.type << ".";
123#ifdef __EXCEPTIONS
124 throw runtime_error(str.str());
125#else
126 gLog << ___err___ << "ERROR - " << str.str() << endl;
127 return false;
128#endif
129 }
130
131 if (!value.empty() && it->second.value!=value)
132 {
133 ostringstream str;
134 str << "Wrong value for key '" << key << "': expected " << value << ", found " << it->second.value << ".";
135#ifdef __EXCEPTIONS
136 throw runtime_error(str.str());
137#else
138 gLog << ___err___ << "ERROR - " << str.str() << endl;
139 return false;
140#endif
141 }
142
143 return true;
144 }
145
146 Keys ParseBlock(const vector<string> &vec) const
147 {
148 map<string,Entry> rc;
149
150 for (unsigned int i=0; i<vec.size(); i++)
151 {
152 const string key = Trim(vec[i].substr(0,8));
153 // Keywords without a value, like COMMENT / HISTORY
154 if (vec[i].substr(8,2)!="= ")
155 continue;
156
157 char type = 0;
158
159 string com;
160 string val = Trim(vec[i].substr(10));
161 if (val[0]=='\'')
162 {
163 // First skip all '' in the string
164 size_t p = 1;
165 while (1)
166 {
167 const size_t pp = val.find_first_of('\'', p);
168 if (pp==string::npos)
169 break;
170
171 p = val[pp+1]=='\'' ? pp+2 : pp+1;
172 }
173
174 // Now find the comment
175 const size_t ppp = val.find_first_of('/', p);
176
177 // Set value, comment and type
178 com = ppp==string::npos ? "" : Trim(val.substr(ppp+1));
179 val = Trim(val.substr(1, p-2));
180 type = 'T';
181 }
182 else
183 {
184 const size_t p = val.find_first_of('/');
185
186 com = Trim(val.substr(p+2));
187 val = Trim(val.substr(0, p));
188
189 if (val.empty() || val.find_first_of('T')!=string::npos || val.find_first_of('F')!=string::npos)
190 type = 'B';
191 else
192 type = val.find_last_of('.')==string::npos ? 'I' : 'F';
193 }
194
195 const Entry e = { type, val, com };
196 rc[key] = e;
197 }
198
199 return rc;
200 }
201
202 Table() : offset(0) { }
203 Table(const vector<string> &vec, off_t off) :
204 offset(off), keys(ParseBlock(vec))
205 {
206 if (!Check("XTENSION", 'T', "BINTABLE") ||
207 !Check("NAXIS", 'I', "2") ||
208 !Check("BITPIX", 'I', "8") ||
209 !Check("PCOUNT", 'I', "0") ||
210 !Check("GCOUNT", 'I', "1") ||
211 !Check("EXTNAME", 'T') ||
212 !Check("NAXIS1", 'I') ||
213 !Check("NAXIS2", 'I') ||
214 !Check("TFIELDS", 'I'))
215 return;
216
217 bytes_per_row = Get<size_t>("NAXIS1");
218 num_rows = Get<size_t>("NAXIS2");
219 num_cols = Get<size_t>("TFIELDS");
220
221 size_t bytes = 0;
222 for (size_t i=1; i<=num_cols; i++)
223 {
224 ostringstream num;
225 num << i;
226
227 if (!Check("TTYPE"+num.str(), 'T') ||
228 !Check("TFORM"+num.str(), 'T'))
229 return;
230
231 const string id = Get<string>("TTYPE"+num.str());
232 const string fmt = Get<string>("TFORM"+num.str());
233 const string unit = Get<string>("TUNIT"+num.str(), "");
234
235 istringstream sin(fmt);
236 int n = 0;
237 sin >> n;
238 if (!sin)
239 n = 1;
240
241 const char type = fmt[fmt.length()-1];
242
243 size_t size = 0;
244 switch (type)
245 {
246 // We could use negative values to mark floats
247 // otheriwse we could just cast them to int64_t?
248 case 'L': // logical
249 case 'A': // char
250 case 'B': size = 1; break; // byte
251 case 'I': size = 2; break; // short
252 case 'J': size = 4; break; // int
253 case 'K': size = 8; break; // long long
254 case 'E': size = 4; break; // float
255 case 'D': size = 8; break; // double
256 // case 'X': size = n; break; // bits (n=number of bytes needed to contain all bits)
257 // case 'C': size = 8; break; // complex float
258 // case 'M': size = 16; break; // complex double
259 // case 'P': size = 8; break; // array descriptor (32bit)
260 // case 'Q': size = 16; break; // array descriptor (64bit)
261 default:
262 {
263 ostringstream str;
264 str << "FITS format TFORM='" << fmt << "' not yet supported.";
265#ifdef __EXCEPTIONS
266 throw runtime_error(str.str());
267#else
268 gLog << ___err___ << "ERROR - " << str.str() << endl;
269 return;
270#endif
271 }
272 }
273
274 const Table::Column col = { bytes, n, size, type, unit };
275
276 cols[id] = col;
277 bytes += n*size;
278 }
279
280 if (bytes!=bytes_per_row)
281 {
282#ifdef __EXCEPTIONS
283 throw runtime_error("Column size mismatch");
284#else
285 gLog << ___err___ << "ERROR - Column size mismatch" << endl;
286 return;
287#endif
288 }
289
290 name = Get<string>("EXTNAME");
291 }
292
293 void PrintKeys(bool display_all=false) const
294 {
295 for (Keys::const_iterator it=keys.begin(); it!=keys.end(); it++)
296 {
297 if (!display_all &&
298 (it->first.substr(0, 6)=="TTYPE" ||
299 it->first.substr(0, 6)=="TFORM" ||
300 it->first.substr(0, 6)=="TUNIT" ||
301 it->first=="TFIELDS" ||
302 it->first=="XTENSION" ||
303 it->first=="NAXIS" ||
304 it->first=="BITPIX" ||
305 it->first=="PCOUNT" ||
306 it->first=="GCOUNT")
307 )
308 continue;
309
310 gLog << ___all___ << setw(2) << it->second.type << '|' << it->first << '=' << it->second.value << '/' << it->second.comment << '|' << endl;
311 }}
312
313 void PrintColumns() const
314 {
315 typedef map<pair<size_t, string>, Column> Sorted;
316
317 Sorted sorted;
318
319 for (Columns::const_iterator it=cols.begin(); it!=cols.end(); it++)
320 sorted[make_pair(it->second.offset, it->first)] = it->second;
321
322 for (Sorted::const_iterator it=sorted.begin(); it!=sorted.end(); it++)
323 {
324 gLog << ___all___ << setw(6) << it->second.offset << "| ";
325 gLog << it->second.num << 'x';
326 switch (it->second.type)
327 {
328 case 'A': gLog << "char(8)"; break;
329 case 'L': gLog << "bool(8)"; break;
330 case 'B': gLog << "byte(8)"; break;
331 case 'I': gLog << "short(16)"; break;
332 case 'J': gLog << "int(32)"; break;
333 case 'K': gLog << "int(64)"; break;
334 case 'E': gLog << "float(32)"; break;
335 case 'D': gLog << "double(64)"; break;
336 }
337 gLog << ": " << it->first.second << " [" << it->second.unit << "]" << endl;
338 }
339 }
340
341 operator bool() const { return !name.empty(); }
342
343 bool HasKey(const string &key) const
344 {
345 return keys.find(key)!=keys.end();
346 }
347
348 bool HasColumn(const string& col) const
349 {
350 return cols.find(col)!=cols.end();
351 }
352
353 const Columns &GetColumns() const
354 {
355 return cols;
356 }
357
358 const Keys &GetKeys() const
359 {
360 return keys;
361 }
362
363 // Values of keys are always signed
364 template<typename T>
365 T Get(const string &key) const
366 {
367 const map<string,Entry>::const_iterator it = keys.find(key);
368 if (it==keys.end())
369 {
370 ostringstream str;
371 str << "Key '" << key << "' not found." << endl;
372#ifdef __EXCEPTIONS
373 throw runtime_error(str.str());
374#else
375 gLog << ___err___ << "ERROR - " << str.str() << endl;
376 return T();
377#endif
378 }
379 return it->second.Get<T>();
380 }
381
382 // Values of keys are always signed
383 template<typename T>
384 T Get(const string &key, const string &deflt) const
385 {
386 const map<string,Entry>::const_iterator it = keys.find(key);
387 return it==keys.end() ? deflt :it->second.Get<T>();
388 }
389
390 size_t GetN(const string &key) const
391 {
392 const Columns::const_iterator it = cols.find(key);
393 return it==cols.end() ? 0 : it->second.num;
394 }
395 };
396
397private:
398 Table fTable;
399
400 typedef pair<void*, Table::Column> Address;
401 typedef vector<Address> Addresses;
402 //map<void*, Table::Column> fAddresses;
403 Addresses fAddresses;
404
405#ifdef __CINT__
406 typedef map<string, void*> Pointers;
407#else
408 typedef unordered_map<string, void*> Pointers;
409#endif
410 Pointers fPointers;
411
412 vector<vector<char>> fGarbage;
413
414 vector<char> fBufferRow;
415 vector<char> fBufferDat;
416
417 size_t fRow;
418
419 bool ReadBlock(vector<string> &vec)
420 {
421 int endtag = 0;
422 for (int i=0; i<36; i++)
423 {
424 char c[81];
425 c[80] = 0;
426 read(c, 80);
427 if (!good())
428 break;
429
430// if (c[0]==0)
431// return vector<string>();
432
433 string str(c);
434
435// if (!str.empty())
436// cout << setw(2) << i << "|" << str << "|" << (endtag?'-':'+') << endl;
437
438 if (endtag==2 || str=="END ")
439 {
440 endtag = 2; // valid END tag found
441 continue;
442 }
443
444 if (endtag==1 || str==" ")
445 {
446 endtag = 1; // end tag not found, but expected to be there
447 continue;
448 }
449
450 vec.push_back(str);
451 }
452
453 // Make sure that no empty vector is returned
454 if (endtag && vec.size()%36==0)
455 vec.push_back(string("END = '' / "));
456
457 return endtag==2;
458 }
459
460 string Compile(const string &key, int16_t i=-1)
461 {
462 if (i<0)
463 return key;
464
465 ostringstream str;
466 str << key << i;
467 return str.str();
468 }
469
470public:
471 fits(const string &fname, bool force=false) : izstream(fname.c_str())
472 {
473 char simple[10];
474 read(simple, 10);
475 if (!good())
476 return;
477
478 if (memcmp(simple, "SIMPLE = ", 10))
479 {
480 clear(rdstate()|ios::badbit);
481#ifdef __EXCEPTIONS
482 throw runtime_error("File is not a FITS file.");
483#else
484 gLog << ___err___ << "ERROR - File is not a FITS file." << endl;
485 return;
486#endif
487 }
488
489 seekg(0);
490
491 while (good())
492 {
493 vector<string> block;
494 while (1)
495 {
496 // FIXME: Set limit on memory consumption
497 const int rc = ReadBlock(block);
498 if (!good())
499 {
500 clear(rdstate()|ios::badbit);
501#ifdef __EXCEPTIONS
502 throw runtime_error("FITS file corrupted.");
503#else
504 gLog << ___err___ << "ERROR - FITS file corrupted." << endl;
505 return;
506#endif
507 }
508
509 if (block.size()%36)
510 {
511 if (!rc && !force)
512 {
513 clear(rdstate()|ios::badbit);
514#ifdef __EXCEPTIONS
515 throw runtime_error("END keyword missing in FITS header.");
516#else
517 gLog << ___err___ << "ERROR - END keyword missing in FITS file... file might be corrupted." << endl;
518 return;
519#endif
520 }
521 break;
522 }
523 }
524
525 if (block.size()==0)
526 break;
527
528 if (block[0].substr(0, 9)=="SIMPLE =")
529 continue;
530
531 if (block[0].substr(0, 9)=="XTENSION=")
532 {
533 // FIXME: Check for table name
534
535 fTable = Table(block, tellg());
536 fRow = (size_t)-1;
537
538 //fTable.PrintKeys();
539
540 if (!fTable)
541 {
542 clear(rdstate()|ios::badbit);
543 return;
544 }
545
546 fBufferRow.resize(fTable.bytes_per_row);
547 fBufferDat.resize(fTable.bytes_per_row);
548
549 /*
550 // Next table should start at:
551 const size_t size = fTable.bytes_per_row*fTable.num_rows;
552 const size_t blks = size/(36*80);
553 const size_t rest = size%(36*80);
554
555 seekg((blks+(rest>0?1:0))*(36*80), ios::cur);
556 if (!good())
557 gLog << ___err___ << "File seems to be incomplete (less data than expected from header)." << endl;
558
559 fRow = fTable.num_rows;
560 */
561
562 break;
563 }
564 }
565 }
566
567 void ReadRow(size_t row)
568 {
569 // if (row!=fRow+1) // Fast seeking is ensured by izstream
570 seekg(fTable.offset+row*fTable.bytes_per_row);
571
572 fRow = row;
573
574 read(fBufferRow.data(), fBufferRow.size());
575 //fin.clear(fin.rdstate()&~ios::eofbit);
576 }
577
578 template<size_t N>
579 void revcpy(char *dest, const char *src, int num)
580 {
581 const char *pend = src + num*N;
582 for (const char *ptr = src; ptr<pend; ptr+=N, dest+=N)
583 reverse_copy(ptr, ptr+N, dest);
584 }
585
586 bool GetRow(size_t row)
587 {
588 ReadRow(row);
589 if (!good())
590 return good();
591
592 for (Addresses::const_iterator it=fAddresses.begin(); it!=fAddresses.end(); it++)
593 {
594 const Table::Column &c = it->second;
595
596 const char *src = fBufferRow.data() + c.offset;
597 char *dest = reinterpret_cast<char*>(it->first);
598
599 // Let the compiler do some optimization by
600 // knowing the we only have 1, 2, 4 and 8
601 switch (c.size)
602 {
603 case 1: memcpy (dest, src, c.num*c.size); break;
604 case 2: revcpy<2>(dest, src, c.num); break;
605 case 4: revcpy<4>(dest, src, c.num); break;
606 case 8: revcpy<8>(dest, src, c.num); break;
607 }
608 }
609
610 return good();
611 }
612
613 bool GetNextRow()
614 {
615 return GetRow(fRow+1);
616 }
617
618 bool SkipNextRow()
619 {
620 seekg(fTable.offset+(++fRow)*fTable.bytes_per_row);
621 return good();
622 }
623
624 static bool Compare(const Address &p1, const Address &p2)
625 {
626 return p1.first>p2.first;
627 }
628
629 template<class T, class S>
630 const T &GetAs(const string &name)
631 {
632 return *reinterpret_cast<S*>(fPointers[name]);
633 }
634
635 void *SetPtrAddress(const string &name)
636 {
637 if (fTable.cols.count(name)==0)
638 {
639 ostringstream str;
640 str <<"SetPtrAddress('" << name << "') - Column not found." << endl;
641#ifdef __EXCEPTIONS
642 throw runtime_error(str.str());
643#else
644 gLog << ___err___ << "ERROR - " << str.str() << endl;
645 return NULL;
646#endif
647 }
648
649 Pointers::const_iterator it = fPointers.find(name);
650 if (it!=fPointers.end())
651 return it->second;
652
653 fGarbage.push_back(vector<char>(fTable.cols[name].size*fTable.cols[name].num));
654
655 void *ptr = fGarbage.back().data();
656
657 fPointers[name] = ptr;
658 fAddresses.push_back(make_pair(ptr, fTable.cols[name]));
659 sort(fAddresses.begin(), fAddresses.end(), Compare);
660 return ptr;
661 }
662
663 template<typename T>
664 bool SetPtrAddress(const string &name, T *ptr, size_t cnt)
665 {
666 if (fTable.cols.count(name)==0)
667 {
668 ostringstream str;
669 str << "SetPtrAddress('" << name << "') - Column not found." << endl;
670#ifdef __EXCEPTIONS
671 throw runtime_error(str.str());
672#else
673 gLog << ___err___ << "ERROR - " << str.str() << endl;
674 return false;
675#endif
676 }
677
678 if (sizeof(T)!=fTable.cols[name].size)
679 {
680 ostringstream str;
681 str << "SetPtrAddress('" << name << "') - Element size mismatch: expected "
682 << fTable.cols[name].size << " from header, got " << sizeof(T) << endl;
683#ifdef __EXCEPTIONS
684 throw runtime_error(str.str());
685#else
686 gLog << ___err___ << "ERROR - " << str.str() << endl;
687 return false;
688#endif
689 }
690
691 if (cnt!=fTable.cols[name].num)
692 {
693 ostringstream str;
694 str << "SetPtrAddress('" << name << "') - Element count mismatch: expected "
695 << fTable.cols[name].num << " from header, got " << cnt << endl;
696#ifdef __EXCEPTIONS
697 throw runtime_error(str.str());
698#else
699 gLog << ___err___ << "ERROR - " << str.str() << endl;
700 return false;
701#endif
702 }
703
704 // if (fAddresses.count(ptr)>0)
705 // gLog << warn << "SetPtrAddress('" << name << "') - Pointer " << ptr << " already assigned." << endl;
706
707 //fAddresses[ptr] = fTable.cols[name];
708 fPointers[name] = ptr;
709 fAddresses.push_back(make_pair(ptr, fTable.cols[name]));
710 sort(fAddresses.begin(), fAddresses.end(), Compare);
711 return true;
712 }
713
714 template<class T>
715 bool SetRefAddress(const string &name, T &ptr)
716 {
717 return SetPtrAddress(name, &ptr, sizeof(ptr)/sizeof(T));
718 }
719
720 template<typename T>
721 bool SetVecAddress(const string &name, vector<T> &vec)
722 {
723 return SetPtrAddress(name, vec.data(), vec.size());
724 }
725
726 template<typename T>
727 T Get(const string &key) const
728 {
729 return fTable.Get<T>(key);
730 }
731
732 template<typename T>
733 T Get(const string &key, const string &deflt) const
734 {
735 return fTable.Get<T>(key, deflt);
736 }
737
738 bool SetPtrAddress(const string &name, void *ptr)
739 {
740 if (fTable.cols.count(name)==0)
741 {
742 ostringstream str;
743 str <<"SetPtrAddress('" << name << "') - Column not found." << endl;
744#ifdef __EXCEPTIONS
745 throw runtime_error(str.str());
746#else
747 gLog << ___err___ << "ERROR - " << str.str() << endl;
748 return false;
749#endif
750 }
751
752 // if (fAddresses.count(ptr)>0)
753 // gLog << warn << "SetPtrAddress('" << name << "') - Pointer " << ptr << " already assigned." << endl;
754
755 //fAddresses[ptr] = fTable.cols[name];
756 fPointers[name] = ptr;
757 fAddresses.push_back(make_pair(ptr, fTable.cols[name]));
758 sort(fAddresses.begin(), fAddresses.end(), Compare);
759 return true;
760 }
761
762 bool HasKey(const string &key) const { return fTable.HasKey(key); }
763 bool HasColumn(const string& col) const { return fTable.HasColumn(col);}
764 const Table::Columns &GetColumns() const { return fTable.GetColumns();}
765 const Table::Keys &GetKeys() const { return fTable.GetKeys();}
766
767 int64_t GetInt(const string &key) const { return fTable.Get<int64_t>(key); }
768 uint64_t GetUInt(const string &key) const { return fTable.Get<uint64_t>(key); }
769 double GetFloat(const string &key) const { return fTable.Get<double>(key); }
770 string GetStr(const string &key) const { return fTable.Get<string>(key); }
771
772 size_t GetN(const string &key) const
773 {
774 return fTable.GetN(key);
775 }
776
777 size_t GetNumRows() const { return fTable.num_rows; }
778 size_t GetRow() const { return fRow; }
779
780 operator bool() const { return fTable && fTable.offset!=0; }
781
782 void PrintKeys() const { fTable.PrintKeys(); }
783 void PrintColumns() const { fTable.PrintColumns(); }
784};
785
786};
787#endif
Note: See TracBrowser for help on using the repository browser.