/*
 * oc_emaLink_at.c
 *
 * Created: 6/24/2019 11:49:48 AM
 * 
 *  ######################################################################################################
 *  DISCLAIMER:
 *  OptConnect Management, LLC provides this documentation in support of its products for the internal use
 *  of its current and prospective customers. The publication of this document does not create any other
 *  right or license in any party to use any content contained in or referred to in this document and any
 *  modification or redistribution of this document is not permitted. While efforts are made to ensure
 *  accuracy, typographical and other errors may exist in this document. OptConnect Management, LLC reserves
 *  the right to modify or discontinue its products and to modify this and any other product documentation
 *  at any time.
 * 
 *  All OptConnect products are sold subject to its published Terms and Conditions and subject to any
 *  separate terms agreed with its customers. No warranty of any type is extended by publication of this
 *  documentation, including, but not limited to, implied warranties of merchantability, fitness for a
 *  particular purpose and non-infringement.
 * 
 *  OptConnect is a trademark, and OptConnect ema is a trademark, of OptConnect Management, LLC. All
 *  trademarks, service marks and similar designations referenced in this document are the property of
 *  their respective owners
 *  #######################################################################################################
 * 
 *  version: 1.0
 * 
 *  History:
 *  Version     Description                                     Date
 *  1.0         Initial Release                                 5/19/2020
 */ 

 #include "oc_emaLink_at.h"

/*########## ema Link At cmds ##########*/
static const char ema_atOKPattern[]         = "\r\nOK\r\n";

static const char ema_atCmdAT[]             = "at\r"; 
static const char ema_atDisEchoCmd[]        = "ate0\r";
static const char ema_atEnEchoCmd[]         = "ate1\r";
static const char ema_atGetFWVer[]          = "at+gmr\r";
static const char ema_atGetSN[]             = "at+gsn\r";
static const char ema_atSetBID[]            = "at+ocbid=";
static const char ema_atGetBID[]            = "at+ocbid?\r";
static const char ema_atTestBID[]           = "at+ocbid=?\r";
static const char ema_atListCMDs[]          = "at+clac\r";
static const char ema_atGlimpse[]           = "at+ocglimpse";
static const char ema_atExtFW[]             = "ati";
/*###########################################*/

/*########### ema Link URCs & codes ###########*/
static const char ema_URCPattern[]          = "OCURC:";

/*###########################################*/

/*############ Helper Functions ############*/
/* 
CheckForMsg: 
Helper function to check an input buffer(*buff) for a valid msg. A valid msg begins and ends with 
"\r\n". The address of the the valid msg is returned(int). This function can be used on partial msgs, 
as long as the *loc variable is updated prior to calling it. 
*/
int CheckForMsg( char *buff, unsigned short len, int *loc )
{ 
    int i;
    char *msg = 0;
    unsigned char r = 0;
    unsigned char n = 0;
    unsigned char msgfound = 0;

    // check for a valid msg containg two pairs of "\r\n"
    for ( i=*loc; i<len; i++ ) {
        if ( *(buff+i) == '\r' ) {
            r++;
        }else if ( *(buff+i) == '\n' ) {
            n++;
        }
        if ( r == 2 && n == 2 ) {
            msgfound = 1;
            break;
        }
    }

    if ( msgfound ) {
        // get a pointer to the beginning of the msg
        msg = strstr( buff+*loc, "\r\n" );
        *loc = i;
    }

    return (int)msg;
}

/* 
ParseMsg: 
Helper function to parse a valid msg(*msg_ptr) and check that it's a URC. The URC ascii format value
is also converted to hex and returned. 
*/
static char ParseMsg( char *msg_ptr )
{
    char val = EMA_LINK_URC_INVALID;
    char *p;
    char ch[2];

    if ( strstr( (char*)msg_ptr, ema_URCPattern ) ) {
        // found urc, convert the ascii values to integer
        p = strstr( (char*)msg_ptr, ": " );
        ch[0] = *(p+2);
        ch[1] = *(p+3);
        val = atoi( ch );
    }

    return  val;
}

/* 
emaLinkAT_BuildCmd: 
Copies a cmd(*cmd) to the emaLink at tx buffer(context->at_cmd). 
*/
static void emaLinkAT_BuildCmd( struct emaLink_context *context, const char *cmd )
{
    sprintf(context->at_cmd, "%s", cmd);
}

