/****************************************************************************/
/* Copyright 1992 to 1997 MBARI                                             */
/****************************************************************************/
/* Summary  : IBC Ground Fault/5V supply Board Functions                    */
/* Filename : gf_board.c                                                    */
/* Author   : Andrew Pearce                                                 */
/* Project  : Tiburon Data Concentrator                                     */
/* Version  : 1.0                                                           */
/* Created  : 09/17/92                                                      */
/* Modified : 11/18/96                                                      */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/* $Header: /usr/tiburon/.cvsroot/micro/ibc/boards/gf_new/gf_board.c,v 1.1.1.1 1997/05/02 18:51:36 pean Exp $
 * $Log: gf_board.c,v $
 * Revision 1.1.1.1  1997/05/02 18:51:36  pean
 * Initial check in of IBC microcontroller software
 *
 * Revision 1.5  96/08/07  15:22:25  15:22:25  pean (Andrew Pearce)
 * Fixed problem with GF scanning stopping when RESTART_SCAN command sent
 * Problem was due to a bug in timer library timer.c. Used read_sysclock instea
 *
 * Revision 1.4  96/02/14  14:27:57  14:27:57  pean (Andrew Pearce)
 * Revised GF/5V Selftest, scan update features, fixed sensor update problems
 *
 * Revision 1.3  96/02/12  09:02:59  09:02:59  pean (Andrew Pearce)
 * Checked in prior to changes for HPS/GF Board interaction
 *
 * Revision 1.2  93/08/06  11:44:05  11:44:05  pean (Andrew Pearce)
 * GF Selftest moved from GF_BoardInit (at power up) to GF_Enable command
 * Therefore, added GF_SELFTEST_STATUS_SRQ to report result
 *
 * Revision 1.1  93/07/02  09:28:03  09:28:03  pean (Andrew Pearce)
 * Initial revision
 *
*/
/****************************************************************************/

#pragma model(196)
#pragma nosignedchar

#include "C:/C96/include/stddef.h"      /* C Standard Definitions           */
#include "const.h"                      /* Misc. constants - TRUE/FALSE etc */
#include "types.h"                      /* MBARI style guide declarations   */
#include "microasm.h"                   /* Assembly language functions      */
#include "malloc.h"                     /* Malloc support routines          */
#include "microdef.h"                   /* Microcontroller Definitions      */
#include "microCmd.h"                   /* microcontroller commands         */
#include "microlib.h"                   /* Microcontroller Library decls.   */
#include "timer.h"                      /* Timer library definitions        */
#include "applay.h"                     /* Application layer funcitions     */

#include "../filter/filter.h"           /* General Filter Routines          */
#include "../ibc/ibc_card.h"            /* IBC Board Types and Addresses    */
#include "../ibc/ibc_cmd.h"             /* Core IBC application definitions */
#include "../ibc/ibc.h"                 /* IBC Function Library Definitions */
#include "../cpu196/ibc_asm.h"          /* IBC Assembly Language Functions  */
#include "../cpu196/pic.h"              /* Intel 82C59 Interrupt Controller */
#include "../regen/regen.h"             /* Regen Board Definitions          */

#include "gf_board.h"                   /* Ground Fault Board Definitions   */

                                        /* Forward declarations for MLocals */
MLocal Int16 GF_BoardRelayCtrl( GF_BoardEntry *GF_Board, Byte state );
MLocal Void  GF_BoardReadHalfscaleVoltage( GF_BoardEntry *GF_Board );
MLocal Void  GF_BoardSelftest( GF_BoardEntry *GF_Board );
MLocal Void  GF_BoardBusCapacitanceModel( GF_BoardEntry *GF_Board,
                 Nat32 busVoltage);

/****************************************************************************/
/* Function    : GF_BoardSearch                                             */
/* Purpose     : Scans IBC backplane looking for Ground Fault Boards        */
/* Inputs      : Pointer to IBC Board Entry Table                           */
/* Outputs     : Returns number of boards found                             */
/****************************************************************************/
    Int16
GF_BoardSearch( IBC_BoardEntry *IBC_Boards[], Int16 maxCards )
{
    Byte  *addr;                        /* GF Board base address            */
    Int16 board;                        /* Counts number of GF board found  */
    IBC_BoardEntry *boardEntry;         /* Pointer to GF board data struct  */

    board = 0;                          /* Set board count to zero          */
    if (maxCards > 0)                   /* Check for space in IBC Card Table*/
    {                                   /* Search for Ground Fault Boards   */
        for (addr =  (Byte *) GROUND_FAULT_0_ADDR;
             addr <= (Byte *) GROUND_FAULT_3_ADDR;
             addr+= (GROUND_FAULT_1_ADDR - GROUND_FAULT_0_ADDR) )
        {                               /* NULL boardEntry if board missing */
            if ((boardEntry = (IBC_BoardEntry *) GF_BoardInit(addr)) != NULL)
            {                           /* Add GF data struct to IBC Table  */
                IBC_Boards[board++] = boardEntry;
                if (board == maxCards)  /* Check if IBC Card Table is full  */
                    return(board);      /* Return number of GF boards found */
            } /* if */
        } /* for */
    } /* if */

    return(board);                      /* Return number of GF boards found */
} /* GF_BoardSearch() */

/****************************************************************************/
/* Function    : GF_BoardInit                                               */
/* Purpose     : Ground Fault Board Initialization routine                  */
/* Inputs      : Base address of Ground Fault board                         */
/* Outputs     : Returns pointer to IBC board data structure                */
/****************************************************************************/
    GF_BoardEntry*
