/*
 * apf_init.c
 *
 *  Created on: Feb 26, 2024
 *      Author: tm, from lc cf2 code
 */


#if BOARD_MSC == 3

#include "system.h"
#include "main.h"
#include "microsd.h"
#include "i2c_fram.h"

#include "apf.h"
#include "apf_init.h"
#include "apf_math.h"
#include "apf_numbr.h"
#include "apf_util.h"
#include "board_util.h"
#include "mms_spec.h"
#include "serial.h"
#include "sleep.h"
#include "user_io.h"
#include "uartstdio.h"
#include "serial_cmd_fxn.h"
#include "init_defaults.h"

extern struct apfRtcTime rtc;       // defd in apf.h


double apf_EcoefData[256+1][3];

extern char  logfname[64];
extern FIL  *dat_file;
extern FIL  *err_file;

extern double FitData[];

extern uint32_t serDbgFlg;          // defd in serial.c
extern int microSD_flg;

void apf_init_hmi_vars(void)
{

    sys_data.echo_flag = 0;             // echo off for APF
    sys_data.terminal_mode = M2M_MODE;  // Machine to Machine
    // JLD-TGM when this flag is set to MSC1_OP the MSC3 board has no chance to take ph or nitrate samples after sleeping - given that the msc_op set to MSC1_OP will default the mode to 0.
    // sys_data.msc_op = MSC1_OP;          // Proposed change of MSC1 behavior of TS command (returns sample) was rejected, minimize changed to APF11
    sys_data.autoSleepSec = 10;         // h2m mode invoked by 'h' sets autoSleepSec to 30
}

//init_zcoef_ecoef... reads ZCOEFDAT.CAL and ECOEFZER.CAL into apfArray and stores that in FRAM
void init_zcoef_ecoef_apfArray_into_fram(void)
{
    //uprintf("DBG: init_zcoef_ecoef_apfArray_into_fram()"); prn_nl();

    uprintf("Reading \\ISUS\\ZCOEFDAT.CAL file"); prn_nl();
    // read file into struct
    apfArray.ZcoefsCnt = LoadIsusZCoefs(apfArray.ZeissCoefs);
    if(apfArray.ZcoefsCnt > MAX_NUMOF_ZCOEFS)           //max defd in apf.h = 5
    {
        apfArray.ZcoefsCnt = 0;
    }

    //store in fram
    fram_apfArray_store();

    uprintf("Zcoef count = %u", apfArray.ZcoefsCnt); prn_nl();

    apfArray.EcoefsCnt = 0;
    if(apfArray.ZcoefsCnt > 0)
    {
        uprintf("Reading ISUS\\ECOEFZER.CAL file"); prn_nl();

        InitLambdaArray();		//once this is functioning, all other instances of InitLambdaArray can be commented out
        //apf_zcoef_print();  //DBG

        // read file
        apfArray.EcoefsCnt = apf_loadIsusECoefs(NULL, apfArray.ZeroData, apfArray.TcompCoefs, &apfArray.SwCalTemp, apfArray.CalDate);

        //store in fram
        fram_apfArray_store();

        uprintf("EcoefsCnt = %u, stored in nonvolatile FRAM", apfArray.EcoefsCnt); prn_nl();
    }
    else
    {
        uprintf("Error reading ZCOEFDAT.CAL, apfArray.ZcoefsCnt should be 5"); prn_nl();
    }
}