/* 
emaLinkURC_GetValue: 
Parses a valid msg(*msg) for the URC value and returns it(int). 
*/
int emaLinkURC_GetValue( char *msg, unsigned short len )
{
    int val = 0;

    // null terminate the entire buffer for string ops
    *(msg+len) = 0;

    val = ParseMsg( msg );

    return val;
}

/* 
emaLinkURC_GetMsg: 
Copies the verbose msg of a valid msg URC into the emaLink context location(context->bn_msg). It is the applications responsibiity to 
pull out the msg before a subsequent call of this function overwrites the previous msg. 
*/
void emaLinkURC_GetMsg( struct emaLink_context *context, char *msg )
{
    char *p1, *p2;
    
    // copy the msg data to the buffer
    p1 = strchr( msg, '"' );
    p2 = strchr( p1+1, '"' );
    // null terminate for string ops
    *p2 = 0;
    // copy the data
    strcpy( context->bn_msg, p1+1 );
}

/* 
emaLinkAT_CheckOK: 
Checks the emaLink AT cmd response(*buff) for the OK pattern. The length of the data(*len) excluding the OK pattern is updated. 
*/
int emaLinkAT_CheckOK( char *buff, unsigned short *len )
{
    int st = EMA_LINK_AT_ST_SUCCESS;
    char *pLen;

    // check the input buff for the "\r\nOK\r\n" pattern
    pLen = strstr(buff, ema_atOKPattern); 
    if ( pLen == 0 ){
        st = EMA_LINK_AT_ST_NO_OK;
    }else{
        *len = pLen - buff;
    }

    return st;
}
/*###########################################*/

/*############ ema Link AT cmd APIs ############*/
/* 
emaLinkAT_Init: 
This API is provided for the user's application to pass in a function pointer(*func_ptr) for a specific AT cmd 
send/recieve function handler. Subsequent APIs in this file will use this function pointer for emaLink AT transactions. 
*/
void emaLinkAT_Init( struct emaLink_context *handle, int (*func_ptr)(struct emaLink_context *) )
{
    // set the user provided function for an at query
    ema_at_comm_handle = func_ptr;

    // reset vars
    handle->at_cmd_flags = 0;
    handle->response_flags = 0;
    handle->urc_status = 0;
}

/* 
emaLinkAT_CheckComm: 
Sends a simple AT cmd(AT) on the emaLink interface for communication validation. The response is checked accordingly. 
*/
int emaLinkAT_CheckComm( struct emaLink_context *context )
{
    int st;
    unsigned short len;

    emaLinkAT_BuildCmd( context, ema_atCmdAT );

    // call the user provided hook for at comms
    st = (*ema_at_comm_handle)( context );
    if ( st > 0 ) {
        // check the response
        st = emaLinkAT_CheckOK( context->response, &len );
    }

    return st;
}

/* 
emaLinkAT_GetFWVersion: 
Queries ema over the emaLink interface for its current firmware version. The current FW version is written to the provided 
output buffer(*fw_string) as a string.
*/
int emaLinkAT_GetFWVersion( struct emaLink_context *context, char *fw_string )
{
    int st, msg, loc;
    unsigned short len;

    emaLinkAT_BuildCmd(context, ema_atGetFWVer);

    // call the user provided hook for at comms
    st = (*ema_at_comm_handle)( context );
    if ( st > 0 ) {
        // check the response
        st = emaLinkAT_CheckOK( context->response, &len );
        if ( st == EMA_LINK_AT_ST_SUCCESS ) {
            // get the fw string
            loc = 0;
            msg = CheckForMsg( context->response, len, &loc );
            if ( msg ) {
                // null terminate for string ops, and update the output buff, skip "\r\n"
                *((char*)msg+len-2) = 0;
                strcpy( fw_string, (char*)msg+2 );
            }
        }
    }

    return st;
}

/* 
emaLinkAT_ListAvailCMDs: 
Queries ema over the emaLink interface for a list of supported AT cmds. The list is written to the provided output 
buffer(*cmd_list) unaltered. 
*/
int emaLinkAT_ListAvailCMDs( struct emaLink_context *context, char *cmd_list )
{
    int st, msg, loc;
    unsigned short len;

    emaLinkAT_BuildCmd(context, ema_atListCMDs);

    // call the user provided hook for at comms
    st = (*ema_at_comm_handle)( context );
    if ( st > 0 ) {
        // check the response
        st = emaLinkAT_CheckOK( context->response, &len );
        if ( st == EMA_LINK_AT_ST_SUCCESS ) {
            // get the clac string
            loc = 0;
            msg = CheckForMsg( context->response, len, &loc );
            if ( msg ) {
                // null terminate for string ops, and update the output buff, skip "\r\n"
                *((char*)msg+len) = 0;
                strcpy( cmd_list, (char*)msg );
            }
        }
    }

    return st;
}