GF_BoardInit( Byte *boardBaseAddr )
{
    GF_BoardEntry *GF_Board;            /* Pointer to board entry           */
    Int16 chan;                         /* Ground Fault channel index       */

                                        /* Check if board exists and also   */
                                        /* Clear any pending A/D interrupt  */
                                        /* Returns OK if board asserts xack */
    if ( ibcBusReadByte( boardBaseAddr + GF_AtoD_REG ) == ERROR )
        return(( GF_BoardEntry *) NULL);
                                        /* Board doesn't exist return NULL  */

                                        /* Allocate space for board entry   */
    GF_Board = (GF_BoardEntry *) malloc( sizeof (GF_BoardEntry) );

    if (GF_Board == (GF_BoardEntry *) NULL )
        return( (GF_BoardEntry *) NULL);
                                        /* Insufficient memory return ERROR */

                                        /* Fill out board entry structure   */
    GF_Board->IBC_Board.baseAddress  = boardBaseAddr;
    GF_Board->IBC_Board.boardType    = GROUND_FAULT;
    GF_Board->IBC_Board.intrLevel    = IBC_INT_LVL_OFF;

    GF_Board->relayState = 0;           /* Clear relay state copy           */
    GF_Board->gfEnabled  = FALSE;       /* Disable GF fault monitoring      */
    GF_Board->gfSelftest = FALSE;       /* Clear self test enable flag      */

    GF_Board->gfChannel = 0;            /* Set GF channel to measure to 0   */
    GF_Board->gfChanUpdate = 0;         /* Set GF channel update bits to 0  */
    GF_Board->gfChanState = GF_IDLE;    /* Measurement state machine = IDLE */

    GF_Board->timerTicks = 0;
    GF_Board->timerState = TIMER_INACTIVE;

                                        /* Disable all GF channels, LED = ON*/
    GF_BoardRelayCtrl( GF_Board, GF_BOARD_LED_ON );

                                        /* Measure A/D offset voltage       */
    GF_BoardReadHalfscaleVoltage(GF_Board);

                                        /* Initialize default alarm levels  */
    GF_Board->tempWarning    = GF_TEMP_WARNING;
    GF_Board->tempAlarm      = GF_TEMP_ALARM;
    GF_Board->voltageWarning = GF_VOLTAGE_WARNING;
    GF_Board->voltageAlarm   = GF_VOLTAGE_ALARM;
    GF_Board->testStatus     = GF_TEST_NOT_RUN;

                                        /* Clear alarm status bits          */
    GF_Board->alarmStatus.tempAlarm    = (unsigned) MICRO_ALARM_OK;
    GF_Board->alarmStatus.voltageAlarm = (unsigned) MICRO_ALARM_OK;

    for (chan = 0; chan < GF_MAX_CHANS; chan++)
    {                                   /* Initialize current filter params */
        GF_Board->gfChan[chan].filteredCurrent = 0;
        initIIRFilter( &GF_Board->gfChan[chan].filter, GF_MAX_CURRENT);

                                        /* Initialize alarm status          */
        GF_Board->gfChan[chan].gfAlarmStatus = GF_NORMAL;
                                        /* Initalize default charge up time */
        GF_Board->gfChan[chan].chargeTime  = GF_CHARGE_TIME;
    } /* for */

    GF_Board->filteredTemp = 0;         /* Initialize temperature filter    */
    initIIRFilter(&GF_Board->tempFilter, GF_MAX_TEMP);

    GF_Board->filteredVicorVoltage = 0; /* Initialize vicor voltage filter  */
    initIIRFilter(&GF_Board->vicorVoltageFilter, GF_MAX_VOLTAGE);

    GF_Board->busCapacitance     = 0;
    GF_Board->capacitorVoltage   = 0;
    GF_Board->capacitorCurrent   = 0;
    GF_Board->filteredCapCurrent = 0;

    initIIRFilter(&GF_Board->capCurrentFilter, GF_FAULT_CURRENT_LIMIT);

    return (GF_Board);                  /* Return pointer to GF Board struct*/
} /* GF_BoardInit() */

/****************************************************************************/
/* Function    : GF_BoardRelayCtrl                                          */
/* Purpose     : Ground Fault Board - Write byte to relay control reg       */
/* Inputs      : Base address of Ground Fault Board & control byte          */
/* Outputs     : Returns result of IBC Bus Write - OK or ERROR if timeout   */
/****************************************************************************/
    MLocal Int16
GF_BoardRelayCtrl( GF_BoardEntry *GF_Board, Byte state )
{
    if ( GF_Board == NULL )             /* Check for valid GF data struct   */
         return (ERROR);

    GF_Board->relayState = state;       /* Save control byte in GF struct   */
                                        /* Write byte to switch control reg */
                                        /* Return result of ibcBusWriteByte */
    return( ibcBusWriteByte( GF_Board->IBC_Board.baseAddress
         + GF_SWITCH_CTRL_REG, GF_Board->relayState ));
} /* GF_BoardRelayCtrl() */

/****************************************************************************/
/* Function    : GF_BoardRelayControl                                      */
/* Purpose     : Controls state of Ground Fault Board channel relay        */
/* Inputs      : GF Board data structure, channel No & desired relay state */
/* Outputs     : Returns result of IBC Bus Write - OK or ERROR if timeout   */
/****************************************************************************/
    MLocal Int16
GF_BoardRelayControl( GF_BoardEntry *GF_Board, Byte chan, Boolean state)
{
    if ( GF_Board == NULL)              /* Check for valid GF data struct   */
         return (ERROR);

    if (state == ON)                    /* Desired switch state is On/Closed*/
                                        /* Set physical relay closed        */
       return (GF_BoardRelayCtrl(GF_Board,
               GF_Board->relayState | (1 << chan)));
    else                                /* Desired relay state is Off/Open  */
                                        /* Set physical relay open          */
       return (GF_BoardRelayCtrl(GF_Board,
               GF_Board->relayState & ~(1 << chan)));
} /* GF_BoardRelayControl() */

/****************************************************************************/
/* Function    : GF_BoardReadAtoD                                           */
/* Purpose     : Read 8 bit value from A to D for channel no specified      */
/* Inputs      : GF Board data structure and A to D channel number          */
/* Outputs     : Returns A to D convertor value or ERROR if timeout         */
/****************************************************************************/
    MLocal Int16
