source: fact/tools/pyscripts/pyfact/fits.h@ 13412

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