/* 
emaLinkAT_GetSN: 
Queries ema over the emaLink interface for its serial number. The serial number is written to the provided output buffer(*sn_string) as a string.
*/
int emaLinkAT_GetSN( struct emaLink_context *context, char *sn_string )
{
    int st, msg, loc;
    unsigned short len;

    emaLinkAT_BuildCmd(context, ema_atGetSN);

    // call the user provided hook for at comms
    st = (*ema_at_comm_handle)( context );
    if ( st > 0 ) {
        // check the response
        st = emaLinkAT_CheckOK( context->response, &len );
        if ( st == EMA_LINK_AT_ST_SUCCESS ) {
            // get the sn string
            loc = 0;
            msg = CheckForMsg( context->response, len, &loc );
            if ( msg ) {
                // null terminate for string ops, and update the output buff, skip "\r\n"
                *((char*)msg+len-2) = 0;
                strcpy( sn_string, (char*)msg+2 );
            }
        }
    }

    return st;
}

/* 
emaLinkAT_SetBID: 
Takes a passed in identifier(*bid_string) string and sets it as ema's board ID using the emaLink interface. The board ID(*bid_string) is then 
autonomously pushed up to the portal(Summit), adhereing to the users Summit account profile.  
*/
int emaLinkAT_SetBID( struct emaLink_context *context, char *bid_string )
{
    int st, l;
    unsigned short len;

    l = sprintf( context->at_cmd, "%s", ema_atSetBID );
    l += sprintf( context->at_cmd+l, "\"%s\"", bid_string );
    sprintf( context->at_cmd+l, "\r" );

    // call the user provided hook for at comms
    st = (*ema_at_comm_handle)( context );
    if ( st > 0 ) {
        // check the response
        st = emaLinkAT_CheckOK( context->response, &len );
    }

    return st;
}

/* 
emaLinkAT_GetBID: 
Queries ema over the emaLink interface for the currently set board ID. The board ID is written to the provided output buffer(*bid_string).  
*/
int emaLinkAT_GetBID( struct emaLink_context *context, char *bid_string )
{
    int st, msg, loc;
    unsigned short len;

    emaLinkAT_BuildCmd(context, ema_atGetBID);

    // call the user provided hook for at comms
    st = (*ema_at_comm_handle)( context );
    if ( st > 0 ) {
        // check the response
        st = emaLinkAT_CheckOK( context->response, &len );
        if ( st == EMA_LINK_AT_ST_SUCCESS ) {
            // get the bid string
            loc = 0;
            msg = CheckForMsg( context->response, len, &loc );
            if ( msg ) {
                // validate "+OCBID:" string in msg
                if ( strstr( context->response, "+OCBID:" ) ) {
                    // null terminate for string ops, and update the output buff, skip "\r\n+OCBID: ", and quotes around BID string
                    *((char*)msg+len-3) = 0;
                    strcpy( bid_string, (char*)msg+11 );
                }else{
                    st = EMA_LINK_AT_ST_BAD_RESPONSE;
                }
            }
        }
    }

    return st;
}

/* 
emaLinkAT_BIDTestCmd: 
Queries ema over the emaLink interface for the acceptable characters/formatting that can be used to set the board ID. 
The acceptable characters/formatting is written to a provided output buffer(*formatting) unaltered.  
*/
int emaLinkAT_BIDTestCmd( struct emaLink_context *context, char *formatting )
{
    int st, msg, loc;
    unsigned short len;

    emaLinkAT_BuildCmd(context, ema_atTestBID);

    // call the user provided hook for at comms
    st = (*ema_at_comm_handle)( context );
    if ( st > 0 ) {
        // check the response
        st = emaLinkAT_CheckOK( context->response, &len );
        if ( st == EMA_LINK_AT_ST_SUCCESS ) {
            // get the bid string
            loc = 0;
            msg = CheckForMsg( context->response, len, &loc );
            if ( msg ) {
                // validate "+OCBID:" string in msg
                if ( strstr( context->response, "+OCBID:" ) ) {
                    // null terminate for string ops, and update the output buff, skip "\r\n+OCBID: ", and quotes around BID string
                    *((char*)msg+len-2) = 0;
                    strcpy( formatting, (char*)msg+10 );
                }else{
                    st = EMA_LINK_AT_ST_BAD_RESPONSE;
                }
            }
        }
    }

    return st;
}

