source: trunk/FACT++/src/Fits.cc @ 10741

Last change on this file since 10741 was 10741, checked in by lyard, 8 years ago
added configurable grouping and did some cosmetics to the code (removed tabs)
File size: 10.4 KB
Line 
1// **************************************************************************
2/** @class FactFits
3
4@brief FITS writter for the FACT project.
5
6The FactFits class is able to open, manage and update FITS files.
7
8The file columns should be given to the class before the file is openned. Once
9a file has been created, the structure of its columns cannot be changed. Only
10row can be added.
11
12This class relies on the CCfits and CFitsIO packages.
13
14*/
15// **************************************************************************
16#include "Fits.h"
17
18#include "Time.h"
19#include "Converter.h"
20#include "MessageImp.h"
21
22//for file stats
23#include <sys/stat.h>
24
25using namespace std;
26using namespace CCfits;
27
28// --------------------------------------------------------------------------
29//
30//! This gives a standard variable to the file writter.
31//! This variable should not be related to the DIM service being logged.
32//! @param desc the description of the variable to add
33//! @param dataFormat the FITS data format corresponding to the variable to add.
34//! @param dataPointer the memory location where the variable is stored
35//! @param numDataBytes the number of bytes taken by the variable
36//
37void Fits::AddStandardColumn(Description& desc, string dataFormat, void* dataPointer, long unsigned int numDataBytes)
38{
39        //check if entry already exist
40        for (vector<Description>::iterator it=fStandardColDesc.begin(); it != fStandardColDesc.end(); it++)
41                if (it->name == desc.name)
42                        return;
43        fStandardColDesc.push_back(desc);
44        fStandardFormats.push_back(dataFormat);
45        fStandardPointers.push_back(dataPointer);
46        fStandardNumBytes.push_back(numDataBytes);
47}
48// --------------------------------------------------------------------------
49//
50//! This gives the file writter access to the DIM data
51//! @param desc a vector containing the description of all the columns to log
52//! @param dataFormat a vector containing the FITS data format of all the columsn to log
53//! @param dataPointer the memory location where the DIM data starts
54//! @param numDataBytes the number of bytes taken by the DIM data.
55//     
56void Fits::InitDataColumns(vector<Description> desc, vector<string>& dataFormat, void* dataPointer, int numDataBytes)
57{//we will copy this information here. It duplicates the data, which is not great, but it is the easiest way of doing it right now
58        if (desc.size() == dataFormat.size())
59        {
60                fDataColDesc = desc;
61        }
62        else
63        {
64                fDataColDesc.clear();
65                for (unsigned int i=0;i<dataFormat.size();i++)
66                {
67                        stringstream stt;
68                        stt << "Data" << i;
69                        fDataColDesc.push_back(Description(stt.str(), "comment", "unit"));
70                }
71        }               
72        fDataFormats = dataFormat;
73        fDataPointer = dataPointer;
74        fDataNumBytes = numDataBytes;   
75}
76// --------------------------------------------------------------------------
77//
78//! This opens the FITS file (after the columns have been passed)
79//! @param fileName the filename with complete or relative path of the file to open
80//! @param tableName the name of the table that will receive the logged data.
81//! @param file a pointer to an existing FITS file. If NULL, file will be opened and managed internally
82//! @param fitsCounter a pointer to the integer keeping track of the opened FITS files
83//! @param out a pointer to the MessageImp that should be used to log errors
84//
85void Fits::Open(const string& fileName, const string& tableName, FITS* file, int* fitsCounter, MessageImp* out)//ostream& out)
86{               
87//      if (fMess)
88//              delete fMess;
89//      fMess = new MessageImp(out);
90        fMess = out;
91        fFileName = fileName;
92        if (file == NULL)
93        {
94                try
95                {
96                        fFile = new FITS(fileName, RWmode::Write);
97                }
98                catch (CCfits::FitsError e)
99                {                       
100                        stringstream str;
101                        str << "Could not open FITS file " << fileName << " reason: " << e.message();
102                        fMess->Error(str);
103                        fFile = NULL;
104                        return;
105                }       
106                fOwner = true;
107                fNumOpenFitsFiles = fitsCounter;
108                (*fNumOpenFitsFiles)++;
109        }
110        else
111        {
112                fFile = file;
113                fOwner = false;
114        }
115        //concatenate the standard and data columns
116        //do it the inneficient way first: its easier and faster to code.
117        vector<string> allNames;
118        vector<string> allDataTypes;
119        vector<string> allUnits;
120        fTotalNumBytes = 0;
121        for (unsigned int i=0;i<fStandardColDesc.size();i++)
122        {
123                allNames.push_back(fStandardColDesc[i].name);
124                allDataTypes.push_back(fStandardFormats[i]);
125                allUnits.push_back(fStandardColDesc[i].unit);
126                fTotalNumBytes += fStandardNumBytes[i];
127        }
128        //for (int i=static_cast<int>(fDataColDesc.size())-1;i>=0;i--)
129        for (int i=0; i< static_cast<int>(fDataColDesc.size()); i++)
130        {
131                if (fDataColDesc[i].name != "")
132                        allNames.push_back(fDataColDesc[i].name);
133                else 
134                {
135                        stringstream stt;
136                        stt << "Data" << i;
137                        allNames.push_back(stt.str());
138                }
139                allDataTypes.push_back(fDataFormats[i]);
140                allUnits.push_back(fDataColDesc[i].unit);       
141        }
142        fTotalNumBytes += fDataNumBytes;
143       
144        bool updating = false;
145        try
146        {
147                string factTableName = "FACT-" + tableName;
148                fTable = fFile->addTable(factTableName, 0, allNames, allDataTypes, allUnits);
149                fTable->makeThisCurrent();
150                fCopyBuffer = new unsigned char[fTotalNumBytes]; 
151                fNumRows = fTable->rows();
152                if (fNumRows !=0)
153                {//If the file already existed, then we must load its data to memory before writing to it.
154                        BinTable* bTable = dynamic_cast<BinTable*>(fTable);
155                        if (!bTable)
156                        {
157                                fMess->Error("The table " + factTableName + " could not be converted to a binary table. skipping");
158                                return;
159                        }       
160                        //read the table binary data.
161                        vector<string> colName;
162                        bTable->readData(true, colName);
163
164                        //double check that the data was indeed read from the disk. Go through the fTable instead as colName is empty (yes, it is !)
165                        map<string, Column*> cMap = fTable->column();
166                        map<string, Column*>::iterator cMapIt;
167
168                        for (cMapIt = cMap.begin(); cMapIt != cMap.end(); cMapIt++)
169                        {
170                                if (!cMapIt->second->isRead())
171                                {
172                                        fMess->Error("Column " + cMapIt->first + "Could not be read back from the disk");
173                                        return; 
174                                }       
175                        }
176                        updating = true;
177                }
178        }
179        catch(CCfits::FitsError e)
180        {
181                stringstream str;
182                str << "Could not open or create FITS table " << tableName << " in  file " << fileName << " reason: " << e.message();
183                fMess->Error(str);
184                fTable = NULL;
185        }
186                       
187        //As requested by Roland, the reference MjD is 0.0
188        fRefMjD = 0;//* static_cast<double*>(fStandardPointers[0]);
189        if (!updating)
190                WriteHeaderKeys();
191}
192// --------------------------------------------------------------------------
193//
194//!This writes a single header key in the currently open file.
195//!@param name the key
196//!@param value the key value
197//!@param a comment explaining the meaning of the key
198template <typename T>
199void Fits::WriteSingleHeaderKey(string name, T value, string comment)
200{
201        try
202        {
203                fTable->addKey(name, value, comment);
204        }
205        catch (CCfits::FitsError e)
206        {
207                stringstream str;
208                str << "Could not add header keys in file " << fFileName << " reason: " << e.message();
209                fMess->Error(str);
210        }
211}
212// --------------------------------------------------------------------------
213//
214//! This writes the standard header
215//
216void Fits::WriteHeaderKeys()
217{
218        if (!fTable)
219                return;
220        string name;
221        string comment;
222
223        string stringValue;
224
225        WriteSingleHeaderKey("EXTREL", 1.0f, "Release Number");
226        WriteSingleHeaderKey("TELESCOP", "FACT", "Telescope that acquired this data");
227        WriteSingleHeaderKey("ORIGIN", "ISDC", "Institution that wrote the file");
228        WriteSingleHeaderKey("CREATOR", "FACT++ DataLogger", "Program that wrote this file");
229        stringValue = Time().GetAsStr();
230        stringValue[10]= 'T';
231        WriteSingleHeaderKey("DATE", stringValue, "File creation data");
232        WriteSingleHeaderKey("TIMESYS", "TT", "Time frame system");
233        WriteSingleHeaderKey("TIMEUNIT", "d", "Time unit");
234        WriteSingleHeaderKey("TIMEREF", "UTC", "Time reference frame");
235        WriteSingleHeaderKey("MJDREF", fRefMjD, "Modified Julian Date of origin");
236    WriteSingleHeaderKey("TSTOP", fEndMjD, "Time of the last receied data");
237}
238// --------------------------------------------------------------------------
239//
240//! This writes one line of data to the file.
241//! @param conv the converter corresponding to the service being logged
242//
243void Fits::Write(Converter* conv)
244{
245
246        fTable->makeThisCurrent();
247        int status(0);
248        if (fits_insert_rows(fTable->fitsPointer(), fNumRows, 1, &status))
249        {
250                stringstream str;
251                str << "Could not insert row in file " << fFileName << ". cfitsio error code: " << status;
252                fMess->Error(str);
253        }
254        fNumRows++;
255
256        //the first standard variable is the current MjD
257        if (fEndMjD == 0)
258        {
259                double doubleValue = *static_cast<double*>(fStandardPointers[0]);// - fRefMjD;
260                WriteSingleHeaderKey("TSTART", doubleValue, "Time of the first received data");
261        }
262        fEndMjD = * static_cast<double*>(fStandardPointers[0]);
263                       
264        //first copy the standard variables to the copy buffer
265        int shift = 0;
266
267        for (unsigned int i=0;i<fStandardNumBytes.size();i++)
268        {
269                const char * charSrc = static_cast<char*>(fStandardPointers[i]);
270                reverse_copy(charSrc, charSrc+fStandardNumBytes[i], &fCopyBuffer[shift]);
271                shift+= fStandardNumBytes[i];   
272        }
273
274        //now take care of the DIM data. The Converter is here for that purpose
275        conv->ToFits(static_cast<void*>(&fCopyBuffer[shift]), fDataPointer, fDataNumBytes);
276                         
277        //data copied to buffer, can write to fits
278        fits_write_tblbytes(fFile->fitsPointer(), fNumRows, 1, fTotalNumBytes, fCopyBuffer, &status);
279        if (status)
280        {
281                char text[30];//max length of cfitsio error strings (from doc)
282                fits_get_errstatus(status, text);
283                stringstream str;
284                str << "Error while writing FITS row in " << fFileName << ". Message: " << text << " [" << status << "]";
285                fMess->Error(str);     
286        }
287}
288// --------------------------------------------------------------------------
289//
290//! This closes the currently openned FITS file.
291//! it also updates the header to reflect the time of the last logged row
292//     
293void Fits::Close() 
294{
295//WARNING: do NOT delete the table as it gets deleted by the fFile object
296//                      if (fTable != NULL)
297//                              delete fTable;
298
299        WriteSingleHeaderKey("TSTOP", fEndMjD, "Time of the last receied data");
300        if (fFile != NULL && fOwner)
301                delete fFile;
302        fFile = NULL;
303        if (fCopyBuffer != NULL)
304                delete [] fCopyBuffer;
305        fCopyBuffer = NULL;
306        if (fOwner && fNumOpenFitsFiles != NULL)
307                (*fNumOpenFitsFiles)--;
308//fMess is the MessageImp part of the dataLogger itself. Thus it should NOT be deleted by the Fits files destructor.
309//      if (fMess)
310//              delete fMess;
311        fMess = NULL;
312}
313
314// --------------------------------------------------------------------------
315//! Returns the size on the disk of the Fits file being written.
316int Fits::GetWrittenSize()
317{
318        if (!IsOpen())
319                return 0;
320               
321        struct stat st;
322        if (stat(fFileName.c_str(), &st))
323                return 0;
324        else
325                return st.st_size;
326}
Note: See TracBrowser for help on using the repository browser.