//**************************************************************** /** @class FitsDumper @brief Dumps contents of fits tables to stdout or a file */ //**************************************************************** #include "Configuration.h" #include #include #include using namespace std; class FitsDumper { public: FitsDumper(); ~FitsDumper(); private: CCfits::FITS *fFile; /// FITS pointer CCfits::Table *fTable; /// Table pointer map fColMap; /// map between the column names and their CCfits objects // Convert CCfits::ValueType into a human readable string string ValueTypeToStr(CCfits::ValueType type) const; // Convert CCfits::ValueType into a number of associated bytes int ValueTypeToSize(CCfits::ValueType type) const; /// Calculate the buffer size required to read a row of the fits table, as well as the offsets to each column vector CalculateOffsets() const; template T PtrToValue(const unsigned char* &ptr) const; // template // double PtrToDouble(const unsigned char *ptr) const; // double PtrToDouble(const unsigned char *ptr, CCfits::ValueType type) const; /// Write a single row of the selected data int WriteRow(ostream &, const vector &, const vector &, unsigned char *) const; bool OpenFile(const string &); /// Open a file bool OpenTable(const string &); /// Open a table /// Lists all columns of an open file void List(); void ListHeader(); void ListKeywords(ostream &); /// Perform the dumping, based on the current dump list bool Dump(const string &, const vector &list, int); // bool Plot(const vector &list); public: ///Configures the fitsLoader from the config file and/or command arguments. int ExecConfig(Configuration& conf); }; // -------------------------------------------------------------------------- // //! Constructor //! @param out //! the ostream where to redirect the outputs // FitsDumper::FitsDumper() : fFile(0), fTable(0) { } // -------------------------------------------------------------------------- // //! Destructor // FitsDumper::~FitsDumper() { if (fFile) delete fFile; } string FitsDumper::ValueTypeToStr(CCfits::ValueType type) const { switch (type) { case CCfits::Tbyte: return "uint8_t"; case CCfits::Tushort: return "uint16_t"; case CCfits::Tshort: return "int16_t"; case CCfits::Tuint: return "uint32_t"; case CCfits::Tint: return "int32_t"; case CCfits::Tulong: return "uint32_t"; case CCfits::Tlong: return "int32_t"; case CCfits::Tlonglong: return "int64_t"; case CCfits::Tfloat: return "float"; case CCfits::Tdouble: return "double"; default: return "unknwown"; } } int FitsDumper::ValueTypeToSize(CCfits::ValueType type) const { switch (type) { case CCfits::Tbyte: return sizeof(uint8_t); case CCfits::Tushort: case CCfits::Tshort: return sizeof(uint16_t); case CCfits::Tuint: case CCfits::Tint: case CCfits::Tulong: case CCfits::Tlong: return sizeof(uint32_t); case CCfits::Tlonglong: return sizeof(uint64_t); case CCfits::Tfloat: return sizeof(float); case CCfits::Tdouble: return sizeof(double); default: return 0; } } template T FitsDumper::PtrToValue(const unsigned char* &ptr) const { T t; reverse_copy(ptr, ptr+sizeof(T), reinterpret_cast(&t)); ptr += sizeof(T); return t; } /* template double FitsDumper::PtrToDouble(const unsigned char *ptr) const { T t; reverse_copy(ptr, ptr+sizeof(T), reinterpret_cast(&t)); return t; } double FitsDumper::PtrToDouble(const unsigned char *ptr, CCfits::ValueType type) const { switch (type) { case CCfits::Tbyte: return PtrToDouble (ptr); case CCfits::Tushort: return PtrToDouble(ptr); case CCfits::Tuint: case CCfits::Tulong: return PtrToDouble(ptr); case CCfits::Tshort: return PtrToDouble (ptr); case CCfits::Tint: case CCfits::Tlong: return PtrToDouble (ptr); case CCfits::Tlonglong: return PtrToDouble (ptr); case CCfits::Tfloat: return PtrToDouble (ptr); case CCfits::Tdouble: return PtrToDouble (ptr); default: throw runtime_error("Data type not implemented yet."); } } */ // -------------------------------------------------------------------------- // //! Writes a single row of the selected FITS data to the output file. //! //! Data type \b not yet implemented: //! CCfits::Tnull, CCfits::Tbit, CCfits::Tlogical, CCfits::Tstring, //! CCfits::Tcomplex, CCfits::Tdblcomplex, CCfits::VTbit, //! CCfits::VTbyte, CCfits::VTlogical, CCfits::VTushort, //! CCfits::VTshort, CCfits::VTuint, CCfits::VTint, CCfits::VTulong, //! CCfits::VTlong, CCfits::VTlonglong, CCfits::VTfloat, //! CCfits::VTdouble, CCfits::VTcomplex, CCfits::VTdblcomplex //! //! @param offsets //! a vector containing the offsets to the columns (in bytes) //! @param targetFile //! the ofstream where to write to //! @param fitsBuffer //! the memory were the row has been loaded by cfitsio // int FitsDumper::WriteRow(ostream &out, const vector &list, const vector &offsets, unsigned char* fitsBuffer) const { int cnt = 0; for (vector::const_iterator it=list.begin(); it!=list.end(); it++) { const CCfits::Column *col = *it; // CCfits starts counting at 1 not 0 const int offset = offsets[col->index()-1]; // Get the pointer to the array which we are supposed to print const unsigned char *ptr = fitsBuffer + offset; // Loop over all array entries for (int width=0; widthwidth(); width++) { switch (col->type()) { case CCfits::Tbyte: out << PtrToValue (ptr); break; case CCfits::Tushort: out << PtrToValue(ptr); break; case CCfits::Tuint: case CCfits::Tulong: out << PtrToValue(ptr); break; case CCfits::Tshort: out << PtrToValue (ptr); break; case CCfits::Tint: case CCfits::Tlong: out << PtrToValue (ptr); break; case CCfits::Tlonglong: out << PtrToValue (ptr); break; case CCfits::Tfloat: out << PtrToValue (ptr); break; case CCfits::Tdouble: out << PtrToValue (ptr); break; default: cerr << "Data type not implemented yet." << endl; return 0; } out << " "; cnt++; } } if (cnt>0) out << endl; if (out.fail()) { cerr << "ERROR - writing output: " << strerror(errno) << endl; return -1; } return cnt; } // -------------------------------------------------------------------------- // //! Calculates the required buffer size for reading one row of the current //! table. Also calculates the offsets to all the columns // vector FitsDumper::CalculateOffsets() const { map sizes; for (map::const_iterator it=fColMap.begin(); it!=fColMap.end(); it++) { const int &width = it->second->width(); const int &idx = it->second->index(); const int size = ValueTypeToSize(it->second->type()); if (size==0) { cerr << "Data type " << (int)it->second->type() << " not implemented yet." << endl; return vector(); } sizes[idx] = size*width; } //calculate the offsets in the vector. vector result(1, 0); int size = 0; int idx = 0; for (map::const_iterator it=sizes.begin(); it!=sizes.end(); it++) { size += it->second; result.push_back(size); if (it->first == ++idx) continue; cerr << "Expected index " << idx << ", but found " << it->first << endl; return vector(); } return result; } // -------------------------------------------------------------------------- // //! Loads the fits file based on the current parameters // bool FitsDumper::OpenFile(const string &filename) { if (fFile) delete fFile; ostringstream str; try { fFile = new CCfits::FITS(filename); } catch (CCfits::FitsException e) { cerr << "Could not open FITS file " << filename << " reason: " << e.message() << endl; return false; } return true; } bool FitsDumper::OpenTable(const string &tablename) { if (!fFile) { cerr << "No file open." << endl; return false; } const multimap< string, CCfits::ExtHDU * > extMap = fFile->extension(); if (extMap.find(tablename) == extMap.end()) { cerr << "Table '" << tablename << "' not found." << endl; return false; } fTable = dynamic_cast(extMap.find(tablename)->second); if (!fTable) { cerr << "Object '" << tablename << "' returned not a CCfits::Table." << endl; return false; } fColMap = fTable->column(); fTable->makeThisCurrent(); fTable->getComments(); fTable->getHistory(); fTable->readAllKeys(); return true; } class MyColumn : public CCfits::Column { public: const string &comment() const { return CCfits::Column::comment(); } }; void FitsDumper::List() { if (!fFile) { cerr << "No file open." << endl; return; } cout << "\nFile: " << fFile->name() << "\n"; const multimap< string, CCfits::ExtHDU * > extMap = fFile->extension(); for (std::multimap::const_iterator it=extMap.begin(); it != extMap.end(); it++) { CCfits::Table *table = dynamic_cast(extMap.find(it->first)->second); cout << " " << it->first << " [" << table->rows() << "]\n"; const map &cols = table->column(); for (map::const_iterator ic=cols.begin(); ic != cols.end(); ic++) { const MyColumn *col = static_cast(ic->second); cout << " " << col->name() << "[" << col->width() << "] * " << col->scale() << " (" << col->unit() << ":" << ValueTypeToStr(col->type())<< ") " << col->comment() << "\n"; /* inline size_t Column::repeat () const inline bool Column::varLength () const inline double Column::zero () const inline const String& Column::display () const inline const String& Column::dimen () const inline const String& Column::TBCOL () inline const String& Column::TTYPE () inline const String& Column::TFORM () inline const String& Column::TDISP () inline const String& Column::TZERO () inline const String& Column::TDIM () inline const String& Column::TNULL () inline const String& Column::TLMIN () inline const String& Column::TLMAX () inline const String& Column::TDMAX () inline const String& Column::TDMIN () */ } cout << '\n'; } cout << flush; } class MyKeyword : public CCfits::Keyword { public: CCfits::ValueType keytype() const { return CCfits::Keyword::keytype(); } }; void FitsDumper::ListKeywords(ostream &out) { map keys = fTable->keyWord(); for (map::const_iterator it=keys.begin(); it!=keys.end(); it++) { string str; double d; const MyKeyword *kw = static_cast(it->second); kw->keytype(); out << "## " << setw(8) << kw->name() << "='"; if (kw->keytype()==16) out << kw->value(str); if (kw->keytype()==82) out << kw->value(d); out << "' \t(" << kw->comment() << ")" << endl; } } void FitsDumper::ListHeader() { if (!fTable) { cerr << "No table open." << endl; return; } cout << "\nTable: " << fTable->name() << " (rows=" << fTable->rows() << ")\n"; if (!fTable->comment().empty()) cout << "Comment: \t" << fTable->comment() << '\n'; if (!fTable->history().empty()) cout << "History: \t" << fTable->history() << '\n'; ListKeywords(cout); cout << endl; } // -------------------------------------------------------------------------- // //! Perform the actual dump, based on the current parameters // bool FitsDumper::Dump(const string &filename, const vector &list, int precision) { for (vector::const_iterator it=list.begin(); it!=list.end(); it++) if (fColMap.find(*it) == fColMap.end()) { cerr << "WARNING - Column '" << *it << "' not found in table." << endl; return false; } // FIXME: Maybe do this when opening a table? const vector offsets = CalculateOffsets(); if (offsets.size()==0) return false; ofstream out(filename=="-"?"/dev/stdout":filename); if (!out) { cerr << "Cannot open file " << filename << ": " << strerror(errno) << endl; return false; } out.precision(precision); out << "## --------------------------------------------------------------------------\n"; if (filename!="-") out << "## File: \t" << filename << '\n'; out << "## Table: \t" << fTable->name() << '\n'; if (!fTable->comment().empty()) out << "## Comment: \t" << fTable->comment() << '\n'; if (!fTable->history().empty()) out << "## History: \t" << fTable->history() << '\n'; out << "## NumRows: \t" << fTable->rows() << '\n'; out << "## --------------------------------------------------------------------------\n"; ListKeywords(out); out << "## --------------------------------------------------------------------------\n"; out << "#\n"; for (vector::const_iterator it=list.begin(); it!=list.end(); it++) { const MyColumn *col = static_cast(fTable->column()[*it]); out << "# " << col->name() << "[" << col->width() << "]: " << col->unit(); if (!col->comment().empty()) out << " (" <comment() << ")"; out << '\n'; } out << "#" << endl; // Loop over all columns in our list of requested columns vector columns; for (vector::const_iterator it=list.begin(); it!=list.end(); it++) columns.push_back(fColMap.find(*it)->second); const int size = offsets[offsets.size()-1]; unsigned char* fitsBuffer = new unsigned char[size]; int status = 0; for (int i=1; i<=fTable->rows(); i++) { fits_read_tblbytes(fFile->fitsPointer(), i, 1, size, fitsBuffer, &status); if (status) { cerr << "An error occurred while reading fits row #" << i << " error code: " << status << endl; break; } if (WriteRow(out, columns, offsets, fitsBuffer)<0) { status=1; break; } } delete[] fitsBuffer; return status==0; } // -------------------------------------------------------------------------- // //! Perform the actual dump, based on the current parameters // /* bool FitsDumper::Plot(const vector &list) { for (vector::const_iterator it=list.begin(); it!=list.end(); it++) if (fColMap.find(*it) == fColMap.end()) { cerr << "WARNING - Column '" << *it << "' not found in table." << endl; return false; } // FIXME: Maybe do this when opening a table? const vector offsets = CalculateOffsets(); if (offsets.size()==0) return false; // Loop over all columns in our list of requested columns const CCfits::Column *col[3] = { list.size()>0 ? fColMap.find(list[0])->second : 0, list.size()>1 ? fColMap.find(list[1])->second : 0, list.size()>2 ? fColMap.find(list[2])->second : 0 }; const int size = offsets[offsets.size()-1]; unsigned char* fitsBuffer = new unsigned char[size]; const int idx = 0; // CCfits starts counting at 1 not 0 const size_t pos[3] = { col[0] ? offsets[col[0]->index()-1] + idx*col[0]->width()*ValueTypeToSize(col[0]->type()) : 0, col[1] ? offsets[col[1]->index()-1] + idx*col[1]->width()*ValueTypeToSize(col[1]->type()) : 0, col[2] ? offsets[col[2]->index()-1] + idx*col[2]->width()*ValueTypeToSize(col[2]->type()) : 0 }; int status = 0; for (int i=1; i<=fTable->rows(); i++) { fits_read_tblbytes(fFile->fitsPointer(), i, 1, size, fitsBuffer, &status); if (status) { cerr << "An error occurred while reading fits row #" << i << " error code: " << status << endl; break; } try { const double x[3] = { col[0] ? PtrToDouble(fitsBuffer+pos[0], col[0]->type()) : 0, col[1] ? PtrToDouble(fitsBuffer+pos[1], col[1]->type()) : 0, col[2] ? PtrToDouble(fitsBuffer+pos[2], col[2]->type()) : 0 }; } catch (const runtime_error &e) { cerr << e.what() << endl; status=1; break; } } delete[] fitsBuffer; return status==0; } */ // -------------------------------------------------------------------------- // //! Retrieves the configuration parameters //! @param conf //! the configuration object // int FitsDumper::ExecConfig(Configuration& conf) { if (conf.Has("fitsfile")) { if (!OpenFile(conf.Get("fitsfile"))) return -1; } if (conf.Get("list")) List(); if (conf.Has("tablename")) { if (!OpenTable(conf.Get("tablename"))) return -1; } if (conf.Get("header")) ListHeader(); if (conf.Get("header") || conf.Get("list")) return 1; if (conf.Has("outfile")) { if (!Dump(conf.Get("outfile"), conf.Get>("col"), conf.Get("precision"))) return -1; } return 0; } void PrintUsage() { cout << "fitsdump is a tool to dump data from a FITS table as ascii.\n" "\n" "Usage: fitsdump [OPTIONS] fitsfile col col ... \n" " or: fitsdump [OPTIONS]\n"; cout << endl; } void PrintHelp() { // } void SetupConfiguration(Configuration& conf) { po::options_description configs("Fitsdump options"); configs.add_options() ("fitsfile,f", var() #if BOOST_VERSION >= 104200 ->required() #endif , "Name of FITS file") ("tablename,t", var("DATA") #if BOOST_VERSION >= 104200 ->required() #endif , "Name of input table") ("col,c", vars(), "List of columns to dump") ("outfile,o", var("/dev/stdout"), "Name of output file (-:/dev/stdout)") ("precision,p", var(20), "Precision of ofstream") ("list,l", po_switch(), "List all tables and columns in file") ("header,h", po_switch(), "Dump header of given table") ; po::positional_options_description p; p.add("fitsfile", 1); // The first positional options p.add("col", -1); // All others conf.AddOptions(configs); conf.SetArgumentPositions(p); } int main(int argc, const char** argv) { Configuration conf(argv[0]); conf.SetPrintUsage(PrintUsage); SetupConfiguration(conf); po::variables_map vm; try { vm = conf.Parse(argc, argv); } #if BOOST_VERSION > 104000 catch (po::multiple_occurrences &e) { cerr << "Program options invalid due to: " << e.what() << " of option '" << e.get_option_name() << "'." << endl; return -1; } #endif catch (exception& e) { cerr << "Program options invalid due to: " << e.what() << endl; return -1; } if (conf.HasVersion() || conf.HasPrint()) return -1; if (conf.HasHelp()) { PrintHelp(); return -1; } FitsDumper loader; return loader.ExecConfig(conf); }