// top level 'runtime' init fxn
int apf_init_msc(void)  //was void InitMSC( void )
{
    int     retVal;
    FILINFO fno;


    if(microSD_flg == 1)
    {
        if(serDbgFlg == 1) { uprintf("Test for DURA dir...");  prn_nl(); }
        fRes = f_stat("DURA", &fno);
        switch (fRes)
        {
            case FR_OK:
            {
#ifdef NOCODE
                uprintf("Size: %lu", fno.fsize);  prn_nl();
                uprintf("Timestamp: %u-%02u-%02u, %02u:%02u",
                       (fno.fdate >> 9) + 1980, fno.fdate >> 5 & 15, fno.fdate & 31,
                       fno.ftime >> 11, fno.ftime >> 5 & 63);  prn_nl();
                uprintf("Attributes: %c%c%c%c%c",
                       (fno.fattrib & AM_DIR) ? 'D' : '-',
                       (fno.fattrib & AM_RDO) ? 'R' : '-',
                       (fno.fattrib & AM_HID) ? 'H' : '-',
                       (fno.fattrib & AM_SYS) ? 'S' : '-',
                       (fno.fattrib & AM_ARC) ? 'A' : '-'); prn_nl();
#endif
                break;
            }

            case FR_NO_FILE:
            case FR_NO_PATH:
            {
                if(serDbgFlg == 1) { uprintf("DBG: DURA does not exist, creating.");  prn_nl(); }
                FRESULT rc = f_mkdir("DURA");
                break;
            }

            default:
            {
                if(serDbgFlg == 1) { uprintf("ERR: An error occurred f_stat DURA directory. (%d)", fRes);   prn_nl();  }   //
                break;
            }
        }


        fRes = f_stat("ISUS", &fno);
        switch (fRes)
        {

            case FR_OK:
            {
                break;
            }

            case FR_NO_FILE:
            case FR_NO_PATH:
            {
                if(serDbgFlg == 1) { uprintf("DBG: ISUS does not exist, creating\r\n");  prn_nl();   }
                FRESULT rc = f_mkdir("ISUS");
                break;
            }

            default:
            {
                if(serDbgFlg == 1) { uprintf("ERR: An error occurred f_stat ISUS directory. (%d)", fRes); prn_nl(); }
                break;
            }
        }
    }

    if(serDbgFlg == 1) { uprintf("  MBARI Sensor Controller v3 Initializing...");  prn_nl(); }

    retVal = apf_init_runtime_vars();      // was InitVars();   loads ZCOEF and ECOEF, calls fram_apf_vars_retrieve();
    if(retVal <= 0)
    {
        return 0;
    }

    apf_fram.sys_por_flag = TRUE;  // Not sure if this is the right logic, perhaps this is wired to watchdog timer reset APFTODO
                                // MSC3 powers down / resets at sleep, so the logic por_flag = TRUE seems appropriate.   Each sleep / pwr off event
                                //will set this flag.   UPDATE: the MSC1 logic uses por_flag for determining profile has started.   PROFILE command added Feb 2025.  APF11
                                //sends profile

    apf_fram.sys_reset_time = read_hib_rtcc();
    fram_apf_vars_store();

    //apf_copyEcoefsFileToData();   //19 May 2025 Moved to mms_spec.c mms_takeIsusSample for just intime loading of apfArray

    retVal = IOPwrUp();   //RTS is set to 0 here, used by APF11 to know MSC3 has booted
    if(retVal != OK)
    {
        return 0;
    }

    apf_init_hmi_vars();      // sys_data terminal settings for apf

    if(serDbgFlg == 1) { uprintf("DBG: not calling DisplaySystemInfo, type sys.  Not calling SyncNextSample (ever)");  prn_nl(); wait_consoleTx(); }
    //error log entry if no ZCOEF or ECOEF file

	return 1;
}


int IOPwrUp( void )
{
    //apf.sys_pump_pwr  = FALSE;  //APFTODO sys_pump_pwr not supported
    apf.isus_spec_pwr = FALSE;
    apf.isus_lamp_pwr = FALSE;
    apf.isus_ref_pwr = FALSE;
    apf.dura_atod_pwr = FALSE;

    //sys_rts(0);         // Assert CF2 RTS output (CTS input to APF, etc)   (bug fix 18 Apr 2025, was mms_rts(1), should be sys_rts(1)
    //moved to main.c

    return( OK );
}

int IOPwrDown( void )
{

    if( dat_file != NULL )
    {
        FRESULT fr = f_close(dat_file);
        dat_file = NULL;
        if(fr != FR_OK)
        {

        }
    }

    if( err_file != NULL )
    {
        FRESULT fr = f_close(err_file);
        err_file = NULL;
        if(fr != FR_OK)
        {

        }
    }

    //sys_rts(1);  // deassert CF2 RTS output (CTS input to APF, etc)
    //moved to sleep.c

    return( OK );

}


