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

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