/* 
emaLinkAT_DisableEcho: 
Disables the echo feature on the emaLink interface. 
*/
int emaLinkAT_DisableEcho( struct emaLink_context *context )
{
    int st;
    unsigned short len;

    emaLinkAT_BuildCmd(context, ema_atDisEchoCmd);

    // call the user provided hook for at comms
    st = (*ema_at_comm_handle)( context );
    if ( st > 0 ) {
        // check the response
        st = emaLinkAT_CheckOK( context->response, &len );
    }

    return st;
}

/* 
emaLinkAT_EnableEcho: 
Enables the echo feature on the emaLink interface.
*/
int emaLinkAT_EnableEcho( struct emaLink_context *context )
{
    int st;
    unsigned short len;

    emaLinkAT_BuildCmd(context, ema_atEnEchoCmd);

    // call the user provided hook for at comms
    st = (*ema_at_comm_handle)( context );
    if ( st > 0 ) {
        // check the response
        st = emaLinkAT_CheckOK( context->response, &len );
    }

    return st;
}

/* 
emaLinkAT_GlimpseTest: 
Queries ema over the emaLink interface for the formatting and value descriptions relative to the Glimpse feature
The formatting(*formatting) is returned in string format unaltered. The descriptions are as follows: 
 
AT+OCGLIMPSE[1-5] 
+OCGLIMPSE1: Ema Uptime, "Ema FW", "Serial Number", "Model" 
+OCGLIMPSE2: "Primary Carrier", "Active Carrier", "Active Firmware", "Reg. Status", "Failover Enabled", Last Failover Event, "Access Tech." 
+OCGLIMPSE3: Cell ID, MCC, MNC, TAC 
+OCGLIMPSE4: Signal Quality, Curr. RSSI, Curr. RSRQ, Curr. RSRP,  RSSI Min., RSSI Max., RSSI Avg., RSRQ Min., RSRQ Max., RSRQ Avg., RSRP Min., RSRP Max., RSRP Avg. 
+OCGLIMPSE5: "Ema Services State" 
 
OK 
*/
int emaLinkAT_GlimpseTest( struct emaLink_context *context, char *formatting )
{
    int st, l;
    unsigned short len;

    l = sprintf( context->at_cmd, "%s", ema_atGlimpse );
    l += sprintf( context->at_cmd+l, "%s", "=?" );
    sprintf( context->at_cmd+l, "\r" );

    // call the user provided hook for at comms
    st = (*ema_at_comm_handle)( context );
    if ( st > 0 ) {
        // check the response
        st = emaLinkAT_CheckOK( context->response, &len );
        if ( st == EMA_LINK_AT_ST_SUCCESS ) {
            // copy the glimpse data as is
            memcpy( formatting, context->response, len );
            // null terminate for string ops
            *(formatting+len) = 0;
        }
    }

    return st;
}

/* 
emaLinkAT_Glimpse: 
Queries ema over the emaLink interface for all Glimpse[1 - 5] current Glimpse data. 
The Glimpse data(*buffer) is returned in string format unaltered.
*/
int emaLinkAT_Glimpse( struct emaLink_context *context, char *buffer )
{
    int st, l;
    unsigned short len;

    l = sprintf( context->at_cmd, "%s", ema_atGlimpse );
    sprintf( context->at_cmd+l, "\r" );

    // call the user provided hook for at comms
    st = (*ema_at_comm_handle)( context );
    if ( st > 0 ) {
        // check the response
        st = emaLinkAT_CheckOK( context->response, &len );
        if ( st == EMA_LINK_AT_ST_SUCCESS ) {
            // copy the glimpse data as is
            memcpy( buffer, context->response, len );
            // null terminate for string ops
            *(buffer+len) = 0;
        }
    }

    return st;
}