GF_BoardReadAtoD( GF_BoardEntry *GF_Board, Int16 channel )
{
    Nat16 ticks = GF_ATOD_TIMEOUT;      /* Conversion loop timeout          */

    if ( GF_Board == NULL )             /* Check for valid GF data struct   */
         return (ERROR);

                                        /* Start A/D conversion for channel */
    ibcBusWriteByte( GF_Board->IBC_Board.baseAddress + GF_AtoD_REG,
        (Byte) (GF_AtoD_SINGLE_ENDED | channel) );

                                        /* Wait for conversion to complete  */
    while(((PIC_intStatus() & (1 << GF_Board->IBC_Board.intrLevel)) == 0)
         && ( -- ticks) );

                                        /* Read A to D conversion result    */
    return (ibcBusReadByte( GF_Board->IBC_Board.baseAddress + GF_AtoD_REG ));
} /* GF_BoardReadAtoD() */

/****************************************************************************/
/* Function    : GF_BoardInterruptOn                                        */
/* Purpose     : Generate Interrupt from Ground Fault/5V Board              */
/* Inputs      : Base address of Ground Fault/5V Board                      */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
GF_BoardInterruptOn( GF_BoardEntry *GF_Board )
{
    Int16 delay;                        /* Used for short delay loop        */
                                        /* Start A/D conversion             */
    ibcBusWriteByte(GF_Board->IBC_Board.baseAddress + GF_AtoD_REG,
        (Byte) (GF_AtoD_SINGLE_ENDED | GF_AIR_TEMP_CHAN) );
                                        /* Short delay for end of conversion*/
    for (delay = 0; delay < 50; delay++);
} /* GF_BoardInterruptOn() */

/****************************************************************************/
/* Function    : GF_BoardInterruptOff                                       */
/* Purpose     : Clear Interrupt from Ground Fault/5V Board                 */
/* Inputs      : Base address of Ground Fault/5V Board                      */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
GF_BoardInterruptOff( GF_BoardEntry *GF_Board )
{                                       /* Read A to D result to clear intr */
    ibcBusReadByte( GF_Board->IBC_Board.baseAddress + GF_AtoD_REG);
} /* GF_BoardInterruptOff() */

/****************************************************************************/
/* Function    : GF_BoardReadHalfscaleVoltage                               */
/* Purpose     : Measure halfscale reference voltages                       */
/* Inputs      : Base address of Ground Fault/5V Board                      */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
GF_BoardReadHalfscaleVoltage( GF_BoardEntry *GF_Board )
{
   GF_Board->offsetVoltage =            /* Measure offset current           */
        GF_BoardReadAtoD( GF_Board, GF_CURRENT_CHAN );
} /* GF_BoardReadHalfscaleVoltage() */

/****************************************************************************/
/* Function    : GF_BoardReadCurrent                                        */
/* Purpose     : Measure Ground Fault current                               */
/* Inputs      : Base address of Ground Fault/5V Board                      */
/* Outputs     : Returns calibrated current measurement in uAmps            */
/****************************************************************************/
    MLocal Int16
GF_BoardReadCurrent( GF_BoardEntry *GF_Board )
{
    Int16 current;
                                        /* current = raw A/D - Offset Volts */
    current = GF_BoardReadAtoD(GF_Board, GF_CURRENT_CHAN)
            - GF_Board->offsetVoltage;

    return GF_MICROAMPS(current);
} /* GF_BoardReadCurrent() */

/****************************************************************************/
/* Function    : GF_BoardAbortScan                                          */
/* Purpose     : Aborts any scan, calibration or selftest in process        */
/* Inputs      : GF Board data structure                                    */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
GF_BoardAbortScan( GF_BoardEntry *GF_Board )
{
    Int16 chan;

    GF_Board->timerState = TIMER_INACTIVE;

    if (GF_Board->gfEnabled)            /* If monitoring is enabled         */
    {

        GF_Board->gfChannel = 0;        /* Set GF Channel to measure to 0   */
        GF_Board->gfChanUpdate = 0;     /* Set GF channel update bits to 0  */
        GF_Board->gfChanState = GF_IDLE;/* Measurement state machine = IDLE */

                                        /* Open all relays, switch LED = ON */
        GF_BoardRelayCtrl( GF_Board, GF_BOARD_LED_ON );

                                        /* Initialize current filter params */
        for (chan = 0; chan < GF_MAX_CHANS; chan++)
            GF_Board->gfChan[chan].filteredCurrent = 0;
    } /* if */
} /* GF_BoardAbortScan() */

/****************************************************************************/
/* Function    : GF_BoardSelftest                                           */
/* Purpose     : Perform Ground Fault circuit selftest sequence             */
/* Inputs      : Base address of Ground Fault/5V Board                      */
/* Outputs     : Returns OK or ERROR if selftest failed                     */
/****************************************************************************/
    MLocal Void
GF_BoardSelftest( GF_BoardEntry *GF_Board )
{
    switch ( GF_Board->gfChanState )
    {
        default:                        /* Abort scan in progress and       */
                                        /* Open all relays                  */
            GF_BoardAbortScan( GF_Board );
            GF_Board->gfChanState = GF_SELFTEST_RELAY_OPEN;
                                        /* Timer to wait for relays to open */

            GF_Board->timerTicks =
                ((Int16) read_sysclock()) + GF_CALIBRATE_DELAY;
            GF_Board->timerState = TIMER_ACTIVE;
            break;

        case GF_SELFTEST_RELAY_OPEN:
            break;

        case GF_SELFTEST_READ_HALFSCALE:
                                        /* Read halfscale voltage           */
            GF_BoardReadHalfscaleVoltage( GF_Board );
                                        /* Halfscale reading error exceeds  */
                                        /* expected threshold               */
            if ( abs( (Int16) (GF_Board->offsetVoltage - GF_QUIESCENT))
                > GF_ATOD_ERROR)
            {
                GF_Board->testStatus = GF_ATOD_FAILED;
                GF_Board->gfChanState = GF_SELFTEST_COMPLETE;
                break;
            } /* if */
                                        /* Close channel 0 relay            */
            GF_BoardRelayControl( GF_Board, GF_CHAN0_RELAY, GF_RELAY_CLOSE);
                                        /* Close selftest relay             */
            GF_BoardRelayControl( GF_Board, GF_SELFTEST_RELAY, GF_RELAY_CLOSE);
            GF_Board->gfChanState = GF_SELFTEST_RELAY_CLOSE;

                                        /* Wait for circuit to charge up    */
            GF_Board->timerTicks =
                 ((Int16) read_sysclock()) + GF_SELFTEST_DELAY;
            GF_Board->timerState = TIMER_ACTIVE;
            break;

        case GF_SELFTEST_RELAY_CLOSE:
            break;

        case GF_SELFTEST_READ_CURRENT:  /* Measure GF fault current         */
            if ( GF_BoardReadCurrent(GF_Board) < GF_SELFTEST_CURRENT)
                GF_Board->testStatus = GF_SELFTEST_FAILED;
            else
                GF_Board->testStatus = GF_SELFTEST_PASSED;
            GF_Board->gfChanState = GF_SELFTEST_COMPLETE;
            break;

        case GF_SELFTEST_COMPLETE:      /* Open all relays & switch LED On  */
            GF_BoardRelayCtrl( GF_Board, GF_BOARD_LED_ON );
            GF_Board->gfSelftest = FALSE;
            GF_Board->gfChanState = GF_IDLE;

                                        /* Report result of selftest to host*/
            writeServiceRequest(3, GF_SELFTEST_STATUS_SRQ,
                GF_Board->testStatus, GF_Board->IBC_Board.baseAddress);
            break;

    } /* switch */
} /* GF_BoardSelftest() */

