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

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