/* 
emaLinkAT_Glimpse1: 
Queries ema over the emaLink interface for the subset Glimpse[1] data.  
The Glimpse data(*glimpse) is returned in string format. 
 
Glimpse[1] description: 
 
+OCGLIMPSE1: Ema Uptime, "Ema FW", "Serial Number", "Model" 
 
OK 
 
*/
int emaLinkAT_Glimpse1( struct emaLink_context *context, struct ema_glimpse1 *glimpse )
{
    int st, l;
    unsigned short len;
    char *p1, *p2;

    l = sprintf( context->at_cmd, "%s", ema_atGlimpse );
    l += sprintf( context->at_cmd+l, "%d", 1 );
    sprintf( context->at_cmd+l, "\r" );

    // flush the string buffers
    memset( glimpse->ema_fw, 0, sizeof(glimpse->ema_fw) );
    memset( glimpse->ema_sn, 0, sizeof(glimpse->ema_sn) );
    memset( glimpse->ema_model, 0, sizeof(glimpse->ema_model) );

    // call the user provided hook for at comms
    st = (*ema_at_comm_handle)( context );
    if ( st > 0 ) {
        // check the response
        st = emaLinkAT_CheckOK( context->response, &len );
        if ( st == EMA_LINK_AT_ST_SUCCESS ) {
            // pull our the relevant data...
            p1 = strchr( context->response, ' ' );
            glimpse->ema_uptime = atoi( p1+1 );
            p1 = strchr( context->response, '"' );
            p2 = strchr( p1+1, '"' );
            memcpy( glimpse->ema_fw, p1+1, (p2-p1)-1 );
            p1 = strchr( p2+1, '"' );
            p2 = strchr( p1+1, '"' );
            memcpy( glimpse->ema_sn, p1+1, (p2-p1)-1 );
            p1 = strchr( p2+1, '"' );
            p2 = strchr( p1+1, '"' );
            memcpy( glimpse->ema_model, p1+1, (p2-p1)-1 );
        }
    }

    return st;
}


/* 
emaLinkAT_Glimpse2: 
Queries ema over the emaLink interface for the subset Glimpse[2] data.  
The Glimpse data(*glimpse) is returned in string format. 
 
Glimpse[2] description: 
 
+OCGLIMPSE2: "Primary Carrier", "Active Carrier", "Active Firmware", "Reg. Status", "Failover Enabled", Last Failover Event, "Access Tech." 
 
OK 
 
*/
int emaLinkAT_Glimpse2( struct emaLink_context *context, struct ema_glimpse2 *glimpse )
{
    int st, l;
    unsigned short len;
    char *p1, *p2;

    l = sprintf( context->at_cmd, "%s", ema_atGlimpse );
    l += sprintf( context->at_cmd+l, "%d", 2 );
    sprintf( context->at_cmd+l, "\r" );

    // flush the string buffers
    memset( glimpse->pri_carr, 0, sizeof(glimpse->pri_carr) );
    memset( glimpse->active_carr, 0, sizeof(glimpse->active_carr) );
    memset( glimpse->active_fw, 0, sizeof(glimpse->active_fw) );
    memset( glimpse->reg_st, 0, sizeof(glimpse->reg_st) );
    memset( glimpse->failover_en, 0, sizeof(glimpse->failover_en) );
    memset( glimpse->acc_tech, 0, sizeof(glimpse->acc_tech) );

    // call the user provided hook for at comms
    st = (*ema_at_comm_handle)( context );
    if ( st > 0 ) {
        // check the response
        st = emaLinkAT_CheckOK( context->response, &len );
        if ( st == EMA_LINK_AT_ST_SUCCESS ) {
            // pull our the relevant data...
            p1 = strchr( context->response, '"' );
            p2 = strchr( p1+1, '"' );
            memcpy( glimpse->pri_carr, p1+1, (p2-p1)-1 );
            p1 = strchr( p2+1, '"' );
            p2 = strchr( p1+1, '"' );
            memcpy( glimpse->active_carr, p1+1, (p2-p1)-1 );
            p1 = strchr( p2+1, '"' );
            p2 = strchr( p1+1, '"' );
            memcpy( glimpse->active_fw, p1+1, (p2-p1)-1 );
            p1 = strchr( p2+1, '"' );
            p2 = strchr( p1+1, '"' );
            memcpy( glimpse->reg_st, p1+1, (p2-p1)-1 );
            p1 = strchr( p2+1, '"' );
            p2 = strchr( p1+1, '"' );
            memcpy( glimpse->failover_en, p1+1, (p2-p1)-1 );
            p1 = strchr( p2+1, ' ' );
            glimpse->last_failover_ev = atoi( p1+1 );
            p1 = strchr( p2+1, '"' );
            p2 = strchr( p1+1, '"' );
            memcpy( glimpse->acc_tech, p1+1, (p2-p1)-1 );
        }
    }

    return st;
}

