/************************************************************
 *  File Name:  AVICReceivePackager.c
 *
 *  Version: V1.0
 *
 *  Requirements:
 *      AVIC
 *
 *  Description:
 *      This file contains the AVIC Receive Packager functions.
 *
 *  History:
 *
 *      Original          24-MAY-2001        R. Caldbeck
 *
 *
 ************************************************************/

#include "AVICReceivePackager.h"


/* The "TIMEMOD" constant should be a value that, when multiplied
   by the RTC-difference between time2 and time1, results in a number
   of elapsed microseconds (usec) between the two RTC values.  This
   number is determined by the clock frequency and its pre-dividers. */

const FLOAT32 TIMEMOD = 1.0;

const UINT8 MAXCOUNT = (UINT8) (MSIZE - 4);

const UINT8 STX = (UINT8) 0x02;
const UINT8 ETX = (UINT8) 0x03;

const UINT8 NOR = (UINT8) 0x00;
const UINT8 ACK = (UINT8) 0x06;
const UINT8 NAK = (UINT8) 0x15;

AVICReceivePackagerType AVICReceivePackager1;
AVICReceivePackagerType AVICReceivePackager2;


static FLOAT32 AVICReceivePackager_Timeusec
                (const UINT32 time1, const UINT32 time2);
static BOOL AVICReceivePackager_ProcessMessage
                (AVICDriverReceiveQueueStruct * const RXbuff);
static BOOL AVICReceivePackager_ValidateMessage
                (AVICReceivePackagerType * const Message);



/************************************************************
 *  Name:  AVICReceivePackager_Initialize
 *
 *  Description:
 *      Initialize the AVICReceivePackager struct as per
 *      the requirements.
 *  Inputs:
 *      None.
 *  Outputs:
 *      None.
 *  Constraints:
 *      None.
 ************************************************************/  


void AVICReceivePackager_Initialize (void)
{
    AVICReceivePackager1.AVIC_Receive_Status = ACK;
    AVICReceivePackager2.AVIC_Receive_Status = ACK;
}



/************************************************************
 *  Name:  AVICReceivePackager_UnpackNewMessages
 *
 *  Description:
 *      If there is a new, complete AVIC message in either
 *      AVIC_1 or AVIC_2, then unpack it/them into RAM and
 *      process the messages (i.e. set flags, etc.).
 *  Inputs:
 *      None.
 *  Outputs:
 *      BOOL - is there a new, valid AVIC message?
 *  Constraints:
 *      None.
 ************************************************************/    


BOOL AVICReceivePackager_UnpackNewMessages
                (AVICDriverReceiveQueueStruct * const RXbuff)
{
    UINT32 time1;       /* RTC time of last-checked received AVIC byte =
*/
    UINT32 time2;       /* RTC time of AVIC byte currently being =
checked */
    UINT16 qptr;        /* Queue position pointer of byte being checked =
*/
    UINT16 qstop;       /* Queue position pointer of last received byte =
*/
    BOOL silence = FALSE;   /* STX with 5 msec of silence? */
    FLOAT32 timediff;   /* RTC time difference between prev & current =
byte */


    qptr = RXbuff->first;
    qstop = RXbuff->next;

    while (qptr != qstop && silence == FALSE)
    {
        /* Loop until start of new, valid AVIC message with no faults.. =
*/
        /* ..or until there are no more new received bytes to check. */

        if (RXbuff->queue[qptr] == STX)
        {
            time1 = RXbuff->rtc[qptr != 0 ? qptr-1 : QSIZE-1];
            time2 = RXbuff->rtc[qptr];
            timediff = AVICReceivePackager_Timeusec (time1, time2);

            if (timediff >= 5.0 && RXbuff->faults[qptr] == (UINT8) 0)
                silence = TRUE;
            else
                silence = FALSE;
        }

        if (++qptr == QSIZE)   /* check for wrap-around */
            qptr = 0;
    }

    /* At this point, qptr is either pointing just past the first byte
       of a new message (i.e. a no-fault ETX), or else it is pointing =
to
       qstop (i.e. no new message to process).  This means that there =
are
       no error-free ETX's preceded by 5 msec of silence.
       Once RXbuff->first is pointing to a no-fault ETX, it will stay
       there until the message is validated or terminated. */

    if (silence != FALSE)
    {
        RXbuff->first = qptr - 1;
        return AVICReceivePackager_ProcessMessage (RXbuff);
    }
    else
    {
        RXbuff->first = qptr;
        if (RXbuff == &AVICDriver_RXbuff1)
            AVICReceivePackager1.AVIC_Receive_Status = NOR;
        else
            AVICReceivePackager2.AVIC_Receive_Status = NOR;
        return FALSE;
    }
}



/************************************************************
 *  Name:  AVICReceivePackager_Timeusec
 *
 *  Description:
 *      Return a Float value that is the difference, in
 *      microseconds (usec), between the two time inputs.
 *  Inputs:
 *      time1 - older time (RTC value)
 *      time2 - newer time (RTC value)
 *  Outputs:
 *      FLOAT32 - difference in microseconds (usec)
 *                between time1 and time2.
 *  Constraints:
 *      The provided code inherently checks for wraparound.
 ************************************************************/     


static FLOAT32 AVICReceivePackager_Timeusec (const UINT32 time1,
                                             const UINT32 time2)
{
    return TIMEMOD * (FLOAT32) (time2 - time1);
}



