#include "core/config.h"
#if CONFIG_NFMBASECLASS
#include <stdio.h>
#include <string.h>
#include "core/main.h" // _snprintf
#include "core/config_pins.h"
#include "crc32.h"
#include "drivers/keycontrol/keycontrol_ll.h"
#include "drivers/power_management/power_management.h"
#include "app/nfm/nfm_base_excls.h"
#include "app/thermo/tsensor.h"
#include "app/led/led.h"
#include "app/thermo/NTCtsensor.h"
#include "app/version/version.h"
#include <stdlib.h> // atoi[NFMCLASS_ENABLE__SERIAL_NUMBER_LEGACY]
#include <ctype.h> // tolower
#define NFMCLASS_ENABLE__SERIAL_NUMBER_LEGACY  0
#include "my_assert.h"
#include "app/control_table/control_table.h"

STATIC_ASSERT( sizeof(xNFMGetFreqPoints_t) == sizeof(sNFMGetFreqPoints_t), "Invalid xNFMGetPoints_t/sNFMGetPoints_t size" );
STATIC_ASSERT( sizeof(xNFMGetPointsSimplified_t) == sizeof(sNFMGetPointsSimplified_t), "Invalid xNFMGetPointsSimplified_t/sNFMGetPointsSimplified_t size" );

#if defined(PLANAR)
// Device Manufacturer ID
const static char manufacturerId[] =  "Planar";
//#elif defined(CMT)
//const static char manufacturerId[] =  "Copper Mountain Technologies";
#elif defined(CMT)
const static char manufacturerId[] =  "CMT";
#else
#error Please, specify VENDOR macro
#endif

// Firmware ID
static char g_firmwareId[16] = "0.1/01";

// NFM model name
static char g_modelName[16] = "MK207-04H";

// Serial Number String
static char g_serialNumber[9] = "00000001";

// NFM Connectors descriptions
const static char * const aConnectorsNames[] =
{
  "Type-N50 -M-",
  "Type-N50 -F-",
  "3.5mm -M-",
  "3.5mm -F-",
  "2.4mm -M-",
  "2.4mm -F-",
  "Type-N75 -M-",
  "Type-N75 -F-",
  "Type-F -M-",
  "Type-F -F-",
  "7/16 -M-",
  "7/16 -F-",
  "APC-7",
};

const static char pcConnectorUndefined[] = "Unknown";

//------------------------------------------------------------------------------
static void           private_ClassInit();
static size_t         static_StrCpyLimit( const char * source, size_t srcMaxLen,
                                          char * dest,   size_t destMaxLen );

static const sNFMModel_t *
                      static_GetModelProfileByDeviceId( uint16_t deviceId );

static bool           private_getModelId( uint16_t * pDeviceId );
static bool           private_CheckTableParams( ePortComb_t portComb, ePortStateId_t portState );

static size_t         NFMGetDeviceManufacturer( char * buffer, size_t size );
static size_t         NFMGetDeviceSerial( char * buffer, size_t size );
static size_t         NFMGetModelName( char * buffer, size_t size );

static bool           NFMCheckPortStateAvailable( ePortStateId_t portState );
static bool           NFMCheckPortCombinationAvailable( ePortComb_t portComb );
// static bool           NFMCheckPortParamsAvailable( ePortComb_t portComb, ePortStateId_t portState );
// static size_t         NFMGetPoints( eChrz_t tableId, ePortComb_t portComb, ePortStateId_t portState, sNFMGetPoints_t * pCtl );

// static size_t         NFMGetPointsMagnThermo( ePortComb_t portComb, ePortStateId_t portState, sNFMGetPoints_t * pCtl );
// static size_t         NFMGetPointsPhaseThermo( ePortComb_t portComb, ePortStateId_t portState, sNFMGetPoints_t * pCtl );

static size_t         NFMGetPointsCount( eChrz_t tableId );
static bool           NFMGetPointsCount_Safe( eChrz_t tableId, int16_t * count );
static bool           NFMGetDate( eChrz_t tableId, char * buffer, size_t size, size_t * bytes );
static bool           NFMGetTime( eChrz_t tableId, char * buffer, size_t size, size_t * bytes );
static bool           NFMGetScaleType( eChrz_t tableId, eChrzScaleType_t * pType );
static size_t         NFMGetScaleSegment( eChrz_t tableId, sEcalSegment_t * pSegment, size_t nSegmentId );
//------------------------------------------------------------------------------
static int32_t        NFMGetScaleFreqs_Begin( eChrz_t tableId, double * pFreqArray, size_t BufferCapacity, xNFMGetFreqPoints_t * xCtl );
static int32_t        NFMGetScaleFreqs_Continue( xNFMGetFreqPoints_t * xCtl, size_t * pnPointsRetrieve );
//------------------------------------------------------------------------------
static int32_t        SWGetTablePoints_Begin( eChrz_t tableId, sSWTablePoint_t * pPointsArray, size_t BufferCapacity, xSWGetTablePoints_t * xCtl );
static int32_t        SWGetTablePoints_Continue( xSWGetTablePoints_t * xCtl, size_t * pnPointsRetrieve );
//------------------------------------------------------------------------------
static size_t         NFMGetPoints_Begin( eChrz_t sectorId, ePortComb_t portComb, ePortStateId_t portState,
                                          sNFMChrzPoint_t * pDataBuffer, size_t szDataBuffer,
                                          xNFMGetPointsSimplified_t * xCtl );

static int32_t        NFMGetPoints_Continue( xNFMGetPointsSimplified_t * xCtl, size_t * pnPointsRetrieve );
//------------------------------------------------------------------------------
static size_t         NFMGetPointsThermo_Begin( ePortComb_t portComb, ePortStateId_t portState,
                                                sNFMChrzPoint_t * pDataBuffer, size_t szDataBuffer,
                                                sNFMGetPointsSimplified_t * pCtl );

static size_t         NFMGetPointsThermoMagn_Begin( ePortComb_t portComb, ePortStateId_t portState,
                                                    sNFMChrzPoint_t * pDataBuffer, size_t szDataBuffer,
                                                    xNFMGetPointsSimplified_t * xCtl );
static size_t         NFMGetPointsThermoPhase_Begin( ePortComb_t portComb, ePortStateId_t portState,
                                                     sNFMChrzPoint_t * pDataBuffer, size_t szDataBuffer,
                                                     xNFMGetPointsSimplified_t * xCtl );
//------------------------------------------------------------------------------
static int32_t        NFMGetPointsThermo_Continue( sNFMGetPointsSimplified_t * pCtl, size_t * pnPointsRetrieve );

static int32_t        NFMGetPointsThermoMagn_Continue( xNFMGetPointsSimplified_t * xCtl, size_t * pnPointsRetrieve );
static int32_t        NFMGetPointsThermoPhase_Continue( xNFMGetPointsSimplified_t * xCtl, size_t * pnPointsRetrieve );
//------------------------------------------------------------------------------
static bool           NFMGetStartFreq( eChrz_t tableId, double * pStartFreq );
static bool           NFMGetStopFreq( eChrz_t tableId, double * pStopFreq );
static bool           NFMGetChrzTemp( eChrz_t tableId, double * pTemperature );
static bool           NFMGetConnectorType( ePortId_t portId, char * buffer, size_t size, size_t * bytes );
static bool           NFMGetAdapterDesc( eChrz_t tableId, ePortId_t portId, char * buffer, size_t size, size_t * bytes );
static bool           NFMGetAnalyzer( eChrz_t tableId, char * buffer, size_t size, size_t * bytes );
static bool           NFMGetPlace( eChrz_t tableId, char * buffer, size_t size, size_t * bytes );
static bool           NFMGetOperator( eChrz_t tableId, char * buffer, size_t size, size_t * bytes ); //------------------------------------------------------------------------------
//------------------------------------------------------------------------------
static bool           NFMGetInterface( eNFMUSBInterface_t * pCurrentIface );
static bool           NFMSetInterface( eNFMUSBInterface_t activateInterface );
//------------------------------------------------------------------------------
static uint16_t       NFMGetPortState (uint16_t portNumber);
static bool           NFMSetPortState (uint16_t portNumber, uint16_t portState);
//------------------------------------------------------------------------------
static bool           NFMSetMemoryProtect();
static bool           NFMSetMemoryUnProtect();
static bool           NFMGetMemoryProtect();
//------------------------------------------------------------------------------
static bool           NFMGetStartFreqThermo( double * pStartFreq );
static bool           NFMGetStopFreqThermo( double * pStopFreq );
static size_t         NFMGetPointsThermo();
static bool           NFMGetPointsThermo_Safe( int16_t * count );
//------------------------------------------------------------------------------

static NFMClassEx_t __NFMClass =
{
    .public =
    {
          .properties =
          {
                .manufacturerId = manufacturerId,
                .firmwareId = g_firmwareId,
                .modelName = g_modelName,
                .serialNumber = g_serialNumber,
                .allowedInputPorts = 2,
                .allowedOutputPorts = 12,
                .defaultInputState = eTerminatePortState,
                //.inputPortStates = {0, 0},
                .deviceId = eModel_undefined,
                .isServiceMode = false
        },
          .methods =
          {
                .usbInterface =
                {
                    .getInterface = NFMGetInterface,
                    .setInterface = NFMSetInterface
                },
                
                .portMethods =
                {
                    .getPortState = NFMGetPortState,
                    .setPortState = NFMSetPortState,
                },

                .getDeviceManufacturer = NFMGetDeviceManufacturer,
                .getDeviceSerial =  NFMGetDeviceSerial,
                .getModelName =  NFMGetModelName,
                .checkPortStateAvailable = NFMCheckPortStateAvailable,
                .checkPortCombinationAvailable = NFMCheckPortCombinationAvailable,
                .checkTableParams = private_CheckTableParams,

                .xCharacterization =
                {
                    //.getPoints          = NFMGetPoints,
                      .getPoints_Init     = NFMGetPoints_Begin,
                      .getPoints_Next     = NFMGetPoints_Continue,
                      .getPointsCount     = NFMGetPointsCount,
                      .getPointsCountSafe = NFMGetPointsCount_Safe,
                      .getDate            = NFMGetDate,
                      .getTime            = NFMGetTime,
                      .getScaleType       = NFMGetScaleType,
                      .getScaleSegment    = NFMGetScaleSegment,
                      .getScaleFreqs_Init = NFMGetScaleFreqs_Begin,
                      .getScaleFreqs_Next = NFMGetScaleFreqs_Continue,
                      .getStartFreq       = NFMGetStartFreq,
                      .getStopFreq        = NFMGetStopFreq,
                      .getChrzTemp        = NFMGetChrzTemp,
                      .getConnectorType   = NFMGetConnectorType,
                      .getAdapterDesc     = NFMGetAdapterDesc,
                      .getAnalyzer        = NFMGetAnalyzer,
                      .getPlace           = NFMGetPlace,
                      .getOperator        = NFMGetOperator,
                },
                
                .xTable = 
                {
                  .getTablePoints_Init    = SWGetTablePoints_Begin,
                  .getTablePoints_Next    = SWGetTablePoints_Continue,
                },

                .xThermo =
                {
                      .getStartFreq       = NFMGetStartFreqThermo,
                      .getStopFreq        = NFMGetStopFreqThermo,
                      .getPointsCount     = NFMGetPointsThermo,
                      .getPointsCountSafe = NFMGetPointsThermo_Safe,
                    //.getPointsMagn      = NFMGetPointsMagnThermo,
                    //.getPointsPhase     = NFMGetPointsPhaseThermo,

                      .getPointsThermoMagn_Init  = NFMGetPointsThermoMagn_Begin,
                      .getPointsThermoMagn_Next  = NFMGetPointsThermoMagn_Continue,
                      .getPointsThermoPhase_Init = NFMGetPointsThermoPhase_Begin,
                      .getPointsThermoPhase_Next = NFMGetPointsThermoPhase_Continue,
                },

                .xMemoryProtection =
                {
                      .enable             = NFMSetMemoryProtect,
                      .disable            = NFMSetMemoryUnProtect,
                      .check              = NFMGetMemoryProtect,
                },
          },
    },

    .private =
    {
        .methods =
        {
              .classInit = private_ClassInit,
              .getModelId = private_getModelId,
              .memory =
              {
                  .setProtect               = NFM_ROM_SetMemoryProtectStatus_Bank0,
                  .getProtect               = NFM_ROM_GetMemoryProtectStatus_Bank0,
                  .getCommonHeader          = NFM_ROM_GetCommonHeader,
                  .getDataHeader            = NFM_ROM_GetDataHeader,
                  .getTCompHeader           = NFM_ROM_GetTCompHeader,
                  .getChrzTableIndex        = NFM_ROM_GetChrzTableIndex,
                  .getTCompTableIndex       = NFM_ROM_GetTCompTableIndex,
                  .getChrzTableHeader       = NFM_ROM_ReadChrzTableHeader,
                  .getChrzPoints            = NFM_ROM_ReadChrzTablePoints,
                  .getTCompMagnTableHeader  = NFM_ROM_GetTCompMagnTableHeader,
                  .getTCompPhaseTableHeader = NFM_ROM_GetTCompPhaseTableHeader,
                  .getTCompMagnPoints       = NFM_ROM_GetTCompMagnPoints,
                  .getTCompPhasePoints      = NFM_ROM_GetTCompPhasePoints,
                  .getSettingsBlock         = NFM_ROM_ReadDeviceSettings,
                  .setSettingsBlock         = NFM_ROM_WriteDeviceSettings,
                  .getSettingsBlockSize     = NFM_ROM_GetDeviceSettingsSize,
                  .validateSettings         = NFM_ROM_ValidateUserSettings,
              }
        },

        .properties =
        {
              .deviceId = eModel_undefined,
              .modelProfile = NULL,
        }
    }
};

__root NFMClass_t   * NFMClass         = (NFMClass_t*)&__NFMClass;
__root NFMClassEx_t * NFMClassExtended =              &__NFMClass;

//------------------------------------------------------------------------------------------------------------
//-- Global methods ------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------------

void nfmbase_init()
{
    // initialize NFM object
    NFMClassExtended->private.methods.classInit();

    // set key-switch state by default
    //NFMClass->methods.keyStatesMethods.setKeyStateDefault();
//    sTableTablePoint_t TablePoint;
//    TablePoint.port1 = 12;
//    TablePoint.port2 = 2;
//    SW_ROM_SetDataPoint(0, &TablePoint);
//    TablePoint.port1 = 4;
//    TablePoint.port2 = 6;
//    SW_ROM_SetDataPoint(0, &TablePoint);
//    TablePoint.port1 = 1;
//    TablePoint.port2 = 3;
//    SW_ROM_SetDataPoint(0, &TablePoint);
//    uint8_t buffer[100];
//    memset(&buffer, 0x00, sizeof(buffer));
//    SW_ROM_GetDataPoint(0, 5, &buffer, sizeof(sTableTablePoint_t));
//    SW_ROM_GetDataPoint(0, 6, &buffer, sizeof(sTableTablePoint_t));
//    SW_ROM_GetDataPoint(0, 7, &buffer, sizeof(sTableTablePoint_t));
//    SW_ROM_GetDataPoint(0, 8, &buffer, sizeof(sTableTablePoint_t));
//    uint32_t crc = SW_ROM_Get_Table_crc( eChValues );
//    SW_ROM_Set_Table_crc ( crc );
//    SW_ROM_Check_Table_crc();
//    SW_ROM_Table_Clear();

    #if CONFIG_NFMCLASS_AUTOPROTECT_MEMORY
    // protect factory data: set memory protection
    NFMClass->methods.xMemoryProtection.enable();
    #endif
}
#define NFMCLASS_TEST                    CONFIG_NFMBASECLASS_TEST
#define NFMCLASS_TEST_TEMPERATURE        0
#define NFMCLASS_TEST_TEMPERATURESTR     0
#define NFMCLASS_TEST_MANUFACTURERSTR    0
#define NFMCLASS_TEST_SERIALSTR          0
#define NFMCLASS_TEST_SERIALLEGACY       0
#define NFMCLASS_TEST_STATENAMES         0
#define NFMCLASS_TEST_STATELIST          0
#define NFMCLASS_TEST_KEYSWSTATENAME     0
#define NFMCLASS_TEST_MODELNAME          0
#define NFMCLASS_TEST_CURRENTSTATE       0
#define NFMCLASS_TEST_LEGACYSTATE        0
#define NFMCLASS_TEST_RAWKEYCODE         0
#define NFMCLASS_TEST_KEYSTATESHORTCUTS  0
#define NFMCLASS_TEST_CHRZ_ADAPTERSTR    0
#define NFMCLASS_TEST_CHRZ_ANALYZERSTR   0
#define NFMCLASS_TEST_CHRZ_TEMP          0
#define NFMCLASS_TEST_CHRZ_CONNECTORSTR  0
#define NFMCLASS_TEST_CHRZ_DATESTR       0
#define NFMCLASS_TEST_CHRZ_TIMESTR       0
#define NFMCLASS_TEST_CHRZ_OPERATORSTR   0
#define NFMCLASS_TEST_CHRZ_PLACESTR      0
#define NFMCLASS_TEST_CHRZ_POINTSCOUNT   0
#define NFMCLASS_TEST_CHRZ_SEGMENTS      0
#define NFMCLASS_TEST_CHRZ_STARTFREQ     0
#define NFMCLASS_TEST_CHRZ_STOPFREQ      0
#define NFMCLASS_TEST_THERMO_POINTSCOUNT 0
#define NFMCLASS_TEST_THERMO_STOPFREQ    0
#define NFMCLASS_TEST_THERMO_STARTFREQ   0
#define NFMCLASS_TEST_MEM_PROTECTION     0