/* 
emaLinkAT_Glimpse3: 
Queries ema over the emaLink interface for the subset Glimpse[3] data.  
The Glimpse data(*glimpse) is returned in string format. 
 
Glimpse[3] description: 
 
+OCGLIMPSE3: Cell ID, MCC, MNC, TAC 
 
OK 
 
*/
int emaLinkAT_Glimpse3( struct emaLink_context *context, struct ema_glimpse3 *glimpse )
{
    int st, l;
    unsigned short len;
    char *p1, *p2;

    l = sprintf( context->at_cmd, "%s", ema_atGlimpse );
    l += sprintf( context->at_cmd+l, "%d", 3 );
    sprintf( context->at_cmd+l, "\r" );

    // call the user provided hook for at comms
    st = (*ema_at_comm_handle)( context );
    if ( st > 0 ) {
        // check the response
        st = emaLinkAT_CheckOK( context->response, &len );
        if ( st == EMA_LINK_AT_ST_SUCCESS ) {
            // pull our the relevant data...
            p1 = strchr( context->response, ' ' );
            glimpse->cell_id = strtol( p1+1, 0, 16 );
            p2 = strchr( p1+1, ' ' );
            glimpse->mcc = atoi( p2+1 );
            p1 = strchr( p2+1, ' ' );
            glimpse->mnc = atoi( p1+1 );
            p2 = strchr( p1+1, ' ' );
            glimpse->tac = atoi( p2+1 );
        }
    }

    return st;
}

/* 
emaLinkAT_Glimpse4: 
Queries ema over the emaLink interface for the subset Glimpse[4] data.  
The Glimpse data(*glimpse) is returned in string format. 
 
Glimpse[4] description: 
 
+OCGLIMPSE4: Signal Quality, Curr. RSSI, Curr. RSRQ, Curr. RSRP,  RSSI Min., RSSI Max., RSSI Avg., RSRQ Min., RSRQ Max., RSRQ Avg., RSRP Min., RSRP Max., RSRP Avg. 
 
OK 
 
*/
int emaLinkAT_Glimpse4( struct emaLink_context *context, struct ema_glimpse4 *glimpse )
{
    int st, l;
    unsigned short len;
    char *p1, *p2;

    l = sprintf( context->at_cmd, "%s", ema_atGlimpse );
    l += sprintf( context->at_cmd+l, "%d", 4 );
    sprintf( context->at_cmd+l, "\r" );

    // call the user provided hook for at comms
    st = (*ema_at_comm_handle)( context );
    if ( st > 0 ) {
        // check the response
        st = emaLinkAT_CheckOK( context->response, &len );
        if ( st == EMA_LINK_AT_ST_SUCCESS ) {
            // pull our the relevant data...
            p1 = strchr( context->response, ' ' );
            glimpse->sig_q = atoi(p1 + 1);
            p2 = strchr( p1+1, ' ' );
            glimpse->curr_rssi = atoi(p2 + 1);
            p1 = strchr( p2+1, ' ' );
            glimpse->curr_rsrq = atoi(p1 + 1);
            p2 = strchr( p1+1, ' ' );
            glimpse->curr_rsrp = atoi( p2+1 );
            p1 = strchr( p2+1, ' ' );
            glimpse->min_rssi = atoi(p1 + 1);
            p2 = strchr( p1+1, ' ' );
            glimpse->max_rssi = atoi( p2+1 );
            p1 = strchr( p2+1, ' ' );
            glimpse->avg_rssi = atoi(p1 + 1);
            p2 = strchr( p1+1, ' ' );
            glimpse->min_rsrq = atoi( p2+1 );
            p1 = strchr( p2+1, ' ' );
            glimpse->max_rsrq = atoi(p1 + 1);
            p2 = strchr( p1+1, ' ' );
            glimpse->avg_rsrq = atoi( p2+1 );
            p1 = strchr( p2+1, ' ' );
            glimpse->min_rsrp = atoi(p1 + 1);
            p2 = strchr( p1+1, ' ' );
            glimpse->max_rsrp = atoi( p2+1 );
            p1 = strchr( p2+1, ' ' );
            glimpse->avg_rsrp = atoi(p1 + 1);
        }
    }

    return st;
}