// log file names are created when INIT and PROFILE commands issued
void apf_create_logFile_names(void)
{
    FIL fLog;      /* File object, following FatFS f_open */
    FRESULT fresult;
    char    ebuf[128];

    if(apf_fram.profileCnt < 9999)
    {
        sprintf(apf_eeprom.logIsus,"//ISUS//PROF%04lu.log", apf_fram.profileCnt);
        sprintf(apf_eeprom.logDura,"//DURA//PROF%04lu.log", apf_fram.profileCnt);

    }
    else
    {
        apf_fram.profileCnt = 9999;      // set to max, in case of not init'd
        sprintf(apf_eeprom.logIsus,"//ISUS//PROF%04lu.log", apf_fram.profileCnt);
        sprintf(apf_eeprom.logDura,"//DURA//PROF%04lu.log", apf_fram.profileCnt);
    }

    apf_fram.logIsus_fsize = 0;
    apf_fram.logDura_fsize = 0;

    //--- Create PROFxxxx.log in ISUS directory
    fresult = f_open(&fLog, apf_eeprom.logIsus, FA_OPEN_APPEND | FA_WRITE | FA_READ);
    if(fresult == FR_OK)
    {
        print_write_isusdura_header(0, 1, ISUS_SENSOR_MODE, &fLog);
        apf_fram.isus_reset_flag = FALSE;
        f_close(&fLog);
    }
    else if(fresult == FR_EXIST)
    {
        //if the file exists, append the header info to mark 'new logging'
        if(serDbgFlg == 1) { uprintf("apf_create_logFile_names() failed, file %s exists", apf_eeprom.logIsus); prn_nl(); }

        //insert line in file indicating append
        f_printf(&fLog, "%s Log file exists, appending", apf_eeprom.logIsus);

        print_write_isusdura_header(0, 1, ISUS_SENSOR_MODE, &fLog);  //write header info

        apf_fram.isus_reset_flag = FALSE;
        f_close(&fLog);

        sprintf(ebuf, "f_open %s, file already exists, writing header", apf_eeprom.logIsus);
        LOGERRORMSG(ebuf, (int)PROFNNNN_FILE_ERROR, apf_eeprom.sys_message_enable);
    }
    else
    {
        //APFTODO: warn user that an error happened other than FR_EXIST
        sprintf(ebuf, "f_open %s failed, fresult = %d", apf_eeprom.logIsus, (int)fresult);
        LOGERRORMSG(ebuf, (int)PROFNNNN_FILE_ERROR, apf_eeprom.sys_message_enable);

        //return;
    }

    //--- Create PROFxxxx.log in DURA directory
    fresult = f_open(&fLog, apf_eeprom.logDura, FA_OPEN_APPEND | FA_WRITE | FA_READ);
    if(fresult == FR_OK)
    {

        print_write_isusdura_header(0, 1, DURA_SENSOR_MODE, &fLog);
        apf_fram.dura_reset_flag = FALSE;
        f_close(&fLog);
    }
    else if(fresult == FR_EXIST)
    {
        //APFTODO warn user
        if(serDbgFlg == 1) { uprintf("apf_create_logFile_names() failed, file %s likely exists", apf_eeprom.logDura); prn_nl(); }

        //insert line in file indicating append
        f_printf(&fLog, "%s Log file exists, appending", apf_eeprom.logIsus);

        print_write_isusdura_header(0, 1, DURA_SENSOR_MODE, &fLog);
        apf_fram.dura_reset_flag = FALSE;
        f_close(&fLog);

        sprintf(ebuf, "f_open %s, file already exists, writing header, fresult = %d", apf_eeprom.logDura, (int)fresult);
        LOGERRORMSG(ebuf, (int)PROFNNNN_FILE_ERROR, apf_eeprom.sys_message_enable);
    }
    else
    {
        sprintf(ebuf, "f_open %s failed, fresult = %d", apf_eeprom.logDura, (int)fresult);
        LOGERRORMSG(ebuf, (int)PROFNNNN_FILE_ERROR, apf_eeprom.sys_message_enable);
    }

    //APFTODO check error.log size and delete it if over 128KB
    LogErrorSizeHander();

    if(fram_apf_vars_store() != 1)
    {
        LOGERRORMSG("apf_create_logFile_names fram error", (int)FRAM_FAIL, apf_eeprom.sys_message_enable);
    }
    if(eeprom_apf_cfg_store() != 0)
    {
        LOGERRORMSG("apf_create_logFile_names eeprom error", (int)EEPROM_FAIL, apf_eeprom.sys_message_enable);
    }

}

