LCOV - code coverage report
Current view: top level - utils/xrif2fits - xrif2fits.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 0.0 % 460 0
Test Date: 2026-01-03 21:03:39 Functions: 0.0 % 9 0

            Line data    Source code
       1              : /** \file xrif2fits.hpp
       2              :  * \brief The xrif2fits class declaration and definition.
       3              :  *
       4              :  * \ingroup xrif2fits_files
       5              :  */
       6              : 
       7              : #ifndef xrif2fits_hpp
       8              : #define xrif2fits_hpp
       9              : 
      10              : #include <ImageStreamIO/ImageStreamIO.h>
      11              : 
      12              : #include <xrif/xrif.h>
      13              : 
      14              : #include <sstream>
      15              : #include <iomanip>
      16              : 
      17              : #include <mx/ioutils/fileUtils.hpp>
      18              : #include <mx/improc/eigenCube.hpp>
      19              : #include <mx/improc/eigenImage.hpp>
      20              : 
      21              : #include <mx/ioutils/fits/fitsFile.hpp>
      22              : 
      23              : #include <mx/sys/timeUtils.hpp>
      24              : using namespace mx::sys::tscomp;
      25              : using namespace mx::sys::tsop;
      26              : 
      27              : #include "../../libMagAOX/libMagAOX.hpp"
      28              : 
      29              : #ifndef DEBUG_CRUMB
      30              : #define DEBUG_CRUMB(msg) {std::cerr << msg << '(' << __FILE__ << ' ' << __LINE__ << "\n";}
      31              : #endif
      32              : 
      33              : #define ERR_INVOKED_NAME( msg )                                                                                        \
      34              :     std::cerr << invokedName + ": " << msg << "\n  at:" << __FILE__ << ' ' << __LINE__ << '\n';
      35              : 
      36              : #define ERR_INVOKED_NAME_ERRNO( msg )                                                                                  \
      37              :     std::cerr << invokedName + ": " << msg << "\n  errno says:" << strerror( errno ) << "\n  at: " << __FILE__ << ' '  \
      38              :               << __LINE__ << '\n'
      39              : 
      40              : /** \defgroup xrif2fits xrif2fits: xrif-archive to FITS cube converter
      41              :  * \brief Read images from an xrif archive and write to FITS
      42              :  *
      43              :  * <a href="../handbook/utils/xrif2fits.html">Utility Documentation</a>
      44              :  *
      45              :  * \ingroup utils
      46              :  *
      47              :  */
      48              : 
      49              : /** \defgroup xrif2fits_files xrif2fits Files
      50              :  * \ingroup xrif2fits
      51              :  */
      52              : 
      53              : bool g_timeToDie = false;
      54              : 
      55            0 : void sigTermHandler( int signum, siginfo_t *siginf, void *ucont )
      56              : {
      57              :     // Suppress those warnings . . .
      58              :     static_cast<void>( signum );
      59              :     static_cast<void>( siginf );
      60              :     static_cast<void>( ucont );
      61              : 
      62            0 :     std::cerr << "\n"; // clear out the ^C char
      63              : 
      64            0 :     g_timeToDie = true;
      65            0 : }
      66              : 
      67              : /// A utility to stream MagaO-X images from xrif compressed archives to an ImageStreamIO stream.
      68              : /**
      69              :  * \todo finish md doc for xrif2fits
      70              :  *
      71              :  * \ingroup xrif2fits
      72              :  */
      73              : class xrif2fits : public mx::app::application
      74              : {
      75              :     typedef XWC_DEFAULT_VERBOSITY verboseT;
      76              : 
      77              :     typedef MagAOX::file::stdFileName<verboseT> stdFileNameT;
      78              : 
      79              :   protected:
      80              :     /** \name Configurable Parameters
      81              :      * @{
      82              :      */
      83              :     std::string m_camera; /**< The INDI device name of the camera to process.
      84              :                                Sets m_cameraHeader to `<m_camera>_header.conf` */
      85              : 
      86              :     std::string m_cameraHeader; /**< The filename of the config file containing the camera header specification.
      87              :                                      Setting this overrides the setting from m_camera.
      88              :                                      The path specified by $MagAOX_PATH/$MagAOX_CONFIG is searched,
      89              :                                      unless XRIF2FITS_CONFIGPATH is set in the environment.*/
      90              : 
      91              :     bool m_noHeader{ false }; /**< if true then no camera header is generated */
      92              : 
      93              :     std::string m_dir; /**< The directory to search for files.  Can be empty if full path given in files.
      94              :                             If files is empty, all archives in dir will be used.  Defaults to `./`.*/
      95              : 
      96              :     bool m_overWriteDir {false}; ///< Overwrite an existing directory.  Default is to stop if directory exists.
      97              : 
      98              :     std::vector<std::string> m_files; /**< List of files to use.  If dir is not empty,
      99              :                                            it will be pre-pended to each name.*/
     100              : 
     101              :     std::vector<MagAOX::file::stdFileName<verboseT>> m_fileNames; /**< The decoded file names broken down into
     102              :                                                               constituent parts */
     103              : 
     104              :     std::vector<std::string> m_logDir;
     105              : 
     106              :     std::vector<std::string> m_telDir;
     107              : 
     108              :     std::string m_outDir = "fits/";
     109              : 
     110              :     bool m_noMeta{ false };
     111              : 
     112              :     bool m_metaOnly{ false };
     113              : 
     114              :     bool m_timesOnly{ false };
     115              : 
     116              :     bool m_cubeMode{ false };
     117              : 
     118              :     logMap<verboseT> m_logs;
     119              : 
     120              :     logMap<verboseT> m_tels;
     121              : 
     122              :   protected:
     123              :     ///@}
     124              : 
     125              :     std::string MagAOXPath;
     126              :     std::string ConfigRelPath;
     127              : 
     128              :     std::vector<logMeta> m_logMetas;
     129              : 
     130              :     xrif_t m_xrif{ nullptr };
     131              :     xrif_t m_xrif_timing{ nullptr };
     132              : 
     133              :   public:
     134              :     /// c-tor
     135              :     /** Sets up the default config paths by reading from the environment
     136              :      *
     137              :      */
     138              :     xrif2fits();
     139              : 
     140              :     ~xrif2fits();
     141              : 
     142              :     virtual void setupConfig();
     143              : 
     144              :     virtual void loadConfig();
     145              : 
     146              :     virtual mx::error_t readHeaderConfig( const std::string &hcfile );
     147              : 
     148              :     virtual int execute();
     149              : 
     150              :     /// Prepare the file list and output directory
     151              :     /** Based on loaded configuration
     152              :      *
     153              :      * \returns mx::error_t::noerror on success
     154              :      * \returns error code on an error
     155              :      *
     156              :      */
     157              :     mx::error_t prepareFiles();
     158              : 
     159              :     template <typename dataT>
     160              :     int writeImages( int n, stdFileNameT &lfn );
     161              : 
     162              :     std::string format_nano( uint64_t n );
     163              : };
     164              : 
     165              : inline xrif2fits::xrif2fits()
     166              : {
     167              :     // setup the default config path
     168              :     MagAOXPath    = mx::sys::getEnv( MAGAOX_env_path );
     169              : 
     170              :     if(MagAOXPath == "")
     171              :     {
     172              :         MagAOXPath = MAGAOX_path;
     173              :     }
     174              : 
     175              :     if(MagAOXPath.size() > 0)
     176              :     {
     177              :         if(MagAOXPath.back() !='/')
     178              :         {
     179              :             MagAOXPath += '/';
     180              :         }
     181              :     }
     182              : 
     183              :     ConfigRelPath = mx::sys::getEnv( MAGAOX_env_config );
     184              : 
     185              :     if(ConfigRelPath == "")
     186              :     {
     187              :         ConfigRelPath = MAGAOX_configRelPath;
     188              :     }
     189              : 
     190              :     if( ConfigRelPath.size() > 0 )
     191              :     {
     192              :         if(ConfigRelPath.back() !='/')
     193              :         {
     194              :             ConfigRelPath += '/';
     195              :         }
     196              : 
     197              :         mx::app::application::m_configPathCLBase = MagAOXPath + ConfigRelPath + '/';
     198              :     }
     199              : 
     200              :     // Allow overriding the config path
     201              :     mx::app::application::m_configPathCLBase_env = "XRIF2FITS_CONFIGPATH";
     202              : }
     203              : 
     204            0 : inline xrif2fits::~xrif2fits()
     205              : {
     206            0 :     if( m_xrif )
     207              :     {
     208            0 :         xrif_delete( m_xrif );
     209              :     }
     210              : 
     211            0 :     if( m_xrif_timing )
     212              :     {
     213            0 :         xrif_delete( m_xrif_timing );
     214              :     }
     215            0 : }
     216              : 
     217            0 : inline void xrif2fits::setupConfig()
     218              : {
     219            0 :     config.add( "camera",
     220              :                 "",
     221              :                 "camera",
     222              :                 argType::Required,
     223              :                 "",
     224              :                 "camera",
     225              :                 false,
     226              :                 "string",
     227              :                 "The device name of the camera.  Sets the header.camera config to <camera>_header.conf" );
     228              : 
     229            0 :     config.add( "header.camera",
     230              :                 "",
     231              :                 "header.camera",
     232              :                 argType::Required,
     233              :                 "header",
     234              :                 "camera",
     235              :                 false,
     236              :                 "string",
     237              :                 "The name of a config file defining a camera header.  Overrides the default for `camera`."
     238              :                 "Searches $MagAOX_PATH/$MagAOX_CONFIG, unless XRIF2FITS_CONFIGPATH is set in the environment." );
     239              : 
     240            0 :     config.add( "noHeader",
     241              :                 "N",
     242              :                 "noHeader",
     243              :                 argType::True,
     244              :                 "",
     245              :                 "noHeader",
     246              :                 false,
     247              :                 "bool",
     248              :                 "If true, then no camera header is generated" );
     249              : 
     250            0 :     config.add( "dir",
     251              :                 "d",
     252              :                 "dir",
     253              :                 argType::Required,
     254              :                 "",
     255              :                 "dir",
     256              :                 false,
     257              :                 "string",
     258              :                 "The directory to search for files. Can be empty if full path given in files." );
     259              : 
     260            0 :     config.add( "overwrite",
     261              :                 "O",
     262              :                 "overwrite",
     263              :                 argType::True,
     264              :                 "",
     265              :                 "overwrite",
     266              :                 false,
     267              :                 "bool",
     268              :                 "Overwrite an existing directory.  Default is to stop if directory exists." );
     269              : 
     270            0 :     config.add( "files",
     271              :                 "f",
     272              :                 "files",
     273              :                 argType::Required,
     274              :                 "",
     275              :                 "files",
     276              :                 false,
     277              :                 "vector<string>",
     278              :                 "List of files to use. If dir is not empty, it will be pre-pended to each name." );
     279              : 
     280            0 :     config.add( "logdir",
     281              :                 "l",
     282              :                 "logdir",
     283              :                 argType::Required,
     284              :                 "",
     285              :                 "logdir",
     286              :                 false,
     287              :                 "vector<string>",
     288              :                 "Directories for log files." );
     289              : 
     290            0 :     config.add( "teldir",
     291              :                 "t",
     292              :                 "teldir",
     293              :                 argType::Required,
     294              :                 "",
     295              :                 "teldir",
     296              :                 false,
     297              :                 "vector<string>",
     298              :                 "Directories for telemetry files." );
     299              : 
     300            0 :     config.add( "outDir",
     301              :                 "D",
     302              :                 "outDir",
     303              :                 argType::Required,
     304              :                 "",
     305              :                 "outDir",
     306              :                 false,
     307              :                 "string",
     308              :                 "The directory in which to write output files.  Default is ./fits/." );
     309              : 
     310            0 :     config.add( "metaOnly",
     311              :                 "",
     312              :                 "metaOnly",
     313              :                 argType::True,
     314              :                 "",
     315              :                 "metaOnly",
     316              :                 false,
     317              :                 "bool",
     318              :                 "If true, output only meta data, without decoding images.  Default is false." );
     319              : 
     320            0 :     config.add( "time",
     321              :                 "T",
     322              :                 "time",
     323              :                 argType::True,
     324              :                 "",
     325              :                 "time",
     326              :                 false,
     327              :                 "bool",
     328              :                 "time span mode: output one line per input file in the format [filename] [start time] [end time] "
     329              :                 "[number of frames], with ISO 8601 timestamps" );
     330              : 
     331            0 :     config.add( "noMeta",
     332              :                 "",
     333              :                 "noMeta",
     334              :                 argType::True,
     335              :                 "",
     336              :                 "noMeta",
     337              :                 false,
     338              :                 "bool",
     339              :                 "If true, the meta data file is not written (FITS headers will still be).  Default is false." );
     340              : 
     341            0 :     config.add( "cubeMode",
     342              :                 "C",
     343              :                 "cubeMode",
     344              :                 argType::True,
     345              :                 "",
     346              :                 "cubeMode",
     347              :                 false,
     348              :                 "bool",
     349              :                 "If true, the archive is written as a FITS cube with minimal header.  Default is false." );
     350            0 : }
     351              : 
     352            0 : inline void xrif2fits::loadConfig()
     353              : {
     354            0 :     config( m_camera, "camera" );
     355              : 
     356            0 :     if( m_camera != "" )
     357              :     {
     358            0 :         m_cameraHeader = m_camera + "_header.conf";
     359              :     }
     360              : 
     361            0 :     config( m_cameraHeader, "header.camera" );
     362              : 
     363            0 :     config( m_noHeader, "noHeader" );
     364              : 
     365            0 :     config( m_dir, "dir" );
     366            0 :     config(m_overWriteDir, "overwrite");
     367            0 :     config( m_files, "files" );
     368            0 :     config( m_outDir, "outDir" );
     369            0 :     config( m_logDir, "logdir" );
     370            0 :     config( m_telDir, "teldir" );
     371            0 :     config( m_metaOnly, "metaOnly" );
     372            0 :     config( m_timesOnly, "time" );
     373            0 :     config( m_noMeta, "noMeta" );
     374            0 :     config( m_cubeMode, "cubeMode" );
     375              : 
     376            0 :     if( m_configPathCLBase.size() > 0 )
     377              :     {
     378            0 :         if( mx::app::application::m_configPathCLBase.back() != '/' )
     379              :         {
     380            0 :             mx::app::application::m_configPathCLBase += '/';
     381              :         }
     382              :     }
     383            0 : }
     384              : 
     385            0 : inline mx::error_t xrif2fits::readHeaderConfig( const std::string &hcfile )
     386              : {
     387            0 :     if( hcfile == "" )
     388              :     {
     389            0 :         return mx::error_t::noerror;
     390              :     }
     391              : 
     392            0 :     mx::app::appConfigurator hconfig;
     393              : 
     394            0 :     hconfig.add( "include", "", "include", argType::Required, "", "include", false, "string", "" );
     395              : 
     396              :     try
     397              :     {
     398            0 :         DEBUG_CRUMB("reading: " + hcfile);
     399              : 
     400            0 :         if( hconfig.readConfig( hcfile, true ) != 0 )
     401              :         {
     402            0 :             return mx::error_report<verboseT>( mx::error_t::error, "Error reading header config: " + hcfile );
     403              :         }
     404              :     }
     405            0 :     catch( const std::exception &e )
     406              :     {
     407            0 :         return mx::error_report<verboseT>( mx::error_t::std_exception,
     408            0 :                                            "Exception reading header config: " + hcfile + ". " + e.what() );
     409            0 :     }
     410              : 
     411            0 :     std::vector<std::string> includes;
     412            0 :     hconfig( includes, "include" );
     413              : 
     414            0 :     for( auto &include : includes )
     415              :     {
     416            0 :         if( include.size() > 4 )
     417              :         {
     418            0 :             if( include.substr( include.size() - 5, 4 ) != ".conf" )
     419              :             {
     420            0 :                 include += ".conf";
     421              :             }
     422              :         }
     423              : 
     424            0 :         DEBUG_CRUMB("reading include: " + mx::app::application::m_configPathCLBase + include);
     425              : 
     426            0 :         mx_error_check( readHeaderConfig( mx::app::application::m_configPathCLBase + include ) );
     427              :     }
     428              : 
     429            0 :     std::vector<std::string> devices;
     430              : 
     431            0 :     hconfig.unusedSections( devices );
     432              : 
     433            0 :     if( devices.size() == 0 && includes.size() == 0) //this allows include-only
     434              :     {
     435            0 :         return mx::error_report<verboseT>( mx::error_t::notfound, "No device sections in header config:" + hcfile );
     436              :     }
     437              : 
     438            0 :     for( auto &device : devices )
     439              :     {
     440              :         // Wind through all the unused targets
     441            0 :         for( auto it = hconfig.m_unusedConfigs.begin(); it != hconfig.m_unusedConfigs.end(); ++it )
     442              :         {
     443            0 :             if( device == it->second.section )
     444              :             {
     445            0 :                 std::string eventCode = it->second.keyword;
     446              : 
     447              :                 // Check if this keyword is a valid flatlogs eventCode
     448            0 :                 flatlogs::eventCodeT ec = MagAOX::logger::eventCode( eventCode );
     449            0 :                 if( ec != eventCodes::UNKNOWN )
     450              :                 {
     451            0 :                     std::vector<std::string> fields;
     452            0 :                     hconfig.configUnused( fields, mx::app::iniFile::makeKey( device, eventCode ) );
     453              : 
     454            0 :                     for( auto &field : fields )
     455              :                     {
     456            0 :                         std::cerr << "adding: " << device << ' ' << ec << ' ' << field << '\n';
     457            0 :                         m_logMetas.push_back( logMetaSpec( { device, ec, field } ) );
     458              :                     }
     459            0 :                 }
     460            0 :             }
     461              :         }
     462              :     }
     463              : 
     464            0 :     return mx::error_t::noerror;
     465            0 : }
     466              : 
     467            0 : inline int xrif2fits::execute()
     468              : {
     469              :     // Install signal handling
     470              :     struct sigaction act;
     471              :     sigset_t         set;
     472              : 
     473            0 :     act.sa_sigaction = sigTermHandler;
     474            0 :     act.sa_flags     = SA_SIGINFO;
     475            0 :     sigemptyset( &set );
     476            0 :     act.sa_mask = set;
     477              : 
     478            0 :     errno = 0;
     479            0 :     if( sigaction( SIGTERM, &act, 0 ) < 0 )
     480              :     {
     481            0 :         std::cerr << " (" << invokedName << "): error setting SIGTERM handler: " << strerror( errno ) << "\n";
     482            0 :         return -1;
     483              :     }
     484              : 
     485            0 :     errno = 0;
     486            0 :     if( sigaction( SIGQUIT, &act, 0 ) < 0 )
     487              :     {
     488            0 :         std::cerr << " (" << invokedName << "): error setting SIGQUIT handler: " << strerror( errno ) << "\n";
     489            0 :         return -1;
     490              :     }
     491              : 
     492            0 :     errno = 0;
     493            0 :     if( sigaction( SIGINT, &act, 0 ) < 0 )
     494              :     {
     495            0 :         std::cerr << " (" << invokedName << "): error setting SIGINT handler: " << strerror( errno ) << "\n";
     496            0 :         return -1;
     497              :     }
     498              : 
     499              :     try
     500              :     {
     501            0 :         mx::error_t errc = prepareFiles();
     502            0 :         if( !!errc )
     503              :         {
     504            0 :             mx::error_report<verboseT>( errc, "error from prepareFiles" );
     505            0 :             return -1;
     506              :         }
     507              :     }
     508            0 :     catch( ... )
     509              :     {
     510            0 :         std::throw_with_nested( MagAOX::xwcException( "error from prepareFiles" ) );
     511            0 :     }
     512              : 
     513              :     // this has to be here
     514            0 :     stdFileNameT &firstFile = m_fileNames[0];
     515            0 :     stdFileNameT &lastFile  = m_fileNames.back();
     516              : 
     517              :     xrif_error_t rv;
     518            0 :     rv = xrif_new( &m_xrif );
     519              : 
     520            0 :     if( rv < 0 )
     521              :     {
     522            0 :         std::cerr << " (" << invokedName << "): Error allocating xrif.\n";
     523            0 :         return -1;
     524              :     }
     525              : 
     526            0 :     rv = xrif_new( &m_xrif_timing );
     527              : 
     528            0 :     if( rv < 0 )
     529              :     {
     530            0 :         std::cerr << " (" << invokedName << "): Error allocating xrif_timing.\n";
     531            0 :         return -1;
     532              :     }
     533              : 
     534            0 :     if( !m_noHeader )
     535              :     {
     536            0 :         m_logMetas.push_back( logMetaSpec( { firstFile.appName(), telem_stdcam::eventCode, "exptime" } ) );
     537              : 
     538              :         // Build list of apps, this will be automagic as part of config
     539            0 :         std::set<std::string> logApps;
     540              : 
     541            0 :         logApps.insert( m_fileNames[0].appName() );
     542              : 
     543            0 :         for( auto &meta : m_logMetas )
     544              :         {
     545            0 :             logApps.insert( meta.device() );
     546              :         }
     547              : 
     548            0 :         for( auto &app : logApps )
     549              :         {
     550            0 :             for( size_t n = 0; n < m_logDir.size(); ++n )
     551              :             {
     552              :                 try
     553              :                 {
     554            0 :                     m_logs.loadAppToFileMap( m_logDir[n], app, ".binlog", firstFile, lastFile );
     555              :                 }
     556            0 :                 catch( ... )
     557              :                 {
     558              :                     /// for now ignore all exceptions. \todo eventually ignore only "no prior logs" etc
     559            0 :                 }
     560              :             }
     561              : 
     562            0 :             for( size_t n = 0; n < m_telDir.size(); ++n )
     563              :             {
     564              :                 try
     565              :                 {
     566            0 :                     m_tels.loadAppToFileMap( m_telDir[n], app, ".bintel", firstFile, lastFile );
     567              :                 }
     568            0 :                 catch( ... )
     569              :                 {
     570              :                     /// for now ignore all exceptions. \todo eventually ignore only "no prior logs" etc
     571            0 :                 }
     572              :             }
     573              : 
     574            0 :             if( m_logs.m_appToFileMap[app].size() == 0 )
     575              :             {
     576            0 :                 throw MagAOX::xwcException( "no logs found for " + app );
     577              :             }
     578              : 
     579            0 :             if( m_tels.m_appToFileMap[app].size() == 0 )
     580              :             {
     581            0 :                 throw MagAOX::xwcException( "no telems found for " + app );
     582              :             }
     583              :         }
     584            0 :     }
     585              : 
     586              :     // Now de-compress and load the frames
     587              :     // Only decompressing the number of files needed, and only copying the number of frames needed
     588            0 :     for( size_t n = 0; n < m_files.size(); ++n )
     589              :     {
     590              : 
     591            0 :         if( g_timeToDie == true )
     592            0 :             break; // check before going on
     593              : 
     594            0 :         if( !m_noHeader )
     595              :         {
     596            0 :             m_tels.loadFiles( m_fileNames[n].appName(), m_fileNames[n].timestamp() );
     597              :         }
     598            0 :         if( !m_timesOnly )
     599              :         {
     600              : 
     601            0 :             std::cout << "******************************************************\n";
     602            0 :             std::cout << "* xrif2fits: decoding for " << m_fileNames[n].appName() << " (" + m_files[n] << ")\n";
     603            0 :             std::cout << "******************************************************\n";
     604              :         }
     605              : 
     606            0 :         FILE *fp_xrif = fopen( m_files[n].c_str(), "rb" );
     607            0 :         if( fp_xrif == nullptr )
     608              :         {
     609            0 :             std::cerr << " (" << invokedName << "): Error opening " << m_files[n] << "\n";
     610            0 :             std::cerr << " (" << invokedName << "): " << strerror( errno ) << "\n";
     611            0 :             return -1;
     612              :         }
     613              : 
     614              :         char header[XRIF_HEADER_SIZE];
     615              : 
     616            0 :         size_t nr = fread( header, 1, XRIF_HEADER_SIZE, fp_xrif );
     617            0 :         if( nr != XRIF_HEADER_SIZE )
     618              :         {
     619            0 :             std::cerr << " (" << invokedName << "): Error reading header of " << m_files[n] << "\n";
     620            0 :             fclose( fp_xrif );
     621            0 :             return -1;
     622              :         }
     623              : 
     624              :         uint32_t header_size;
     625            0 :         xrif_read_header( m_xrif, &header_size, header );
     626            0 :         if( !m_timesOnly )
     627              :         {
     628            0 :             std::cout << "xrif compression details:\n";
     629            0 :             std::cout << "  difference method:  " << xrif_difference_method_string( m_xrif->difference_method ) << '\n';
     630            0 :             std::cout << "  reorder method:     " << xrif_reorder_method_string( m_xrif->reorder_method ) << '\n';
     631            0 :             std::cout << "  compression method: " << xrif_compress_method_string( m_xrif->compress_method ) << '\n';
     632            0 :             if( m_xrif->compress_method == XRIF_COMPRESS_LZ4 )
     633              :             {
     634            0 :                 std::cout << "    LZ4 acceleration: " << m_xrif->lz4_acceleration << '\n';
     635              :             }
     636            0 :             std::cout << "  dimensions:         " << m_xrif->width << " x " << m_xrif->height << " x " << m_xrif->depth
     637            0 :                       << " x " << m_xrif->frames << "\n";
     638            0 :             std::cout << "  raw size:           "
     639            0 :                       << m_xrif->width * m_xrif->height * m_xrif->depth * m_xrif->frames * m_xrif->data_size
     640            0 :                       << " bytes\n";
     641            0 :             std::cout << "  encoded size:       " << m_xrif->compressed_size << " bytes\n";
     642            0 :             std::cout << "  ratio:              "
     643            0 :                       << ( (double)m_xrif->compressed_size ) /
     644            0 :                              ( m_xrif->width * m_xrif->height * m_xrif->depth * m_xrif->frames * m_xrif->data_size )
     645            0 :                       << '\n';
     646              :         }
     647            0 :         rv = xrif_allocate_raw( m_xrif );
     648            0 :         if( rv != XRIF_NOERROR )
     649              :         {
     650            0 :             std::cerr << " (" << invokedName << "): Error allocating raw buffer for " << m_files[n] << "\n";
     651            0 :             std::cerr << "\t code: " << rv << "\n";
     652            0 :             return -1;
     653              :         }
     654              : 
     655            0 :         rv = xrif_allocate_reordered( m_xrif );
     656            0 :         if( rv != XRIF_NOERROR )
     657              :         {
     658            0 :             std::cerr << " (" << invokedName << "): Error allocating reordered buffer for " << m_files[n] << "\n";
     659            0 :             std::cerr << "\t code: " << rv << "\n";
     660            0 :             return -1;
     661              :         }
     662              : 
     663            0 :         nr = fread( m_xrif->raw_buffer, 1, m_xrif->compressed_size, fp_xrif );
     664              : 
     665            0 :         if( nr != m_xrif->compressed_size )
     666              :         {
     667            0 :             std::cerr << " (" << invokedName << "): Error reading data from " << m_files[n] << "\n";
     668            0 :             return -1;
     669              :         }
     670              : 
     671              :         // Now get timing data
     672            0 :         nr = fread( header, 1, XRIF_HEADER_SIZE, fp_xrif );
     673            0 :         if( nr != XRIF_HEADER_SIZE )
     674              :         {
     675            0 :             std::cerr << " (" << invokedName << "): Error reading timing header of " << m_files[n] << "\n";
     676            0 :             fclose( fp_xrif );
     677            0 :             return -1;
     678              :         }
     679              : 
     680            0 :         xrif_read_header( m_xrif_timing, &header_size, header );
     681              : 
     682            0 :         if( !m_timesOnly )
     683              :         {
     684            0 :             std::cout << "xrif timing data compression details:\n";
     685            0 :             std::cout << "  difference method:  " << xrif_difference_method_string( m_xrif_timing->difference_method )
     686            0 :                       << '\n';
     687            0 :             std::cout << "  reorder method:     " << xrif_reorder_method_string( m_xrif_timing->reorder_method )
     688            0 :                       << '\n';
     689            0 :             std::cout << "  compression method: " << xrif_compress_method_string( m_xrif_timing->compress_method )
     690            0 :                       << '\n';
     691            0 :             if( m_xrif_timing->compress_method == XRIF_COMPRESS_LZ4 )
     692              :             {
     693            0 :                 std::cout << "    LZ4 acceleration: " << m_xrif_timing->lz4_acceleration << '\n';
     694              :             }
     695            0 :             std::cout << "  dimensions:         " << m_xrif_timing->width << " x " << m_xrif_timing->height << " x "
     696            0 :                       << m_xrif_timing->depth << " x " << m_xrif_timing->frames << "\n";
     697            0 :             std::cout << "  raw size:           "
     698            0 :                       << m_xrif_timing->width * m_xrif_timing->height * m_xrif_timing->depth * m_xrif_timing->frames *
     699            0 :                              m_xrif_timing->data_size
     700            0 :                       << " bytes\n";
     701            0 :             std::cout << "  encoded size:       " << m_xrif_timing->compressed_size << " bytes\n";
     702            0 :             std::cout << "  ratio:              "
     703            0 :                       << ( (double)m_xrif_timing->compressed_size ) /
     704            0 :                              ( m_xrif_timing->width * m_xrif_timing->height * m_xrif_timing->depth *
     705            0 :                                m_xrif_timing->frames * m_xrif_timing->data_size )
     706            0 :                       << '\n';
     707              :         }
     708            0 :         rv = xrif_allocate_raw( m_xrif_timing );
     709            0 :         if( rv != XRIF_NOERROR )
     710              :         {
     711            0 :             std::cerr << " (" << invokedName << "): Error allocating raw buffer for timing data from " << m_files[n]
     712            0 :                       << "\n";
     713            0 :             std::cerr << "\t code: " << rv << "\n";
     714            0 :             return -1;
     715              :         }
     716              : 
     717            0 :         rv = xrif_allocate_reordered( m_xrif_timing );
     718            0 :         if( rv != XRIF_NOERROR )
     719              :         {
     720            0 :             std::cerr << " (" << invokedName << "): Error allocating reordered buffer for  timing data from "
     721            0 :                       << m_files[n] << "\n";
     722            0 :             std::cerr << "\t code: " << rv << "\n";
     723            0 :             return -1;
     724              :         }
     725              : 
     726            0 :         nr = fread( m_xrif_timing->raw_buffer, 1, m_xrif_timing->compressed_size, fp_xrif );
     727              : 
     728            0 :         if( nr != m_xrif_timing->compressed_size )
     729              :         {
     730            0 :             std::cerr << " (" << invokedName << "): Error reading timing data from " << m_files[n] << "\n";
     731            0 :             return -1;
     732              :         }
     733              : 
     734            0 :         fclose( fp_xrif );
     735              : 
     736            0 :         if( g_timeToDie == true )
     737            0 :             break; // check after the long read.
     738              : 
     739            0 :         if( !m_metaOnly )
     740              :         {
     741            0 :             rv = xrif_decode( m_xrif );
     742            0 :             if( rv != XRIF_NOERROR )
     743              :             {
     744            0 :                 std::cerr << " (" << invokedName << "): Error decoding image data from " << m_files[n] << "\n";
     745            0 :                 std::cerr << "\t code: " << rv << "\n";
     746            0 :                 return -1;
     747              :             }
     748              :         }
     749              : 
     750            0 :         rv = xrif_decode( m_xrif_timing );
     751            0 :         if( rv != XRIF_NOERROR )
     752              :         {
     753            0 :             std::cerr << " (" << invokedName << "): Error decoding timing data from " << m_files[n] << "\n";
     754            0 :             std::cerr << "\t code: " << rv << "\n";
     755            0 :             return -1;
     756              :         }
     757              : 
     758            0 :         if( g_timeToDie == true )
     759              :         {
     760            0 :             break; // check after the decompress.
     761              :         }
     762              : 
     763            0 :         if( m_timesOnly )
     764              :         {
     765            0 :             std::cout << m_files[n] << " ";
     766            0 :             double totalExposureTime = 0;
     767              : 
     768            0 :             for( xrif_dimension_t q = 0; q < m_xrif->frames; ++q )
     769              :             {
     770              :                 timespec atime;            // This is the acquisition time of the exposure
     771            0 :                 timespec stime = { 0, 0 }; // This is the start time of the exposure, calculated as atime-exptime.
     772              : 
     773            0 :                 uint64_t *curr_timing = (uint64_t *)m_xrif_timing->raw_buffer + 5 * q;
     774              : 
     775            0 :                 atime.tv_sec  = curr_timing[1];
     776            0 :                 atime.tv_nsec = curr_timing[2];
     777              : 
     778              :                 // We have to bootstrap the exposure time
     779            0 :                 char *prior = nullptr;
     780            0 :                 m_tels.getPriorLog( prior, m_fileNames[n].appName(), eventCodes::TELEM_STDCAM, atime );
     781            0 :                 double exptime = -1;
     782            0 :                 if( prior )
     783              :                 {
     784            0 :                     char *priorprior = nullptr;
     785            0 :                     exptime          = telem_stdcam::exptime( logHeader::messageBuffer( prior ) );
     786            0 :                     stime            = atime - exptime;
     787            0 :                     m_tels.getPriorLog( priorprior, m_fileNames[n].appName(), eventCodes::TELEM_STDCAM, stime );
     788              : 
     789              :                     ///\todo this needs to check for any log entries between end and start
     790            0 :                     if( telem_stdcam::exptime( logHeader::messageBuffer( priorprior ) ) != exptime )
     791              :                     {
     792            0 :                         std::cerr << "Change in exposure time mid-exposure\n";
     793              :                     }
     794              :                 }
     795              :                 else
     796              :                 {
     797            0 :                     std::cerr << "no prior\n";
     798              :                 }
     799            0 :                 totalExposureTime += exptime;
     800              : 
     801            0 :                 std::string timestamp;
     802            0 :                 mx::sys::timeStamp( timestamp, atime );
     803              : 
     804            0 :                 std::string dateobs = mx::sys::ISO8601DateTimeStr( atime, 1 );
     805            0 :                 if( q == 0 )
     806              :                 {
     807            0 :                     std::cout << dateobs << " ";
     808              :                 }
     809            0 :                 if( q == ( m_xrif->frames - 1 ) )
     810              :                 {
     811            0 :                     std::cout << dateobs << " " << totalExposureTime << " " << m_xrif->frames << "\n";
     812              :                 }
     813            0 :             }
     814              :         }
     815              :         else // Normal writing
     816              :         {
     817            0 :             if( m_xrif->type_code == XRIF_TYPECODE_UINT8 )
     818              :             {
     819            0 :                 if( writeImages<uint8_t>( n, m_fileNames[n] ) < 0 )
     820              :                 {
     821            0 :                     ERR_INVOKED_NAME( "error writing to file: " + m_files[n] );
     822            0 :                     return -1;
     823              :                 }
     824              :             }
     825            0 :             else if( m_xrif->type_code == XRIF_TYPECODE_INT8 )
     826              :             {
     827            0 :                 if( writeImages<int8_t>( n, m_fileNames[n] ) < 0 )
     828              :                 {
     829            0 :                     ERR_INVOKED_NAME( "error writing to file: " + m_files[n] );
     830            0 :                     return -1;
     831              :                 }
     832              :             }
     833            0 :             if( m_xrif->type_code == XRIF_TYPECODE_UINT16 )
     834              :             {
     835            0 :                 if( writeImages<uint16_t>( n, m_fileNames[n] ) < 0 )
     836              :                 {
     837            0 :                     ERR_INVOKED_NAME( "error writing to file: " + m_files[n] );
     838            0 :                     return -1;
     839              :                 }
     840              :             }
     841            0 :             else if( m_xrif->type_code == XRIF_TYPECODE_INT16 )
     842              :             {
     843            0 :                 if( writeImages<int16_t>( n, m_fileNames[n] ) < 0 )
     844              :                 {
     845            0 :                     ERR_INVOKED_NAME( "error writing to file: " + m_files[n] );
     846            0 :                     return -1;
     847              :                 }
     848              :             }
     849            0 :             else if( m_xrif->type_code == XRIF_TYPECODE_UINT32 )
     850              :             {
     851            0 :                 if( writeImages<uint32_t>( n, m_fileNames[n] ) < 0 )
     852              :                 {
     853            0 :                     ERR_INVOKED_NAME( "error writing to file: " + m_files[n] );
     854            0 :                     return -1;
     855              :                 }
     856              :             }
     857            0 :             else if( m_xrif->type_code == XRIF_TYPECODE_INT32 )
     858              :             {
     859            0 :                 if( writeImages<int32_t>( n, m_fileNames[n] ) < 0 )
     860              :                 {
     861            0 :                     ERR_INVOKED_NAME( "error writing to file: " + m_files[n] );
     862            0 :                     return -1;
     863              :                 }
     864              :             }
     865            0 :             else if( m_xrif->type_code == XRIF_TYPECODE_UINT64 )
     866              :             {
     867            0 :                 if( writeImages<uint32_t>( n, m_fileNames[n] ) < 0 )
     868              :                 {
     869            0 :                     ERR_INVOKED_NAME( "error writing to file: " + m_files[n] );
     870            0 :                     return -1;
     871              :                 }
     872              :             }
     873            0 :             else if( m_xrif->type_code == XRIF_TYPECODE_INT64 )
     874              :             {
     875            0 :                 if( writeImages<int32_t>( n, m_fileNames[n] ) < 0 )
     876              :                 {
     877            0 :                     ERR_INVOKED_NAME( "error writing to file: " + m_files[n] );
     878            0 :                     return -1;
     879              :                 }
     880              :             }
     881            0 :             else if( m_xrif->type_code == XRIF_TYPECODE_FLOAT )
     882              :             {
     883            0 :                 if( writeImages<float>( n, m_fileNames[n] ) < 0 )
     884              :                 {
     885            0 :                     ERR_INVOKED_NAME( "error writing to file: " + m_files[n] );
     886            0 :                     return -1;
     887              :                 }
     888              :             }
     889            0 :             else if( m_xrif->type_code == XRIF_TYPECODE_DOUBLE )
     890              :             {
     891            0 :                 if( writeImages<float>( n, m_fileNames[n] ) < 0 )
     892              :                 {
     893            0 :                     ERR_INVOKED_NAME( "error writing to file: " + m_files[n] );
     894            0 :                     return -1;
     895              :                 }
     896              :             }
     897              :             else
     898              :             {
     899            0 :                 ERR_INVOKED_NAME( "unsupported data type in file: " + m_files[n] );
     900            0 :                 return -1;
     901              :             }
     902              :         }
     903              :     }
     904              : 
     905            0 :     std::cerr << " (" << invokedName << "): exited normally.\n";
     906              : 
     907            0 :     return 0;
     908              : }
     909              : 
     910            0 : inline mx::error_t xrif2fits::prepareFiles()
     911              : {
     912              :     // If files aren't specified, we search the given directory.
     913            0 :     if( m_files.size() == 0 )
     914              :     {
     915            0 :         if( m_dir == "" ) // search pwd
     916              :         {
     917            0 :             m_dir = "./";
     918              :         }
     919              : 
     920            0 :         mx_error_check( mx::ioutils::getFileNames( m_files, m_dir, "", "", ".xrif" ) );
     921              : 
     922            0 :         for( size_t n = 0; n < m_files.size(); ++n )
     923              :         {
     924            0 :             MagAOX::file::stdFileName sfn;
     925              :             try
     926              :             {
     927            0 :                 mx_error_check( sfn.fullName( m_files[n] ) );
     928              :             }
     929            0 :             catch( ... )
     930              :             {
     931            0 :                 std::throw_with_nested( mx::exception( "From stdFileName for " + m_files[n] ) );
     932            0 :             }
     933              : 
     934              :             // add only if it passed
     935              :             try
     936              :             {
     937            0 :                 m_fileNames.push_back( sfn );
     938              :             }
     939            0 :             catch( const std::bad_alloc &e )
     940              :             {
     941            0 :                 std::throw_with_nested(
     942            0 :                     mx::exception( mx::error_t::std_bad_alloc, "error adding file " + m_files[n] ) );
     943            0 :             }
     944            0 :             catch( ... )
     945              :             {
     946            0 :                 std::throw_with_nested( mx::exception( "error adding file " + m_files[n] ) );
     947            0 :             }
     948            0 :         }
     949              :     }
     950              :     else // If files are specified we attach a directory to them if needed
     951              :     {
     952            0 :         if( m_dir != "" )
     953              :         {
     954              :             try
     955              :             {
     956            0 :                 if( m_dir[m_dir.size() - 1] != '/' )
     957            0 :                     m_dir += '/';
     958              : 
     959            0 :                 for( size_t n = 0; n < m_files.size(); ++n )
     960              :                 {
     961            0 :                     m_files[n] = m_dir + m_files[n];
     962              :                 }
     963              :             }
     964            0 :             catch( const std::bad_alloc &e )
     965              :             {
     966            0 :                 std::throw_with_nested( mx::exception( mx::error_t::std_bad_alloc, "adding dir to files" ) );
     967            0 :             }
     968            0 :             catch( ... )
     969              :             {
     970            0 :                 std::throw_with_nested( mx::exception("adding dir to files") );
     971            0 :             }
     972              :         }
     973              : 
     974            0 :         for( size_t n = 0; n < m_files.size(); ++n )
     975              :         {
     976              :             // since the files were specified they have to pass
     977              :             try
     978              :             {
     979            0 :                 m_fileNames.push_back( MagAOX::file::stdFileName( m_files[n] ) );
     980              :             }
     981            0 :             catch( const std::bad_alloc &e )
     982              :             {
     983            0 :                 std::throw_with_nested(
     984            0 :                     mx::exception( mx::error_t::std_bad_alloc, "error adding file " + m_files[n] ) );
     985            0 :             }
     986            0 :             catch( ... )
     987              :             {
     988            0 :                 std::throw_with_nested( mx::exception( "error adding file " + m_files[n] ) );
     989            0 :             }
     990              :         }
     991              :     }
     992              : 
     993            0 :     if( m_files.size() == 0 )
     994              :     {
     995            0 :         return mx::error_report<verboseT>( mx::error_t::notfound, "No xrif files found" );
     996              :     }
     997              : 
     998            0 :     if( m_outDir == "" )
     999              :     {
    1000            0 :         m_outDir = "./";
    1001              :     }
    1002              :     else
    1003              :     {
    1004              :         // Make sure the slash exists, then mkdir.  We know size is > 0 here.
    1005            0 :         if( m_outDir[m_outDir.size() - 1] != '/' )
    1006              :         {
    1007            0 :             m_outDir += '/';
    1008              :         }
    1009              : 
    1010            0 :         if( !m_timesOnly )
    1011              :         {
    1012            0 :             if(!m_overWriteDir)
    1013              :             {
    1014              :                 mx::error_t errc;
    1015            0 :                 if(mx::ioutils::dir_exists_is(m_outDir, errc))
    1016              :                 {
    1017            0 :                     return mx::error_report<verboseT>(mx::error_t::eexist, "Directory " + m_outDir + " already exists.");
    1018              :                 }
    1019              : 
    1020            0 :                 if(!!errc)
    1021              :                 {
    1022            0 :                     return mx::error_report<verboseT>(errc, "Checking " + m_outDir);
    1023              :                 }
    1024              :             }
    1025              : 
    1026            0 :             mx_error_check( mx::ioutils::createDirectories(m_outDir) );
    1027              :         }
    1028              :     }
    1029              : 
    1030            0 :     for( size_t n = 1; n < m_files.size(); ++n )
    1031              :     {
    1032            0 :         if( m_fileNames.back().appName() != m_fileNames[0].appName() )
    1033              :         {
    1034            0 :             return mx::error_report<verboseT>(mx::error_t::invalidarg, "can only operate on a single camera at a time" );
    1035              :         }
    1036              :     }
    1037              : 
    1038            0 :     if( m_camera == "" )
    1039              :     {
    1040            0 :         m_camera = m_fileNames[0].appName();
    1041            0 :         std::cerr << "Set camera to: " << m_camera << '\n';
    1042              :     }
    1043              : 
    1044            0 :     if( m_cameraHeader == "" )
    1045              :     {
    1046            0 :         m_cameraHeader = m_camera + "_header.conf";
    1047            0 :         std::cerr << "Set camera header to: " << m_cameraHeader << '\n';
    1048              :     }
    1049              : 
    1050            0 :     if( !m_noHeader )
    1051              :     {
    1052            0 :         mx::error_t errc = readHeaderConfig( mx::app::application::m_configPathCLBase + m_cameraHeader );
    1053              : 
    1054            0 :         if( !!errc )
    1055              :         {
    1056            0 :             return mx::error_report<verboseT>(
    1057            0 :                 errc, "Error reading camera header: " + mx::app::application::m_configPathCLBase + m_cameraHeader );
    1058              : 
    1059              :         }
    1060              :     }
    1061              : 
    1062            0 :     return mx::error_t::noerror;
    1063              : }
    1064              : 
    1065              : template <typename dataT>
    1066            0 : int xrif2fits::writeImages( int n, stdFileNameT &lfn )
    1067              : {
    1068            0 :     mx::improc::eigenCube<dataT> tmpc(
    1069            0 :         reinterpret_cast<dataT *>( m_xrif->raw_buffer ), m_xrif->width, m_xrif->height, m_xrif->frames );
    1070              : 
    1071            0 :     mx::fits::fitsFile<dataT, verboseT> ff;
    1072            0 :     mx::fits::fitsHeader<verboseT>      fh;
    1073              : 
    1074              :     // Special handling for meta output
    1075            0 :     logMeta exptimeMeta( logMetaSpec( lfn.appName(), telem_stdcam::eventCode, "exptime" ) );
    1076              : 
    1077            0 :     std::ofstream metaOut;
    1078              : 
    1079              :     // Print the meta-file header
    1080            0 :     if( !m_noMeta && !m_timesOnly )
    1081              :     {
    1082            0 :         metaOut.open( m_outDir + "meta_data.txt" );
    1083              :         /*metaOut << "#DATE-OBS FRAMENO ACQSEC ACQNSEC WRTSEC WRTNSEC";
    1084              :         metaOut << " EXPTIME";
    1085              :         for(size_t u=0;u<logMetas.size();++u)
    1086              :         {
    1087              :            metaOut << " " << logMetas[u].keyword() ;
    1088              :         }
    1089              :         metaOut << "\n";*/
    1090              :     }
    1091            0 :     if( m_cubeMode )
    1092              :     {
    1093            0 :         std::string outfname = m_outDir + mx::ioutils::pathStem( m_files[n] ) + ".fits";
    1094            0 :         ff.write( outfname, tmpc );
    1095            0 :     }
    1096              :     else
    1097              :     {
    1098            0 :         for( int q = 0; q < tmpc.planes(); ++q )
    1099              :         {
    1100              :             uint64_t cnt0;
    1101              :             timespec atime; // This is the acquisition time of the exposure
    1102              :             timespec wtime;
    1103            0 :             timespec stime = { 0, 0 }; // This is the start time of the exposure, calculated as atime-exptime.
    1104              : 
    1105            0 :             uint64_t *curr_timing = (uint64_t *)m_xrif_timing->raw_buffer + 5 * q;
    1106              : 
    1107            0 :             cnt0          = curr_timing[0];
    1108            0 :             atime.tv_sec  = curr_timing[1];
    1109            0 :             atime.tv_nsec = curr_timing[2];
    1110            0 :             wtime.tv_sec  = curr_timing[3];
    1111            0 :             wtime.tv_nsec = curr_timing[4];
    1112              : 
    1113            0 :             double exptime = -1;
    1114            0 :             if( !m_noHeader )
    1115              :             {
    1116              :                 // We have to bootstrap the exposure time
    1117            0 :                 char *prior = nullptr;
    1118            0 :                 m_tels.getPriorLog( prior, lfn.appName(), eventCodes::TELEM_STDCAM, atime );
    1119              : 
    1120            0 :                 if( prior )
    1121              :                 {
    1122            0 :                     char *priorprior = nullptr;
    1123            0 :                     exptime          = telem_stdcam::exptime( logHeader::messageBuffer( prior ) );
    1124              : 
    1125            0 :                     stime = atime - exptime;
    1126            0 :                     m_tels.getPriorLog( priorprior, lfn.appName(), eventCodes::TELEM_STDCAM, stime );
    1127              : 
    1128              :                     ///\todo this needs to check for any log entries between end and start
    1129            0 :                     if( telem_stdcam::exptime( logHeader::messageBuffer( priorprior ) ) != exptime )
    1130              :                     {
    1131            0 :                         std::cerr << "Change in exposure time mid-exposure\n";
    1132              :                     }
    1133              :                 }
    1134              :                 else
    1135              :                 {
    1136            0 :                     std::cerr << "no prior\n";
    1137              :                 }
    1138              :             }
    1139              : 
    1140              :             // timespecX midexp = mx::meanTimespec( atime, stime);
    1141              : 
    1142            0 :             std::string timestamp;
    1143            0 :             mx::sys::timeStamp( timestamp, atime );
    1144            0 :             std::string outfname = m_outDir + lfn.appName() + "_" + timestamp + ".fits";
    1145              : 
    1146            0 :             fh.clear();
    1147              : 
    1148            0 :             std::string dateobs = mx::sys::ISO8601DateTimeStr( atime, 1 );
    1149              : 
    1150            0 :             fh.append( "DATE-OBS", dateobs, "Date of obs. YYYY-mm-ddTHH:MM:SS" );
    1151            0 :             fh.append( "INSTRUME", "MagAO-X " + lfn.appName() );
    1152            0 :             fh.append( "CAMERA", lfn.appName() );
    1153            0 :             fh.append( "TELESCOP", "Magellan Clay, Las Campanas Obs." );
    1154              : 
    1155            0 :             if( !m_noMeta )
    1156              :             {
    1157            0 :                 metaOut << dateobs << " " << cnt0 << " " << atime.tv_sec << " " << format_nano( atime.tv_nsec ) << " "
    1158            0 :                         << wtime.tv_sec << " " << format_nano( wtime.tv_nsec ) << " ";
    1159              :             }
    1160              : 
    1161            0 :             if( exptime > -1 )
    1162              :             {
    1163              :                 // First output exposure time
    1164            0 :                 if( !m_noMeta )
    1165              :                 {
    1166            0 :                     metaOut << exptimeMeta.value( m_tels, stime, atime );
    1167              :                 }
    1168              : 
    1169              :                 // Then output each value in turn
    1170            0 :                 for( size_t u = 0; u < m_logMetas.size(); ++u )
    1171              :                 {
    1172            0 :                     mx::fits::fitsHeaderCard<verboseT> fc = m_logMetas[u].card( m_tels, stime, atime );
    1173            0 :                     fh.append( fc );
    1174            0 :                     if( !m_noMeta )
    1175              :                     {
    1176            0 :                         metaOut << " " << m_logMetas[u].value( m_tels, stime, atime );
    1177              :                     }
    1178              :                 }
    1179              :             }
    1180              : 
    1181            0 :             fh.append( "FRAMENO", cnt0 );
    1182            0 :             fh.append( "ACQSEC", atime.tv_sec, "Image acq. time, seconds since Unix epoch" );
    1183            0 :             fh.append( "ACQNSEC", atime.tv_nsec, "Image acq. time, nanosecond component" );
    1184            0 :             fh.append( "WRTSEC", wtime.tv_sec, "Image write time, seconds since Unix epoch" );
    1185            0 :             fh.append( "WRTNSEC", wtime.tv_nsec, "Image write time, nanosecond component" );
    1186              : 
    1187            0 :             if( !m_noMeta )
    1188              :             {
    1189            0 :                 metaOut << "\n";
    1190              :             }
    1191            0 :             if( !m_metaOnly )
    1192              :             {
    1193            0 :                 mx::improc::eigenImage<dataT> im = tmpc.image( q );
    1194            0 :                 ff.write( outfname, tmpc.image( q ), fh );
    1195            0 :             }
    1196              :         }
    1197              :     }
    1198              : 
    1199            0 :     return 0;
    1200            0 : }
    1201              : 
    1202            0 : inline std::string xrif2fits::format_nano( uint64_t n )
    1203              : {
    1204            0 :     std::ostringstream oss;
    1205            0 :     oss << std::setw( 9 ) << std::setfill( '0' ) << n;
    1206            0 :     return oss.str();
    1207            0 : };
    1208              : 
    1209              : #endif // xrif2fits_hpp
        

Generated by: LCOV version 2.0-1