/* 
emaLinkAT_Glimpse5: 
Queries ema over the emaLink interface for the subset Glimpse[5] data.  
The Glimpse data(*glimpse) is returned in string format. 
 
Glimpse[5] description: 
 
+OCGLIMPSE5: "ema Services State" 
 
OK 
 
*/
int emaLinkAT_Glimpse5( struct emaLink_context *context, struct ema_glimpse5 *glimpse )
{
    int st, l;
    unsigned short len;
    char *p1, *p2;

    l = sprintf( context->at_cmd, "%s", ema_atGlimpse );
    l += sprintf( context->at_cmd+l, "%d", 5 );
    sprintf( context->at_cmd+l, "\r" );

    // flush the string buffers
    memset( glimpse->ema_services_state, 0, sizeof(glimpse->ema_services_state) );

    // call the user provided hook for at comms
    st = (*ema_at_comm_handle)( context );
    if ( st > 0 ) {
        // check the response
        st = emaLinkAT_CheckOK( context->response, &len );
        if ( st == EMA_LINK_AT_ST_SUCCESS ) {
            // pull our the relevant data...
            p1 = strchr( context->response, '"' );
            p2 = strchr( p1+1, '"' );
            memcpy( glimpse->ema_services_state, p1+1, (p2-p1)-1 );
        }
    }
    return st;
}

/* 
emaLinkAT_ExtFWFull: 
Queries ema over the emaLink interface for the full firmware version information. 
The firmware version(ema_ext_fw *fw->full) is updated in string format unaltered. This command has the same result as 
emaLinkAT_GetFWVersion(). 
*/
int emaLinkAT_ExtFWFull( struct emaLink_context *context, struct ema_ext_fw *fw )
{
    int st, l;
    unsigned short len;
    char *p1, *p2;

    l = sprintf( context->at_cmd, "%s", ema_atExtFW );
    l += sprintf( context->at_cmd+l, "%d", 0 );
    sprintf( context->at_cmd+l, "\r" );

    // flush the string buffers
    memset( fw->fw_full, 0, sizeof(fw->fw_full) );

    // call the user provided hook for at comms
    st = (*ema_at_comm_handle)( context );
    if ( st > 0 ) {
        // check the response
        st = emaLinkAT_CheckOK( context->response, &len );
        if ( st == EMA_LINK_AT_ST_SUCCESS ) {
            // pull our the relevant data...
            p1 = strchr( context->response, 'v' );
            p2 = strchr( p1+1, '\r' );
            memcpy( fw->fw_full, p1, p2-p1 );
        }
    }

    return st;
}

/* 
emaLinkAT_ExtFWRevision: 
Queries ema over the emaLink interface for the firmware revision information. 
The firmware revision(ema_ext_fw *fw->fw_rev) is returned in string format unaltered. 
*/
int emaLinkAT_ExtFWRevision( struct emaLink_context *context, struct ema_ext_fw *fw )
{
    int st, l;
    unsigned short len;
    char *p1, *p2;

    l = sprintf( context->at_cmd, "%s", ema_atExtFW );
    l += sprintf( context->at_cmd+l, "%d", 1 );
    sprintf( context->at_cmd+l, "\r" );

    // flush the string buffers
    memset( fw->fw_rev, 0, sizeof(fw->fw_rev) );

    // call the user provided hook for at comms
    st = (*ema_at_comm_handle)( context );
    if ( st > 0 ) {
        // check the response
        st = emaLinkAT_CheckOK( context->response, &len );
        if ( st == EMA_LINK_AT_ST_SUCCESS ) {
            // pull our the relevant data...
            p1 = strchr( context->response, '\n' );
            p2 = strchr( p1+1, '\r' );
            memcpy( fw->fw_rev, p1+1, (p2-p1)-1 );
        }
    }

    return st;
}

