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

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