/****************************************************************************/
/* Function    : GF_FaultMonitorCtrlCmd                                     */
/* Purpose     : Process command to enable/disabled GF fault monitoring     */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
GF_FaultMonitorCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
    Byte *commandBuf )
{
    Byte    *boardAddr;         /* GF Board base address from command buf   */
    Nat16   card;               /* Card index into Card Table               */
    Word    command;            /* Serial command - discarded               */
    GF_BoardEntry *GF_Board;    /* Pointer to ground fault board entry      */

    Boolean gfEnable;           /* Enable/disable status from serial port   */
    Int16 chan;                 /* Ground Fault channel index               */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 3, &command, &boardAddr, &gfEnable);

                                /* Find card table index for this address   */
    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
                 boardAddr, GROUND_FAULT) ) == ERROR)
    {                           /* Card not present so return error         */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */
                                /* Get pointer to GF board data structure   */
    GF_Board = (GF_BoardEntry *) IBC_CardTable[card];

    if (!gfEnable)              /* If GF Monitoring is disabled, abort scan */
    {
        GF_BoardAbortScan(GF_Board);    /* Stop any scan in progress and    */
        GF_Board->gfSelftest = FALSE;   /* Stop self test, if in progress   */
    } /* if */
                                /* Store new enable state in GF data struct */
    GF_Board->gfEnabled = (gfEnable ? TRUE : FALSE);

    replyToCommand(1, CMD_OK);  /* Report OK to serial port                 */
} /* GF_FaultMonitorCmd() */

/****************************************************************************/
/* Function    : GF_SelftestCmd                                             */
/* Purpose     : Process command to execute board GF self test              */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
GF_SelftestCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
    Byte *commandBuf )
{
    Byte    *boardAddr;         /* GF Board base address from command buf   */
    Nat16   card;               /* Card index into Card Table               */
    Word    command;            /* Serial command - discarded               */
    GF_BoardEntry *GF_Board;    /* Pointer to ground fault board entry      */

                                /* Extract board address from buffer        */
    boardAddr = (Byte*) wordFromBuf(commandBuf, sizeof(Word));

                                /* Find card table index for this address   */
    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
                 boardAddr, GROUND_FAULT) ) == ERROR)
    {                           /* Card not present so return error         */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */
                                /* Get pointer to GF board data structure   */
    GF_Board = (GF_BoardEntry *) IBC_CardTable[card];
    if (GF_Board->gfEnabled)    /* Set flag to run selftest                 */
        GF_Board->gfSelftest = TRUE;

    replyToCommand(1, CMD_OK);  /* Report OK to serial port                 */
} /* GF_SelftestCmd() */

/****************************************************************************/
/* Function    : GF_RestartChanChargeCmd                                    */
/* Purpose     : Process command to restart channel charging                */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
GF_RestartChanChargeCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
    Byte *commandBuf )
{
    Byte    *boardAddr;         /* GF Board base address from command buf   */
    Nat16   card;               /* Card index into Card Table               */
    Word    command;            /* Serial command - discarded               */
    GF_BoardEntry *GF_Board;    /* Pointer to ground fault board entry      */

                                /* Extract board address from buffer        */
    boardAddr = (Byte*) wordFromBuf(commandBuf, sizeof(Word));

                                /* Find card table index for this address   */
    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
                 boardAddr, GROUND_FAULT) ) == ERROR)
    {                           /* Card not present so return error         */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */
                                /* Get pointer to GF board data structure   */
    GF_Board = (GF_BoardEntry *) IBC_CardTable[card];

                                /* Verify that GF monitoring is enabled and */
                                /* that a measurement is being made for the */
                                /* channel specified                        */
    if ( GF_Board->gfEnabled &&
        (GF_Board->gfChanState >= GF_CHARGING) &&
        (GF_Board->gfChanState < GF_COMPLETE) )
    {
                                /* Restart charge up timer                  */
        GF_Board->timerTicks = ((Int16) read_sysclock()) +
            GF_Board->gfChan[GF_Board->gfChannel].chargeTime;
        GF_Board->timerState = TIMER_ACTIVE;

                                /* Set state machine state accordingly      */
        GF_Board->gfChanState = GF_CHARGING;
    } /* if */

    replyToCommand(1, CMD_OK);  /* Report OK to serial port                 */
} /* GF_RestartChanChargeCmd() */

/****************************************************************************/
/* Function    : GF_SetChargeTimesCmd                                       */
/* Purpose     : Process command to set GF channel charge times             */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
GF_SetChargeTimesCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
    Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Word    command;            /* Serial command - discarded               */
    Byte    *boardAddr;         /* GF Board base address from command buf   */
    GF_BoardEntry *GF_Board;    /* Pointer to ground fault board entry      */

    Int16   chargeTimes[GF_MAX_CHANS];  /* Channel charge up times in .1 sec*/
    Int16   chan;               /* channel index counter                    */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 6, &command, &boardAddr, &chargeTimes[0],
         &chargeTimes[1], &chargeTimes[2], &chargeTimes[3]);

                                /* Find card table index for this address   */
    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
                 boardAddr, GROUND_FAULT) ) == ERROR)
    {                           /* Card not present so return error         */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */
                                /* Get pointer to GF board data structure   */
    GF_Board = (GF_BoardEntry *) IBC_CardTable[card];

                                /* Store new charge time in GF data struct  */
    for (chan = 0; chan < GF_MAX_CHANS; chan++)
        GF_Board->gfChan[chan].chargeTime =  chargeTimes[chan];

    replyToCommand(1, CMD_OK);  /* Report OK to serial port                 */
} /* GF_SetChargeTimesCmd() */