// called by "init" command, stores to NV fram, deletes error.log
int apf_deployment_init(void)
{
    wdog_pet();

    //called by "INIT" "init" command, called by apf_init_persistent_vars() in serial_cmd_fxn.c
    apf_fram.profileCnt = 0;
    apf_fram.profileStartTime = read_hib_rtcc();
    apf_create_logFile_names();

    apf_fram.sendlast_byteCnt = 0;
    apf_eeprom.sys_watchdog_enable = TRUE;
    apf_eeprom.sys_message_enable = 0;                  //when set, LogErrorMessage prints to console
    apf_fram.sys_data_mode = SYS_DATAMODE_VAL;           //deprecated was ISUS_SENSOR_MODE;
    apf_eeprom.isus_tcomp_mode = TRUE;
    apf_eeprom.isus_salfit_mode = NO3_SAL_FIX;
    apf_eeprom.dura_led_enable = 0;

    apf_eeprom.isus_fit_concs = 2;
    apf_eeprom.sys_ef_limit = 128;
    apf_eeprom.sys_df_limit = 5;
    apf_eeprom.sys_apf_tmout = 10;

    apf_fram.sys_wdog_ctr = 0;                           //counts wdog events
    apf_fram.sys_reset_ctr = 0;

    apf_fram.sys_last_error = 0;
    apf_fram.sys_error_ctr = 0;          //was commented out?? 18 May 2025
    apf_fram.sys_error_time = 0;

    apf_fram.isus_last_error = 0;
    apf_fram.isus_error_ctr = 0;
    apf_fram.isus_error_time = 0;
    apf_fram.isus_sample_ctr = 0;
    apf_fram.isus_sample_time = 0 ;

    apf_fram.dura_last_error = 0;
    apf_fram.dura_error_ctr = 0;
    apf_fram.dura_error_time = 0;
    apf_fram.dura_sample_ctr = 0;
    apf_fram.dura_sample_time = 0;

    apf_fram.isus_sampleCnt_perProfile = 0;
    apf_fram.dura_sampleCnt_perProfile = 0;

    wdog_pet();

    if( (apfArray.ZcoefsCnt == 5) && (apfArray.EcoefsCnt == 256) )      //zcoefs is 5, protect from garbage
    { //wavelength info SHOULD be loaded
        //uprintf("DBG: apf_init.c set fit window"); prn_nl();
        //SET FIT WINDOW
        apf_eeprom.lambda_beg = 217;
        apf_eeprom.lambda_end = 240;
        MatchPixelsToLambda(apfArray.LambdaData, apf_eeprom.lambda_beg, apf_eeprom.lambda_end, &apf_eeprom.isus_pixel_beg, &apf_eeprom.isus_pixel_end);   //         wdog_pet();

        apf_eeprom.isus_fit_pixels = (apf_eeprom.isus_pixel_end - apf_eeprom.isus_pixel_beg) + 1;

        wdog_pet();

        //SET DATA WINDOW (SEE TS AND SL COMMANDS)
        apf_eeprom.lambda_dat_beg = 217;
        apf_eeprom.lambda_dat_end = 250;
        MatchPixelsToLambda(apfArray.LambdaData, apf_eeprom.lambda_dat_beg, apf_eeprom.lambda_dat_end, &apf_eeprom.isus_datpixel_beg, &apf_eeprom.isus_datpixel_end);
    }

    resetErrorLog();        //remove ERROR.LOG in INIT

    FRESULT fr = get_storage_stat(&apf.storage_size, &apf.storage_free);   //storage in Kbytes
    if(fr != FR_OK)
    {
        LOGERRORMSG("INIT get_storage_stat failed", FILE_OPEN_FAILURE, apf_eeprom.sys_message_enable);
    }

    int ret = 1;
    if(fram_apf_vars_store() != 1)
    {
        LOGERRORMSG("apf_deployment_init fram error", (int)FRAM_FAIL, apf_eeprom.sys_message_enable);
        ret = 0;
    }
    if(eeprom_apf_cfg_store() != 0)
    {
        LOGERRORMSG("apf_deployment_init eeprom error", (int)EEPROM_FAIL, apf_eeprom.sys_message_enable);
        ret = 0;
    }

    return ret;
}