void nfmbase_test()
{
    #if NFMCLASS_TEST
    asm( "bkpt #0" );
    #if 1 && NFMCLASS_TEST_TEMPERATURE
    {
       float avgTemp = NFMClass->methods.getAverageTemperature();
       asm( "bkpt #0" );
       (void)avgTemp;
    }
    #endif

    #if 0 &&  NFMCLASS_TEST_TEMPERATURESTR
    {
      char avgTempStrBuffer[ 20 ] = {0};
      NFMClass->methods.getAverageTemperatureString( avgTempStrBuffer, sizeof(avgTempStrBuffer) )
      asm( "bkpt #0" );
      (void)avgTempStrBuffer;
    }
    #endif

    #if 1 && NFMCLASS_TEST_MANUFACTURERSTR
    {
      char manufacturerStrBuffer[ 20 ] = {0};
      NFMClass->methods.getDeviceManufacturer( manufacturerStrBuffer, sizeof(manufacturerStrBuffer) );
      asm( "bkpt #0" );
      (void)manufacturerStrBuffer;
    }
    #endif

    #if 1 && NFMCLASS_TEST_SERIALSTR
    {
      char serialStrBuffer[ 20 ] = {0};
      NFMClass->methods.getDeviceSerial( serialStrBuffer, sizeof(serialStrBuffer) );
      asm( "bkpt #0" );
      (void)serialStrBuffer;
    }
    #endif

    #if 1 && NFMCLASS_TEST_SERIALLEGACY
    {
       uint32_t serialNumber = NFMClass->methods.getDeviceSerialLegacy();
       asm( "bkpt #0" );
       (void)serialNumber;
    }
    #endif

    #if 1 && NFMCLASS_TEST_STATENAMES
    {
       char stateNameStrBuffer[ 20 ] = {0};
       eNFMKeyState_t state = NFMClass->methods.getKeyStateByName( "SALL", 4 );
       asm( "bkpt #0" );
       NFMClass->methods.getKeyStateName( state, stateNameStrBuffer, sizeof(stateNameStrBuffer) );
       asm( "bkpt #0" );
       (void)state, (void)stateNameStrBuffer;
    }
    {
       char stateNameStrBuffer[ 20 ] = {0};
       eNFMKeyState_t state = NFMClass->methods.getKeyStateByName( "OALL", 4 );
       asm( "bkpt #0" );
       NFMClass->methods.getKeyStateName( state, stateNameStrBuffer, sizeof(stateNameStrBuffer) );
       asm( "bkpt #0" );
       (void)state, (void)stateNameStrBuffer;
    }
    {
       char stateNameStrBuffer[ 20 ] = {0};
       eNFMKeyState_t state = NFMClass->methods.getKeyStateByName( "LALL", 4 );
       asm( "bkpt #0" );
       NFMClass->methods.getKeyStateName( state, stateNameStrBuffer, sizeof(stateNameStrBuffer) );
       asm( "bkpt #0" );
       (void)state, (void)stateNameStrBuffer;
    }
    #endif

    #if 1 && NFMCLASS_TEST_STATELIST
    {
      char statesListStrBuffer[ 64 ] = {0};
      NFMClass->methods.getKeyStateList( statesListStrBuffer, sizeof(statesListStrBuffer) );
      asm( "bkpt #0" );
      (void)statesListStrBuffer;
    }
    #endif

    #if 1 && NFMCLASS_TEST_KEYSWSTATENAME
    {
      char keySwitchNameStrBuffer[ 20 ] = {0};
      NFMClass->methods.getKeySwitchName( keySwitchNameStrBuffer, sizeof(keySwitchNameStrBuffer) );
      asm( "bkpt #0" );
      (void)keySwitchNameStrBuffer;
    }
    #endif

    #if 1 && NFMCLASS_TEST_MODELNAME
    {
      char modelNameStrBuffer[ 20 ] = {0};
      NFMClass->methods.getModelName( modelNameStrBuffer, sizeof(modelNameStrBuffer) );
      asm( "bkpt #0" );
      (void)modelNameStrBuffer;
    }
    #endif

    #if 1 && NFMCLASS_TEST_CURRENTSTATE
    {
      eNFMKeyState_t currentState = NFMClass->methods.keyStatesMethods.getKeyStateCommon();
      asm( "bkpt #0" );
      (void)currentState;
    }
    #endif

    #if 1 && NFMCLASS_TEST_LEGACYSTATE
    {
      uint32_t legacyState = 0;
      eNFMKeyState_t testState = eKeyState_CHECK;

      if( NFMClass->methods.keyStatesMethods.getKeyStateLegacy( &legacyState ) )
      {
         asm( "bkpt #0" );    // OK

         NFMClass->methods.keyStatesMethods.setKeyStateCommon( testState );
         if( testState == NFMClass->methods.keyStatesMethods.getKeyStateCommon() )
            asm( "bkpt #0" );  // OK
         else
            asm( "bkpt #0" );  // ERROR

         if( NFMClass->methods.keyStatesMethods.setKeyStateLegacy( legacyState ) )
            asm( "bkpt #0" );  // OK
         else
            asm( "bkpt #0" );  // ERROR

         uint32_t legacyState_2 = 0;
         if( NFMClass->methods.keyStatesMethods.getKeyStateLegacy( &legacyState_2 ) )
            asm( "bkpt #0" );  // OK
         else
            asm( "bkpt #0" );  // ERROR

         if( legacyState_2 == legacyState )
            asm( "bkpt #0" );  // OK
         else
            asm( "bkpt #0" );  // ERROR
      }
      else
         asm( "bkpt #0" );     // ERROR
    }
    #endif

    #if 1 && NFMCLASS_TEST_RAWKEYCODE
    {
      uint32_t rawCode = NFMClass->methods.keyStatesMethods.getRawKeyControlCode();
      asm( "bkpt #0" );
      (void)rawCode;
    }
    #endif

    #if 1 && NFMCLASS_TEST_KEYSTATESHORTCUTS
    {
        eNFMKeyState_t prevState = NFMClass->methods.keyStatesMethods.getKeyStateCommon();

        int32_t eKeyState_SALL_shortcut=-1, eKeyState_OALL_shortcut=-1, eKeyState_LALL_shortcut=-1;

        if( !NFMClass->methods.keyStatesMethods.shortcuts.findShortCutForState( eKeyState_SALL, &eKeyState_SALL_shortcut ) )
        asm( "bkpt #0" );     // ERROR
        if( !NFMClass->methods.keyStatesMethods.shortcuts.findShortCutForState( eKeyState_OALL, &eKeyState_OALL_shortcut ) )
        asm( "bkpt #0" );     // ERROR
        if( !NFMClass->methods.keyStatesMethods.shortcuts.findShortCutForState( eKeyState_LALL, &eKeyState_LALL_shortcut ) )
        asm( "bkpt #0" );     // ERROR

        if( !NFMClass->methods.keyStatesMethods.shortcuts.setKeyStateByShortcut( eKeyState_SALL_shortcut ) )
        asm( "bkpt #0" );     // ERROR
        else
        if( eKeyState_SALL != NFMClass->methods.keyStatesMethods.getKeyStateCommon() )
        asm( "bkpt #0" );     // ERROR

        if( !NFMClass->methods.keyStatesMethods.shortcuts.setKeyStateByShortcut( eKeyState_OALL_shortcut ) )
        asm( "bkpt #0" );     // ERROR
        else
        if( eKeyState_OALL != NFMClass->methods.keyStatesMethods.getKeyStateCommon() )
        asm( "bkpt #0" );     // ERROR

        if( !NFMClass->methods.keyStatesMethods.shortcuts.setKeyStateByShortcut( eKeyState_LALL_shortcut ) )
        asm( "bkpt #0" );     // ERROR
        else
        if( eKeyState_LALL != NFMClass->methods.keyStatesMethods.getKeyStateCommon() )
        asm( "bkpt #0" );     // ERROR

        if( !NFMClass->methods.keyStatesMethods.setKeyStateCommon( prevState ) )
        asm( "bkpt #0" );     // ERROR
    }
    #endif

    asm( "bkpt #0" );
    #if 1 && NFMCLASS_TEST_CHRZ_ADAPTERSTR
    {
      size_t n;
      char adapterStrBuffer[ 20 ] = {0};
      NFMClass->methods.xCharacterization.getAdapterDesc( eChFactory, ePortId_A, adapterStrBuffer, sizeof(adapterStrBuffer), &n );
      asm( "bkpt #0" );
      (void)adapterStrBuffer;
    }
    {
      size_t n;
      char adapterStrBuffer[ 20 ] = {0};
      NFMClass->methods.xCharacterization.getAdapterDesc( eChFactory, ePortId_B, adapterStrBuffer, sizeof(adapterStrBuffer), &n );
      asm( "bkpt #0" );
      (void)adapterStrBuffer;
    }
    #endif
    #if 1 && NFMCLASS_TEST_CHRZ_ANALYZERSTR
    {
      size_t n;
      char analyzerStrBuffer[ 20 ] = {0};
      NFMClass->methods.xCharacterization.getAnalyzer( eChFactory, analyzerStrBuffer, sizeof(analyzerStrBuffer), &n );
      asm( "bkpt #0" );
      (void)analyzerStrBuffer;
    }
    #endif

    #if 1 && NFMCLASS_TEST_CHRZ_TEMP
    {
      double chrzTemp = 0.0;
      NFMClass->methods.xCharacterization.getChrzTemp( eChFactory, &chrzTemp );
      asm( "bkpt #0" );
    }
    #endif

    #if 1 && NFMCLASS_TEST_CHRZ_CONNECTORSTR
    {
      size_t n;
      char connectorStrBuffer[ 20 ] = {0};
      NFMClass->methods.xCharacterization.getConnectorType( ePortId_A, connectorStrBuffer, sizeof(connectorStrBuffer), &n );
      asm( "bkpt #0" );
      (void)connectorStrBuffer;
    }
    {
      size_t n;
      char connectorStrBuffer[ 20 ] = {0};
      NFMClass->methods.xCharacterization.getConnectorType( ePortId_B, connectorStrBuffer, sizeof(connectorStrBuffer), &n );
      asm( "bkpt #0" );
      (void)connectorStrBuffer;
    }
    #endif

    #if 1 && NFMCLASS_TEST_CHRZ_DATESTR
    {
      size_t n;
      char dateStrBuffer[ 20 ] = {0};
      NFMClass->methods.xCharacterization.getDate( eChFactory, dateStrBuffer, sizeof(dateStrBuffer), &n );
      asm( "bkpt #0" );
      (void)dateStrBuffer;
    }
    #endif
    
    #if 1 && SWCLASS_TEST_TABLE_HEADER
    {
        //SW_ROM_ChangeTableHeader(5);
    }
    #endif

    #if 1 && NFMCLASS_TEST_CHRZ_TIMESTR
    {
      size_t n;
      char timeStrBuffer[ 20 ] = {0};
      NFMClass->methods.xCharacterization.getTime( eChFactory, timeStrBuffer, sizeof(timeStrBuffer), &n );
      asm( "bkpt #0" );
      (void)timeStrBuffer;
    }
    #endif

    #if 1 && NFMCLASS_TEST_CHRZ_OPERATORSTR
    {
      size_t n;
      char operatorStrBuffer[ 20 ] = {0};
      NFMClass->methods.xCharacterization.getOperator( eChFactory, operatorStrBuffer, sizeof(operatorStrBuffer), &n );
      asm( "bkpt #0" );
      (void)operatorStrBuffer;
    }
    #endif

    #if 1 && NFMCLASS_TEST_CHRZ_PLACESTR
    {
      size_t n;
      char placeStrBuffer[ 20 ] = {0};
      NFMClass->methods.xCharacterization.getPlace( eChFactory, placeStrBuffer, sizeof(placeStrBuffer), &n );
      asm( "bkpt #0" );
      (void)placeStrBuffer;
    }
    #endif

    #if 1 && NFMCLASS_TEST_CHRZ_POINTSCOUNT
    {
      size_t nPoints = NFMClass->methods.xCharacterization.getPointsCount( eChFactory );
      asm( "bkpt #0" );
      (void)nPoints;
    }
    #endif

    #if 1 && NFMCLASS_TEST_CHRZ_SEGMENTS
    {
      size_t nSegments = NFMClass->methods.xCharacterization.getScaleSegment( eChFactory, NULL, 0 );
      asm( "bkpt #0" );
      (void)nSegments;
      asm( "bkpt #0" );
      for( size_t i = 0; i < nSegments; ++i )
      {
         sEcalSegment_t segm;
         if( 0 == NFMClass->methods.xCharacterization.getScaleSegment( eChFactory, &segm, i ) )
         asm( "bkpt #0" ); // ERROR
         else
         asm( "bkpt #0" ); // OK
         (void)segm;
      }
      asm( "bkpt #0" );
    }
    #endif

    #if 1 && NFMCLASS_TEST_CHRZ_STARTFREQ
    {
      double startFreq = 0.0;
      if( NFMClass->methods.xCharacterization.getStartFreq( eChFactory, &startFreq ) )
      asm( "bkpt #0" );  // OK
      else
      asm( "bkpt #0" );  // ERROR
      (void)startFreq;
    }
    #endif

    #if 1 && NFMCLASS_TEST_CHRZ_STOPFREQ
    {
      double stopFreq = 0.0;
      if( NFMClass->methods.xCharacterization.getStopFreq( eChFactory, &stopFreq ) )
      asm( "bkpt #0" );  // OK
      else
      asm( "bkpt #0" );  // ERROR
      (void)stopFreq;
    }
    #endif

    asm( "bkpt #0" );

    #if 1 && NFMCLASS_TEST_MEM_PROTECTION
    {
       bool oldstate = NFMClass->methods.xMemoryProtection.check();
       asm( "bkpt #0" );
       NFMClass->methods.xMemoryProtection.disable();
       asm( "bkpt #0" );
       bool state = NFMClass->methods.xMemoryProtection.check();
       asm( "bkpt #0" );
       NFMClass->methods.xMemoryProtection.enable();
       asm( "bkpt #0" );
       state = NFMClass->methods.xMemoryProtection.check();
       asm( "bkpt #0" );
       if( oldstate ) NFMClass->methods.xMemoryProtection.enable();
       else           NFMClass->methods.xMemoryProtection.disable();
    }
    #endif

    #if 1 && NFMCLASS_TEST_THERMO_STARTFREQ
    {
      double startFreq = 0.0;
      if( NFMClass->methods.xThermo.getStartFreq( &startFreq ) )
      asm( "bkpt #0" );  // OK
      else
      asm( "bkpt #0" );  // ERROR
      (void)startFreq;
    }
    #endif

    #if 1 && NFMCLASS_TEST_THERMO_STOPFREQ
    {
      double stopFreq = 0.0;
      if( NFMClass->methods.xThermo.getStopFreq( &stopFreq ) )
      asm( "bkpt #0" );  // OK
      else
      asm( "bkpt #0" );  // ERROR
      (void)stopFreq;
    }
    #endif

    #if 1 && NFMCLASS_TEST_THERMO_POINTSCOUNT
    {
      size_t nPoints = NFMClass->methods.xThermo.getPointsCount( eChFactory );
      asm( "bkpt #0" );
      (void)nPoints;
    }
    #endif

    #endif
}

