source: trunk/FACT++/src/fitsdump.cc @ 12943

Last change on this file since 12943 was 12943, checked in by tbretz, 8 years ago
Implemented a root-mode which allows to use mathematical expressions instead of just column names.
File size: 28.0 KB
Line 
1//****************************************************************
2/** @class FitsDumper
3
4  @brief Dumps contents of fits tables to stdout or a file
5
6 */
7 //****************************************************************
8#include "Configuration.h"
9
10#include <float.h>
11
12#include <map>
13#include <fstream>
14
15#include <boost/regex.hpp>
16
17#include "Time.h"
18#include "externals/fits.h"
19
20#ifdef HAVE_ROOT
21#include "TFormula.h"
22#endif
23
24using namespace std;
25
26struct MyColumn
27{
28    string name;
29
30    fits::Table::Column col;
31
32    uint32_t first;
33    uint32_t last;
34
35    void *ptr;
36};
37
38struct minMaxStruct
39{
40    double min;
41    double max;
42    long double average;
43    long double squared;
44    long numValues;
45    minMaxStruct() : min(FLT_MAX), max(-FLT_MAX), average(0), squared(0), numValues(0) { }
46
47    void add(double val)
48    {
49        average += val;
50        squared += val*val;
51
52        if (val<min)
53            min = val;
54
55        if (val>max)
56            max = val;
57
58        numValues++;
59    }
60};
61
62
63class FitsDumper : public fits
64{
65private:
66    string fFilename;
67
68    // Convert CCfits::ValueType into a human readable string
69    string ValueTypeToStr(char type) const;
70
71    /// Lists all columns of an open file
72    void List();                         
73    void ListHeader(const string& filename);
74    void ListKeywords(ostream &);
75
76    vector<MyColumn> InitColumns(vector<string> list);
77    vector<MyColumn> InitColumnsRoot(vector<string> &list);
78
79    double GetDouble(const MyColumn &, size_t) const;
80
81    ///Display the selected columns values VS time
82    void Dump(ofstream &, const vector<MyColumn> &, const string &, size_t, size_t, const string &);
83    void DumpRoot(ofstream &, const vector<string> &, const string &, size_t, size_t, const string &);
84    void DumpMinMax(ofstream &, const vector<MyColumn> &, size_t, size_t, bool);
85    void DumpStats(ofstream &, const vector<MyColumn> &, size_t, size_t);
86
87public:
88    FitsDumper(const string &fname);
89
90    ///Configures the fitsLoader from the config file and/or command arguments.
91    int Exec(Configuration& conf);
92};
93
94// --------------------------------------------------------------------------
95//
96//! Constructor
97//! @param out
98//!        the ostream where to redirect the outputs
99//
100FitsDumper::FitsDumper(const string &fname) : fits(fname), fFilename(fname)
101{
102}
103
104string FitsDumper::ValueTypeToStr(char type) const
105{
106    switch (type)
107    {
108        case 'L': return "bool(8)";
109        case 'A': return "char(8)";
110        case 'B': return "byte(8)";
111        case 'I': return "short(16)";
112        case 'J': return "int(32)";
113        case 'K': return "int(64)";
114        case 'E': return "float(32)";
115        case 'D': return "double(64)";
116    default:
117        return "unknown";
118    }
119}
120
121void FitsDumper::List()
122{
123    const fits::Table::Keys    &fKeyMap = GetKeys();
124    const fits::Table::Columns &fColMap = GetColumns();
125
126    cout << "\nFile: " << fFilename << "\n";
127
128    cout << " " << fKeyMap.find("EXTNAME")->second.value << " [";
129    cout << fKeyMap.find("NAXIS2")->second.value << "]\n";
130
131    for (auto it = fColMap.begin(); it != fColMap.end(); it++)
132    {
133        cout << "   " << it->first << "[" << it->second.num << "] (" << it->second.unit << ":" << ValueTypeToStr(it->second.type) << ") ";
134        for (auto jt = fKeyMap.begin(); jt != fKeyMap.end(); jt++)
135            if (jt->second.value == it->first)
136                cout << "/ " << jt->second.comment << endl;
137    }
138
139    cout << endl;
140}
141
142void FitsDumper::ListKeywords(ostream &fout)
143{
144    const fits::Table::Keys &fKeyMap = GetKeys();
145
146    for (auto it=fKeyMap.begin(); it != fKeyMap.end(); it++)
147    {
148        fout << "## " << ::left << setw(8) << it->first << "= ";
149
150        if (it->second.type=='T')
151            fout << ::left  << setw(20) << ("'"+it->second.value+"'");
152        else
153            fout << ::right << setw(20) << it->second.value;
154
155        if (it->second.comment.size()>0)
156            fout << " / " << it->second.comment;
157        fout << '\n';
158    }
159
160    fout << flush;
161}
162
163void FitsDumper::ListHeader(const string& filename)
164{
165    ofstream fout(filename=="-"?"/dev/stdout":filename);
166    if (!fout)
167    {
168        cerr << "Cannot open file " << filename << ": " << strerror(errno) << endl;
169        return;
170    }
171
172    const fits::Table::Keys &fKeyMap = GetKeys();
173
174    fout << "\nTable: " << fKeyMap.find("EXTNAME")->second.value << " (rows=" << fKeyMap.find("NAXIS2")->second.value << ")\n";
175    if (fKeyMap.find("COMMENT") != fKeyMap.end())
176        fout << "Comment: \t" << fKeyMap.find("COMMENT")->second.value << "\n";
177
178    ListKeywords(fout);
179    fout << endl;
180}
181
182vector<MyColumn> FitsDumper::InitColumns(vector<string> names)
183{
184    static const boost::regex expr("([[:word:].]+)(\\[([[:digit:]]+)?(:)?([[:digit:]]+)?\\])?");
185
186    const fits::Table::Columns &fColMap = GetColumns();
187
188    if (names.size()==0)
189        for (auto it=fColMap.begin(); it!=fColMap.end(); it++)
190            if (it->second.num>0)
191                names.push_back(it->first);
192
193    vector<MyColumn> vec;
194
195    for (auto it=names.begin(); it!=names.end(); it++)
196    {
197        boost::smatch what;
198        if (!boost::regex_match(*it, what, expr, boost::match_extra))
199        {
200            cerr << "Couldn't parse expression '" << *it << "' " << endl;
201            return vector<MyColumn>();
202        }
203
204        const string name = what[1];
205
206        const auto iter = fColMap.find(name);
207        if (iter==fColMap.end())
208        {
209            cerr << "ERROR - Column '" << name << "' not found in table." << endl;
210            return vector<MyColumn>();
211        }
212
213        const fits::Table::Column &col = iter->second;
214
215        const string val0  = what[3];
216        const string delim = what[4];
217        const string val1  = what[5];
218
219        const uint32_t first = atol(val0.c_str());
220        const uint32_t last  = (val0.empty() && delim.empty()) ? col.num-1 : (val1.empty() ? first : atoi(val1.c_str()));
221
222        if (first>=col.num)
223        {
224            cerr << "ERROR - First index " << first << " for column " << name << " exceeds number of elements " << col.num << endl;
225            return vector<MyColumn>();
226        }
227
228        if (last>=col.num)
229        {
230            cerr << "ERROR - Last index " << last << " for column " << name << " exceeds number of elements " << col.num << endl;
231            return vector<MyColumn>();
232        }
233
234        if (first>last)
235        {
236            cerr << "ERROR - Last index " << last << " for column " << name << " exceeds first index " << first << endl;
237            return vector<MyColumn>();
238        }
239
240        MyColumn mycol;
241
242        mycol.name  = name;
243        mycol.col   = col;
244        mycol.first = first;
245        mycol.last  = last;
246        mycol.ptr   = SetPtrAddress(name);
247
248        vec.push_back(mycol);
249    }
250
251    return vec;
252}
253
254double FitsDumper::GetDouble(const MyColumn &it, size_t i) const
255{
256    switch (it.col.type)
257    {
258    case 'A':
259        return reinterpret_cast<const char*>(it.ptr)[i];
260
261    case 'L':
262        return reinterpret_cast<const bool*>(it.ptr)[i];
263
264    case 'B':
265        return (unsigned int)reinterpret_cast<const uint8_t*>(it.ptr)[i];
266
267    case 'I':
268        return reinterpret_cast<const int16_t*>(it.ptr)[i];
269
270    case 'J':
271        return reinterpret_cast<const int32_t*>(it.ptr)[i];
272
273    case 'K':
274        return reinterpret_cast<const int64_t*>(it.ptr)[i];
275
276    case 'E':
277        return reinterpret_cast<const float*>(it.ptr)[i];
278
279    case 'D':
280        return reinterpret_cast<const double*>(it.ptr)[i];
281    }
282
283    return 0;
284}
285
286// --------------------------------------------------------------------------
287//
288//! Perform the actual dump, based on the current parameters
289//
290void FitsDumper::Dump(ofstream &fout, const vector<MyColumn> &cols, const string &filter, size_t first, size_t limit, const string &filename)
291{
292    const fits::Table::Keys &fKeyMap = GetKeys();
293
294#ifdef HAVE_ROOT
295    TFormula select;
296    if (!filter.empty() && select.Compile(filter.c_str()))
297        throw runtime_error("Syntax Error: TFormula::Compile failed for '"+filter+"'");
298#endif
299
300    fout << "## --------------------------------------------------------------------------\n";
301    fout << "## Fits file:  \t" << fFilename << '\n';
302    if (filename!="-")
303        fout << "## File:      \t" << filename << '\n';
304    fout << "## Table:     \t" << fKeyMap.find("EXTNAME")->second.value << '\n';
305    fout << "## NumRows:   \t" << GetInt("NAXIS2") << '\n';
306    fout << "## Comment:   \t" << ((fKeyMap.find("COMMENT") != fKeyMap.end()) ? fKeyMap.find("COMMENT")->second.value : "") << '\n';
307#ifdef HAVE_ROOT
308    if (!filter.empty())
309        fout << "## Selection: \t" << select.GetExpFormula() << '\n';
310#endif
311    fout << "## --------------------------------------------------------------------------\n";
312    ListKeywords(fout);
313    fout << "## --------------------------------------------------------------------------\n";
314    fout << "#\n";
315
316    size_t num = 0;
317    for (auto it=cols.begin(); it!=cols.end(); it++)
318    {
319        fout << "# " << it->name;
320
321        if (it->first==it->last)
322        {
323            if (it->first!=0)
324                fout << "[" << it->first << "]";
325        }
326        else
327            fout << "[" << it->first << ":" << it->last << "]";
328
329        if (!it->col.unit.empty())
330            fout << ": " << it->col.unit;
331        fout << '\n';
332
333        num += it->last-it->first+1;
334    }
335    fout << "#" << endl;
336
337    // -----------------------------------------------------------------
338
339#ifdef HAVE_ROOT
340    vector<Double_t> data(num);
341#endif
342
343    const size_t last = limit ? first + limit : size_t(-1);
344
345    while (GetRow(first++))
346    {
347        const size_t row = GetRow();
348        if (row==GetNumRows() || row==last)
349            break;
350
351        size_t p = 0;
352
353#ifdef HAVE_ROOT
354        data[p++] = first-1;
355#endif
356
357        ostringstream out;
358        out.precision(fout.precision());
359        for (auto it=cols.begin(); it!=cols.end(); it++)
360        {
361            string msg;
362            for (uint32_t i=it->first; i<=it->last; i++, p++)
363            {
364                switch (it->col.type)
365                {
366                case 'A':
367                    msg += reinterpret_cast<const char*>(it->ptr)[i];
368                    break;
369                case 'B':
370                    out << (unsigned int)reinterpret_cast<const unsigned char*>(it->ptr)[i] << " ";
371                    break;
372                case 'L':
373                    out << reinterpret_cast<const bool*>(it->ptr)[i] << " ";
374                    break;
375                case 'I':
376                    out << reinterpret_cast<const int16_t*>(it->ptr)[i] << " ";
377                    break;
378                case 'J':
379                    out << reinterpret_cast<const int32_t*>(it->ptr)[i] << " ";
380                    break;
381                case 'K':
382                    out << reinterpret_cast<const int64_t*>(it->ptr)[i] << " ";
383                    break;
384                case 'E':
385                    out << reinterpret_cast<const float*>(it->ptr)[i] << " ";
386                    break;
387                case 'D':
388                    out << reinterpret_cast<const double*>(it->ptr)[i] << " ";
389                    break;
390                default:
391                    ;
392                }
393
394#ifdef HAVE_ROOT
395                if (!filter.empty())
396                    data[p] = GetDouble(*it, i);
397#endif
398            }
399
400            if (it->col.type=='A')
401                out << "'" << msg << "' ";
402        }
403#ifdef HAVE_ROOT
404        if (!filter.empty() && select.EvalPar(0, data.data())<0.5)
405            continue;
406#endif
407        fout << out.str() << endl;
408    }
409}
410
411vector<MyColumn> FitsDumper::InitColumnsRoot(vector<string> &names)
412{
413    static const boost::regex expr("[^\\[]([[:word:].]+)(\\[([[:digit:]]+)\\])?");
414
415    const fits::Table::Columns &cols = GetColumns();
416
417    vector<MyColumn> vec;
418
419    for (auto it=names.begin(); it!=names.end(); it++)
420    {
421        if (it->empty())
422            continue;
423
424        *it = ' '+*it;
425
426        string::const_iterator beg = it->begin();
427        string::const_iterator end = it->end();
428
429        boost::smatch what;
430        while (boost::regex_search(beg, end, what, expr, boost::match_extra))
431        {
432            const string all  = what[0];
433            const string name = what[1];
434            const size_t idx  = atol(string(what[3]).c_str());
435
436            // Check if found colum is valid
437            const auto ic = cols.find(name);
438            if (ic==cols.end())
439            {
440                beg++;
441                //cout << "Column '" << name << "' does not exist." << endl;
442                //return vector<MyColumn>();
443                continue;
444            }
445            if (idx>=ic->second.num)
446            {
447                cout << "Column '" << name << "' has no index " << idx << "." << endl;
448                return vector<MyColumn>();
449            }
450
451            // find index if column already exists
452            size_t p = 0;
453            for (; p<vec.size(); p++)
454                if (vec[p].name==name)
455                    break;
456
457            ostringstream id;
458            id << '[' << p << ']';
459
460            it->replace(beg-it->begin()+what.position(1), what.length()-1, id.str());
461
462            beg = what[0].first+3;
463            end = it->end();
464
465            if (p<vec.size())
466                continue;
467
468            // Column not found, add new column
469            MyColumn mycol;
470
471            mycol.name  = name;
472            mycol.col   = ic->second;
473            mycol.first = idx;
474            mycol.last  = idx;
475            mycol.ptr   = SetPtrAddress(name);
476
477            vec.push_back(mycol);
478        }
479    }
480
481    ostringstream id;
482    id << '[' << vec.size() << ']';
483
484    for (auto it=names.begin(); it!=names.end(); it++)
485    {
486        while (1)
487        {
488            auto p = it->find_first_of('#');
489            if (p==string::npos)
490                break;
491
492            it->replace(p, 1, id.str());
493        }
494    }
495
496    //cout << endl;
497    //for (size_t i=0; i<vec.size(); i++)
498    //    cout << "val[" << i << "] = " << vec[i].name << '[' << vec[i].first << ']' << endl;
499    //cout << endl;
500
501    return vec;
502}
503
504void FitsDumper::DumpRoot(ofstream &fout, const vector<string> &cols, const string &filter, size_t first, size_t limit, const string &filename)
505{
506#ifdef HAVE_ROOT
507    vector<string> names(cols);
508    names.insert(names.begin(), filter);
509
510    const vector<MyColumn> vec = InitColumnsRoot(names);
511    if (vec.size()==0)
512        return;
513
514    vector<TFormula> form(names.size());
515
516    auto ifo = form.begin();
517    for (auto it=names.begin(); it!=names.end(); it++, ifo++)
518    {
519        if (!it->empty() && ifo->Compile(it->c_str()))
520            throw runtime_error("Syntax Error: TFormula::Compile failed for '"+*it+"'");
521    }
522
523    const fits::Table::Keys &fKeyMap = GetKeys();
524
525    fout << "## --------------------------------------------------------------------------\n";
526    fout << "## Fits file:  \t" << fFilename << '\n';
527    if (filename!="-")
528        fout << "## File:      \t" << filename << '\n';
529    fout << "## Table:     \t" << fKeyMap.find("EXTNAME")->second.value << '\n';
530    fout << "## NumRows:   \t" << GetInt("NAXIS2") << '\n';
531    fout << "## Comment:   \t" << ((fKeyMap.find("COMMENT") != fKeyMap.end()) ? fKeyMap.find("COMMENT")->second.value : "") << '\n';
532    fout << "## --------------------------------------------------------------------------\n";
533    ListKeywords(fout);
534    fout << "## --------------------------------------------------------------------------\n";
535    fout << "##\n";
536    if (!filter.empty())
537        fout << "## Selection: " << form[0].GetExpFormula() << "\n##\n";
538
539    size_t num = 0;
540    for (auto it=vec.begin(); it!=vec.end(); it++, num++)
541    {
542        fout << "## [" << num << "] = " << it->name;
543
544        if (it->first==it->last)
545        {
546            if (it->first!=0)
547                fout << "[" << it->first << "]";
548        }
549        else
550            fout << "[" << it->first << ":" << it->last << "]";
551
552        if (!it->col.unit.empty())
553            fout << ": " << it->col.unit;
554        fout << '\n';
555    }
556    fout << "##\n";
557    fout << "## --------------------------------------------------------------------------\n";
558    fout << "#\n";
559
560    fout << "# ";
561    for (auto it=form.begin()+1; it!=form.end(); it++)
562        fout << " \"" << it->GetExpFormula() << "\"";
563    fout << "\n#" << endl;
564
565    // -----------------------------------------------------------------
566
567    vector<Double_t> data(vec.size()+1);
568
569    const size_t last = limit ? first + limit : size_t(-1);
570
571    while (GetRow(first++))
572    {
573        const size_t row = GetRow();
574        if (row==GetNumRows() || row==last)
575            break;
576
577        //data[p++] = first-1;
578
579        ostringstream out;
580        out.precision(fout.precision());
581
582        size_t p = 0;
583        for (auto it=vec.begin(); it!=vec.end(); it++, p++)
584            data[p] = GetDouble(*it, it->first);
585
586        data[p] = first;
587
588        if (!filter.empty() && form[0].EvalPar(0, data.data())<0.5)
589            continue;
590
591        for (auto iform=form.begin()+1; iform!=form.end(); iform++)
592            out << iform->EvalPar(0, data.data()) << " ";
593
594        fout << out.str() << endl;
595    }
596#endif
597}
598
599void FitsDumper::DumpMinMax(ofstream &fout, const vector<MyColumn> &cols, size_t first, size_t limit, bool fNoZeroPlease)
600{
601    vector<minMaxStruct> statData(cols.size());
602
603    // Loop over all columns in our list of requested columns
604    const size_t last = limit ? first + limit : size_t(-1);
605
606    while (GetRow(first++))
607    {
608        const size_t row = GetRow();
609        if (row==GetNumRows() || row==last)
610            break;
611
612        auto statsIt = statData.begin();
613
614        for (auto it=cols.begin(); it!=cols.end(); it++, statsIt++)
615        {
616            if ((it->name=="UnixTimeUTC" || it->name=="PCTime") && it->first==0 && it->last==1)
617            {
618                const uint32_t *val = reinterpret_cast<const uint32_t*>(it->ptr);
619                if (fNoZeroPlease && val[0]==0 && val[1]==0)
620                    continue;
621
622                statsIt->add(Time(val[0], val[1]).Mjd());
623                continue;
624            }
625
626            for (uint32_t i=it->first; i<=it->last; i++)
627            {
628                const double cValue = GetDouble(*it, i);
629
630                if (fNoZeroPlease && cValue == 0)
631                    continue;
632
633                statsIt->add(cValue);
634            }
635        }
636    }
637
638    // okay. So now I've got ALL the data, loaded.
639    // let's do the summing and averaging in a safe way (i.e. avoid overflow
640    // of variables as much as possible)
641    auto statsIt = statData.begin();
642    for (auto it=cols.begin(); it!=cols.end(); it++, statsIt++)
643    {
644        fout << "\n[" << it->name << ':' << it->first;
645        if (it->first!=it->last)
646            fout << ':' << it->last;
647        fout << "]\n";
648
649        if (statsIt->numValues == 0)
650        {
651            fout << "Min: -\nMax: -\nAvg: -\nRms: -" << endl;
652            continue;
653        }
654
655        const long &num = statsIt->numValues;
656
657        long double &avg = statsIt->average;
658        long double &rms = statsIt->squared;
659
660        avg /= num;
661        rms  = sqrt(rms/num - avg*avg);
662
663        fout << "Min: " << statsIt->min << '\n';
664        fout << "Max: " << statsIt->max << '\n';
665        fout << "Avg: " << avg << '\n';
666        fout << "Rms: " << rms << endl;
667    }
668}
669
670template<typename T>
671void displayStats(vector<char> &array, ofstream& out)
672{
673    const size_t numElems = array.size()/sizeof(T);
674    if (numElems == 0)
675    {
676        out << "Min: -\nMax: -\nMed: -\nAvg: -\nRms: -" << endl;
677        return;
678    }
679
680    T *val = reinterpret_cast<T*>(array.data());
681
682    sort(val, val+numElems);
683
684    out << "Min: " << double(val[0]) << '\n';
685    out << "Max: " << double(val[numElems-1]) << '\n';
686
687    if (numElems>2)
688    {
689        if (numElems%2 == 0)
690            out << "Med: " << (double(val[numElems/2]) + double(val[numElems/2+1]))/2 << '\n';
691        else
692            out << "Med: " << double(val[numElems/2+1]) << '\n';
693    }
694
695    long double avg = 0;
696    long double rms = 0;
697    for (uint32_t i=0;i<numElems;i++)
698    {
699        avg += double(val[i]);
700        rms += double(val[i])*double(val[i]);
701    }
702
703    avg /= numElems;
704    rms  = sqrt(rms/numElems - avg*avg);
705
706    out << "Avg: " << avg << '\n';
707    out << "Rms: " << rms << endl;
708
709}
710
711void FitsDumper::DumpStats(ofstream &fout, const vector<MyColumn> &cols, size_t first, size_t limit)
712{
713    // Loop over all columns in our list of requested columns
714    vector<vector<char>> statData;
715
716    const size_t num = limit==0 || GetNumRows()<limit ? GetNumRows() : limit;
717
718    for (auto it=cols.begin(); it!=cols.end(); it++)
719        statData.push_back(vector<char>(it->col.size*num*(it->last-it->first+1)));
720
721    // Loop over all columns in our list of requested columns
722    const size_t last = limit ? first + limit : size_t(-1);
723
724    while (GetRow(first++))
725    {
726        const size_t row = GetRow();
727        if (row==GetNumRows() || row==last)
728            break;
729
730
731        auto statsIt = statData.begin();
732        for (auto it=cols.begin(); it!=cols.end(); it++, statsIt++)
733        {
734            const char *src = reinterpret_cast<const char*>(it->ptr);
735            const size_t sz = (it->last-it->first+1)*it->col.size;
736            memcpy(statsIt->data()+row*sz, src+it->first*it->col.size, sz);
737        }
738    }
739
740    auto statsIt = statData.begin();
741    for (auto it=cols.begin(); it!=cols.end(); it++, statsIt++)
742    {
743        fout << "\n[" << it->name << ':' << it->first;
744        if (it->last!=it->first)
745            fout << ':' << it->last;
746        fout << "]\n";
747
748        switch (it->col.type)
749        {
750        case 'L':
751            displayStats<bool>(*statsIt, fout);
752            break;
753        case 'B':
754            displayStats<char>(*statsIt, fout);
755            break;
756        case 'I':
757            displayStats<int16_t>(*statsIt, fout);
758            break;
759        case 'J':
760            displayStats<int32_t>(*statsIt, fout);
761            break;
762        case 'K':
763            displayStats<int64_t>(*statsIt, fout);
764            break;
765        case 'E':
766            displayStats<float>(*statsIt, fout);
767            break;
768        case 'D':
769            displayStats<double>(*statsIt, fout);
770            break;
771        default:
772            ;
773        }
774    }
775}
776
777// --------------------------------------------------------------------------
778//
779//! Retrieves the configuration parameters
780//! @param conf
781//!             the configuration object
782//
783int FitsDumper::Exec(Configuration& conf)
784{
785    if (conf.Get<bool>("list"))
786        List();
787
788    if (conf.Get<bool>("header"))
789        ListHeader(conf.Get<string>("outfile"));
790
791
792    if (conf.Get<bool>("header") || conf.Get<bool>("list"))
793        return 1;
794
795    // ------------------------------------------------------------
796
797    if (conf.Get<bool>("minmax") && conf.Get<bool>("stat"))
798    {
799        cerr << "Invalid combination of options: cannot do stats and minmax. Aborting" << endl;
800        return -1;
801    }
802    if (conf.Get<bool>("stat") && conf.Get<bool>("nozero"))
803    {
804        cerr << "Invalid combination of options: nozero only works with minmax. Aborting" << endl;
805        return -1;
806    }
807
808    // ------------------------------------------------------------
809
810    const string filename = conf.Get<string>("outfile");
811
812    ofstream fout(filename=="-"?"/dev/stdout":filename);
813    if (!fout)
814    {
815        cerr << "Cannot open file " << filename << ": " << strerror(errno) << endl;
816        return false;
817    }
818    fout.precision(conf.Get<int>("precision"));
819
820    const string filter = conf.Has("filter") ? conf.Get<string>("filter") : "";
821
822    const size_t first  = conf.Get<size_t>("first");
823    const size_t limit  = conf.Get<size_t>("limit");
824
825    if (conf.Get<bool>("root"))
826    {
827        DumpRoot(fout, conf.Vec<string>("col"), filter, first, limit, filename);
828        return 0;
829    }
830
831    const vector<MyColumn> cols = InitColumns(conf.Vec<string>("col"));
832    if (cols.size()==0)
833        return false;
834
835
836    if (conf.Get<bool>("minmax"))
837    {
838        DumpMinMax(fout, cols, first, limit, conf.Get<bool>("nozero"));
839        return 0;
840    }
841
842    if (conf.Get<bool>("stat"))
843    {
844        DumpStats(fout, cols, first, limit);
845        return 0;
846    }
847
848    Dump(fout, cols, filter, first, limit, filename);
849
850    return 0;
851}
852
853void PrintUsage()
854{
855    cout <<
856        "fitsdump is a tool to dump data from a FITS table as ascii.\n"
857        "\n"
858        "Usage: fitsdump [OPTIONS] fitsfile col col ... \n"
859        "  or:  fitsdump [OPTIONS]\n";
860
861    cout << endl;
862}
863
864void PrintHelp()
865{
866    cout <<
867        "\n"
868        "To address a column several syntax is possible:\n"
869        "  ColumnName:        Will address all fields of a column\n"
870        "  ColumnName[n]:     Will address the n-th field of a column (starts with 0)\n"
871        "  ColumnName[n1:n2]: Will address all fields between n1 and including n2\n"
872#ifdef HAVE_ROOT
873        "\n"
874        "To select some columns can use --filter\n"
875        "Such a selction is evaluated using TFormula, hence, every "
876        "mathematical operation allowed in TFormula is allowed there, too. "
877        "The reference is the column index as printed in the output stream, "
878        "starting with 1. The index 0 is reserved for the row number.\n"
879        "\n"
880        "Example:\n"
881        "  fitsdump Zd --filter=\"[0]>20 && cos([1])*TMath::RadToDeg()<45\"\n"
882        "\n"
883        "In --root mode, fitsdump support TFormula's syntax for all columns and the filter "
884        "You can then refer to a column or a (single) index of the column just by its name "
885        "If the index is omitted, 0 is assumed\n"
886        "\n"
887
888        "Example:\n"
889        "  fitsdump \"(Zd+Err)*TMath::DegToRad()\" --filter=\"Num>100 && Num<200\"\n"
890        "is identical to\n"
891        "  fitsdump \"(Zd[0]+Err[0])*TMath::DegToRad()\" --filter=\"Num[0]>100 && Num[0]<200\"\n"
892        "A special placeholder exists for the row number:\n"
893        "  fitsdump \"#\" --filter=\"#>10 && #<100\"\n"
894#endif
895        "\n";
896    cout << endl;
897}
898
899void SetupConfiguration(Configuration& conf)
900{
901    po::options_description configs("Fitsdump options");
902    configs.add_options()
903        ("fitsfile,f",  var<string>()
904#if BOOST_VERSION >= 104200
905         ->required()
906#endif
907                                                  , "Name of FITS file")
908        ("col,c",       vars<string>(),             "List of columns to dump\narg is a list of columns, separated by a space.\nAdditionnally, a list of sub-columns can be added\ne.g. Data[3] will dump sub-column 3 of column Data\nData[3:4] will dump sub-columns 3 and 4\nOmitting this argument dump the entire column\nnota: all indices start at zero")
909        ("outfile,o",   var<string>("/dev/stdout"), "Name of output file (-:/dev/stdout)")
910        ("precision,p", var<int>(20),               "Precision of ofstream")
911        ("list,l",      po_switch(),                "List all tables and columns in file")
912        ("header,h",    po_switch(),                "Dump header of given table")
913        ("stat,s",      po_switch(),                "Perform statistics instead of dump")
914        ("minmax,m",    po_switch(),                "Calculates min and max of data")
915        ("nozero,z",    po_switch(),                "skip 0 values for stats")
916        ("force",       po_switch(),                "Force reading the fits file even if END key is missing")
917        ("first",       var<size_t>(size_t(0)),     "First number of row to read")
918        ("limit",       var<size_t>(size_t(0)),     "Limit for the maximum number of rows to read (0=unlimited)")
919#ifdef HAVE_ROOT
920        ("root,r",      po_switch(),                "Enable root mode")
921        ("filter,r",    var<string>(""),            "Filter to restrict the selection of events (e.g. '[0]>10 && [0]<20';  does not work with stat and minmax yet)")
922#endif
923        ;
924
925    po::positional_options_description p;
926    p.add("fitsfile",  1); // The first positional options
927    p.add("col",      -1); // All others
928
929    conf.AddOptions(configs);
930    conf.SetArgumentPositions(p);
931}
932
933int main(int argc, const char** argv)
934{
935    Configuration conf(argv[0]);
936    conf.SetPrintUsage(PrintUsage);
937    SetupConfiguration(conf);
938
939    if (!conf.DoParse(argc, argv, PrintHelp))
940        return -1;
941
942    if (!conf.Has("fitsfile"))
943    {
944        cerr << "Filename required." << endl;
945        return -1;
946    }
947
948    FitsDumper loader(conf.Get<string>("fitsfile"));
949    if (!loader)
950    {
951        cerr << "ERROR - Opening " << conf.Get<string>("fitsfile");
952        cerr << " failed: " << strerror(errno) << endl;
953        return -1;
954    }
955
956    return loader.Exec(conf);
957}
Note: See TracBrowser for help on using the repository browser.