/****************************************************************************/
/* Function    : GF_GetChargeTimesCmd                                       */
/* Purpose     : Process command to read GF channel charge times            */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
GF_GetChargeTimesCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
    Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Byte    *boardAddr;         /* GF Board base address from command buf   */
    GF_BoardEntry *GF_Board;    /* Pointer to ground fault board entry      */

                                /* Extract command and arguments from buffer*/
    boardAddr = (Byte*) wordFromBuf(commandBuf, sizeof(Word));

                                /* Find card table index for this address   */
    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
                 boardAddr, GROUND_FAULT) ) == ERROR)
    {                           /* Card not present so return error         */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */
                                /* Get pointer to GF board data structure   */
    GF_Board = (GF_BoardEntry *) IBC_CardTable[card];

    replyToCommand(5, CMD_OK,   /* Send charge times to serial port         */
       (Word) GF_Board->gfChan[0].chargeTime,
       (Word) GF_Board->gfChan[1].chargeTime,
       (Word) GF_Board->gfChan[2].chargeTime,
       (Word) GF_Board->gfChan[3].chargeTime);
} /* GF_GetChargeTimesCmd() */

/****************************************************************************/
/* Function    : GF_GetTemperatureCmd                                       */
/* Purpose     : Process command to read filtered temperature measurement   */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
GF_GetTemperatureCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
    Byte *commandBuf )
{
    Byte    *boardAddr;         /* GF Board base address from command buf  i */
    Nat16   card;               /* Card index into Card Table               */

                                /* Extract command and arguments from buffer*/
    boardAddr = (Byte*) wordFromBuf(commandBuf, sizeof(Word));

                                /* Find card table index for this address   */
    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
                 boardAddr, GROUND_FAULT) ) == ERROR)
    {                           /* Card not present so return error         */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */

    replyToCommand(2, CMD_OK,   /* Send filtered temperature to serial port */
         (Word) (((GF_BoardEntry *) IBC_CardTable[card])->filteredTemp));
} /* GF_GetTemperatureCmd() */

/****************************************************************************/
/* Function    : GF_SetTempThreshCmd                                        */
/* Purpose     : Process command to set temperature warning & alarm points  */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
GF_SetTempThreshCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
    Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Word    command;            /* Serial command - discarded               */
    Byte    *boardAddr;         /* GF Board base address from command buf   */
    GF_BoardEntry *GF_Board;    /* Pointer to ground fault board entry      */

    Int16   tempWarn, tempAlarm;/* Temperature thresholds                   */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 4, &command, &boardAddr, &tempWarn, &tempAlarm);

                                /* Find card table index for this address   */
    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
                 boardAddr, GROUND_FAULT) ) == ERROR)
    {                           /* Card not present so return error         */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */
                                /* Get pointer to GF board data structure   */
    GF_Board = (GF_BoardEntry *) IBC_CardTable[card];

                                /* Store new thresholds in GF data struct   */
    GF_Board->tempWarning = tempWarn;
    GF_Board->tempAlarm = tempAlarm;

    replyToCommand(1, CMD_OK);  /* Report OK to serial port                 */
} /* GF_SetTempThreshCmd() */

/****************************************************************************/
/* Function    : GF_GetTempThreshCmd                                        */
/* Purpose     : Process command to read temperature warning & alarm points */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
GF_GetTempThreshCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
    Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Byte    *boardAddr;         /* GF Board base address from command buf   */
    GF_BoardEntry *GF_Board;    /* Pointer to ground fault board entry      */

                                /* Extract command and arguments from buffer*/
    boardAddr = (Byte*) wordFromBuf(commandBuf, sizeof(Word));

                                /* Find card table index for this address   */
    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
                 boardAddr, GROUND_FAULT) ) == ERROR)
    {                           /* Card not present so return error         */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */
                                /* Get pointer to GF board data structure   */
    GF_Board = (GF_BoardEntry *) IBC_CardTable[card];

    replyToCommand(3, CMD_OK,   /* Send threshold values to serial port     */
         (Word) GF_Board->tempWarning, (Word) GF_Board->tempAlarm );
} /* GF_GetTempThreshCmd() */

/****************************************************************************/
/* Function    : GF_SetVoltageThreshCmd                                     */
/* Purpose     : Process command to set voltage warning & alarm points      */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
GF_SetVoltageThreshCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
    Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Word    command;            /* Serial command - discarded               */
    Byte    *boardAddr;         /* GF Board base address from command buf   */
    GF_BoardEntry *GF_Board;    /* Pointer to ground fault board entry      */
    Int16   voltageWarn, voltageAlarm;   /* Voltage thresholds              */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 4, &command, &boardAddr,
         &voltageWarn, &voltageAlarm);

                                /* Find card table index for this address   */
    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
                 boardAddr, GROUND_FAULT) ) == ERROR)
    {                           /* Card not present so return error         */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */
                                /* Get pointer to GF board data structure   */
    GF_Board = (GF_BoardEntry *) IBC_CardTable[card];

                                /* Store new thresholds in GF data struct   */
    GF_Board->voltageWarning = voltageWarn;
    GF_Board->voltageAlarm = voltageAlarm;

    replyToCommand(1, CMD_OK);  /* Report OK to serial port                 */
} /* GF_SetVoltageThreshCmd() */