//------------------------------------------------------------------------------------------------------------
//-- Public methods ------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------------

// Enable Memory Protection for BANK0
static bool NFMSetMemoryProtect()
{
    return NFMClassExtended->private.methods.memory.setProtect( true );
}

// Disable Memory Protection for BANK0
static bool NFMSetMemoryUnProtect()
{
    return NFMClassExtended->private.methods.memory.setProtect( false );
}

// Check the Memory Protection Status for BANK0
static bool NFMGetMemoryProtect()
{
    return NFMClassExtended->private.methods.memory.getProtect();
}

#if 0
// Set common key-switch state
static bool NFMSetKeyStateCommon( eNFMKeyState_t state )
{
     if( NFMClassExtended->public.methods.checkStateAvailable( state ) )
     {
         if( NULL != NFMClassExtended->private.properties.statesEncoding )
         {
             for( size_t i = 0; i < NFMClassExtended->public.properties.allowedStatesCount; ++i )
             {
                  if( state == NFMClassExtended->private.properties.statesEncoding[i].commonKeyState )
                  {
                      NFMClassExtended->private.methods.switchControl(
                        &NFMClassExtended->private.properties.statesEncoding[i]
                      );

                      NFMClassExtended->private.properties.keyState = state;

                      return true;
                  }
             }
         }
     }

     return false;
}
#endif

// Get device serial number in string format
static size_t NFMGetDeviceSerial( char * buffer, size_t size )
{
   const sEcalHeader_t * Header = NFMClassExtended->private.methods.memory.getCommonHeader( NULL, 0 );

   if( NULL != Header )
   {
       size_t n = static_StrCpyLimit( (const char*)Header->SN, sizeof(Header->SN), buffer, size );

       if( n + 1 <= size )
       {
           buffer[n] = '\0';
           return (n + 1);
       }
   }

   return 0;
}

// Set device serial number in string format
static size_t NFMSetDeviceSerial( char * buffer, size_t size )
{
   const sEcalHeader_t * Header = NFMClassExtended->private.methods.memory.getCommonHeader( NULL, 0 );

   if( NULL != Header )
   {
       size_t n = static_StrCpyLimit( (const char*)Header->SN, sizeof(Header->SN), buffer, size );

       if( n + 1 <= size )
       {
           buffer[n] = '\0';
           return (n + 1);
       }
   }

   return 0;
}

// Get device manufacturer ID in string format
// See more: NFMClass->properties.manufacturerId
static size_t NFMGetDeviceManufacturer( char * buffer, size_t size )
{
    size_t retval = 0;

    if( NULL != buffer && size > 1 )
    {
        const char * manufacturer_id = NFMClassExtended->public.properties.manufacturerId;

        while( (size > 1) && (*manufacturer_id != '\0') )
        {
            *buffer = *manufacturer_id;
            manufacturer_id++;
            buffer++;
            retval++; size--;
        }
        *buffer = '\0';
    }

    return retval;
}

// Get device model name in string format
static size_t NFMGetModelName( char * buffer, size_t size )
{
   if( NULL == buffer || 0 == size )
      return 0;

   size_t l = strlen( NFMClassExtended->public.properties.modelName );

   if( l <= size )
   {
       memcpy( buffer, NFMClassExtended->public.properties.modelName, l );
   }
   else
   {
       l = 0;
   }

   return l;
}

static bool NFMCheckPortStateAvailable( ePortStateId_t portState )
{
    extern NFMClassEx_t * NFMClassExtended;
    
//    if(    ePortStateId_MAX <= portState
//        || ePortStateId_UNDEFINED == portState
//      )    
//    {
//        return (false);
//    }
//    
//    if( 
//         NULL == NFMClassExtended->private.properties.memProfile
//      || NULL == NFMClassExtended->private.properties.memProfile->tbls_raw )
//    {
//        return (false);
//    }
//           
//    const sNFMMemory_ChrzTableMatrix_XPort_t * tableProfile_XPort = NFMClassExtended->private.properties.memProfile->tbls_raw;
//    
//    switch(tableProfile_XPort->Header.ChrzTableType.eType)
//    {
//        case eNfmChrzTable2Port:
//        {
//           if(   ePortStateId_MIN_SINGLE_2Port <= portState
//              && ePortStateId_MAX_SINGLE_2Port >= portState )
//           {
//              return true;
//           }
//           if(   ePortStateId_MIN_THRU_2Port   <= portState
//              && ePortStateId_MAX_THRU_2Port   >= portState )
//           {
//              return true;
//           }
//           if(   ePortStateId_MIN_CHECK_2Port  <= portState
//              && ePortStateId_MAX_CHECK_2Port  >= portState )
//           {
//              return true;
//           }
//        }
//        break;
//        case eNfmChrzTable4Port:
//        {
//           if(   ePortStateId_MIN_SINGLE_4Port <= portState
//              && ePortStateId_MAX_SINGLE_4Port >= portState )
//           {
//              return true;
//           }
//           if(   ePortStateId_MIN_THRU_4Port <= portState
//              && ePortStateId_MAX_THRU_4Port >= portState )
//           {
//              return true;
//           }
//           if(   ePortStateId_MIN_CHECK_4Port <= portState
//              && ePortStateId_MAX_CHECK_4Port >= portState )
//           {
//              return true;
//           }
//        }
//        break;
//    }
    
    return (false); // unsupported characterization table type
}

static bool NFMCheckPortCombinationAvailable( ePortComb_t portCombination )
{
    extern NFMClassEx_t * NFMClassExtended;
    
    if(    ePortComb_MAX <= portCombination
        || ePortComb_UNDEFINED == portCombination
      )    
    {
        return (false);
    }
    
//    if( 
//         NULL == NFMClassExtended->private.properties.memProfile
//      || NULL == NFMClassExtended->private.properties.memProfile->tbls_raw )
//    {
//        return (false);
//    }
//           
//    const sNFMMemory_ChrzTableMatrix_XPort_t * tableProfile_XPort = NFMClassExtended->private.properties.memProfile->tbls_raw;
//    
//    if(   ePortComb_MIN_SINGLE_XPort <= portCombination
//       && ePortComb_MAX_SINGLE_XPort >= portCombination )
//    {
//        switch(tableProfile_XPort->Header.ChrzTableType.eType)
//        {
//            case eNfmChrzTable2Port:
//            {
//                if(   ePortComb_MIN_SINGLE_2Port <= portCombination
//                   && ePortComb_MAX_SINGLE_2Port >= portCombination )
//                {
//                   return true;
//                }
//            }
//            break;
//            case eNfmChrzTable4Port:
//            {
//                if(   ePortComb_MIN_SINGLE_4Port <= portCombination
//                   && ePortComb_MAX_SINGLE_4Port >= portCombination )
//                {
//                   return true;
//                }
//            }
//            break;
//        }
//        
//        return 0; // unsupported characterization table type
//    }
//    else
//    if(   ePortComb_MIN_THRU_XPort <= portCombination
//       && ePortComb_MAX_THRU_XPort >= portCombination )
//    {
//        switch(tableProfile_XPort->Header.ChrzTableType.eType)
//        {
//            case eNfmChrzTable2Port:
//            {
//               if(   ePortComb_MIN_THRU_2Port <= portCombination
//                  && ePortComb_MAX_THRU_2Port >= portCombination )
//                {
//                   return true;
//                }
//            }
//            break;
//            case eNfmChrzTable4Port:
//            {
//               if(   ePortComb_MIN_THRU_4Port <= portCombination
//                  && ePortComb_MAX_THRU_4Port >= portCombination )
//                {
//                   return true;
//                }  
//            }
//            break;
//        }
//        
//        return 0; // unsupported characterization table type
//    }  
//    else
//    if( ePortComb_CHECK == portCombination )
//    {
//       return true; // always available
//    } 
    
    return (false); 
}
#if 0
static bool NFMCheckPortParamsAvailable( ePortComb_t portComb, ePortStateId_t portState )
{
   return ( NFM_CHRZ_TABLEIDX_INVALID
             !=
              NFMClassExtended->private.methods.memory
               .getChrzTableIndex( portComb, portState )
          );
}
#endif
//------------------------------------------------------------------------------------------------------------
//-- Private methods -----------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------------

// private_ClassInit:
// NFMClass object constructor
static void private_ClassInit()
{
    // -------------------------------------------------------------------------
    // Load device model profile by DeviceID from Common Header
    uint16_t deviceId;

    NFMClassExtended->private.properties.deviceId = eModel_undefined;
    NFMClassExtended->private.properties.modelProfile = NULL;
    NFMClassExtended->private.properties.memProfile = NULL;

    NFMClassExtended->public.properties.modelName = NULL;
    NFMClassExtended->public.properties.firmwareId = g_firmwareId;
    
    // prepare firmware ID string
    memset( (char*)g_firmwareId, 0, sizeof(g_firmwareId) );
    uint16_t firmware_version = pProgramVersion->firmware_version;
    _snprintf(g_firmwareId, sizeof(g_firmwareId) - 1,
              "%d%c%d%c%02d",
              ((firmware_version & 0xF000)>>12),
              '.',
              ((firmware_version & 0x0F00)>>8),
              '/',
              ((firmware_version & 0xFF)) );

//    // check model version
//    if(!NFMClassExtended->private.methods.getModelId(&deviceId)) // FOR DEBUGGING (пишем девайс ID во флеш)
//    {
//        bool xPro = NFMClassExtended->public.methods.xMemoryProtection.check();
//
//        if( xPro )
//        {
//            NFMClassExtended->public.methods.xMemoryProtection.disable();
//        }
//
//        NFM_ROM_ChangeModel( eModel_NFM );
//        
//
//        if( xPro )
//        {
//            NFMClassExtended->public.methods.xMemoryProtection.enable();
//        }
//    }
    
    //const char serial[10] = {'0', '0', '0', '1', '5', '9', '1', '6', '5', '7'};
    //NFM_ROM_ChangeSerialNumber(serial);
    
    //NFM_ROM_ChangeModel(eModel_SWB);
        
    if( NFMClassExtended->private.methods.getModelId( &deviceId ) )
    {
        const sNFMModel_t * modelProfile = static_GetModelProfileByDeviceId( deviceId );

        if( NULL != modelProfile )
        {
            NFMClassExtended->public.properties.deviceId = (uint16_t)deviceId;
            NFMClassExtended->public.properties.modelName = modelProfile->modelName;
            NFMClassExtended->private.properties.deviceId = deviceId;
            NFMClassExtended->private.properties.modelProfile = modelProfile;
            NFMClassExtended->private.properties.memProfile = modelProfile->memProfile;

            if( NULL != modelProfile->nfProfile )
            {
                // NOTE: do not initialize if wrong number has been set
                // ---------------------------------------------------
                NFMClassExtended->public.properties.allowedInputPorts
                  = modelProfile->nfProfile->controlProfile.allowedInputPorts;
                NFMClassExtended->public.properties.allowedOutputPorts
                  = modelProfile->nfProfile->controlProfile.allowedOutputPorts;
                NFMClassExtended->public.properties.defaultInputState
                  = modelProfile->nfProfile->controlProfile.defaultInputState;
                NFMClassExtended->public.properties.inputPortStates
                  = modelProfile->nfProfile->controlProfile.inputPortStates;
            }
        }
        
        NFMClassExtended->public.methods.getDeviceSerial(
               g_serialNumber,
               sizeof(g_serialNumber)
        );
        NFMClassExtended->public.properties.serialNumber = g_serialNumber;
        
        if( ! NFMClassExtended->private.methods.memory.validateSettings())
        {
            // set default parameters
            
            unsigned char interface[8] = {'U', 'S', 'B', 'T', 'M', 'C', '!', 0};
            sNFMSettingsBlockCrc_t sSettingsBlock;
            for(int i = 0; i < sizeof(interface); i++)
            {
                sSettingsBlock.settings.rawBytes[i] = interface[i];
            }
            sSettingsBlock.settings.settingsVersion = 1;
            sSettingsBlock.settings.tempCoeff = 3988;   // Not used
            
            TCRC crc = ICRC32;
            crc = CRC32( crc, (uint8_t *)&sSettingsBlock.settings, sizeof(sSettingsBlock.settings));
            
            sSettingsBlock.CRCValue = crc;
            
            NFMClassExtended->private.methods.memory.setSettingsBlock( (uint8_t*)&sSettingsBlock,
                                                                      0,
                                                                      sizeof(sSettingsBlock) );
        }
    }
    /*
    else
    {
        asm("bkpt #0");

        bool xPro = NFMClassExtended->public.methods.xMemoryProtection.check();

        if( xPro )
        {
            NFMClassExtended->public.methods.xMemoryProtection.disable();
        }

        NFM_ROM_ChangeModel( eModel_SC4520_v1 );

        if( xPro )
        {
            NFMClassExtended->public.methods.xMemoryProtection.enable();
        }

        asm("bkpt #0");
    }
    */
}

// Checks the characterization table parameters for compatibility
bool private_CheckTableParams( ePortComb_t portComb, ePortStateId_t portState )
{
   // Validate the combination of @portComb, @portState and
   // validate the @portComb and @portState itself.
   // Retrieve the table index (ordered index)
   size_t tableIdx = NFMClassExtended->private.methods.memory.getChrzTableIndex( portComb, portState );

   if( NFM_CHRZ_TABLEIDX_INVALID == tableIdx )
   {
       // Invalid combination or value
       return false;
   }

   return true;
}

/*
// private_getModelName
// Get device model name by DeviceID
static const char * private_getModelName()
{
   uint16_t deviceId = eModel_undefined;

   if( ! NFMClassExtended->private.methods.getModelId( &deviceId ) )
   {
       deviceId = eModel_undefined;
   }

   return NFMClassExtended->private.methods.getModelNameById( deviceId );
}
*/

/*
// private_getModelNameById
// Get device model name by DeviceID
static const char * private_getModelNameById( uint16_t deviceId )
{
   const sNFMModel_t * modelProfile = static_GetModelProfileByDeviceId( deviceId );

   if( NULL == modelProfile )
      return NULL;

   return modelProfile->modelName;
}
*/

// private_getModelId
// Get device model ID (DeviceID)
static bool private_getModelId( uint16_t * pDeviceId )
{
   const sEcalHeader_t * Header = NFMClassExtended->private.methods.memory.getCommonHeader( NULL, 0 );

   if( NULL != Header )
   {
       if( NULL != pDeviceId )
       {
           *pDeviceId = Header->DeviceID;

           return true;
       }
   }

   return false;
}

//------------------------------------------------------------------------------------------------------------
//--  Static methods -----------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------------
static bool static_KeyStateNameCompare( const char * name1,
                                        const char * name2,
                                        size_t size )
{
    if( NULL == name1 || NULL == name2 || 0 == size )
        return false;

    for( size_t i = 0; i < size; ++i )
    {
         if( tolower(name1[i]) != tolower(name2[i]) )
         {
             return false;
         }
    }

    return true;
}

static size_t static_StrCpyLimit( const char * source, size_t srcMaxLen,
                                        char * dest,   size_t destMaxLen )
{
    size_t srcLen = srcMaxLen;

    for( size_t i = 0; (0 == srcMaxLen) || (i < srcMaxLen); ++i )
    {
         if( source[i] == '\0' )
         {
             srcLen = i;
             break;
         }
    }

    size_t rc = 0;
    for( size_t i = 0; true; ++i )
    {
         if( (i >= destMaxLen) || (i >= srcLen) )
         {
             rc = (i);
             break;
         }

         dest[i] = source[i];
    }

    return (rc);
}