//init apf struct with defaults, does not store to FRAM, callers responsibility
int apf_init_apf_struct_defaults(void)
{
    int retVal = 1;

    fram_apf_var_defaults();        //defd in init_defaults.c

    eeprom_apf_cfg_defaults();

    ram_apf_var_defaults();

    apf_create_logFile_names();     // create log file names based on profileCnt

    return retVal;
}


// NOTE: see serial_cmd_fxn for apf_init_persistent_vars();

// formerly in CF2: void InitVars( void );
int apf_init_runtime_vars(void)
{
    fram_apf_vars_refresh();       //added 2 Apr 2025

    apf_fram.isus_reset_flag = TRUE;   //APFTODO - what are these reset flags for??
    apf_fram.dura_reset_flag = TRUE;

    apf_fram.sys_reset_time = read_hib_rtcc();
    apf_fram.sys_reset_ctr++;
    //apf_fram.sys_por_ctr++;       //counts wdog events

    dat_file = NULL;
    err_file = NULL;

    apf.pH = 0.0;
    apf.dura_pH = 0.0;

//    clear the CTD values on each entry
    apf.CTD_Time = 0;
    apf.CTD_Temp = -1.0;
    apf.CTD_Salinity = -1.0;
    apf.CTD_Depth = -1.0;

    apf.sys_humidity = 0;

    //retrieve arrays in NV mem
#ifdef MOVEDTO_JUSTINTIME_MMS_TAKEISUSSAMPLE
    fram_apfArray_refresh();
#endif

    //init ram vars
    apf.gotSample = FALSE;

    apf.storage_size = 0;            //1 size of uSD or NAND flash in Kbytes
    apf.storage_free = 0;            //1 storage available
    apf.isus_spec_pwr = 0;           //1
    apf.isus_lamp_pwr = 0;           //1
    apf.isus_lamp_deuterium = 0;     //1
    apf.isus_lamp_tungsten = 0;      //1
    apf.isus_ref_pwr = 0;            //1

    apf.isus_sal = -1.0;                //1   //Added by TM to globals 7 Oct 2024, from temp var isus_sal = FitData[SALINITY];
    apf.isus_no3 = -1.0;                //1   //was uint32_t    from = FitData[NITRATE];
    apf.isus_hs = -1.0;                 //1  //= FitData[BISULFIDE];
    apf.isus_err = -1.0;                //1  //= FitData[FIT_ERROR];
    apf.dura_atod_pwr = 0;           //1  Not needed

    apf.backupVoltage = 0.0;           //1  NOT NEEDED
    apf.VrsExt = 0.0;                  //1
    apf.StdDevVrs = 0.0;               //1
    apf.Vk = 0.0;                      //1
    apf.StdDevVk = 0.0;                //1
    apf.Ik = 0.0;                      //1
    apf.Ib = 0.0;                      //1
    apf.sys_temperature = 0.0;         //1
    apf.sys_pressure = 0.0;            //1
    apf.sys_current = 0.0;             //1
    apf.sys_voltage = 0.0;             //1
    apf.acquire_time = -1.0;            //1

    apf.DataMean = -1.0;                //1
    apf.DataStdDev = -1.0;              //1

    apf.chkSum = 0;                  //1

	return(1);
}