/****************************************************************************/
/* Function    : GF_GetVoltageThreshCmd                                     */
/* Purpose     : Process command to read voltage warning & alarm points     */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
GF_GetVoltageThreshCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
    Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Byte    *boardAddr;         /* GF Board base address from command buf   */
    GF_BoardEntry *GF_Board;    /* Pointer to ground fault board entry      */

                                /* Extract command and arguments from buffer*/
    boardAddr = (Byte*) wordFromBuf(commandBuf, sizeof(Word));

                                /* Find card table index for this address   */
    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
                 boardAddr, GROUND_FAULT) ) == ERROR)
    {                           /* Card not present so return error         */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */
                                /* Get pointer to GF board data structure  */
    GF_Board = (GF_BoardEntry *) IBC_CardTable[card];

    replyToCommand(3, CMD_OK,   /* Send threshold values to serial port    */
         (Word) GF_Board->voltageWarning, (Word) GF_Board->voltageAlarm);
} /* GF_GetVoltageThreshCmd() */

/****************************************************************************/
/* Function    : GF_GetCurrentsCmd                                          */
/* Purpose     : Process command to read filtered GF currents               */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
GF_GetCurrentsCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
    Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Byte    *boardAddr;         /* GF Board base address from command buf   */
    GF_BoardEntry *GF_Board;    /* Pointer to ground fault board entry      */
    Nat16   offsetCurrent;      /* A/D and offset voltage changed to current*/

                                /* Extract command and arguments from buffer*/
    boardAddr = (Byte*) wordFromBuf(commandBuf, sizeof(Word));

                                /* Find card table index for this address   */
    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
                 boardAddr, GROUND_FAULT) ) == ERROR)
    {                           /* Card not present so return error         */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */
                                /* Get pointer to GF board data structure   */
    GF_Board = (GF_BoardEntry *) IBC_CardTable[card];

    offsetCurrent = (Nat16)
            GF_MICROAMPS( (Int16) (GF_Board->offsetVoltage - GF_QUIESCENT));

                                /* Put filtered current reading into buffer */
    replyToCommand(9, CMD_OK,
       (Word) GF_Board->gfChan[0].filteredCurrent,
       (Word) GF_Board->gfChan[1].filteredCurrent,
       (Word) GF_Board->gfChan[2].filteredCurrent,
       (Word) GF_Board->gfChan[3].filteredCurrent,
       (Word) GF_Board->gfChanUpdate,
       (Word) offsetCurrent,
       (Word) GF_Board->filteredVicorVoltage,
       (Word) GF_Board->capacitorCurrent);

    GF_Board->gfChanUpdate = 0; /* Clear channel update flag                */
} /* GF_GetCurrentsCmd() */

/****************************************************************************/
/* Function    : GF_GetStatusCmd                                            */
/* Purpose     : Process command to report GF board status                  */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
GF_GetStatusCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
    Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Byte    *boardAddr;         /* GF Board base address from command buf   */
    GF_BoardEntry *GF_Board;    /* Pointer to ground fault board entry      */

                                /* Extract command and arguments from buffer*/
    boardAddr = (Byte*) wordFromBuf(commandBuf, sizeof(Word));

                                /* Find card table index for this address   */
    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
                 boardAddr, GROUND_FAULT) ) == ERROR)
    {                           /* Card not present so return error         */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */

                                /* Get pointer to GF board data structure   */
    GF_Board = (GF_BoardEntry *) IBC_CardTable[card];

                                /* Send GF status words to serial port      */
    replyToCommand(8, CMD_OK,
        GF_Board->gfChan[0].gfAlarmStatus,
        GF_Board->gfChan[1].gfAlarmStatus,
        GF_Board->gfChan[2].gfAlarmStatus,
        GF_Board->gfChan[3].gfAlarmStatus,

        GF_Board->alarmStatus.tempAlarm,
        GF_Board->alarmStatus.voltageAlarm,
        GF_Board->testStatus );
} /* GF_GetStatusCmd() */

/****************************************************************************/
/* Function    : GF_SetBusCapacitanceCmd                                    */
/* Purpose     : Process command to set bus capacitance for predictive model*/
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
GF_SetBusCapacitanceCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
    Byte *commandBuf )
{
    Byte    *boardAddr;         /* GF Board base address from command buf   */
    Nat16   card;               /* Card index into Card Table               */
    Word    command;            /* Serial command - discarded               */
    Nat16   busCapacitance;     /* Bus capacitance in micro farads          */

    GF_BoardEntry *GF_Board;    /* Pointer to GF/5V board entry             */
    regenBoardEntry *regenBoard;/* Pointer to regen board entry             */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 3, &command, &boardAddr, &busCapacitance);

                                /* Find card table index for this address   */
    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
                 boardAddr, GROUND_FAULT) ) == ERROR)
    {                           /* Card not present so return error         */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */
                                /* Get pointer to regen board data structure*/
    GF_Board = (GF_BoardEntry *) IBC_CardTable[card];

                                /* Store new values in regen data struct    */
    GF_Board->busCapacitance = busCapacitance;

                                /* Find card table index for this address   */
    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
                 (Byte *) REGEN_CONTROL_0_ADDR, REGEN_CONTROL) ) == ERROR)
    {                           /* Card not present so return error         */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */
                                /* Get pointer to regen board data structure*/
    regenBoard = (regenBoardEntry *) IBC_CardTable[card];

                                /* Hook in GF bus capacitance model function*/
                                /* to be called on each bus voltage sample  */
    regenBoardBusVoltageReadHookAdd(regenBoard,
        GF_BoardBusCapacitanceModel, (Int16) GF_Board);

    replyToCommand(1, CMD_OK);  /* Report OK to serial port                 */
} /* regenBusCapacitanceCmd() */

/****************************************************************************/
/* Function    : GF_CheckVoltage                                            */
/* Purpose     : Compare vicor voltage to alarm thresholds                  */
/* Inputs      : GF Board data structure                                    */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
GF_CheckVoltage( GF_BoardEntry *GF_Board )
{
    microAlarm alarmStatus;             /* Voltage alarm status             */

                                        /* Extract current alarm status     */
    alarmStatus = (microAlarm) GF_Board->alarmStatus.voltageAlarm;

                                        /* Check if current has exceeded the*/
                                        /* current WARNING/ALARM threshold  */
                                        /* & send alarm SRQ's if necessary  */
    if (IBC_BelowThresholdCheck( GF_Board->filteredVicorVoltage,
        GF_Board->voltageWarning, GF_Board->voltageAlarm,
        GF_VOLTAGE_HYSTERESIS, &alarmStatus ) == TRUE)
    {
        writeServiceRequest(3, GF_VOLTAGE_ALARM_SRQ, alarmStatus,
           GF_Board->IBC_Board.baseAddress);
    } /* if */

                                        /* Update voltage alarm bit field */
    GF_Board->alarmStatus.voltageAlarm = (unsigned) alarmStatus;
} /* GF_CheckVoltage() */