/* 
emaLinkAT_ExtFWSubRevision: 
Queries ema over the emaLink interface for the firmware sub-revision information. 
The firmware sub-revision(ema_ext_fw *fw->fw_sub_rev) is returned in string format unaltered. 
*/
int emaLinkAT_ExtFWSubRevision( struct emaLink_context *context, struct ema_ext_fw *fw )
{
    int st, l;
    unsigned short len;
    char *p1, *p2;

    l = sprintf( context->at_cmd, "%s", ema_atExtFW );
    l += sprintf( context->at_cmd+l, "%d", 2 );
    sprintf( context->at_cmd+l, "\r" );

    // flush the string buffers
    memset( fw->fw_sub_rev, 0, sizeof(fw->fw_sub_rev) );

    // call the user provided hook for at comms
    st = (*ema_at_comm_handle)( context );
    if ( st > 0 ) {
        // check the response
        st = emaLinkAT_CheckOK( context->response, &len );
        if ( st == EMA_LINK_AT_ST_SUCCESS ) {
            // pull our the relevant data...
            p1 = strchr( context->response, '\n' );
            p2 = strchr( p1+1, '\r' );
            memcpy( fw->fw_sub_rev, p1+1, (p2-p1)-1 );
        }
    }

    return st;
}

/* 
emaLinkAT_ExtFWHash: 
Queries ema over the emaLink interface for the firmware hash information. 
The firmware hash(ema_ext_fw *fw->fw_hash) is returned in string format unaltered. 
*/
int emaLinkAT_ExtFWHash( struct emaLink_context *context, struct ema_ext_fw *fw )
{
    int st, l;
    unsigned short len;
    char *p1, *p2;

    l = sprintf( context->at_cmd, "%s", ema_atExtFW );
    l += sprintf( context->at_cmd+l, "%d", 3 );
    sprintf( context->at_cmd+l, "\r" );

    // flush the string buffers
    memset( fw->fw_hash, 0, sizeof(fw->fw_hash) );

    // call the user provided hook for at comms
    st = (*ema_at_comm_handle)( context );
    if ( st > 0 ) {
        // check the response
        st = emaLinkAT_CheckOK( context->response, &len );
        if ( st == EMA_LINK_AT_ST_SUCCESS ) {
            // pull our the relevant data...
            p1 = strchr( context->response, '\n' );
            p2 = strchr( p1+1, '\r' );
            memcpy( fw->fw_hash, p1+1, (p2-p1)-1 );
        }
    }

    return st;
}

/* 
emaLinkAT_ExtFWBuild: 
Queries ema over the emaLink interface for the firmware build information. 
The firmware build(ema_ext_fw *fw->fw_build) is returned in string format unaltered. 
*/
int emaLinkAT_ExtFWBuild( struct emaLink_context *context, struct ema_ext_fw *fw )
{
    int st, l;
    unsigned short len;
    char *p1, *p2;

    l = sprintf( context->at_cmd, "%s", ema_atExtFW );
    l += sprintf( context->at_cmd+l, "%d", 4 );
    sprintf( context->at_cmd+l, "\r" );

    // flush the string buffers
    memset( fw->fw_build, 0, sizeof(fw->fw_build) );

    // call the user provided hook for at comms
    st = (*ema_at_comm_handle)( context );
    if ( st > 0 ) {
        // check the response
        st = emaLinkAT_CheckOK( context->response, &len );
        if ( st == EMA_LINK_AT_ST_SUCCESS ) {
            // pull our the relevant data...
            p1 = strchr( context->response, '\n' );
            p2 = strchr( p1+1, '\r' );
            memcpy( fw->fw_build, p1+1, (p2-p1)-1 );
        }
    }

    return st;
}

/* 
emaLinkAT_ExtFWVersion: 
Queries ema over the emaLink interface for the firmware version information. 
The firmware version(ema_ext_fw *fw->fw_ver) is returned in string format unaltered. 
*/
int emaLinkAT_ExtFWVersion( struct emaLink_context *context, struct ema_ext_fw *fw )
{
    int st, l;
    unsigned short len;
    char *p1, *p2;

    l = sprintf( context->at_cmd, "%s", ema_atExtFW );
    l += sprintf( context->at_cmd+l, "%d", 5 );
    sprintf( context->at_cmd+l, "\r" );

    // call the user provided hook for at comms
    st = (*ema_at_comm_handle)( context );
    if ( st > 0 ) {
        // check the response
        st = emaLinkAT_CheckOK( context->response, &len );
        if ( st == EMA_LINK_AT_ST_SUCCESS ) {
            // pull our the relevant data...
            p1 = strchr( context->response, '\n' );
            p2 = strchr( p1+1, '\r' );
            memcpy( &fw->fw_ver, p1+1, (p2-p1)-1 );
        }
    }

    return st;
}
/*###########################################*/