//apf_initEcoefMatrix(EcoefFile);
// using global data structurs instead of double ** pointers
void apf_initEcoefFileMatrix(void)
{
    int indx;

    for(indx = 1; indx <= 256; indx++)
    {
        //SrcEcoefs[i][0] = 0.0;
        //SrcEcoefs[i][1] = 0.0;
        //SrcEcoefs[i][2] = 0.0;
        apfArray.EcoefFile[indx][0] = 0.0;
        apfArray.EcoefFile[indx][1] = 0.0;
        apfArray.EcoefFile[indx][2] = 0.0;
    }
}

void apf_initEcoefDataMatrix(void)
{
    int indx;

    for(indx = 1; indx <= 256; indx++)
    {
        apf_EcoefData[indx][0] = 0.0;
        apf_EcoefData[indx][1] = 0.0;
        apf_EcoefData[indx][2] = 0.0;
    }
}

// changed from general purpose NR function to copy the arrays needed for MSC APF use case
//CopyEcoefs
void apf_copyEcoefsFileToData(void)
{
    int indx;

    for(indx = 0; indx <= 256; indx++)   // see comment below in apf_copySwEcoefsFileToData
    {
        apf_EcoefData[indx][0] = apfArray.EcoefFile[indx][0];
        apf_EcoefData[indx][1] = apfArray.EcoefFile[indx][1];
        apf_EcoefData[indx][2] = apfArray.EcoefFile[indx][2];
    }
}

//CopySwEcoefs.  It is thought that Sw means Saltwater
void apf_copySwEcoefsFileToData(void)
{
    int indx;

    //uprintf("DBG: apf_copySwEcoefsFileToData for loop is 1 to 256, but zero based C arrays expect 0 to 255"); prn_nl();
    //for(indx = 1; indx <= 256; indx++)      //MSC1 'for loop', working thru an 'off by 1' array problem associated with change from dmatrix to apfArray c struct with 0 based arrays for ease of debugging and serialization to NV (decouple the uSD card dependency from Nitrate sampling, MSC1 requires loadisusEcoefs on each power up, which for MSC3 is on each sample in the APF11 use scenario.
    for(indx = 0; indx <= 256; indx++)
    {
        apf_EcoefData[indx][0] = apfArray.EcoefFile[indx][0];
    }
}

//     XformSwEcoefs(EcoefFile,              EcoefData,        apfArray.LambdaData, apfArray.TcompCoefs, apfArray.SwCalTemp, CTD_Temp);
//void XformSwEcoefs(double **Ecoefs, double **XformedEcoefs, double *Lambda, double *Tcoefs, double CalTemp, double SamTemp)

// Example: apf_XformSwEcoefs(apfArray.LambdaData, apfArray.TcompCoefs, apfArray.SwCalTemp, apf_fram.CTD_Temp);
//void apf_XformSwEcoefs(apfArray.LambdaData, apfArray.TcompCoefs, apfArray.SwCalTemp, CTD_Temp)
void apf_XformSwEcoefs(double *Lambda, double *Tcoefs, double CalTemp, double SamTemp)
{
    int indx;
    double L_210, dAbsAtCalT, dAbsAtSamT;

    for(indx = 1; indx <= 256; indx++)
    {

        //if( Ecoefs[i][0] != 0.0 ) {
        if( apfArray.EcoefFile[indx][0] != 0.0 )
        {
            L_210 = Lambda[indx] - 210.0;

            dAbsAtCalT  = ( Tcoefs[0] + Tcoefs[1] * CalTemp ) *
                          exp( (Tcoefs[2] + Tcoefs[3] * CalTemp) * L_210 );

            dAbsAtSamT  = ( Tcoefs[0] + Tcoefs[1] * SamTemp ) *
                          exp( (Tcoefs[2] + Tcoefs[3] * SamTemp) * L_210 );

            apf_EcoefData[indx][0] = apfArray.EcoefFile[indx][0] * ( dAbsAtSamT / dAbsAtCalT );
        }
        else
        {
            apf_EcoefData[indx][0] = apfArray.EcoefFile[indx][0];
        }

    } //end for loop

}