/****************************************************************************/
/* Function    : GF_CheckTemperature                                        */
/* Purpose     : Compare temperature to alarm thresholds                    */
/* Inputs      : GF Board data structure                                    */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
GF_CheckTemperature( GF_BoardEntry *GF_Board )
{
    microAlarm alarmStatus;             /* Temperature alarm status         */
                                        /* Extract temperature WARN status  */
    alarmStatus = (microAlarm) GF_Board->alarmStatus.tempAlarm;

                                        /* Check if temperature has exceeded*/
                                        /* temperature WARN/ALARM thresholds*/
                                        /* and send alarm SRQ's if necessary*/
    if (IBC_AlarmThresholdCheck( GF_Board->filteredTemp,
            GF_Board->tempWarning, GF_Board->tempAlarm,
            GF_TEMP_HYSTERESIS, &alarmStatus ) == TRUE)

        writeServiceRequest(3, GF_TEMP_ALARM_SRQ, alarmStatus,
           GF_Board->IBC_Board.baseAddress);

                                        /* Update temperature alarm status  */
    GF_Board->alarmStatus.tempAlarm = (unsigned) alarmStatus;
} /* GF_CheckTemperature() */

/****************************************************************************/
/* Function    : thresholdTest                                              */
/* Purpose     : Compare value to a threshold with hysteresis               */
/* Inputs      : Value, threshold, hysteresis, pointer to previous state    */
/* Outputs     : State is changed and also returned by the function         */
/****************************************************************************/
    MLocal Boolean
thresholdTest(Int16 value, Int16 threshold, Int16 hysteresis, Boolean *status)
{
    if (value >= threshold)         /* If value >= threshold then alarm     */
    {
        if (*status == FALSE)       /* If status has changed                */
            *status = TRUE;         /* then return TRUE                     */
    } /* if */
    else
    {                               /* If status was true and has changed   */
        if ( (*status == TRUE) && (value < threshold - hysteresis) )
            *status = FALSE;        /* then report FALSE                    */
    } /* else */

    return(*status);                /* return status                        */
} /* thresholdTest() */

/****************************************************************************/
/* Function    : GF_CheckCurrent                                            */
/* Purpose     : Compare GF current to alarm thresholds                     */
/* Inputs      : GF Board data structure                                    */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
GF_CheckCurrent( GF_BoardEntry *GF_Board, Byte channel )
{
    gfStatus gfAlarmStatus;             /* GF Current alarm status          */
    gfStatus newGfStatus;               /* New GF Current alarm status      */
    Boolean status;                     /* Local status for threshold tests */
    Int16   current;
    Int16   capCurrent = 0;

    if (GF_Board->busCapacitance)
    {
        capCurrent = abs(GF_Board->filteredCapCurrent);
        capCurrent += (capCurrent >> 2); /* Scale current by 1.25 (fudge    */
                                         /* factor)                         */
    } /* if */
                                        /* If the measure fault current     */
                                        /* exceeds the predicted capacitor  */
                                        /* current, then check for an alarm */
                                        /* Use absolute val for alarm check */
    current = abs(GF_Board->gfChan[channel].filteredCurrent) - capCurrent;
    if (current < 0) 			 
	current = 0;
                                        /* Extract GF current alarm status  */
    gfAlarmStatus = GF_Board->gfChan[channel].gfAlarmStatus;

    newGfStatus = GF_NORMAL;            /* Set new GF fault status NORMAL   */

                                        /* Check if GF current is critical  */
    status = ((gfAlarmStatus == GF_CRITICAL) ? TRUE : FALSE);

    if (thresholdTest(current,
        GF_CRITICAL_LEVEL, GF_CRITICAL_HYSTERESIS, &status) == TRUE)
            newGfStatus = GF_CRITICAL;
    else
    {                                   /* Check if GF current is warning   */
        status = ((gfAlarmStatus == GF_WARNING) ? TRUE : FALSE);

        if (thresholdTest(current,
            GF_WARNING_LEVEL, GF_WARNING_HYSTERESIS, &status) == TRUE)
                newGfStatus = GF_WARNING;
        else
        {                               /* Check if GF current is advisory  */
            status = ((gfAlarmStatus == GF_ADVISORY) ? TRUE : FALSE);

            if (thresholdTest(current,
                GF_ADVISORY_LEVEL, GF_ADVISORY_HYSTERESIS, &status) == TRUE)
                    newGfStatus = GF_ADVISORY;

        } /* else */
    } /* else */

    if (gfAlarmStatus != newGfStatus)   /* If GF fault status has changed   */
    {                                   /* then save new status and         */
        GF_Board->gfChan[channel].gfAlarmStatus = newGfStatus;

                                        /* send SRQ packet to serial port   */
        writeServiceRequest(4, GF_CURRENT_ALARM_SRQ, newGfStatus,
                GF_Board->IBC_Board.baseAddress, channel);
    } /* if */
                                        /* Update GF current ALARM status   */
} /* GF_CheckCurrent() */

    MLocal Void
GF_BoardBusCapacitanceModel( GF_BoardEntry *GF_Board, Nat32 busVoltage )
{
    Int32 resistorVoltage;
    Int32 capCurrent;

    if (GF_Board->gfChannel > 1)
        busVoltage = 0;

    resistorVoltage = (busVoltage - GF_Board->capacitorVoltage);
                                      /* Calculate current in microamps */
    capCurrent = (resistorVoltage * 1000) / GF_MODEL_RESISTANCE;
    if (abs(capCurrent) > GF_FAULT_CURRENT_LIMIT)
        capCurrent = GF_FAULT_CURRENT_LIMIT * sign(capCurrent);

                                     /* cap volts is in millivolts     */
    GF_Board->capacitorVoltage +=
         (10 * capCurrent * REGEN_SAMPLE_TIME) /
                  GF_Board->busCapacitance;

    GF_Board->capacitorCurrent = (Int16) capCurrent;
} /* GF_BoardBusCapacitanceModel() */