/************************************************************
 *  Name:  AVICReceivePackager_ProcessMessage
 *
 *  Description:
 *      An ETX has been detected, so see if there is a complete
 *      message attached to it.  If so, then validate the message
 *      and, if it's good, save it.
 *  Inputs:
 *      RXbuff - An AVICDriverReceiveQueue (AVIC_1 or AVIC_2)
 *  Outputs:
 *      msg    - A complete, valid AVIC message.
 *      BOOL   - is there a new, valid AVIC message?
 *  Constraints:
 *      None.
 ************************************************************/  


static BOOL AVICReceivePackager_ProcessMessage
                (AVICDriverReceiveQueueStruct * const RXbuff)
{
    AVICReceivePackagerType *Message;   /* Pointer to Packager 1 or 2 */
    UINT16 qptr;        /* Queue position pointer of byte being checked */
    UINT16 qstop;       /* Queue position pointer of last received byte */
    UINT16 msgptr = 0;  /* Position pointer for message being stored */


    if (RXbuff == &AVICDriver_RXbuff1)
        Message = &AVICReceivePackager1;
    else
        Message = &AVICReceivePackager2;

    Message->last = 0;

    qptr = RXbuff->first;
    qstop = RXbuff->next;

    /* Copy bytes from receive queue until end is reached (i.e. ETX). */
    /* If any of the bytes have an error, set flag and abort mission! */

    while (qptr != qstop && RXbuff->queue[qptr] != ETX &&
                            RXbuff->faults[qptr] == (UINT8) 0)
    {
        Message->msg[msgptr++] = RXbuff->queue[qptr];

        if (++qptr == QSIZE)   /* check for wrap-around */
            qptr = 0;
    }

    /* If there was a fault, push RXbuff->first up past the fault. */
    if (RXbuff->faults[qptr] != (UINT8) 0)
    {
        RXbuff->first = qptr + 1;
        Message->AVIC_Receive_Status = NAK;
        return FALSE;
    }

    /* Else, if an ETX was reached, then store the message and set
       RXbuff->first to be just past the ETX. */
    else if (RXbuff->queue[qptr] == ETX)
    {
        RXbuff->first = qptr + 1;
        Message->msg[msgptr] = ETX;
        Message->last = msgptr;
        return AVICReceivePackager_ValidateMessage (Message);
    }

    /* Else, an ETX was not reached, so sit and wait 'til next time. */
    else
    {
        /* for reference: qptr == qstop */
        Message->AVIC_Receive_Status = NAK;
        return FALSE;
    }
}


/************************************************************
 *  Name:  AVICReceivePackager_ValidateMessage
 *
 *  Description:
 *      Validate the message.  Check for valid header, data,
 *      checksum, and trailer.  Verify that character count
 *      is correct and check the ACK/NAK byte.
 *  Inputs:
 *      Message - Message structure for AVIC_1 or AVIC_2
 *  Outputs:
 *      BOOL - is the message valid?
 *  Constraints:
 *      None.
 ************************************************************/  


static BOOL AVICReceivePackager_ValidateMessage
                (AVICReceivePackagerType * const Message)
{
    UINT32 i;           /* loop counter for current Data byte */
    UINT32 count;       /* number of Data & Checksum bytes */

    union {             /* running 16-bit checksum */
        UINT16 hword;
        struct {
            UINT8 MSB;
            UINT8 LSB;
        } byte;
    } checksum;


/*  Message->msg[0] will always be "STX". */

/*  Message->msg[1] should be "character count".
    This is a binary number range 0..255, of which 0..72 shall be the
    valid range.  This byte shall specify the combined length of the
    Data and Checksum bytes in this message. */

    count = (UINT32) (Message->msg[1]);

    if (count > (UINT32) MAXCOUNT)
    {
        /* INVALID -- Character count too long! */
        Message->last = 0;
        Message->AVIC_Receive_Status = NAK;
        return FALSE;
    }

    if (count == 0)
    {
        /* There are no data or checksum bytes -- only a handshake! */
        if ((Message->msg[2] != ACK &&
             Message->msg[2] != NAK) ||
            (Message->msg[3] != ETX))
        {
            Message->last = 0;
            Message->AVIC_Receive_Status = NAK;
            return FALSE;
        }
        else
        {
            Message->AVIC_Handshake_Rcv = Message->msg[2];
            Message->AVIC_Receive_Status = ACK;
            return FALSE;   /* no info to process!  don't know what to do */
            /* return TRUE; */
        }
    }


    /* Loop through all the Data, then compare it with the Checksum.
       The Checksum is a 16-bit binary integer sum of the bytes in
       the Data portion of the message.  It is sent as LSB first,
       followed by the MSB. */

    /* count = 1..72; last two are 16-bit Checksum LSB, MSB. */

    checksum.hword = 0;

    for (i=2; i<count; i++)
        checksum.hword += (UINT16) (Message->msg[i]);


    /* Verify Checksum values:
       Message->msg[count] is the LSB of Checksum.
       Message->msg[count+1] is the MSB of Checksum. */

    if (checksum.byte.LSB != Message->msg[count] ||
        checksum.byte.MSB != Message->msg[count+1])
    {
        Message->last = 0;
        Message->AVIC_Receive_Status = NAK;
        return FALSE;
    }


    /* Verify Trailer (ACK/NAK and ETX)
       Message->msg[count+2] is ACK (0x06) or NAK (0x15).
       Message->msg[count+3] is ETX (0x03). */

    if ((Message->msg[count+2] != ACK &&
         Message->msg[count+2] != NAK) ||
        (Message->msg[count+3] != ETX))
    {
        Message->last = 0;
        Message->AVIC_Receive_Status = NAK;
        return FALSE;
    }
    else
    {
        Message->AVIC_Handshake_Rcv = Message->msg[count+2];
        Message->AVIC_Receive_Status =  ACK;
        return TRUE;
    }
}
