LCOV - code coverage report
Current view: top level - apps/cred2Ctrl - cred2Utils.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 96.3 % 136 131
Test Date: 2026-04-15 19:34:29 Functions: 100.0 % 15 15

            Line data    Source code
       1              : /** \file cred2Utils.hpp
       2              :  * \brief Utilities for the C-RED 2 camera controller.
       3              :  *
       4              :  * \author Jared R. Males (jaredmales@gmail.com)
       5              :  *
       6              :  * \ingroup cred2Ctrl_files
       7              :  */
       8              : 
       9              : #ifndef cred2Utils_hpp
      10              : #define cred2Utils_hpp
      11              : 
      12              : #include <algorithm>
      13              : #include <cctype>
      14              : #include <cmath>
      15              : #include <cstdlib>
      16              : #include <string>
      17              : #include <vector>
      18              : 
      19              : #include <mx/ioutils/stringUtils.hpp>
      20              : 
      21              : namespace MagAOX
      22              : {
      23              : namespace app
      24              : {
      25              : 
      26              : /// Structure holding the temperature values reported by the C-RED 2.
      27              : /**
      28              :  * \ingroup cred2Ctrl
      29              :  */
      30              : struct cred2Temps
      31              : {
      32              :     float motherboard{ 0 }; ///< Motherboard temperature [C].
      33              :     float frontend{ 0 };    ///< Front-end temperature [C].
      34              :     float powerboard{ 0 };  ///< Power-board temperature [C].
      35              :     float snake{ 0 };       ///< Detector temperature [C].
      36              :     float setpoint{ 0 };    ///< Detector temperature setpoint [C].
      37              :     float peltier{ 0 };     ///< External TEC temperature [C].
      38              :     float heatsink{ 0 };    ///< Heatsink temperature [C].
      39              : 
      40              :     /// Compare two cached temperature sets.
      41           18 :     bool operator==( const cred2Temps &t /**< [in] the values to compare against */ ) const
      42              :     {
      43           15 :         return motherboard == t.motherboard && frontend == t.frontend && powerboard == t.powerboard &&
      44           33 :                snake == t.snake && setpoint == t.setpoint && peltier == t.peltier && heatsink == t.heatsink;
      45              :     }
      46              : 
      47              :     /// Mark all temperature values invalid.
      48          118 :     int setInvalid()
      49              :     {
      50          118 :         motherboard = -999;
      51          118 :         frontend    = -999;
      52          118 :         powerboard  = -999;
      53          118 :         snake       = -999;
      54          118 :         setpoint    = -999;
      55          118 :         peltier     = -999;
      56          118 :         heatsink    = -999;
      57              : 
      58          118 :         return 0;
      59              :     }
      60              : };
      61              : 
      62              : /// C-RED 2 ROI expressed as 0-based inclusive column and row limits.
      63              : /**
      64              :  * \ingroup cred2Ctrl
      65              :  */
      66              : struct cred2Roi
      67              : {
      68              :     int  startColumn{ 0 };  ///< First included column.
      69              :     int  endColumn{ 0 };    ///< Last included column.
      70              :     int  startRow{ 0 };     ///< First included row.
      71              :     int  endRow{ 0 };       ///< Last included row.
      72              :     bool fullFrame{ true }; ///< True when the ROI spans the full detector.
      73              : };
      74              : 
      75              : /// Strip an optional prompt and surrounding whitespace from a C-RED 2 CLI response.
      76          511 : inline std::string cred2CleanResponse( const std::string &response /**< [in] raw CLI response */ )
      77              : {
      78          511 :     std::string clean = response;
      79              : 
      80          511 :     size_t promptPos = clean.find( "fli-cli>" );
      81          511 :     if( promptPos != std::string::npos )
      82              :     {
      83            7 :         clean.erase( promptPos );
      84              :     }
      85              : 
      86          511 :     size_t first = 0;
      87          512 :     while( first < clean.size() && std::isspace( static_cast<unsigned char>( clean[first] ) ) )
      88              :     {
      89            1 :         ++first;
      90              :     }
      91              : 
      92          511 :     size_t last = clean.size();
      93          704 :     while( last > first && std::isspace( static_cast<unsigned char>( clean[last - 1] ) ) )
      94              :     {
      95          193 :         --last;
      96              :     }
      97              : 
      98         1022 :     return clean.substr( first, last - first );
      99          511 : }
     100              : 
     101              : /// Parse a raw numeric response into a float.
     102          172 : inline int cred2ParseFloat( float             &value,   ///< [out] parsed floating-point value
     103              :                             const std::string &response /**< [in] raw or cleaned CLI response */
     104              : )
     105              : {
     106          172 :     std::string clean = cred2CleanResponse( response );
     107          172 :     if( clean.empty() )
     108              :     {
     109            2 :         return -1;
     110              :     }
     111              : 
     112          170 :     char *end = nullptr;
     113          170 :     value     = std::strtof( clean.c_str(), &end );
     114              : 
     115          170 :     if( end == clean.c_str() )
     116              :     {
     117           14 :         return -1;
     118              :     }
     119              : 
     120          157 :     while( end != nullptr && *end != '\0' && std::isspace( static_cast<unsigned char>( *end ) ) )
     121              :     {
     122            1 :         ++end;
     123              :     }
     124              : 
     125          156 :     if( end != nullptr && *end != '\0' )
     126              :     {
     127            1 :         return -1;
     128              :     }
     129              : 
     130          155 :     return 0;
     131          172 : }
     132              : 
     133              : /// Parse a delimited list of raw numeric responses into a float vector.
     134           21 : inline int cred2ParseFloatVector( std::vector<float> &values,        ///< [out] parsed floating-point values
     135              :                                   const std::string  &response,      /**< [in] raw or cleaned CLI response */
     136              :                                   size_t              expectedValues /**< [in] expected number of parsed values, or 0 */
     137              : )
     138              : {
     139           21 :     std::string              clean = cred2CleanResponse( response );
     140           21 :     std::vector<std::string> tokens;
     141              : 
     142           21 :     mx::ioutils::parseStringVector( tokens, clean, ":, \t\r\n" );
     143              : 
     144           21 :     if( tokens.empty() )
     145              :     {
     146            0 :         return -1;
     147              :     }
     148              : 
     149           21 :     if( expectedValues > 0 && tokens.size() != expectedValues )
     150              :     {
     151            2 :         return -1;
     152              :     }
     153              : 
     154           19 :     values.clear();
     155           19 :     values.reserve( tokens.size() );
     156              : 
     157          119 :     for( const auto &token : tokens )
     158              :     {
     159          102 :         float value = 0;
     160              : 
     161          102 :         if( cred2ParseFloat( value, token ) < 0 )
     162              :         {
     163            2 :             values.clear();
     164            2 :             return -1;
     165              :         }
     166              : 
     167          100 :         values.push_back( value );
     168              :     }
     169              : 
     170           17 :     return 0;
     171           21 : }
     172              : 
     173              : /// Parse a raw on/off response into a boolean.
     174           38 : inline int cred2ParseBool( bool              &value,   ///< [out] parsed boolean value
     175              :                            const std::string &response /**< [in] raw or cleaned CLI response */
     176              : )
     177              : {
     178           38 :     std::string clean = cred2CleanResponse( response );
     179           38 :     std::transform( clean.begin(),
     180              :                     clean.end(),
     181              :                     clean.begin(),
     182          148 :                     []( unsigned char c ) { return static_cast<char>( std::tolower( c ) ); } );
     183              : 
     184           38 :     if( clean == "on" || clean == "true" || clean == "1" )
     185              :     {
     186           14 :         value = true;
     187           14 :         return 0;
     188              :     }
     189              : 
     190           24 :     if( clean == "off" || clean == "false" || clean == "0" )
     191              :     {
     192           11 :         value = false;
     193           11 :         return 0;
     194              :     }
     195              : 
     196           13 :     return -1;
     197           38 : }
     198              : 
     199              : /// Parse a raw range response such as `0-639`.
     200              : inline int cred2ParseRange( int               &firstValue,  ///< [out] first parsed range value
     201              :                             int               &secondValue, ///< [out] second parsed range value
     202              :                             const std::string &response     /**< [in] raw or cleaned CLI response */
     203              : );
     204              : 
     205              : /// Parse a raw cropping status response such as `on` or `on:192-447:128-383`.
     206           23 : inline int cred2ParseCropState( bool              &enabled,     ///< [out] parsed cropping-enabled flag
     207              :                                 int               &startColumn, ///< [out] parsed first included column
     208              :                                 int               &endColumn,   ///< [out] parsed last included column
     209              :                                 int               &startRow,    ///< [out] parsed first included row
     210              :                                 int               &endRow,      ///< [out] parsed last included row
     211              :                                 const std::string &response     /**< [in] raw or cleaned CLI response */
     212              : )
     213              : {
     214           23 :     std::string              clean = cred2CleanResponse( response );
     215           23 :     std::vector<std::string> tokens;
     216              : 
     217           23 :     mx::ioutils::parseStringVector( tokens, clean, ":" );
     218              : 
     219           23 :     if( tokens.empty() )
     220              :     {
     221            0 :         return -1;
     222              :     }
     223              : 
     224           23 :     if( cred2ParseBool( enabled, tokens[0] ) < 0 )
     225              :     {
     226            5 :         return -1;
     227              :     }
     228              : 
     229           18 :     if( tokens.size() == 1 )
     230              :     {
     231           13 :         startColumn = 0;
     232           13 :         endColumn   = 0;
     233           13 :         startRow    = 0;
     234           13 :         endRow      = 0;
     235           13 :         return 0;
     236              :     }
     237              : 
     238            5 :     if( tokens.size() != 3 )
     239              :     {
     240            1 :         return -1;
     241              :     }
     242              : 
     243            4 :     if( cred2ParseRange( startColumn, endColumn, tokens[1] ) < 0 || cred2ParseRange( startRow, endRow, tokens[2] ) < 0 )
     244              :     {
     245            1 :         return -1;
     246              :     }
     247              : 
     248            3 :     return 0;
     249           23 : }
     250              : 
     251              : /// Parse a raw range response such as `0-639`.
     252           15 : inline int cred2ParseRange( int               &firstValue,  ///< [out] first parsed range value
     253              :                             int               &secondValue, ///< [out] second parsed range value
     254              :                             const std::string &response     /**< [in] raw or cleaned CLI response */
     255              : )
     256              : {
     257           15 :     std::string              clean = cred2CleanResponse( response );
     258           15 :     std::vector<std::string> tokens;
     259           15 :     mx::ioutils::parseStringVector( tokens, clean, "-, \t\r\n" );
     260              : 
     261           15 :     if( tokens.size() != 2 )
     262              :     {
     263            4 :         return -1;
     264              :     }
     265              : 
     266              :     try
     267              :     {
     268           11 :         firstValue  = mx::ioutils::stoT<int>( tokens[0] );
     269           11 :         secondValue = mx::ioutils::stoT<int>( tokens[1] );
     270              :     }
     271            0 :     catch( ... )
     272              :     {
     273            0 :         return -1;
     274            0 :     }
     275              : 
     276           11 :     return 0;
     277           15 : }
     278              : 
     279              : /// Check whether a command response looks successful.
     280           23 : inline bool cred2ResponseOK( const std::string &response /**< [in] raw or cleaned CLI response */ )
     281              : {
     282           23 :     std::string clean = cred2CleanResponse( response );
     283           23 :     std::string lower = clean;
     284           23 :     std::transform( lower.begin(),
     285              :                     lower.end(),
     286              :                     lower.begin(),
     287           85 :                     []( unsigned char c ) { return static_cast<char>( std::tolower( c ) ); } );
     288              : 
     289           23 :     if( lower.find( "error" ) != std::string::npos || lower.find( "fail" ) != std::string::npos )
     290              :     {
     291           11 :         return false;
     292              :     }
     293              : 
     294           12 :     return true;
     295           23 : }
     296              : 
     297              : /// Convert a MagAO-X ROI center/size description into C-RED 2 corners.
     298           10 : inline int cred2RoiFromCenter( cred2Roi &roi,       ///< [out] the corresponding C-RED 2 ROI
     299              :                                float     centerX,   /**< [in] ROI x center coordinate */
     300              :                                float     centerY,   /**< [in] ROI y center coordinate */
     301              :                                int       width,     /**< [in] ROI width in pixels */
     302              :                                int       height,    /**< [in] ROI height in pixels */
     303              :                                int       fullWidth, /**< [in] detector full-frame width */
     304              :                                int       fullHeight /**< [in] detector full-frame height */
     305              : )
     306              : {
     307           10 :     if( width < 1 || height < 1 || fullWidth < 1 || fullHeight < 1 )
     308              :     {
     309            1 :         return -1;
     310              :     }
     311              : 
     312            9 :     roi.startColumn = static_cast<int>( std::lround( centerX - 0.5f * ( static_cast<float>( width ) - 1.0f ) ) );
     313            9 :     roi.endColumn   = roi.startColumn + width - 1;
     314            9 :     roi.startRow    = static_cast<int>( std::lround( centerY - 0.5f * ( static_cast<float>( height ) - 1.0f ) ) );
     315            9 :     roi.endRow      = roi.startRow + height - 1;
     316              : 
     317            9 :     if( roi.startColumn < 0 || roi.startRow < 0 || roi.endColumn >= fullWidth || roi.endRow >= fullHeight )
     318              :     {
     319            1 :         return -1;
     320              :     }
     321              : 
     322            8 :     roi.fullFrame =
     323            8 :         roi.startColumn == 0 && roi.endColumn == fullWidth - 1 && roi.startRow == 0 && roi.endRow == fullHeight - 1;
     324              : 
     325            8 :     return 0;
     326              : }
     327              : 
     328              : /// Convert C-RED 2 ROI corners into a MagAO-X ROI center/size description.
     329            6 : inline int cred2RoiToCenter( float          &centerX,   ///< [out] ROI x center coordinate
     330              :                              float          &centerY,   ///< [out] ROI y center coordinate
     331              :                              int            &width,     ///< [out] ROI width in pixels
     332              :                              int            &height,    ///< [out] ROI height in pixels
     333              :                              const cred2Roi &roi,       /**< [in] the C-RED 2 ROI to convert */
     334              :                              int             fullWidth, /**< [in] detector full-frame width */
     335              :                              int             fullHeight /**< [in] detector full-frame height */
     336              : )
     337              : {
     338            6 :     if( roi.startColumn < 0 || roi.startRow < 0 || roi.endColumn < roi.startColumn || roi.endRow < roi.startRow ||
     339            4 :         roi.endColumn >= fullWidth || roi.endRow >= fullHeight )
     340              :     {
     341            2 :         return -1;
     342              :     }
     343              : 
     344            4 :     width  = roi.endColumn - roi.startColumn + 1;
     345            4 :     height = roi.endRow - roi.startRow + 1;
     346              : 
     347            4 :     centerX = roi.startColumn + 0.5f * ( static_cast<float>( width ) - 1.0f );
     348            4 :     centerY = roi.startRow + 0.5f * ( static_cast<float>( height ) - 1.0f );
     349              : 
     350            4 :     return 0;
     351              : }
     352              : 
     353              : /// Format the column command payload for `set cropping columns`.
     354            3 : inline std::string cred2ColumnsSpec( const cred2Roi &roi /**< [in] the ROI to format */ )
     355              : {
     356            3 :     return std::to_string( roi.startColumn ) + "-" + std::to_string( roi.endColumn );
     357              : }
     358              : 
     359              : /// Format the row command payload for `set cropping rows`.
     360            3 : inline std::string cred2RowsSpec( const cred2Roi &roi /**< [in] the ROI to format */ )
     361              : {
     362            3 :     return std::to_string( roi.startRow ) + "-" + std::to_string( roi.endRow );
     363              : }
     364              : 
     365              : } // namespace app
     366              : } // namespace MagAOX
     367              : 
     368              : #endif // cred2Utils_hpp
        

Generated by: LCOV version 2.0-1