/****************************************************************************/
/* Function    : GF_BoardMeasureGfCurrent                                   */
/* Purpose     : Executer FSM based algorithm to measure GF fault current   */
/* Inputs      : GF Board data structure                                    */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
GF_BoardMeasureGfCurrent( GF_BoardEntry *GF_Board )
{
    Int16 gfChannel;                    /* Ground Fault Channel number      */
    Int16 faultCurrent;


    if (GF_Board->gfEnabled != TRUE)    /* If GF monitoring is disabled     */
        return;                         /* then bypass GF Fault Monitoring  */

    if ((GF_Board->timerState == TIMER_ACTIVE) &&
        ( ((Int16) read_sysclock()) >= GF_Board->timerTicks))
    {
        GF_Board->timerState = TIMER_EXPIRED;
        GF_Board->gfChanState++;
    } /* if */

    if (GF_Board->gfSelftest)           /* See if selftest mode is enabled  */
    {
        GF_BoardSelftest( GF_Board );
        return;
    } /* if */

    gfChannel = GF_Board->gfChannel;    /* Get GF channel number            */

                                        /* Check Measurement State Machine  */
    if ( (GF_Board->gfChanState >= GF_CHARGED) &&
         (GF_Board->gfChanState <  GF_COMPLETE) )
    {
                                        /* Read raw fault current           */
        faultCurrent = GF_BoardReadCurrent( GF_Board );

                                        /* Apply filtering to model current */
        IIRfilter(GF_Board->capacitorCurrent, &GF_Board->filteredCapCurrent,
            &GF_Board->capCurrentFilter);

                                        /* Apply filtering to fault current */
        if (IIRfilter( faultCurrent,
            &GF_Board->gfChan[gfChannel].filteredCurrent,
            &GF_Board->gfChan[gfChannel].filter) == OK)
        {                               /* Bump state machine to next state */
            if (++GF_Board->gfChanState == GF_COMPLETE)
            {                           /* Open this GF Channel relay       */
                GF_BoardRelayControl(GF_Board, gfChannel, GF_RELAY_OPEN);
                                        /* Check current warning & alarm    */
                GF_CheckCurrent( GF_Board, gfChannel);

                                        /* Set GF channel update bit TRUE   */
                GF_Board->gfChanUpdate |= (1 << gfChannel);
            } /* if */
        } /* if */
    } /* if */

    if (GF_Board->gfChanState == GF_COMPLETE)
    {                                   /* Increment GF Channel Number      */
        if (++gfChannel >= GF_MAX_CHANS)
        {
            gfChannel = 0;              /* Set GF Channel number to zero    */
                                        /* Set State Machine to CAL_START   */
            GF_Board->gfChanState = GF_CAL_START;
                                        /* Open all relays, switch LED = ON */
                                        /* For Offset Current calibration   */
            GF_BoardRelayCtrl( GF_Board, GF_BOARD_LED_ON );

                                        /* Start Relay opening timer        */
            GF_Board->timerTicks = ((Int16) read_sysclock())
                + GF_CALIBRATE_DELAY;
            GF_Board->timerState = TIMER_ACTIVE;
        } /* if */
        else                            /* Set State Machine to IDLE        */
            GF_Board->gfChanState = GF_IDLE;
    } /* if */
                                        /* Check Measurement State Machine  */
    if (GF_Board->gfChanState == GF_IDLE)
    {                                   /* If charge time is zero then skip */
                                        /* to next channel                  */
        if (GF_Board->gfChan[gfChannel].chargeTime == 0)
        {
            if (++gfChannel >= GF_MAX_CHANS)
                gfChannel = 0;          /* Set GF Channel number to zero    */
        } /* if */

        else
        {                               /* Set State Machine to CHARGING    */
            GF_Board->gfChanState = GF_CHARGING;
                                        /* Close GF Channel relay           */
            GF_BoardRelayControl( GF_Board, gfChannel, GF_RELAY_CLOSE);

                                        /* Start Charge up timer            */
            GF_Board->timerTicks = ((Int16) read_sysclock()) +
                GF_Board->gfChan[gfChannel].chargeTime,
            GF_Board->timerState = TIMER_ACTIVE;
        } /* if */
    } /* if */

    GF_Board->gfChannel = gfChannel;    /* Save new GF channel number       */

                                        /* Check Measurement State Machine  */
    if (GF_Board->gfChanState == GF_CAL_READ)
    {                                   /* Read and filter halfscale voltage*/
        GF_BoardReadHalfscaleVoltage( GF_Board );
        GF_Board->gfChanState = GF_IDLE;/* Set State Machine to IDLE        */
    } /* if */
} /* GF_BoardMeasureGfCurrent() */

/****************************************************************************/
/* Function    : GF_BoardFunctions                                          */
/* Purpose     : Ground Fault/5V real-time loop functions                   */
/* Inputs      : GF Board data structure                                    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
GF_BoardFunctions( GF_BoardEntry *GF_Board )
{

    if (IIRfilter( GF_DEGREES(          /* Read & Filter Temperature Sensor */
        GF_BoardReadAtoD( GF_Board, GF_AIR_TEMP_CHAN )),
        &GF_Board->filteredTemp, &GF_Board->tempFilter) == OK)

        GF_CheckTemperature( GF_Board );/* Check temperature warning & alarm*/

    if (IIRfilter( GF_MILLIVOLTS(       /* Read & Filter Vicor Voltage      */
        GF_BoardReadAtoD( GF_Board, GF_VOLTAGE_CHAN )),
        &GF_Board->filteredVicorVoltage, &GF_Board->vicorVoltageFilter) == OK)

        GF_CheckVoltage( GF_Board );    /* Check vicor voltage alarms       */

    GF_BoardMeasureGfCurrent( GF_Board );
} /* GF_BoardFunctions() */