// Searches the model profile by device ID
static const sNFMModel_t *  static_GetModelProfileByDeviceId( uint16_t deviceId )
{
   for( size_t i = 0; i < aNFMModels_count; ++i )
   {
        if( aNFMModels[i].deviceId == deviceId )
        {
            return &aNFMModels[i];
        }
   }

   for( size_t i = 0; i < aNFMModels_count; ++i )
   {
        if( eModel_undefined == aNFMModels[i].deviceId )
        {
            return &aNFMModels[i];
        }
   }

   return NULL;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------

#if 0
// MEM:TABL:DATA?
// Reads the data points of the characterization table
size_t NFMGetPoints( eChrz_t sectorId, ePortComb_t portComb, ePortStateId_t portState,
                     sNFMGetPoints_t * pCtl )
{
   if(    portComb >= ePortComb_MAX
       || portComb == ePortComb_UNDEFINED
       || portState >= ePortStateId_MAX
       || portState == ePortStateId_UNDEFINED
       || NULL == pCtl
       || NULL == pCtl->in.pDataArray
     )
   {
       if( NULL != pCtl )
           pCtl->svc.errCode = ERR_NFMGETPOINTS_INVAL;

       return 0;
   }

   /* --- do it later: inside @getChrzTableHeader() call
   // Validate the combination of @portComb, @portState and
   // validate the @portComb and @portState itself.
   // Retrieve the table index (ordered index)
   size_t tableIdx = NFMClassExtended->private.methods.memory.getChrzTableIndex( portComb, portState );

   if( NFM_CHRZ_TABLEIDX_INVALID == tableIdx )
   {
       // Invalid combination or value
       return 0;
   }
   */

   // Check the input parameter: @pCtl->in.nCount
   // For zero value: required to load the table header
   // During the loading the table header it is required:
   // - load the main characterization header, check CRC
   // - retrieve number of points (@nPoints) from the header for specified @sectorId
   // - load the table header and check it's CRC
   // - retrieve the table address @TableAddress (for next operations)
   // - retrieve the @min and @max fields from the table header
   // - store @min, @max, @TableAddress, @nPoints into @pCtl.out
   // - return (1) in case of success, or (0) on error
   if( 0 == pCtl->in.nCount )
   {
       // load characterization main header
       // and check the CRC
       const sEcalDataHeader_t * pChrzHeader = NFMClassExtended->private.methods.memory
         .getDataHeader( sectorId, NULL, 0 );

       // check the main header: number of points
       if(   NULL == pChrzHeader
          || 0    == pChrzHeader->Points )
       {
          pCtl->svc.errCode = ERR_NFMGETPOINTS_INVHDR;
          return (0);
       }

       // move @.Points into @nPoints
       // due to @pChrzHeader will be corrupted
       size_t nPoints = pChrzHeader->Points;

       sEcalChrzTableHeader_t tableMicroHeader;

       // load the table base address
       // Note: the data by @pChrzHeader is not valid after this call,
       // due to the function uses the same internal buffer!
       size_t tableAddress = NFMClassExtended->private.methods.memory
         .getChrzTableHeader(        // load the table base address
               sectorId,             //  for specified memory sector
               portComb,             //  for specified port combination
               portState,            //  for specified port state
               nPoints,              //  for retrieved number of points
              &tableMicroHeader,     //  and store the header into @tableMicroHeader
              &pCtl->svc.errCode     //  And fill the error code
         );

       // check the result: @tableAddress
       // 0 - table corrupted or invalid port combination or port state.
       // but since the @portComb and @portState are validated above...
       // this result means only that the table is corrupted
       // non zero - this is the absolute address of the requested table.
       if( 0 == tableAddress )
       {
           (void)pCtl->svc.errCode;
           return 0; // error, sorry
       }

       // - store @min, @max, @TableAddress, @nPoints into @pCtl.out
       pCtl->out.max = tableMicroHeader.MaxMagn;
       pCtl->out.min = tableMicroHeader.MinMagn;
       pCtl->out.TableAddress = tableAddress;
       pCtl->out.nPoints = nPoints;
       pCtl->svc.errCode = ERR_NFMGETPOINTS_NOERR;

       // success
       return 1;
   }

   // -----------------
   // User must call this function with (pCtl->in.nCount=0) first time
   // to initialize the @pCtl->out contents.
   // Here: 0 != pCtl->in.nCount
   // -----------------

   // Check the header data, loaded at previous step ( see pCtl->in.nCount == 0 )
   if(    0 == pCtl->out.nPoints                      // check the number of points in the table
       || 0 == pCtl->out.TableAddress )               // check the table base address
   {
       // error, invalid parameters
       // Call the function with (pCtl->in.nCount = 0) to load the header
       pCtl->svc.errCode = ERR_NFMGETPOINTS_INVAL;
       return 0;
   }

   // Check the input parameters
   if(    pCtl->in.nCount > pCtl->out.nPoints         // check the amount of requested points
       || pCtl->in.nStartPoint >= pCtl->out.nPoints   // check the starting point
       || NULL == pCtl->in.pDataArray )               // check receiving data buffer
   {
       // error: invalid input parameters
       pCtl->svc.errCode = ERR_NFMGETPOINTS_INVAL;

       return 0;
   }

   return NFMClassExtended->private.methods.memory.getChrzPoints( pCtl );
}
#endif

// MEM:TABL:DATA?
// Prepares the context before reading the data points of the characterization table
// This method is designed to replace the obsolete @NFMGetPoints.
// Parameters:
// @sectorId - bank id, characterization bank;
// @portComb - port combination to request (NFM path);
// @portState - port state to request (S-parameter);
// @pDataBuffer - output data buffer;
// @szDataBuffer - output data buffer capacity;
// @xCtl - the control context to initialize;
// Returns: 0 in case error, or number of points in case success.
size_t NFMGetPoints_Begin( eChrz_t sectorId, ePortComb_t portComb, ePortStateId_t portState,
                           sNFMChrzPoint_t * pDataBuffer, size_t szDataBuffer,
                           xNFMGetPointsSimplified_t * xCtl )
{
   sNFMGetPointsSimplified_t * pCtl = (sNFMGetPointsSimplified_t*)xCtl;

   // Check if arguments are valid in general
   if( ( sectorId >= eCh_MAX )
   ||  ( portComb >= ePortComb_MAX )
   ||  ( portState >= ePortStateId_MAX )
   ||  ( portComb == ePortComb_UNDEFINED )
   ||  ( NULL == xCtl )
   ||  ( 0 == szDataBuffer ) )
   {
       if( NULL != pCtl )
           pCtl->intCtx.svc.errCode = ERR_NFMGETPOINTS_INVAL;

       return 0;
   }

   // Check if arguments are valid particularly for this device
   if( ! NFMClassExtended->public.methods.checkTableParams( portComb, portState ) )
   {
       // Invalid combination or value
       pCtl->intCtx.svc.errCode = ERR_NFMGETPOINTS_INVAL;
       return 0;
   }

   // Try to validate configuration (table ID)
   size_t tableIdx = NFMClassExtended->private.methods.memory.getChrzTableIndex( portComb, portState );

   if( NFM_CHRZ_TABLEIDX_INVALID == tableIdx )
   {
       // Invalid combination or value
       pCtl->intCtx.svc.errCode = ERR_NFMGETPOINTS_INVAL;
       return 0;
   }

   // Fill input parameters:
   pCtl->BufferCapacity = szDataBuffer;      // - output buffer capacity
   pCtl->sectorId = sectorId;                // - requested bank
   pCtl->portComb = portComb;                // - requested port combination (nfm path)
   pCtl->portState = portState;              // - requested state (S-parameter)
   pCtl->intCtx.in.nCount = szDataBuffer;    // - number of points to get
   pCtl->intCtx.in.nStartPoint = 0;          // - start point
   pCtl->intCtx.in.pDataArray = pDataBuffer; // - output buffer

   // During the loading the table header it is required:
   // - load the main characterization header, check CRC
   // - retrieve number of points (@nPoints) from the header for specified @sectorId
   // - load the table header and check it's CRC
   // - retrieve the table address @TableAddress (for next operations)
   // - retrieve the @min and @max fields from the table header
   // - store @min, @max, @TableAddress, @nPoints into @pCtl.intCtx.out   

   // load characterization main header
   // and check the CRC
   const sEcalDataHeader_t * pChrzHeader = NFMClassExtended->private.methods.memory
     .getDataHeader( sectorId, NULL, 0 );

   // check the main header: number of points
   if(   NULL == pChrzHeader
      || 0    == pChrzHeader->Points )
   {
      pCtl->intCtx.svc.errCode = ERR_NFMGETPOINTS_INVHDR;
      return (0);
   }

   // move @.Points into @nPoints
   // due to @pChrzHeader will be corrupted
   size_t nPoints = pChrzHeader->Points;

   sEcalChrzTableHeader_t tableMicroHeader;

   // load the table base address
   // Note: the data by @pChrzHeader is not valid after this call,
   // due to the function uses the same internal buffer!
   size_t tableAddress = NFMClassExtended->private.methods.memory
     .getChrzTableHeader(        // load the table base address
           sectorId,             //  for specified memory sector
           portComb,             //  for specified port combination
           portState,            //  for specified port state
           nPoints,              //  for retrieved number of points
          &tableMicroHeader,     //  and store the header into @tableMicroHeader
          &pCtl->intCtx.svc.errCode  //  And fill the error code
     );

   // check the result: @tableAddress
   // 0 - table corrupted or invalid port combination or port state.
   // but since the @portComb and @portState are validated above...
   // this result means only that the table is corrupted
   // non zero - this is the absolute address of the requested table.
   if( 0 == tableAddress )
   {
       (void)pCtl->intCtx.svc.errCode;
       return 0; // error, sorry
   }

   // - store @min, @max, @TableAddress, @nPoints into @pCtl.out
   pCtl->intCtx.out.max = tableMicroHeader.MaxMagn;
   pCtl->intCtx.out.min = tableMicroHeader.MinMagn;
   pCtl->intCtx.out.TableAddress = tableAddress;
   pCtl->intCtx.out.nPoints = nPoints;
   pCtl->intCtx.svc.errCode = ERR_NFMGETPOINTS_NOERR;

   // success
   return nPoints;
}

// MEM:TABL:DATA?
// @NFMGetPoints_Continue
// Uses already prepared context and reads the data points of the characterization 
// table to the top of specified buffer (see @NFMGetPoints_Begin).
// This method is designed to replace the obsolete @NFMGetPoints.
// Parameters:
// @xCtl - control context prepared by previous NFMGetPoints_Begin call
// @pnPointsRetrieve - IN/OUT; IN: specifies a number of points to retrieve; OUT: stores the value of actually read points;
//
// Returns:
// - eNFMGetPointError_DataError: error, can not load data;
// - eNFMGetPointError_InvalidValue: error, invalid parameters;
// - eNFMGetPointError_Success: success, all the points from the table have been read;
// - eNFMGetPointError_OutOfBuffer: warning, points have been read, but it is required to continue because user buffer ran out;
// - eNFMGetPointError_Limit: warning, points have been read, but the caller specified less points to read than actually available;
int32_t NFMGetPoints_Continue( xNFMGetPointsSimplified_t * xCtl, size_t * pnPointsRetrieve )
{
   sNFMGetPointsSimplified_t * pCtl = (sNFMGetPointsSimplified_t*)xCtl;

   if( NULL == pCtl || NULL == pnPointsRetrieve )
   {
       return eNFMGetPointError_InvalidValue;
   } 

   // Check if arguments are valid in general
   if( ( pCtl->sectorId >= eCh_MAX )
   ||  ( pCtl->portComb >= ePortComb_MAX )
   ||  ( pCtl->portState >= ePortStateId_MAX )
   ||  ( pCtl->portComb == ePortComb_UNDEFINED )
   ||  ( 0 == pCtl->BufferCapacity ) 
   ||  ( 0 == pCtl->intCtx.out.nPoints )
   ||  ( 0 == pCtl->intCtx.out.TableAddress ) 
   ||  ( NULL == pCtl->intCtx.in.pDataArray ) )
   {
       pCtl->intCtx.svc.errCode = ERR_NFMGETPOINTS_INVAL;
       return eNFMGetPointError_InvalidValue;
   }

   // check ranges
   if( pCtl->intCtx.in.nCount > pCtl->intCtx.out.nPoints ) pCtl->intCtx.in.nCount = pCtl->intCtx.out.nPoints;
   if( pCtl->intCtx.in.nStartPoint >= pCtl->intCtx.out.nPoints ) pCtl->intCtx.in.nStartPoint = pCtl->intCtx.out.nPoints; // end-of-points
   if( pCtl->intCtx.in.nStartPoint +
       pCtl->intCtx.in.nCount > pCtl->intCtx.out.nPoints )
   {
       pCtl->intCtx.in.nCount = pCtl->intCtx.out.nPoints - pCtl->intCtx.in.nStartPoint;
   }

   // check for end condition
   if( 0 == pCtl->intCtx.in.nCount
    || pCtl->intCtx.in.nStartPoint == pCtl->intCtx.out.nPoints )
   {
       *pnPointsRetrieve = 0; // number of points read is zero
       pCtl->intCtx.svc.errCode = ERR_NFMGETPOINTS_NOERR;
       return eNFMGetPointError_Success;
   }

   int32_t success_rc = eNFMGetPointError_Success;
   
   // check buffer capacity range
   if( pCtl->intCtx.in.nCount > pCtl->BufferCapacity )
   {
       pCtl->intCtx.in.nCount = pCtl->BufferCapacity;
       success_rc = eNFMGetPointError_OutOfBuffer; // out of buffer, the number of points is limited
   }
   else
   if( pCtl->intCtx.in.nStartPoint + pCtl->intCtx.in.nCount < pCtl->intCtx.out.nPoints )
   {       
       success_rc = eNFMGetPointError_Limit; // number of points is limited by user
   }

   pCtl->intCtx.svc.errCode = ERR_NFMGETPOINTS_IO;
   size_t nRead = NFMClassExtended->private.methods.memory.getChrzPoints( &pCtl->intCtx );

   // check for error condition
   if( nRead == 0 )
   {
       *pnPointsRetrieve = 0; // number of points read is zero
       (void)pCtl->intCtx.svc.errCode;
       return eNFMGetPointError_DataError;
   }

   pCtl->intCtx.in.nStartPoint += nRead; // increment start point for next call

   // check range
   if( pCtl->intCtx.in.nStartPoint +
       pCtl->intCtx.in.nCount > pCtl->intCtx.out.nPoints )
   {
       pCtl->intCtx.in.nCount = pCtl->intCtx.out.nPoints - pCtl->intCtx.in.nStartPoint; 
   }

   *pnPointsRetrieve = nRead;            // number of points read
   
   return success_rc;
}

// Reads amount of points in the characterization table
size_t NFMGetPointsCount( eChrz_t tableId )
{
   const sEcalDataHeader_t * pChrzHeader = NFMClassExtended->private.methods.memory.getDataHeader( tableId, NULL, 0 );

   if( NULL == pChrzHeader )
     return (0);

   return (size_t)pChrzHeader->Points;
}

// MEM:TABL:POIN?
// Reads amount of points in the characterization table
bool NFMGetPointsCount_Safe( eChrz_t tableId, int16_t * count )
{
   const sEcalDataHeader_t * pChrzHeader = NFMClassExtended->private.methods.memory.getDataHeader( tableId, NULL, 0 );

   if( NULL == pChrzHeader || NULL == count )
     return false;

   *count = pChrzHeader->Points;

   return true;
}

// MEM:TABL:DATE?
// Reads the date of characterization table
bool NFMGetDate( eChrz_t tableId, char * buffer, size_t size, size_t * bytes )
{
   const sEcalDataHeader_t * pChrzHeader = NFMClassExtended->private.methods.memory.getDataHeader( tableId, NULL, 0 );

   if( NULL == pChrzHeader || NULL == buffer || 0 == size || NULL == bytes )
     return (false);

   size_t nCpy = static_StrCpyLimit(
                       (const char*)pChrzHeader->CalDate,
                       sizeof(pChrzHeader->CalDate),
                       buffer,
                       size );
   *bytes = nCpy;

   return true;
}

// MEM:TABL:TIME?
// Reads the time of characterization table
bool NFMGetTime( eChrz_t tableId, char * buffer, size_t size, size_t * bytes )
{
   const sEcalDataHeader_t * pChrzHeader = NFMClassExtended->private.methods.memory.getDataHeader( tableId, NULL, 0 );

   if( NULL == pChrzHeader || NULL == buffer || 0 == size || NULL == bytes )
     return (false);

   size_t nCpy = static_StrCpyLimit(
                       (const char*)pChrzHeader->CalTime,
                       sizeof(pChrzHeader->CalTime),
                       buffer,
                       size );

   *bytes = nCpy;

   return true;
}

// MEM:TABL:FREQ:TYPE?
// Reads the type of the frequency scale of data characterization table
bool NFMGetScaleType( eChrz_t tableId, eChrzScaleType_t * pType )
{
   const sEcalDataHeader_t * pChrzHeader = NFMClassExtended->private.methods.memory.getDataHeader( tableId, NULL, 0 );

   if( NULL == pChrzHeader )
     return (false);

   if( NULL != pType )
   {
       if( SWEEP_VERSION_LINEAR == pChrzHeader->Version )
       {
           *pType = eChScaleLinear;

           return true;
       }
       else if( SWEEP_VERSION_SEGMENT == pChrzHeader->Version )
       {
           *pType = eChScaleSegment;

           return true;
       }
   }

   return false;
}

// MEM:TABL:FREQ:SEGM:DATA?
// Reads the scale segment of data characterization table
// Pass NULL in @pSegment to retrieve amount of segments in return value
size_t NFMGetScaleSegment( eChrz_t tableId, sEcalSegment_t * pSegment, size_t nSegmentId )
{
   const sEcalDataHeader_t * pChrzHeader = NFMClassExtended->private.methods.memory.getDataHeader( tableId, NULL, 0 );

   if( NULL == pChrzHeader )
     return (0);

   // in case @pSegment is NULL, return amount of segments
   if( NULL == pSegment )
   {
       if( 1 == pChrzHeader->Version )  // Linear Sweep
       {
            return 1; // One segment 
       }

       return pChrzHeader->NSegm;
   }

   if(  ( (nSegmentId >= pChrzHeader->NSegm) && (pChrzHeader->Version == 2) )
     || ( (nSegmentId != 0) && (pChrzHeader->Version == 1) )
     || ( pChrzHeader->Version != 1 && pChrzHeader->Version != 2) )
   {
       // error: check the @nSegmentId, it must be less than amount of segments
       return 0;
   }
   else
   {
       if( 1 == pChrzHeader->Version )  // Linear Sweep
         pSegment->Points = pChrzHeader->Points;
       else
         pSegment->Points = pChrzHeader->Segm[ nSegmentId ].Points;

       double Fstart = 0.0;
       double Fstop = 0.0;

       // aligned access to the unaligned 'double' field
       // memcpy( & Fstart,
       //         & pChrzHeader->Segm[ nSegmentId ].Fstart_unaligned,
       //         sizeof(Fstart)
       // );

       if( 1 == pChrzHeader->Version )  // Linear Sweep
         Fstart = pChrzHeader->Fmin_unaligned;
       else
         Fstart = pChrzHeader->Segm[ nSegmentId ].Fstart_unaligned;

       // aligned access to the unaligned 'double' field
       // memcpy( & Fstop,
       //         & pChrzHeader->Segm[ nSegmentId ].Fstop_unaligned,
       //         sizeof(Fstop)
       // );

       if( 1 == pChrzHeader->Version )  // Linear Sweep
         Fstop = pChrzHeader->Fmax_unaligned;
       else
         Fstop = pChrzHeader->Segm[ nSegmentId ].Fstop_unaligned;

       pSegment->Fstart = Fstart;
       pSegment->Fstop = Fstop;
   }

   return 1;
}

// @NFMGetScaleFreqs_Begin
// Prepares a retrieving context for calling the @NFMGetScaleFreqs_Continue routine.
// Initializes such values as:
// - a number of starting point to retrive
// - a buffer to retrieve the values to
// - characterization table (unit) identifier (@tableId)
// Parameters:
// @tableId - characterization table to operate with;
// @nStartPoint - starting point to begin retrieving from (default = 0);
// @nStartSegment - starting segment to begin retrieving from (default = 0);
// @pFreqArray - a floating point values buffer to store data to;
// @BufferCapacity - receiving buffer capacity [@pFreqArray]
// @xCtl - a control structure to intialize; use this structure to call @NFMGetScaleFreqs_Continue.
//
// Returns: integer result (see @eNFMGetPointError_t):
// - eNFMGetPointError_DataError: error, can not load data;
// - eNFMGetPointError_InvalidValue: error, invalid parameters;
// - eNFMGetPointError_Success: success, all the points from all the segments have been read;
int32_t NFMGetScaleFreqs_Begin( eChrz_t tableId, double * pFreqArray, size_t BufferCapacity, xNFMGetFreqPoints_t * xCtl )
{
    sNFMGetFreqPoints_t * pCtl = (sNFMGetFreqPoints_t*)(xCtl);

    // Checking arguments:
    if( tableId >= eCh_MAX 
     || NULL == pFreqArray
     || NULL == xCtl
     || 0 == BufferCapacity )
    {
        return (eNFMGetPointError_InvalidValue); // error condition
    }

    // initialize the fields    
    pCtl->pDataArray = pFreqArray;       // specify receiving buffer
    pCtl->tableId = tableId;             // specify table ID;                               

    // Get the scale type (LIN | SEGM):
    if( ! NFMClass->methods.xCharacterization.getScaleType( tableId, &pCtl->scaleType ) )
    {
        return (eNFMGetPointError_DataError); // error condition
    }

    // Get segments count for linear or segment scale
    switch( pCtl->scaleType )
    {
        case eChScaleSegment:
        {
             // Get segments count
             pCtl->nSegments = NFMClass->methods.xCharacterization.getScaleSegment( tableId, NULL, 0 );

             // Check segments count
             if( 0 == pCtl->nSegments )
             {
                 return (eNFMGetPointError_DataError); // error condition: invalid segments count (=0)
             }
        }
        break;

        case eChScaleLinear:
        {
             // For linear scale: number of segments is 1
             pCtl->nSegments = 1;
        }
        break;

        default:
           return (eNFMGetPointError_DataError); // error condition: invalid scale type
    }

    // Initialize the rest fields
    pCtl->BufferCapacity = BufferCapacity; // set user buffer capacity
    pCtl->nStartSegment = 0;  // specify starting segment
    pCtl->nStartPoint = 0;    // specify starting point
    pCtl->Segment.Points = 0; // forward set, will be updated in @NFMGetScaleFreqs_Continue
    pCtl->Segment.Fstart = 0.0;
    pCtl->Segment.Fstop = 0.0;

    return eNFMGetPointError_Success;
}

// @NFMGetScaleFreqs_Continue
// Related SCPI command: 'MEM:TABL:FREQ:DATA?'
// Reads the frequencies of data characterization table's points (either for Linear and Segment scale)
// and stores it into the buffer specified by calling @NFMGetScaleFreqs_Begin
// Note: the points are stored to the top of the specified buffer each call.
//
// Parameters:
// @xCtl - retrieve context, must be initialized by @NFMGetScaleFreqs_Begin
// @pnPointsRetrieve - IN/OUT; IN: specifies a number of points to retrieve; OUT: stores the value of actually read points;
//
// !!! Attention: user shall control the @pFreqArray buffer range itself when calling @NFMGetScaleFreqs_Begin
// !!! and guarantee, that it has enough room to recevie specified number of points [@nPointsRetrieve].
//
// Returns:
// - eNFMGetPointError_DataError: error, can not load data;
// - eNFMGetPointError_InvalidValue: error, invalid parameters;
// - eNFMGetPointError_Success: success, all the points from all the segments have been read;
// - eNFMGetPointError_OutOfBuffer: warning, points have been read, but it is required to continue because user buffer ran out;
// - eNFMGetPointError_Limit: warning, points have been read, but the caller specified less points to read than actually available;
int32_t NFMGetScaleFreqs_Continue( xNFMGetFreqPoints_t * xCtl, size_t * pnPointsRetrieve )
{
    sNFMGetFreqPoints_t * pCtl = (sNFMGetFreqPoints_t*)(xCtl);

    // Checking arguments:
    if( NULL == xCtl || NULL == pnPointsRetrieve || 0 == *pnPointsRetrieve )
    {
        return (eNFMGetPointError_InvalidValue); // error condition
    }

    // Check input parameters from context:
    if( 0 == pCtl->nSegments || eChScale_undefined == pCtl->scaleType || eCh_MAX <= pCtl->tableId || NULL == pCtl->pDataArray || 0 == pCtl->BufferCapacity )
    {
        return (eNFMGetPointError_InvalidValue); // error condition
    }

    size_t iPointRequested = 0;
    bool bBreak = false;
    int32_t rc = eNFMGetPointError_Success; // forward set to success code

    // For each segment and each point:
    for( (void)pCtl->nStartSegment;
              (pCtl->nStartSegment < pCtl->nSegments) && (!bBreak);
            ++ pCtl->nStartSegment )
    {
        for( (void)pCtl->nStartPoint;
            ((void)pCtl->Segment.Points, (!bBreak));
                ++ pCtl->nStartPoint )
        {

           // Check if the segment is loaded
           if( 0 == pCtl->Segment.Points )
           {
               // load segment data: 
               switch( pCtl->scaleType )
               {
                   case eChScaleSegment:
                   {
                        // Retrieve the segment data
                        if( 1 != NFMClass->methods.xCharacterization.getScaleSegment( pCtl->tableId, &pCtl->Segment, pCtl->nStartSegment ) )
                        {
                            return (eNFMGetPointError_DataError); // error condition: can not load segment data
                        }
                   }
                   break;
               
                   case eChScaleLinear:
                   {
                        // For linear scale: retrieve from the common characterization header
                        if( ! NFMClass->methods.xCharacterization.getPointsCountSafe( pCtl->tableId, &pCtl->Segment.Points )
                         || ! NFMClass->methods.xCharacterization.getStartFreq( pCtl->tableId, &pCtl->Segment.Fstart )
                         || ! NFMClass->methods.xCharacterization.getStopFreq( pCtl->tableId, &pCtl->Segment.Fstop ) )
                        {
                            return (eNFMGetPointError_DataError); // error condition: can not load segment data
                        }
                   }
                   break;
               
                   default:
                      return (eNFMGetPointError_DataError); // error condition: invalid scale type
               }

               // Calculate the discrette
               pCtl->Fdesc = (pCtl->Segment.Fstop - pCtl->Segment.Fstart) / (pCtl->Segment.Points - 1);
               pCtl->nStartPoint = 0; // Reset start point for current segment
           }

           // Segment loaded?
           if( 0 != pCtl->Segment.Points )
           {
               // yes, check condition: all the points of segment have been processed?
               if( pCtl->nStartPoint >= pCtl->Segment.Points ) break;
           }
           else
           {
               // Invalid segment
               return (eNFMGetPointError_DataError); // error condition: can not load segment data
           }

           // Fill the output array from the begining
           // Note @pDataArray have at least one cell due to input checking 
           pCtl->pDataArray[ iPointRequested++ ] = (pCtl->Segment.Fstart + pCtl->Fdesc * pCtl->nStartPoint);

           // Check for overflow: user buffer overflow
           if( iPointRequested >= pCtl->BufferCapacity )
           {
               bBreak = true;
               rc = eNFMGetPointError_OutOfBuffer; // warning
               /* break; DO NOT BREAK, need to increment @nStartPoint */
           }

           // Check for end condition: user limit
           if( iPointRequested >= *pnPointsRetrieve )
           {
               bBreak = true;
               rc = eNFMGetPointError_Limit; // warning
               /* break; DO NOT BREAK, need to increment @nStartPoint */;
           }
        }

        // Check for end condition: out of points in segment or out of segments
        if( pCtl->nStartPoint >= pCtl->Segment.Points )
        {
            if( pCtl->nStartSegment >= pCtl->nSegments - 1 ) /* -1: because @nStartSegment has not been incremented yet */
            {
                pCtl->nStartSegment++; // instead of reaching the top of operator 'for'
                bBreak = true;
                (void)rc; // eNFMGetPointError_Success, end-of-data
            }
            
            pCtl->Segment.Points = 0; // Reset points count: make to load the segment data at next iteration
            pCtl->nStartPoint = 0; // Reset start point for next segment
        }

        if( bBreak ) break; // do not increment @nStartSegment
    }

    (void)rc; // If no points are available, the @eNFMGetPointError_Success code is returned with *pnPointsRetrieve set to zero
    *pnPointsRetrieve = iPointRequested; // write number of points read

    return rc;
}

// @SWGetTablePoints_Begin
// Prepares a retrieving context for calling the @SWGetTablePoints_Continue routine.
// Initializes such values as:
// - a number of starting point to retrive
// - a buffer to retrieve the values to
// - points table (unit) identifier (@tableId)
// Parameters:
// @tableId - characterization table to operate with;
// @nStartPoint - starting point to begin retrieving from (default = 0);
// @nStartSegment - starting segment to begin retrieving from (default = 0);
// @pPointsArray - a floating point values buffer to store data to;
// @BufferCapacity - receiving buffer capacity [@pFreqArray]
// @xCtl - a control structure to intialize; use this structure to call @SWGetTablePoints_Continue.
//
// Returns: integer result (see @eNFMGetPointError_t):
// - eNFMGetPointError_DataError: error, can not load data;
// - eNFMGetPointError_InvalidValue: error, invalid parameters;
// - eNFMGetPointError_Success: success, all the points from all the segments have been read;
int32_t SWGetTablePoints_Begin( eChrz_t tableId, sSWTablePoint_t * pPointsArray, size_t BufferCapacity, xSWGetTablePoints_t * xCtl )
{
    sSWGetTablePoints_t * pCtl = (sSWGetTablePoints_t*)(xCtl);

    // Checking arguments:
    if( tableId >= eCh_MAX 
     || NULL == pPointsArray
     || NULL == xCtl
     || 0 == BufferCapacity )
    {
        return (eNFMGetPointError_InvalidValue); // error condition
    }

    // initialize the fields    
    pCtl->pDataArray = pPointsArray;     // specify receiving buffer
    pCtl->tableId = tableId;             // specify table ID;

    // Initialize the rest fields
    pCtl->BufferCapacity = BufferCapacity; // set user buffer capacity
    pCtl->nStartPoint = 0;    // specify starting point
    //pCtl->nNumberOfPoints = TableHandle.GetNumberOfPoints();
    pCtl->nNumberOfPoints = TableHandle.GetNumberOfPoints();
    pCtl->TablePoint.port1 = 0;
    pCtl->TablePoint.port2 = 0;

    return eNFMGetPointError_Success;
}

// @NFMGetScaleFreqs_Continue
// Related SCPI command: 'MEM:TABL:FREQ:DATA?'
// Reads the frequencies of data characterization table's points (either for Linear and Segment scale)
// and stores it into the buffer specified by calling @NFMGetScaleFreqs_Begin
// Note: the points are stored to the top of the specified buffer each call.
//
// Parameters:
// @xCtl - retrieve context, must be initialized by @NFMGetScaleFreqs_Begin
// @pnPointsRetrieve - IN/OUT; IN: specifies a number of points to retrieve; OUT: stores the value of actually read points;
//
// !!! Attention: user shall control the @pFreqArray buffer range itself when calling @NFMGetScaleFreqs_Begin
// !!! and guarantee, that it has enough room to recevie specified number of points [@nPointsRetrieve].
//
// Returns:
// - eNFMGetPointError_DataError: error, can not load data;
// - eNFMGetPointError_InvalidValue: error, invalid parameters;
// - eNFMGetPointError_Success: success, all the points from all the segments have been read;
// - eNFMGetPointError_OutOfBuffer: warning, points have been read, but it is required to continue because user buffer ran out;
// - eNFMGetPointError_Limit: warning, points have been read, but the caller specified less points to read than actually available;
int32_t SWGetTablePoints_Continue( xSWGetTablePoints_t * xCtl, size_t * pnPointsRetrieve )
{
    sSWGetTablePoints_t * pCtl = (sSWGetTablePoints_t*)(xCtl);

    // Checking arguments:
    if( NULL == xCtl || NULL == pnPointsRetrieve || 0 == *pnPointsRetrieve )
    {
        return (eNFMGetPointError_InvalidValue); // error condition
    }

    // Check input parameters from context:
    if( eCh_MAX <= pCtl->tableId || NULL == pCtl->pDataArray || 0 == pCtl->BufferCapacity )
    {
        return (eNFMGetPointError_InvalidValue); // error condition
    }

    size_t iPointRequested = 0;
    bool bBreak = false;
    int32_t rc = eNFMGetPointError_Success; // forward set to success code

    // For each point:
    for( (void)pCtl->nStartPoint;
        ((void)pCtl->nNumberOfPoints, (!bBreak));
            ++ pCtl->nStartPoint )
    {
    
        // Check if the point is loaded
        if( 0 != pCtl->nNumberOfPoints )
        {
            // yes, check condition: all the points of segment have been processed?
            if( pCtl->nStartPoint >= pCtl->nNumberOfPoints )
            { // Check for end condition: out of points in segment or out of segments
                bBreak = true;
                (void)rc; // eNFMGetPointError_Success, end-of-data
                pCtl->nNumberOfPoints = 0; // Reset points count: make to load the segment data at next iteration
                pCtl->nStartPoint = 0; // Reset start point for next segment
                break;
            }
            if( ! TableHandle.Get_Point(pCtl->nStartPoint, &pCtl->TablePoint))
            {
                return (eNFMGetPointError_DataError); // error condition: can not load segment data
            }
        }
        else
        {
            // Invalid segment
            return (eNFMGetPointError_DataError); // error condition: can not load segment data
        }
        
        // Fill the output array from the begining
        // Note @pDataArray have at least one cell due to input checking 
        pCtl->pDataArray[ iPointRequested ].port1 = pCtl->TablePoint.port1;
        pCtl->pDataArray[ iPointRequested ].port2 = pCtl->TablePoint.port2;
        // To next point
        iPointRequested++; 
        // Check for overflow: user buffer overflow
        if( iPointRequested >= pCtl->BufferCapacity )
        {
            bBreak = true;
            rc = eNFMGetPointError_OutOfBuffer; // warning
            /* break; DO NOT BREAK, need to increment @nStartPoint */
        }
        
        // Check for end condition: user limit
        if( iPointRequested >= *pnPointsRetrieve )
        {
            bBreak = true;
            rc = eNFMGetPointError_Limit; // warning
            /* break; DO NOT BREAK, need to increment @nStartPoint */;
        }
    }

    (void)rc; // If no points are available, the @eNFMGetPointError_Success code is returned with *pnPointsRetrieve set to zero
    *pnPointsRetrieve = iPointRequested; // write number of points read

    return rc;
}

/*
/// --------------------------------------------------------------------------
///
/// \brief createAbscissa
/// \details пїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ
/// \param[in] segment - QVector пїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ (Fmin, Fmax, NPoints)
/// \return пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅ
///  пїЅпїЅ пїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ (RndAbscissa)
/// \note пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅ lambda пїЅпїЅпїЅпїЅпїЅ пїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ Hardware пїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅ NfmKit.h,
/// пїЅ.пїЅ. пїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅ.
/// \note пїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ, пїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅ.
static auto createAbscissa( const QVector<Planar::VNA::Hardware::SegmentInfo>& segments )
{
    // пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅ пїЅпїЅ пїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ:
    size_t nPointsTotal = 0;

    // пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ:
    // (пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅ пїЅпїЅ пїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ):
    for ( auto& segment : segments )
    {
        // пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅ пїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ:
        size_t nPoints = static_cast<size_t>(segment.Points);

        // пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅ пїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ
        nPointsTotal += nPoints;
    }

    // пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅ пїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ (пїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅ):
    Dsp::RndAbscissa* customAbscissa = new Dsp::RndAbscissa( nPointsTotal );

    // пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅ, пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ
    // пїЅпїЅпїЅпїЅпїЅ пїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ: @abscissaPointIndex
    size_t abscissaPointIndex = 0;

    // пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ:
    // (пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅ пїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅ):
    for ( auto& segment : segments )
    {

        // пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅ пїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ:
        size_t nPoints = static_cast<size_t>(segment.Points);

        // пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅ:
        //  - пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ: @Fmin
        auto Fmin = Unit(segment.Fmin).toSI().value();
        //  - пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ: @Fmax
        auto Fmax = Unit(segment.Fmax).toSI().value();
        //  - пїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅ пїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ: @Fstep
        auto Fstep = (Fmax - Fmin) / nPoints;

        // пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅ:
        for ( size_t p = 0; p < nPoints; ++p )
        {
            customAbscissa->setValue( abscissaPointIndex + p, Fmin + Fstep * p  );
        }

        // пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅ пїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ,
        // пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ:
        abscissaPointIndex += nPoints;
    }

    return QSharedPointer<Dsp::RndAbscissa>(customAbscissa);
*/

// MEM:TABL:FREQ:STAR?
// Reads the start frequency of the characterization table
bool NFMGetStartFreq( eChrz_t tableId, double * pStartFreq )
{
   const sEcalDataHeader_t * pChrzHeader = NFMClassExtended->private.methods.memory.getDataHeader( tableId, NULL, 0 );

   if( NULL == pChrzHeader || NULL == pStartFreq )
       return (false);

   if( SWEEP_VERSION_LINEAR == pChrzHeader->Version )
   {
       double Fmin = 0.0;

       // aligned access to the unaligned 'double' field
       // memcpy( & Fmin,
       //         & pChrzHeader->Fmin_unaligned,
       //         sizeof(Fmin)
       // ); // *pStartFreq = pChrzHeader->Fmin, fixed 28/08/18
       Fmin = pChrzHeader->Fmin_unaligned;

       *pStartFreq = Fmin;

       return true;
   }
   else if( SWEEP_VERSION_SEGMENT == pChrzHeader->Version )
   {
       if( 0 < pChrzHeader->NSegm )
       {
           // double min = pChrzHeader->Segm[0].Fstart; // fixed 28/08/18

           double Fstart_min = 0.0;

           // aligned access to the unaligned 'double' field
           // memcpy( & Fstart_min,
           //         & pChrzHeader->Segm[0].Fstart_unaligned,
           //         sizeof(Fstart_min)
           // ); // min = pChrzHeader->Segm[0].Fstart

           Fstart_min = pChrzHeader->Segm[0].Fstart_unaligned;

           for( size_t n = 0; n < pChrzHeader->NSegm; ++n )
           {
                double Fstart_n = 0.0;

                // aligned access to the unaligned 'double' field
                // memcpy( & Fstart_n,
                //         & pChrzHeader->Segm[n].Fstart_unaligned,
                //         sizeof(Fstart_n)
                // ); // min = pChrzHeader->Segm[n].Fstart, fixed 28/08/18
                Fstart_n = pChrzHeader->Segm[n].Fstart_unaligned;

                if( Fstart_n < Fstart_min )
                {
                    Fstart_min = Fstart_n;
                }
           }

           *pStartFreq = Fstart_min;

           return true;
       }
   }

   return false;
}

// MEM:TABL:FREQ:STOP?
// Reads the stop frequency of the characterization table
bool NFMGetStopFreq( eChrz_t tableId, double * pStopFreq )
{
   const sEcalDataHeader_t * pChrzHeader = NFMClassExtended->private.methods.memory.getDataHeader( tableId, NULL, 0 );

   if( NULL == pChrzHeader || NULL == pStopFreq )
       return (false);

   if( SWEEP_VERSION_LINEAR == pChrzHeader->Version )
   {
       double Fmax = 0.0;

       // aligned access to the unaligned 'double' field
       // memcpy( & Fmax,
       //         & pChrzHeader->Fmax_unaligned,
       //         sizeof(Fmax)
       // ); // *pStopFreq = pChrzHeader->Fmax;, fixed 28/08/18
       Fmax = pChrzHeader->Fmax_unaligned;

       *pStopFreq = Fmax;

       return true;
   }
   else if( SWEEP_VERSION_SEGMENT == pChrzHeader->Version )
   {
       if( 0 < pChrzHeader->NSegm )
       {
           double Fstop_max = 0.0;

           // aligned access to the unaligned 'double' field
           // memcpy( & Fstop_max,
           //         & pChrzHeader->Segm[ pChrzHeader->NSegm - 1 ].Fstop_unaligned,
           //         sizeof(Fstop_max)
           // ); // double max = pChrzHeader->Segm[ pChrzHeader->NSegm - 1 ].Fstop;, fixed 28/08/18
           Fstop_max = pChrzHeader->Segm[ pChrzHeader->NSegm - 1 ].Fstop_unaligned;

           for( size_t n = 0; n < pChrzHeader->NSegm; ++n )
           {
                double Fstop_n = 0.0;

                // aligned access to the unaligned 'double' field
                // memcpy( & Fstop_n,
                //         & pChrzHeader->Segm[ pChrzHeader->NSegm - 1 - n ].Fstop_unaligned,
                //         sizeof(Fstop_n)
                // ); // max = pChrzHeader->Segm[ pChrzHeader->NSegm - 1 - n ].Fstop;, fixed 28/08/18
                Fstop_n = pChrzHeader->Segm[ pChrzHeader->NSegm - 1 - n ].Fstop_unaligned;

                if( Fstop_n > Fstop_max )
                {
                    Fstop_max = Fstop_n;
                }
           }

           *pStopFreq = Fstop_max;

           return true;
       }
   }

   return false;
}

// MEM:TABL:TEMP?
// Reads the characterization temperature of characterization table
bool NFMGetChrzTemp( eChrz_t tableId, double * pTemperature )
{
   const sEcalDataHeader_t * pChrzHeader = NFMClassExtended->private.methods.memory.getDataHeader( tableId, NULL, 0 );

   if( NULL == pChrzHeader || NULL == pTemperature )
       return (false);

   double temperature = 0.0;

   // aligned access to the unaligned 'double' field
   // memcpy( & temperature,
   //         & pChrzHeader->Temperature_unaligned,
   //         sizeof(temperature)
   // ); // *pTemperature = pChrzHeader->Temperature;, fixed 28/08/18
   temperature = pChrzHeader->Temperature_unaligned;

   *pTemperature = temperature;

   return true;
}

// MEM:TABL:CONN?
// Reads the connector type of the NFM
bool NFMGetConnectorType( ePortId_t portId, char * buffer, size_t size, size_t * bytes )
{
    const sEcalDataHeader_t * pChrzHeader = NFMClassExtended->private.methods.memory.getDataHeader( eChFactory, NULL, 0 );

    if( NULL == pChrzHeader || NULL == buffer || size == 0 || NULL == bytes )
       return (false);

    uint16_t connector;
    const char * pDescConnector = NULL;
    size_t nCpy = 0;

    switch( portId )
    {
       case ePortId_A:
           connector = pChrzHeader->ConnectorA;
       break;
       case ePortId_B:
           connector = pChrzHeader->ConnectorB;
       break;
       case ePortId_C:
           connector = pChrzHeader->ConnectorC;
       break;
       case ePortId_D:
           connector = pChrzHeader->ConnectorD;
       break;
       default: return (false);
    }

    if( connector >= sizeof(aConnectorsNames)/sizeof(aConnectorsNames[0]) )
    {
        pDescConnector = pcConnectorUndefined;
    }
    else
    {
        pDescConnector = aConnectorsNames[ connector ];
    }

    nCpy = static_StrCpyLimit(
                                (const char*)pDescConnector,
                                0,
                                buffer,
                                size );

    *bytes = nCpy;

    return true;
}

// MEM:TABL:ADAP?
// Reads the adapter description of the NFM connector
bool NFMGetAdapterDesc( eChrz_t tableId, ePortId_t portId, char * buffer, size_t size, size_t * bytes )
{
    const sEcalDataHeader_t * pChrzHeader = NFMClassExtended->private.methods.memory.getDataHeader( tableId, NULL, 0 );

    if( NULL == pChrzHeader || NULL == buffer || size == 0 || NULL == bytes )
       return (false);

    size_t nCpy = 0;
    switch( portId )
    {
       case ePortId_A:
            nCpy = static_StrCpyLimit(
                             (const char*)pChrzHeader->AdapterDescriptionA,
                             sizeof(pChrzHeader->AdapterDescriptionA),
                             buffer,
                             size );
       break;
       case ePortId_B:
            nCpy = static_StrCpyLimit(
                             (const char*)pChrzHeader->AdapterDescriptionB,
                             sizeof(pChrzHeader->AdapterDescriptionB),
                             buffer,
                             size );
       break;
       case ePortId_C:
            nCpy = static_StrCpyLimit(
                             (const char*)pChrzHeader->AdapterDescriptionC,
                             sizeof(pChrzHeader->AdapterDescriptionC),
                             buffer,
                             size );
       break;
       case ePortId_D:
            nCpy = static_StrCpyLimit(
                             (const char*)pChrzHeader->AdapterDescriptionD,
                             sizeof(pChrzHeader->AdapterDescriptionD),
                             buffer,
                             size );
       break;
       default: return (false);
    }

    *bytes = nCpy;

    return true;
}

// MEM:TABL:ANAL? ( =ANALyzer)
// Reads the analyzer type
bool NFMGetAnalyzer( eChrz_t tableId, char * buffer, size_t size, size_t * bytes )
{
   const sEcalDataHeader_t * pChrzHeader = NFMClassExtended->private.methods.memory.getDataHeader( tableId, NULL, 0 );

   if( NULL == pChrzHeader || NULL == buffer || size == 0 || NULL == bytes )
       return (false);

   size_t nCpy = static_StrCpyLimit(
                       (const char*)pChrzHeader->Analyzer,
                       sizeof(pChrzHeader->Analyzer),
                       buffer,
                       size );


   *bytes = nCpy;

   return true;
}

// MEM:TABL:PLAC?
// Reads the place where the characterization had been created
bool NFMGetPlace( eChrz_t tableId, char * buffer, size_t size, size_t * bytes )
{
   const sEcalDataHeader_t * pChrzHeader = NFMClassExtended->private.methods.memory.getDataHeader( tableId, NULL, 0 );

   if( NULL == pChrzHeader || NULL == buffer || size == 0 || NULL == bytes )
       return (false);

   size_t nCpy = static_StrCpyLimit(
                       (const char*)pChrzHeader->Location,
                       sizeof(pChrzHeader->Location),
                       buffer,
                       size );

   *bytes = nCpy;

   return true;
}

// MEM:TABL:OPER?
// Reads the the characterization operator's name
bool NFMGetOperator( eChrz_t tableId, char * buffer, size_t size, size_t * bytes )
{
   const sEcalDataHeader_t * pChrzHeader = NFMClassExtended->private.methods.memory.getDataHeader( tableId, NULL, 0 );

   if( NULL == pChrzHeader || NULL == buffer || size == 0 || NULL == bytes )
       return (false);

   size_t nCpy = static_StrCpyLimit(
                       (const char*)pChrzHeader->Operator,
                       sizeof(pChrzHeader->Operator),
                       buffer,
                       size );
   *bytes = nCpy;

   return true;
}

// MEM:TABL:THER:CORR:FREQ:START
// Reads the the start frequency of thermocompensation data
static bool NFMGetStartFreqThermo( double * pStartFreq )
{
   const sEcalTCompHeader_t * pTCompHeader = NFMClassExtended->private.methods.memory.getTCompHeader( NULL, 0 );

   if( NULL == pTCompHeader || NULL == pStartFreq )
       return (false);

   double Fmin = 0.0;

   // aligned access to the unaligned 'double' field
   // memcpy( & Fmin,
   //         & pTCompHeader->Fmin_unaligned,
   //         sizeof(Fmin)
   // ); // *pStartFreq = pTCompHeader->Fmin;, fixed 28/08/18
   Fmin = pTCompHeader->Fmin_unaligned;

   *pStartFreq = Fmin;

   return true;
}

// MEM:TABL:THER:CORR:FREQ:STOP
// Reads the the stop frequency of thermocompensation data
static bool NFMGetStopFreqThermo( double * pStopFreq )
{
   const sEcalTCompHeader_t * pTCompHeader = NFMClassExtended->private.methods.memory.getTCompHeader( NULL, 0 );

   if( NULL == pTCompHeader || NULL == pStopFreq )
       return (false);

   double Fmax = 0.0;

   // aligned access to the unaligned 'double' field
   // memcpy( & Fmax,
   //         & pTCompHeader->Fmax_unaligned,
   //         sizeof(Fmax)
   // ); // *pStopFreq = pTCompHeader->Fmax;, fixed 28/08/18
   Fmax = pTCompHeader->Fmax_unaligned;

   *pStopFreq = Fmax;

   return true;
}

// Reads the amount of points of thermocompensation data
static size_t NFMGetPointsThermo()
{
   const sEcalTCompHeader_t * pTCompHeader = NFMClassExtended->private.methods.memory.getTCompHeader( NULL, 0 );

   if( NULL == pTCompHeader )
       return (0);

   return pTCompHeader->Points;
}

// MEM:TABL:THER:CORR:POIN?
// Reads the amount of points of thermocompensation data
static bool NFMGetPointsThermo_Safe( int16_t * count )
{
   const sEcalTCompHeader_t * pTCompHeader = NFMClassExtended->private.methods.memory.getTCompHeader( NULL, 0 );

   if( NULL == pTCompHeader || NULL == count )
       return false;

   *count = pTCompHeader->Points;

   return true;
}

// // MEM:TABL:THER:CORR:MAGN?
// // Reads the magnitude data points of the thermocompensation table
// static size_t NFMGetPointsMagnThermo( ePortComb_t portComb, ePortStateId_t portState, sNFMGetPoints_t * pCtl )
// {
//    if(    portComb >= ePortComb_MAX
//        || portComb == ePortComb_UNDEFINED
//        || portState >= ePortStateId_MAX
//        || portState == ePortStateId_UNDEFINED
//        || NULL == pCtl
//        || NULL == pCtl->in.pDataArray
//      )
//    {
//        if( NULL != pCtl )
//            pCtl->svc.errCode = ERR_NFMGETPOINTS_INVAL;
// 
//        return 0;
//    }
// 
//    /* --- do it later: inside @getChrzTableHeader() call
//    // Validate the combination of @portComb, @portState and
//    // validate the @portComb and @portState itself.
//    // Retrieve the table index (ordered index)
//    size_t tableIdx = NFMClassExtended->private.methods.memory.getChrzTableIndex( portComb, portState );
// 
//    if( NFM_CHRZ_TABLEIDX_INVALID == tableIdx )
//    {
//        // Invalid combination or value
//        return 0;
//    }
//    */
// 
//    // Check the input parameter: @pCtl->in.nCount
//    // For zero value: required to load the table header
//    // During the loading the table header it is required:
//    // - load the main thermocompensation header, check CRC
//    // - retrieve number of points (@nPoints) from the header
//    // - load the table header and check it's CRC
//    // - retrieve the table address @TableAddress (for next operations)
//    // - retrieve the @min and @max fields from the table header
//    // - store @min, @max, @TableAddress, @nPoints into @pCtl.out
//    // - return (1) in case of success, or (0) on error
//    if( 0 == pCtl->in.nCount )
//    {
//        // load thermocompensation main header
//        // and check the CRC
//        const sEcalTCompHeader_t * pTCompHeader = NFMClassExtended->private.methods.memory
//          .getTCompHeader( NULL, 0 );
// 
//        // check the TComp header: number of points
//        if(   NULL == pTCompHeader
//           || 0    == pTCompHeader->Points )
//        {
//           pCtl->svc.errCode = ERR_NFMGETPOINTS_INVHDR;
//           return (0);
//        }
// 
//        // move @.Points into @nPoints
//        // due to @pTCompHeader will be corrupted
//        size_t nPoints = pTCompHeader->Points;
// 
//        sEcalTCompTableMagnHeader_t tableMicroHeader;
// 
//        // load the table base address
//        // Note: the data by @pChrzHeader is not valid after this call,
//        // due to the function uses the same internal buffer!
//        size_t tableAddress = NFMClassExtended->private.methods.memory
//          .getTCompMagnTableHeader(        // load the table base address
//                portComb,             //  for specified port combination
//                portState,            //  for specified port state
//                nPoints,              //  for retrieved number of points
//               &tableMicroHeader,     //  and store the header into @tableMicroHeader
//               &pCtl->svc.errCode     //  And fill up the error code
//          );
// 
//        // check the result: @tableAddress
//        // 0 - table corrupted or invalid port combination or port state.
//        // but since the @portComb and @portState are validated above...
//        // this result means only that the table is corrupted
//        // non zero - this is the absolute address of the requested table.
//        if( 0 == tableAddress )
//        {
//            (void)pCtl->svc.errCode;
//            return 0; // error, sorry
//        }
// 
//        // - store @min, @max, @TableAddress, @nPoints into @pCtl.out
//        pCtl->out.max = tableMicroHeader.MaxMagn;
//        pCtl->out.min = tableMicroHeader.MinMagn;
//        pCtl->out.TableAddress = tableAddress;
//        pCtl->out.nPoints = nPoints;
// 
//        pCtl->svc.errCode = ERR_NFMGETPOINTS_NOERR;
//        // success
//        return 1;
//    }
// 
//    // -----------------
//    // User must call this function with (pCtl->in.nCount=0) first time
//    // to initialize the @pCtl->out contents.
//    // Here: 0 != pCtl->in.nCount
//    // -----------------
// 
//    // Check the header data, loaded at previous step ( see pCtl->in.nCount == 0 )
//    if(    0 == pCtl->out.nPoints                      // check the number of points in the table
//        || 0 == pCtl->out.TableAddress )               // check the table base address
//    {
//        // error, invalid parameters
//        // Call the function with (pCtl->in.nCount = 0) to load the header
//        pCtl->svc.errCode = ERR_NFMGETPOINTS_INVAL;
// 
//        return 0;
//    }
// 
//    // Check the input parameters
//    if(    pCtl->in.nCount > pCtl->out.nPoints         // check the amount of requested points
//        || pCtl->in.nStartPoint >= pCtl->out.nPoints   // check the starting point
//        || NULL == pCtl->in.pDataArray )               // check receiving data buffer
//    {
//        // error: invalid input parameters
//        pCtl->svc.errCode = ERR_NFMGETPOINTS_INVAL;
// 
//        return 0;
//    }
// 
//    return NFMClassExtended->private.methods.memory.getTCompMagnPoints( pCtl );
// }

// // MEM:TABL:THER:CORR:PHASE?
// // Reads the phase data points of the thermocompensation table
// static size_t NFMGetPointsPhaseThermo( ePortComb_t portComb, ePortStateId_t portState, sNFMGetPoints_t * pCtl )
// {
//    if(    portComb >= ePortComb_MAX
//        || portComb == ePortComb_UNDEFINED
//        || portState >= ePortStateId_MAX
//        || portState == ePortStateId_UNDEFINED
//        || NULL == pCtl
//        || NULL == pCtl->in.pDataArray
//      )
//    {
//        if( NULL != pCtl )
//            pCtl->svc.errCode = ERR_NFMGETPOINTS_INVAL;
// 
//        return 0;
//    }
// 
//    /* --- do it later: inside @getChrzTableHeader() call
//    // Validate the combination of @portComb, @portState and
//    // validate the @portComb and @portState itself.
//    // Retrieve the table index (ordered index)
//    size_t tableIdx = NFMClassExtended->private.methods.memory.getChrzTableIndex( portComb, portState );
// 
//    if( NFM_CHRZ_TABLEIDX_INVALID == tableIdx )
//    {
//        // Invalid combination or value
//        return 0;
//    }
//    */
// 
//    // Check the input parameter: @pCtl->in.nCount
//    // For zero value: required to load the table header
//    // During the loading the table header it is required:
//    // - load the main thermocompensation header, check CRC
//    // - retrieve number of points (@nPoints) from the header
//    // - load the table header and check it's CRC
//    // - retrieve the table address @TableAddress (for next operations)
//    // - retrieve the @min and @max fields from the table header
//    // - store @min, @max, @TableAddress, @nPoints into @pCtl.out
//    // - return (1) in case of success, or (0) on error
//    if( 0 == pCtl->in.nCount )
//    {
//        // load thermocompensation main header
//        // and check the CRC
//        const sEcalTCompHeader_t * pTCompHeader = NFMClassExtended->private.methods.memory
//          .getTCompHeader( NULL, 0 );
// 
//        // check the TComp header: number of points
//        if(   NULL == pTCompHeader
//           || 0    == pTCompHeader->Points )
//        {
//           pCtl->svc.errCode = ERR_NFMGETPOINTS_INVHDR;
// 
//           return (0);
//        }
// 
//        // move @.Points into @nPoints
//        // due to @pTCompHeader will be corrupted
//        size_t nPoints = pTCompHeader->Points;
// 
//        sEcalTCompTablePhaseHeader_t tableMicroHeader;
// 
//        // load the table base address
//        // Note: the data by @pChrzHeader is not valid after this call,
//        // due to the function uses the same internal buffer!
//        size_t tableAddress = NFMClassExtended->private.methods.memory
//          .getTCompPhaseTableHeader(        // load the table base address
//                portComb,             //  for specified port combination
//                portState,            //  for specified port state
//                nPoints,              //  for retrieved number of points
//               &tableMicroHeader,     //  and store the header into @tableMicroHeader
//               &pCtl->svc.errCode     //  And fill up the error code
//          );
// 
//        // check the result: @tableAddress
//        // 0 - table corrupted or invalid port combination or port state.
//        // but since the @portComb and @portState are validated above...
//        // this result means only that the table is corrupted
//        // non zero - this is the absolute address of the requested table.
//        if( 0 == tableAddress )
//        {
//            (void)pCtl->svc.errCode;
//            return 0; // error, sorry
//        }
// 
//        // - store @min, @max, @TableAddress, @nPoints into @pCtl.out
//        pCtl->out.max = tableMicroHeader.MaxPhase;
//        pCtl->out.min = tableMicroHeader.MinPhase;
//        pCtl->out.TableAddress = tableAddress;
//        pCtl->out.nPoints = nPoints;
// 
//        pCtl->svc.errCode = ERR_NFMGETPOINTS_NOERR;
// 
//        // success
//        return 1;
//    }
// 
//    // -----------------
//    // User must call this function with (pCtl->in.nCount=0) first time
//    // to initialize the @pCtl->out contents.
//    // Here: 0 != pCtl->in.nCount
//    // -----------------
// 
//    // Check the header data, loaded at previous step ( see pCtl->in.nCount == 0 )
//    if(    0 == pCtl->out.nPoints                      // check the number of points in the table
//        || 0 == pCtl->out.TableAddress )               // check the table base address
//    {
//        // error, invalid parameters
//        // Call the function with (pCtl->in.nCount = 0) to load the header
//        pCtl->svc.errCode = ERR_NFMGETPOINTS_INVAL;
// 
//        return 0;
//    }
// 
//    // Check the input parameters
//    if(    pCtl->in.nCount > pCtl->out.nPoints         // check the amount of requested points
//        || pCtl->in.nStartPoint >= pCtl->out.nPoints   // check the starting point
//        || NULL == pCtl->in.pDataArray )               // check receiving data buffer
//    {
//        // error: invalid input parameters
//        pCtl->svc.errCode = ERR_NFMGETPOINTS_INVAL;
// 
//        return 0;
//    }
// 
//    return NFMClassExtended->private.methods.memory.getTCompPhasePoints( pCtl );
// }

// MEM:TABL:THERmo:CORRection:MAGNitude?
// NFMGetPointsThermoMagn_Begin
// Prepares the context before reading the data points of the thermocompensation table of magnitudes
// This method is designed to replace the obsolete @NFMGetPointsMagnThermo
// Parameters:
// @portComb - port combination to request (NFM path);
// @portState - port state to request (S-parameter);
// @pDataBuffer - output data buffer;
// @szDataBuffer - output data buffer capacity;
// @xCtl - the control context to initialize;
// Returns: 0 in case error, or number of points in case success.
size_t NFMGetPointsThermoMagn_Begin( ePortComb_t portComb, ePortStateId_t portState,
                                     sNFMChrzPoint_t * pDataBuffer, size_t szDataBuffer,
                                     xNFMGetPointsSimplified_t * xCtl )
{
   sNFMGetPointsSimplified_t * pCtl = (sNFMGetPointsSimplified_t*)xCtl;

   if( NULL == pCtl )
   {
       return eNFMGetPointError_InvalidValue;
   }

   pCtl->isThermoPhase = 0;

   return NFMGetPointsThermo_Begin( portComb, portState, pDataBuffer, szDataBuffer, pCtl );
}

// MEM:TABL:THERmo:CORRection:PHASe?
// NFMGetPointsThermoPhase_Begin
// Prepares the context before reading the data points of the thermocompensation table of magnitudes/phases
// This method is designed to replace the obsolete NFMGetPointsPhaseThermo.
// Parameters:
// @portComb - port combination to request (NFM path);
// @portState - port state to request (S-parameter);
// @pDataBuffer - output data buffer;
// @szDataBuffer - output data buffer capacity;
// @xCtl - the control context to initialize;
// Returns: 0 in case error, or number of points in case success.
size_t NFMGetPointsThermoPhase_Begin( ePortComb_t portComb, ePortStateId_t portState,
                                      sNFMChrzPoint_t * pDataBuffer, size_t szDataBuffer,
                                      xNFMGetPointsSimplified_t * xCtl )
{
   sNFMGetPointsSimplified_t * pCtl = (sNFMGetPointsSimplified_t*)xCtl;

   if( NULL == pCtl )
   {
       return eNFMGetPointError_InvalidValue;
   }

   pCtl->isThermoPhase = 1;

   return NFMGetPointsThermo_Begin( portComb, portState, pDataBuffer, szDataBuffer, pCtl );
}

// MEM:TABL:THERmo:CORRection:MAGNitude?
// MEM:TABL:THERmo:CORRection:PHASe?
// NFMGetPointsThermo_Begin
// Prepares the context before reading the data points of the thermocompensation table of magnitudes/phases
// This method is designed to replace the obsolete @NFMGetPointsMagnThermo/NFMGetPointsPhaseThermo.
// Parameters:
// @portComb - port combination to request (NFM path);
// @portState - port state to request (S-parameter);
// @pDataBuffer - output data buffer;
// @szDataBuffer - output data buffer capacity;
// @xCtl - the control context to initialize;
// Returns: 0 in case error, or number of points in case success.
static size_t NFMGetPointsThermo_Begin( ePortComb_t portComb, ePortStateId_t portState,
                                        sNFMChrzPoint_t * pDataBuffer, size_t szDataBuffer,
                                        sNFMGetPointsSimplified_t * pCtl )
{
   my_assert( pCtl ); // see @NFMGetPointsThermoMagn_Begin/NFMGetPointsThermoPhase_Begin

   // Check if arguments are valid in general
   if( ( portComb >= ePortComb_MAX )
   ||  ( portState >= ePortStateId_MAX )
   ||  ( portComb == ePortComb_UNDEFINED )
   ||  ( 0 == szDataBuffer ) )
   {
       pCtl->intCtx.svc.errCode = ERR_NFMGETPOINTS_INVAL;

       return 0;
   }

   // Check if arguments are valid particularly for this device
   if( ! NFMClassExtended->public.methods.checkTableParams( portComb, portState ) )
   {
       // Invalid combination or value
       pCtl->intCtx.svc.errCode = ERR_NFMGETPOINTS_INVAL;
       return 0;
   }

   // Fill input parameters:
   pCtl->BufferCapacity = szDataBuffer;      // - output buffer capacity
   pCtl->sectorId = eChFactory;              // - don't care for Thermocompensation
   pCtl->portComb = portComb;                // - requested port combination (nfm path)
   pCtl->portState = portState;              // - requested state (S-parameter)
   pCtl->intCtx.in.nCount = szDataBuffer;    // - number of points to get
   pCtl->intCtx.in.nStartPoint = 0;          // - start point
   pCtl->intCtx.in.pDataArray = pDataBuffer; // - output buffer

   // During the loading the table header it is required:
   // - load the main characterization header, check CRC
   // - retrieve number of points (@nPoints) from the header for specified @sectorId
   // - load the table header and check it's CRC
   // - retrieve the table address @TableAddress (for next operations)
   // - retrieve the @min and @max fields from the table header
   // - store @min, @max, @TableAddress, @nPoints into @pCtl.intCtx.out   
   // load thermocompensation main header
   // and check the CRC
   const sEcalTCompHeader_t * pTCompHeader = NFMClassExtended->private.methods.memory.getTCompHeader( NULL, 0 );

   // check the TComp header: number of points
   if(   NULL == pTCompHeader
      || 0    == pTCompHeader->Points )
   {
      pCtl->intCtx.svc.errCode = ERR_NFMGETPOINTS_INVHDR;
      return (0);
   }

   // move @.Points into @nPoints
   // due to @pTCompHeader will be corrupted
   size_t nPoints = pTCompHeader->Points;

   union 
   {
   sEcalTCompTableMagnHeader_t tableMicroHeaderMagn;
   sEcalTCompTablePhaseHeader_t tableMicroHeaderPhase;
   };

   // load the table base address
   // Note: the data by @pChrzHeader is not valid after this call,
   // due to the function uses the same internal buffer!
   size_t tableAddress = 0;

   if( 0 == pCtl->isThermoPhase )
   tableAddress = NFMClassExtended->private.methods.memory
     .getTCompMagnTableHeader(      // load the table base address: magnitude
           portComb,                //  for specified port combination
           portState,               //  for specified port state
           nPoints,                 //  for retrieved number of points
          &tableMicroHeaderMagn,    //  and store the header into @tableMicroHeaderMagn
          &pCtl->intCtx.svc.errCode //  And fill up the error code
     );
   else
   tableAddress = NFMClassExtended->private.methods.memory
     .getTCompPhaseTableHeader(     // load the table base address: phase
           portComb,                //  for specified port combination
           portState,               //  for specified port state
           nPoints,                 //  for retrieved number of points
          &tableMicroHeaderPhase,   //  and store the header into @tableMicroHeaderPhase
          &pCtl->intCtx.svc.errCode //  And fill up the error code
     );

   // check the result: @tableAddress
   // 0 - table corrupted or invalid port combination or port state.
   // but since the @portComb and @portState are validated above...
   // this result means only that the table is corrupted
   // non zero - this is the absolute address of the requested table.
   if( 0 == tableAddress )
   {
       (void)pCtl->intCtx.svc.errCode;
       return 0; // error, sorry
   }

   // - store @min, @max, @TableAddress, @nPoints into @pCtl.out
   if( 0 == pCtl->isThermoPhase )
   {
      pCtl->intCtx.out.max = tableMicroHeaderMagn.MaxMagn;
      pCtl->intCtx.out.min = tableMicroHeaderMagn.MinMagn;
   } 
   else
   {
      pCtl->intCtx.out.max = tableMicroHeaderPhase.MaxPhase;
      pCtl->intCtx.out.min = tableMicroHeaderPhase.MinPhase;
   }
   pCtl->intCtx.out.TableAddress = tableAddress;
   pCtl->intCtx.out.nPoints = nPoints;

   pCtl->intCtx.svc.errCode = ERR_NFMGETPOINTS_NOERR;

   // success
   return nPoints;
}

// MEM:TABL:THER:CORR:MAGN?
// @NFMGetPointsThermoMagn_Continue
// Uses already prepared context and reads magnutude data points of the thermocompensation 
// table to the top of specified buffer (see @NFMGetPointsThermo_Begin).
// This method is designed to replace the obsolete @NFMGetPointsMagnThermo.
// Parameters:
// @xCtl - control context prepared by previous NFMGetPointsThermo_Begin call
// @pnPointsRetrieve - IN/OUT; IN: specifies a number of points to retrieve; OUT: stores the value of actually read points;
//
// Returns:
// - eNFMGetPointError_DataError: error, can not load data;
// - eNFMGetPointError_InvalidValue: error, invalid parameters;
// - eNFMGetPointError_Success: success, all the points from the table have been read;
// - eNFMGetPointError_OutOfBuffer: warning, points have been read, but it is required to continue because user buffer ran out;
// - eNFMGetPointError_Limit: warning, points have been read, but the caller specified less points to read than actually available;
int32_t NFMGetPointsThermoMagn_Continue( xNFMGetPointsSimplified_t * xCtl, size_t * pnPointsRetrieve )
{
   sNFMGetPointsSimplified_t * pCtl = (sNFMGetPointsSimplified_t*)xCtl;

   if( NULL == pCtl || NULL == pnPointsRetrieve || 0 != pCtl->isThermoPhase )
   {
       return eNFMGetPointError_InvalidValue;
   }

   return NFMGetPointsThermo_Continue( pCtl, pnPointsRetrieve );
}

// MEM:TABL:THER:CORR:PHAS?
// @NFMGetPointsThermoPhase_Continue
// Uses already prepared context and reads phase data points of the thermocompensation 
// table to the top of specified buffer (see @NFMGetPointsThermo_Begin).
// This method is designed to replace the obsolete @NFMGetPointsPhaseThermo.
// Parameters:
// @xCtl - control context prepared by previous NFMGetPointsThermo_Begin call
// @pnPointsRetrieve - IN/OUT; IN: specifies a number of points to retrieve; OUT: stores the value of actually read points;
//
// Returns:
// - eNFMGetPointError_DataError: error, can not load data;
// - eNFMGetPointError_InvalidValue: error, invalid parameters;
// - eNFMGetPointError_Success: success, all the points from the table have been read;
// - eNFMGetPointError_OutOfBuffer: warning, points have been read, but it is required to continue because user buffer ran out;
// - eNFMGetPointError_Limit: warning, points have been read, but the caller specified less points to read than actually available;
int32_t NFMGetPointsThermoPhase_Continue( xNFMGetPointsSimplified_t * xCtl, size_t * pnPointsRetrieve )
{
   sNFMGetPointsSimplified_t * pCtl = (sNFMGetPointsSimplified_t*)xCtl;

   if( NULL == pCtl || NULL == pnPointsRetrieve || 0 == pCtl->isThermoPhase )
   {
       return eNFMGetPointError_InvalidValue;
   }

   return NFMGetPointsThermo_Continue( pCtl, pnPointsRetrieve );
}

// MEM:TABL:THER:CORR:MAGN?
// MEM:TABL:THER:CORR:PHAS?
// @NFMGetPointsThermo_Continue
// Uses already prepared context and reads magnutude/phase data points of the thermocompensation 
// table to the top of specified buffer (see @NFMGetPointsThermo_Begin).
// This method is designed to replace the obsolete @NFMGetPointsMagnThermo/NFMGetPointsPhaseThermo.
// Parameters:
// @xCtl - control context prepared by previous NFMGetPointsThermo_Begin call
// @pnPointsRetrieve - IN/OUT; IN: specifies a number of points to retrieve; OUT: stores the value of actually read points;
//
// Returns:
// - eNFMGetPointError_DataError: error, can not load data;
// - eNFMGetPointError_InvalidValue: error, invalid parameters;
// - eNFMGetPointError_Success: success, all the points from the table have been read;
// - eNFMGetPointError_OutOfBuffer: warning, points have been read, but it is required to continue because user buffer ran out;
// - eNFMGetPointError_Limit: warning, points have been read, but the caller specified less points to read than actually available;
static int32_t NFMGetPointsThermo_Continue( sNFMGetPointsSimplified_t * pCtl, size_t * pnPointsRetrieve )
{
   my_assert( pCtl );              // see @NFMGetPointsThermoMagn_Continue, @NFMGetPointsThermoPhase_Continue
   my_assert( pnPointsRetrieve );  // see @NFMGetPointsThermoMagn_Continue, @NFMGetPointsThermoPhase_Continue
   
   // Check if arguments are valid in general
   if( ( pCtl->sectorId != eChFactory ) // Formal checking: actually it is does not matter what value is specified here
   ||  ( pCtl->portComb >= ePortComb_MAX )
   ||  ( pCtl->portState >= ePortStateId_MAX )
   ||  ( pCtl->portComb == ePortComb_UNDEFINED )
   ||  ( 0 == pCtl->BufferCapacity ) 
   ||  ( 0 == pCtl->intCtx.out.nPoints )
   ||  ( 0 == pCtl->intCtx.out.TableAddress ) 
   ||  ( NULL == pCtl->intCtx.in.pDataArray ) )
   {
       pCtl->intCtx.svc.errCode = ERR_NFMGETPOINTS_INVAL;
       return eNFMGetPointError_InvalidValue;
   }

   // check ranges
   if( pCtl->intCtx.in.nCount > pCtl->intCtx.out.nPoints ) pCtl->intCtx.in.nCount = pCtl->intCtx.out.nPoints;
   if( pCtl->intCtx.in.nStartPoint >= pCtl->intCtx.out.nPoints ) pCtl->intCtx.in.nStartPoint = pCtl->intCtx.out.nPoints; // end-of-points
   if( pCtl->intCtx.in.nStartPoint +
       pCtl->intCtx.in.nCount > pCtl->intCtx.out.nPoints )
   {
       pCtl->intCtx.in.nCount = pCtl->intCtx.out.nPoints - pCtl->intCtx.in.nStartPoint;
   }

   // check for end condition
   if( 0 == pCtl->intCtx.in.nCount
    || pCtl->intCtx.in.nStartPoint == pCtl->intCtx.out.nPoints )
   {
       *pnPointsRetrieve = 0; // number of points read is zero
       pCtl->intCtx.svc.errCode = ERR_NFMGETPOINTS_NOERR;
       return eNFMGetPointError_Success;
   }

   int32_t success_rc = eNFMGetPointError_Success;
   
   // check buffer capacity range
   if( pCtl->intCtx.in.nCount > pCtl->BufferCapacity )
   {
       pCtl->intCtx.in.nCount = pCtl->BufferCapacity;
       success_rc = eNFMGetPointError_OutOfBuffer; // out of buffer, the number of points is limited
   }
   else
   if( pCtl->intCtx.in.nStartPoint + pCtl->intCtx.in.nCount < pCtl->intCtx.out.nPoints )
   {       
       success_rc = eNFMGetPointError_Limit; // number of points is limited by user
   }

   pCtl->intCtx.svc.errCode = ERR_NFMGETPOINTS_IO;

   size_t nRead = 0;
   
   if( 0 == pCtl->isThermoPhase )
       nRead = NFMClassExtended->private.methods.memory.getTCompMagnPoints( &pCtl->intCtx );
   else
       nRead = NFMClassExtended->private.methods.memory.getTCompPhasePoints( &pCtl->intCtx );

   // check for error condition
   if( nRead == 0 )
   {
       *pnPointsRetrieve = 0; // number of points read is zero
       (void)pCtl->intCtx.svc.errCode;
       return eNFMGetPointError_DataError;
   }

   pCtl->intCtx.in.nStartPoint += nRead; // increment start point for next call

   // check range
   if( pCtl->intCtx.in.nStartPoint +
       pCtl->intCtx.in.nCount > pCtl->intCtx.out.nPoints )
   {
       pCtl->intCtx.in.nCount = pCtl->intCtx.out.nPoints - pCtl->intCtx.in.nStartPoint; 
   }

   *pnPointsRetrieve = nRead;            // number of points read
   
   return success_rc;
}

static bool NFMGetInterface( eNFMUSBInterface_t * pCurrentIface )
{
   if( NULL != pCurrentIface )
   {
       if( sizeof(sNFMSettingsBlockCrc_t) == NFMClassExtended->private.methods.memory.getSettingsBlockSize() )
       {
           sNFMSettingsBlockCrc_t sSettingsBlock;

           if( NFMClassExtended->private.methods.memory.getSettingsBlock( (uint8_t*)&sSettingsBlock,
                                                                          0,
                                                                          sizeof(sSettingsBlock) ) )
           {
               *pCurrentIface = eNFM_IfaceUSBVendor;

               if( sSettingsBlock.settings.rawBytes[0] == 'U' )
                if( sSettingsBlock.settings.rawBytes[1] == 'S' )
                 if( sSettingsBlock.settings.rawBytes[2] == 'B' )
                  if( sSettingsBlock.settings.rawBytes[3] == 'T' )
                   if( sSettingsBlock.settings.rawBytes[4] == 'M' )
                    if( sSettingsBlock.settings.rawBytes[5] == 'C' )
                     if( sSettingsBlock.settings.rawBytes[6] == '!' )
                      if( sSettingsBlock.settings.rawBytes[7] ==  0  )
                        *pCurrentIface = eNFM_IfaceUSBTMC;

               return true;
           }
       }
   }
   return false;
}

static bool NFMSetInterface( eNFMUSBInterface_t activateInterface )
{
   if(  (activateInterface == eNFM_IfaceUSBVendor)
      ||(activateInterface == eNFM_IfaceUSBTMC) )
   {
       if( sizeof(sNFMSettingsBlockCrc_t) == NFMClassExtended->private.methods.memory.getSettingsBlockSize() )
       {
           sNFMSettingsBlockCrc_t sSettingsBlock;

           if( NFMClassExtended->private.methods.memory.getSettingsBlock( (uint8_t*)&sSettingsBlock,
                                                                          0,
                                                                          sizeof(sSettingsBlock) ) )
           {
                if( eNFM_IfaceUSBTMC == activateInterface )
                {
                    sSettingsBlock.settings.rawBytes[0] = 'U';
                    sSettingsBlock.settings.rawBytes[1] = 'S';
                    sSettingsBlock.settings.rawBytes[2] = 'B';
                    sSettingsBlock.settings.rawBytes[3] = 'T';
                    sSettingsBlock.settings.rawBytes[4] = 'M';
                    sSettingsBlock.settings.rawBytes[5] = 'C';
                    sSettingsBlock.settings.rawBytes[6] = '!';
                    sSettingsBlock.settings.rawBytes[7] =  0 ;
                }
                else
                {
                    sSettingsBlock.settings.rawBytes[0] = 'V';
                    sSettingsBlock.settings.rawBytes[1] = 'E';
                    sSettingsBlock.settings.rawBytes[2] = 'N';
                    sSettingsBlock.settings.rawBytes[3] = 'D';
                    sSettingsBlock.settings.rawBytes[4] = 'O';
                    sSettingsBlock.settings.rawBytes[5] = 'R';
                    sSettingsBlock.settings.rawBytes[6] = '!';
                    sSettingsBlock.settings.rawBytes[7] =  0 ;
                }
                
                TCRC crc = ICRC32;
                crc = CRC32( crc, (uint8_t *)&sSettingsBlock.settings, sizeof(sSettingsBlock.settings));
                sSettingsBlock.CRCValue = crc;
            
                return NFMClassExtended->private.methods.memory.setSettingsBlock( (uint8_t*)&sSettingsBlock,
                                                                                    0,
                                                                                    sizeof(sSettingsBlock) );
           }
       }
   }
   return false;
}

static uint16_t* GetPort(uint16_t portNumber)
{
    switch(portNumber)
    {
    case eNFMPort_1:
        return &NFMClassExtended->public.properties.inputPortStates[ePort_1];
    case eNFMPort_2:
        return &NFMClassExtended->public.properties.inputPortStates[ePort_2];
    default:
        return 0;
    }
}

static uint16_t NFMGetPortState (uint16_t portNumber)
{
    return *GetPort(portNumber);
}

static bool NFMSetPortState (uint16_t portNumber, uint16_t portState)
{
    uint16_t* port = GetPort(portNumber);
    if(port == NULL)
        return false;
    
    *port = portState;
    return true;
}

#endif