/* Luke's Linear Method, nice and simple!

   void XformSwEcoefs(double **Ecoefs, double **XformedEcoefs, double SolutionRefTemp, double SolutionTemp)
   {
   int  i;
   double deltaTemp;

    deltaTemp = SolutionRefTemp - SolutionTemp;

    for(i = 1; i <= 256; i++) {
      if(Ecoefs[i][0] != 0.0)
        XformedEcoefs[i][0] = Ecoefs[i][0] - (deltaTemp * Ecoefs[i][2]);
      else
        XformedEcoefs[i][0] = Ecoefs[i][0];
      printf("%le, %le, %le, %lf\n", Ecoefs[i][0], XformedEcoefs[i][0], Ecoefs[i][2], deltaTemp);
      }

   }

 */

//int LoadIsusECoefs(double *Lambda, double **Ecoefs, double *Zeros, double *Tcoefs, double *CalTemp, char *CalDate)
//  LoadIsusECoefs(NULL, apfArray.ZeroData, apfArray.TcompCoefs, &apfArray.SwCalTemp, apf_eeprom.CalDate);
int apf_loadIsusECoefs(double *Lambda, double *Zeros, double *Tcoefs, double *CalTemp, char *CalDate)
{
    FIL coef_file;
    char *srchptr, *dataptr;
    char data[512];
    int indx, j, ret;
    int got_date, month, day, year;
    int got_temp;
    double sw_calib_temp;
    uint32_t coefCnt;
    uint32_t prnFlg;

    prnFlg = 0;      //print array, DBG: set to zero unless you want to print coefs as they are read in

    coefCnt = 0;

    //hard code Tcoefs for now...
    for(indx = 0; indx < TCOEFS; indx++)
    {
        if(indx == 0)
            apfArray.TcompCoefs[indx] = 1.1500276;
        else if(indx == 1)
            apfArray.TcompCoefs[indx] = 0.0284;
        else if(indx == 2)
            apfArray.TcompCoefs[indx] = -0.3101349;
        else if(indx == 3)
            apfArray.TcompCoefs[indx] = 0.001222;
        else
            apfArray.TcompCoefs[indx] = 0.0;
    }

    /* open Extinction Coefficients and Zero Scan file */
    //coef_file = f_open("C:\\ISUS\\ECOEFZER.CAL", "r");
    fRes = f_open(&coef_file, "\\ISUS\\ECOEFZER.CAL", FA_READ);
    //uprintf("DBG: \\ISUS\\ECOEFZER.CAL fRes = %d", fRes); prn_nl();
    if(fRes)
    {
        uprintf("LoadIsusECoefs in apfutil.c, f_open failed, %d", (int)fRes);    prn_nl();
        return( -1 );
    }

    got_date = FALSE;
    got_temp = FALSE;
    apfArray.ZerosCnt = 0;      //init counter

    // starting at 0 we end at 256, the count we want...
    //for(i = 0; i < 256; )
    // read the file and break out when fgets fails to retrieve a string, file is 266 lines, with garbage on the last line
    for(indx = 0; indx < 270; )
    {
        wdog_pet();         // likely not needed, but just to be safe, pet the dog


        //uprintf("DBG: loadIsusECoefs coefCnt = %u", coefCnt);  prn_nl();
        if(f_gets(data, sizeof(data), &coef_file) == NULL)
        {
            if(indx == 0)
            {
                prn_nl(); uprintf("  NULL ret in Ext. Coef / Zero Data!"); prn_nl();
                return( -2 );
            }
            else
            {
                break;
            }
        }
        else
        {
            wait_consoleTx();

            //uprintf("[%02u] %s", coefCnt, data); prn_nl();

            if(got_date == FALSE)
            {
                srchptr = memchr(data, 'H', 1);
                if(srchptr != NULL)
                {
                    srchptr=srchptr+2;
                    ret = sscanf(srchptr,"%d/%d/%d,,,,\n",&month, &day, &year);
                    if(ret == 3)
                    {
                        got_date = TRUE;
                        sprintf(apfArray.CalDate,"%02d/%02d/%02d", month, day, year);
                        continue;
                    }
                }
            }


            if(got_temp == FALSE)
            {
                srchptr = memchr(data, 'H', 1);
                if(srchptr != NULL)
                {
                    srchptr=srchptr+2;
                    ret = sscanf(srchptr,"CalTemp,%lf,,,\n",&sw_calib_temp);
                    if(ret == 1)
                    {
                        got_temp = TRUE;
                        *CalTemp = sw_calib_temp;
                        continue;
                    }
                }
            }

            srchptr = memchr(data, 'E', 1);
            if(srchptr != NULL)
            {
                coefCnt++;          //added coefCnt var due to MSC1 code when ported had >256 for number of coefficients

                j=0;
                dataptr = strtok(srchptr, ",");
                while ((dataptr = strtok(NULL, ",")) != NULL)
                {
                    switch( j )
                    {
                        case 0:
                            if(Lambda != NULL)      //skip value, if Lambda array has not already been initialized via ZCoef values
                            {
                                //Lambda[indx] = strtod(dataptr, NULL);
                                if(prnFlg) {  uprintf("%.5E, ", Lambda[indx]);  }   //DBG: debug APFTODO
								errno = 0; // Reset errno before calling strtod
                                Lambda[indx] = strtod(dataptr, NULL);
                                if(prnFlg) {  uprintf("%.5E, ", Lambda[indx]);  }   //DBG: debug APFTODO
                            }
                            else
                            {
								errno = 0; // Reset errno before calling strtod
                                if(prnFlg) {  uprintf("Z%.5E, ", strtod(dataptr, NULL));  }     //DBG: debug APFTODO
                            }
                            break;

                        case 1:
                            //if(Ecoefs != NULL)          //bug fix,
                                //Ecoefs[indx][0] = strtod(dataptr, NULL);
							errno = 0; // Reset errno before calling strtod
                            apfArray.EcoefFile[indx][0] = strtod(dataptr, NULL);
                            if(prnFlg) {  uprintf("%03u: %.5E, ", indx, apfArray.EcoefFile[indx][0]);   }    //DBG: debug APFTODO
                            break;

                        case 2:
                            //if(Ecoefs != NULL)
                                //Ecoefs[indx][1] = strtod(dataptr, NULL);
                            apfArray.EcoefFile[indx][1] = strtod(dataptr, NULL);
                            if(prnFlg) {  uprintf("%.5E, ", apfArray.EcoefFile[indx][1]);  }   //DBG: debug APFTODO
                            break;

                        case 3:
                            //if(Ecoefs != NULL)
                                //Ecoefs[indx][2] = strtod(dataptr, NULL);
                            apfArray.EcoefFile[indx][2] = strtod(dataptr, NULL);
                            if(prnFlg) {  uprintf("%.5E, ", apfArray.EcoefFile[indx][1]);   }  //DBG: debug APFTODO
                            break;

                        case 4:
                            //if(Zeros != NULL)
                            //{
                                apfArray.ZerosCnt++;        //APFTODO  need to figure out ZerosCnt...  How did MSC1 handle this?
                                Zeros[indx] = strtod(dataptr, NULL);
                                if(prnFlg) {  uprintf("%.5E", Zeros[indx]);  prn_nl();   }     //DBG: debug APFTODO
                            //}
                            break;

                        default:
                            break;

                    }
                    ++j;
                }

                if(j != 5)
                {
                    prn_nl(); uprintf("  Bad data at %d in ECoef File!", indx); prn_nl();
                    uprintf("%s", srchptr);  prn_nl();
                    f_close(&coef_file);
                    return( -3 );
                }

                indx++;
            }

        }
    } //end for loop

    if(got_date == FALSE)
        sprintf(apfArray.CalDate, "NODATEFOUND");

    if(got_temp == FALSE)
        *CalTemp = 20.0;

    //prn_nl(); uprintf("LoadIsusECoefs done, EcoefsCnt = %u", apfArray.EcoefsCnt);

    f_close(&coef_file);
    return(coefCnt);

}

#endif // #if BOARD_MSC == 3
