/****************************************************************************/
/* Copyright 1992 to 1997 MBARI                                             */
/****************************************************************************/
/* Summary  : IBC Regeneration Control Board Functions                      */
/* Filename : regen.c                                                       */
/* Author   : Andrew Pearce                                                 */
/* Project  : Tiburon Data Concentrator                                     */
/* Version  : 1.0                                                           */
/* Created  : 09/17/92                                                      */
/* Modified : 04/23/97                                                      */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/* $Header: /usr/tiburon/.cvsroot/micro/ibc/boards/regen/regen.c,v 1.2 1997/05/07 15:33:47 pean Exp $
 * $Log: regen.c,v $
 * Revision 1.2  1997/05/07 15:33:47  pean
 * Cleaned up Include files and makefile for new directory structure.
 *
 * Revision 1.1.1.1  1997/05/02 18:51:31  pean
 * Initial check in of IBC microcontroller software
 *
 * Revision 1.2  93/07/02  09:40:51  09:40:51  pean (Andrew Pearce)
 * Minor bug fixes and enhancements for Prototype Vehicle Tests
 *
 * Revision 1.1  93/03/31  17:37:26  17:37:26  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 "malloc.h"                     /* Malloc support routines          */
#include "microdef.h"                   /* Microcontroller definitions      */
#include "microCmd.h"                   /* microcontroller commands         */
#include "applay.h"                     /* Application layer funcitions     */
#include "microlib.h"                   /* Microcontroller Library decls.   */
#include "timer.h"                      /* Timer library definitions        */

#include "filter.h"                     /* General Filter Routines          */
#include "ibc_card.h"                   /* IBC Board Types and Addresses    */
#include "ibc_cmd.h"                    /* Core IBC application definitions */
#include "ibc.h"                        /* IBC Function Library Definitions */
#include "ibc_asm.h"                    /* IBC Assembly Language Functions  */

#include "regen.h"                      /* IBC Regeneration Board Defs      */

                                        /* Forward declarations for MLocals */
MLocal Void regenBoardInitMinMax( regenBoardEntry *regenBoard );
MLocal Void regenBoardReadBusVoltage( regenBoardEntry *regenBoard );

/****************************************************************************/
/* Function    : regenBoardSearch                                           */
/* Purpose     : Scans IBC backplane looking for Regeneration Boards        */
/* Inputs      : IBC Board Entry Table and maximum table size               */
/* Outputs     : Returns number of boards found                             */
/****************************************************************************/
    Int16
regenBoardSearch( IBC_BoardEntry *IBC_Boards[], Int16 maxCards )
{
    Byte  *addr;                        /* Regen Board base address         */
    Int16 board;                        /* Counts number regen board founds */
    IBC_BoardEntry *boardEntry;         /* Ptr to regen board data structure*/

    board = 0;                          /* Set board count to zero          */
    if (maxCards > 0)                   /* Check for space in IBC Card Table*/
    {
                                        /* Search for Regen Control Boards  */
        for (addr =  (Byte *) REGEN_CONTROL_0_ADDR;
             addr <= (Byte *) REGEN_CONTROL_1_ADDR;
             addr+= (REGEN_CONTROL_1_ADDR - REGEN_CONTROL_0_ADDR) )
        {                               /* NULL boardEntry if board missing */
            if ((boardEntry = (IBC_BoardEntry *) regenBoardInit(addr)) != NULL)
            {                           /* Add regen data struct to IBC Table*/
                IBC_Boards[board++] = boardEntry;
                if (board == maxCards)  /* Check if IBC Card Table is full  */
                    return(board);      /* Return number regen boards found */
            } /* if */
        } /* for */
    } /* if */

    return(board);                      /* Return number regen boards found*/
} /* regenBoardSearch() */

/****************************************************************************/
/* Function    : regenBoardInit                                             */
/* Purpose     : Regeneration Board Initialization routine                  */
/* Inputs      : Base address of regeneration board                         */
/* Outputs     : Returns pointer to IBC board data structure                */
/****************************************************************************/
    regenBoardEntry*
regenBoardInit( Byte *boardBaseAddr )
{
    regenBoardEntry *regenBoard;        /* Pointer to board entry           */

                                        /* Check if board exists            */
                                        /* Returns OK if board asserts xack */
    if ( ibcBusReadByte( boardBaseAddr + REGEN_AtoD_REG) == ERROR )
        return(( regenBoardEntry *) NULL);
                                        /* Board doesn't exist return NULL  */

                                        /* Allocate space for board entry   */
    regenBoard = (regenBoardEntry *) malloc( sizeof (regenBoardEntry) );
    if (regenBoard == (regenBoardEntry *) NULL )
            return( (regenBoardEntry *) NULL);
                                        /* Insufficient memory return ERROR */

                                        /* Fill out board entry structure   */
    regenBoard->IBC_Board.baseAddress = boardBaseAddr;
    regenBoard->IBC_Board.boardType   = REGEN_CONTROL;
    regenBoard->IBC_Board.intrLevel   = IBC_INT_LVL_OFF;

    regenBoard->voltageMinWarning = REGEN_MIN_VOLTAGE_WARN;
    regenBoard->voltageMinAlarm   = REGEN_MIN_VOLTAGE_ALARM;
    regenBoard->voltageMaxWarning = REGEN_MAX_VOLTAGE_WARN;
    regenBoard->voltageMaxAlarm   = REGEN_MAX_VOLTAGE_ALARM;

    regenBoard->tempWarning       = REGEN_TEMP_WARNING;
    regenBoard->tempAlarm         = REGEN_TEMP_ALARM;

                                    /* Clear alarm status flags          */
    regenBoard->alarmStatus.voltageMinAlarm = (unsigned) MICRO_ALARM_OK;
    regenBoard->alarmStatus.voltageMaxAlarm = (unsigned) MICRO_ALARM_OK;
    regenBoard->alarmStatus.tempAlarm       = (unsigned) MICRO_ALARM_OK;

    regenBoard->alarmStatus.openHeaterAlarm = (unsigned) FALSE;
    regenBoard->alarmStatus.dutyCycleAlarm  = (unsigned) FALSE;

    regenBoard->filteredDutyCycle1  = 0;/* Initialize filtered Duty Cycle 1 */
    initIIRFilter(&regenBoard->dutyCycle1Filter, REGEN_MAX_DUTY_CYCLE);

    regenBoard->filteredDutyCycle2  = 0;/* Initialize filtered Duty Cycle 2 */
    initIIRFilter(&regenBoard->dutyCycle2Filter, REGEN_MAX_DUTY_CYCLE);

    regenBoard->filteredBusVoltage  = 0;/* Initialize filtered bus voltage  */
    initIIRFilter(&regenBoard->voltageFilter, REGEN_MAX_VOLTAGE);

    regenBoard->filteredTemperature = 0;/* Initialize filtered temperature  */
    initIIRFilter(&regenBoard->temperatureFilter, REGEN_MAX_TEMP);

    regenBoardInitMinMax(regenBoard);   /* Initialize min/max bus voltages   */

    regenBoard->atodSampleTimer    = timerCreate();
    regenBoard->atodReadInProgress = FALSE;
    regenBoard->atodReadDeferred   = FALSE;
    regenBoard->busVoltage         = 0;

    regenBoard->busVoltageReadHook      = NULL;
    regenBoard->busVoltageReadHookParam = 0;

    timerReloadDelay(regenBoard->atodSampleTimer, REGEN_SAMPLE_TIME);
    timerStart(regenBoard->atodSampleTimer, REGEN_SAMPLE_TIME,
            regenBoardReadBusVoltage, (Int16) regenBoard);

    return (regenBoard);                /* Return ptr to regen data struct   */
} /* regenBoardInit() */

/****************************************************************************/
/* Function    : regenBoardReadAtoD                                         */
/* Purpose     : Read 8 bit value from A to D for channel no specified      */
/* Inputs      : Regen Board data structure and A to D channel number       */
/* Outputs     : Returns A to D convertor value or ERROR if timeout         */
/****************************************************************************/
    MLocal Int16
regenBoardReadAtoD( regenBoardEntry *regenBoard, Int16 channel )
{
    Nat16 delay;                        /* Loop counter for A/D delay       */
    Int16 atod;                         /* Result of A/D conversion         */

    if ( regenBoard == NULL )           /* Check for valid Regen data struct*/
         return (ERROR);

                                        /* Start A/D conversion for channel */
    ibcBusWriteByte( regenBoard->IBC_Board.baseAddress + REGEN_AtoD_REG,
        (Byte) (REGEN_AtoD_SINGLE_ENDED | channel) );

                                        /* Wait for conversion to complete  */
                                        /* Approx 50 usec @ 10 MHz Clock    */
    for (delay = 0; delay < REGEN_ATOD_TIMEOUT; delay++);

                                        /* Read A/D conversion result       */
    atod = ibcBusReadByte(regenBoard->IBC_Board.baseAddress + REGEN_AtoD_REG);

                                        /* Set A/D mux on ground ref channel*/
    ibcBusWriteByte( regenBoard->IBC_Board.baseAddress + REGEN_AtoD_REG,
        (Byte) (REGEN_AtoD_SINGLE_ENDED | REGEN_GROUND_REF_CHAN) );

    return(atod);                       /* Return result of A/D conversion  */
} /* regenBoardReadAtoD() */

    MLocal Int16
regenBoardMeasureAtoD( regenBoardEntry *regenBoard, Int16 channel )
{
    Int16 atodReading;

    regenBoard->atodReadInProgress = TRUE;
    atodReading = regenBoardReadAtoD( regenBoard, channel );
    regenBoard->atodReadInProgress = FALSE;

    if (regenBoard->atodReadDeferred)
    {
        regenBoard->atodReadDeferred = FALSE;
        regenBoardReadBusVoltage( regenBoard );
    } /* if */

    return (atodReading);
} /* regenBoardMeasureAtoD() */

    MLocal Void
regenBoardReadBusVoltage( regenBoardEntry *regenBoard )
{
    if (regenBoard->atodReadInProgress)
    {
        regenBoard->atodReadDeferred = TRUE;
        return;
    } /* if */

    regenBoard->busVoltage = REGEN_MILLIVOLTS(     /* Read bus voltage      */
        regenBoardReadAtoD( regenBoard, REGEN_VOLTAGE_CHAN ));

    if (regenBoard->busVoltageReadHook)
	regenBoard->busVoltageReadHook(regenBoard->busVoltageReadHookParam,
	    regenBoard->busVoltage);
} /* regenBoardReadBusVoltage() */

/****************************************************************************/
/* Function    : regenBoardInitMinMax                                       */
/* Purpose     : Initialize minimum/maximum Bus Voltage values              */
/* Inputs      : Regen Board data structure                                 */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
regenBoardInitMinMax( regenBoardEntry *regenBoard )
{
    Nat32 busVoltage;

    busVoltage = REGEN_MILLIVOLTS(      /* Read bus voltage      */
        regenBoardMeasureAtoD( regenBoard, REGEN_VOLTAGE_CHAN ));

                                       /* Set min and max to new value     */
    regenBoard->minBusVoltage = regenBoard->maxBusVoltage =
        MILLIVOLTS_TO_VOLTS(busVoltage);
} /* regenBoardInitMinMax() */

/****************************************************************************/
/* Function    : regenBoardCalcMinMax                                       */
/* Purpose     : Calculate minimum/maximum Bus Voltage values               */
/* Inputs      : Regen Board data structure                                 */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
regenBoardCalcMinMax( regenBoardEntry *regenBoard )
{
                                        /* Raw bus voltage in volts         */
    Int16 busVoltage = MILLIVOLTS_TO_VOLTS(regenBoard->busVoltage);

                                        /* Calculate new minimum bus voltage*/
    if (busVoltage < regenBoard->minBusVoltage)
            regenBoard->minBusVoltage = busVoltage;

                                        /* Calculate new maximum bus voltage*/
    if (busVoltage > regenBoard->maxBusVoltage)
            regenBoard->maxBusVoltage = busVoltage;
} /* regenBoardCalcMinMax() */

/****************************************************************************/
/* Function    : regenBoardBusVoltageReadHookAdd                            */
/* Purpose     : Sets function to be called when bus voltage is read        */
/* Inputs      : Regen Board data structure                                 */
/* Outputs     : None                                                       */
/****************************************************************************/
    Int16
regenBoardBusVoltageReadHookAdd( regenBoardEntry *regenBoard, 
    Void (*functionPtr) (), Int16 param )
{
    if (regenBoard == (regenBoardEntry *) NULL)
	return (ERROR);

    regenBoard->busVoltageReadHook      = functionPtr;
    regenBoard->busVoltageReadHookParam = param;

    return (OK);
} /* regenBoardBusVoltageReadHookAdd() */

/****************************************************************************/
/* Function    : regenSetTempThreshCmd                                      */
/* Purpose     : Process command to set temperature warning & alarm points  */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
regenSetTempThreshCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
    Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Byte    *boardAddr;         /* Regen Board base address from command buf*/
    regenBoardEntry *regenBoard;/* Regen Controller Board data structure    */
    Word    command;            /* Serial command - discarded               */
    Int16   tempWarn, tempAlarm;/* Temperature warning and alarm 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, 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];

                                /* Store new thresholds in Regen data struct*/
    regenBoard->tempWarning = tempWarn;
    regenBoard->tempAlarm = tempAlarm;

    replyToCommand(1, CMD_OK);  /* Report OK to serial port                 */
} /* regenSetTempThreshCmd() */

/****************************************************************************/
/* Function    : regenGetTempThreshCmd                                      */
/* Purpose     : Process command to read temperature warning & alarm points */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
regenGetTempThreshCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
    Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Byte    *boardAddr;         /* Regen Board base address from command buf*/
    regenBoardEntry *regenBoard;/* Regen Controller Board data structure    */

                                /* 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, 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];

    replyToCommand(3, CMD_OK,   /* Put threshold values in serial buffer*/
         (Word) regenBoard->tempWarning, (Word) regenBoard->tempAlarm );

} /* regenGetTempThreshCmd() */

/****************************************************************************/
/* Function    : regenSetVoltageThreshCmd                                   */
/* Purpose     : Process command to set bus voltage warning & alarm points  */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
regenSetVoltageThreshCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
    Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Word    command;            /* Serial command - discarded               */
    Byte    *boardAddr;         /* Regen Board base address from command buf*/
    regenBoardEntry *regenBoard;/* Regen Controller Board data structure    */

    Int16   minVWarn, minVAlarm;/* Bus voltage warning and alarm thresholds */
    Int16   maxVWarn, maxVAlarm;

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 6, &command, &boardAddr,
        &minVWarn, &minVAlarm, &maxVWarn, &maxVAlarm);

                                /* Find card table index for this address   */
    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
                 boardAddr, 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];

                                /* Store new thresholds in Regen data struct*/
    regenBoard->voltageMinWarning = minVWarn;
    regenBoard->voltageMinAlarm   = minVAlarm;
    regenBoard->voltageMaxWarning = maxVWarn;
    regenBoard->voltageMaxAlarm   = maxVAlarm;

     replyToCommand(1, CMD_OK); /* Report OK to serial port                 */
} /* regenSetVoltageThreshCmd() */

/****************************************************************************/
/* Function    : regenGetVoltageThreshCmd                                   */
/* Purpose     : Process command to read bus voltage warning & alarm points */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
regenGetVoltageThreshCmd( IBC_BoardEntry* IBC_CardTable[],
    Int16 IBC_CardCount, Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Byte    *boardAddr;         /* Regen Board base address from command buf*/
    regenBoardEntry *regenBoard;/* Regen Controller Board data structure    */

                                /* 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, 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];

    replyToCommand(5, CMD_OK,   /* Put threshold values in serial buffer*/
         (Word) regenBoard->voltageMinWarning,
         (Word) regenBoard->voltageMinAlarm,
         (Word) regenBoard->voltageMaxWarning,
         (Word) regenBoard->voltageMaxAlarm);

} /* regenGetVoltageThreshCmd() */

/****************************************************************************/
/* Function    : regenGetStatusCmd                                          */
/* Purpose     : Process command to read voltage, duty cycle, temperature   */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
regenGetStatusCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
    Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Byte    *boardAddr;         /* Regen Board base address from command buf*/
    regenBoardEntry *regenBoard;/* Regen Controller Board data structure    */

                                /* 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, 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];

    replyToCommand(7, CMD_OK,   /* Send Regen Board measurements            */
       (Word) regenBoard->filteredDutyCycle1,
       (Word) regenBoard->filteredDutyCycle2,
       (Word) regenBoard->filteredBusVoltage,
       (Word) regenBoard->minBusVoltage,
       (Word) regenBoard->maxBusVoltage,
       (Word) regenBoard->filteredTemperature );

} /* regenGetStatusCmd() */

/****************************************************************************/
/* Function    : regenResetMinMaxCmd                                        */
/* Purpose     : Process command to reset minimum/maximum bus voltages      */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
regenResetMinMaxCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
    Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Byte    *boardAddr;         /* Regen Board base address from command buf*/

                                /* 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, REGEN_CONTROL) ) == ERROR)
    {                           /* Card not present so return error         */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */
                                /* Reset minimum/maximum Bus Voltage Values */
    regenBoardInitMinMax( (regenBoardEntry *) IBC_CardTable[card] );

    replyToCommand(1, CMD_OK);  /* Report OK to serial port                 */
} /* regenResetMinMaxCmd() */

/****************************************************************************/
/* Function    : regenGetAlarmStatusCmd                                     */
/* Purpose     : Process command to read Regen Board Alarm Status           */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
regenGetAlarmStatusCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
    Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Byte    *boardAddr;         /* Regen Board base address from command buf*/
    regenBoardEntry *regenBoard;/* Regen Board Entry data structure         */

                                /* 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, REGEN_CONTROL) ) == ERROR)
    {                           /* Card not present so return error         */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */
                                /* Extract pointer to Regen data structure  */
    regenBoard = (regenBoardEntry *) IBC_CardTable[card];

                                /* Send Alarm Status to serial port         */
    replyToCommand(6, CMD_OK,
       (microAlarm) regenBoard->alarmStatus.tempAlarm,
       (microAlarm) regenBoard->alarmStatus.voltageMinAlarm,
       (microAlarm) regenBoard->alarmStatus.voltageMaxAlarm,

       (Word) regenBoard->alarmStatus.openHeaterAlarm,
       (Word) regenBoard->alarmStatus.dutyCycleAlarm );
} /* regenGetAlarmStatusCmd() */

/****************************************************************************/
/* Function    : regenDigitalScopeCmd                                       */
/* Purpose     : Process command for digital scope mode for analog chan     */
/* Inputs      : IBC Card Table, Number of cards & serial command buffer    */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
regenDigitalScopeCmd( IBC_BoardEntry* IBC_CardTable[], Int16 IBC_CardCount,
    Byte *commandBuf )
{
    Nat16   card;               /* Card index into Card Table               */
    Word    command;            /* Serial command - discarded               */
    Byte    *boardAddr;         /* Regen Board base address from command buf*/
    regenBoardEntry *regenBoard;/* Regen Controller Board data structure    */

    Int16   channel;            /* Analog channel selected to read          */
    Int16   sample;             /* Analog sample counter                    */
                                /* Array of sampled analog channel data     */
    Byte    analogValue[REGEN_SAMPLES + sizeof(Word)];

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 3, &command, &boardAddr, &channel);

                                /* Find card table index for this address   */
    if ( ((card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
                 boardAddr, REGEN_CONTROL) ) == ERROR) ||
        (channel >= REGEN_AtoD_MAX_CHAN) )
    {                           /* Card not present so return error         */
        replyToCommand(1, EARGUMENT);
        return;
    } /* if */
                                /* Get pointer to Regen Board data structure*/
    regenBoard = (regenBoardEntry *) IBC_CardTable[card];

                                /* Send CMD_OK as first item of data        */
    wordToBuf(analogValue, CMD_OK);

    for (sample = sizeof(Word); sample < REGEN_SAMPLES + sizeof(Word);
        sample++)               /* Read A/D convertor and save data         */
        analogValue[sample] = (Byte) regenBoardMeasureAtoD(regenBoard, channel);

    writeDataPacket(analogValue, REGEN_SAMPLES + sizeof(Word));
} /* regenDigitalScopeCmd() */

/****************************************************************************/
/* Function    : regenCheckTemperatures                                     */
/* Purpose     : Compare temperatures to alarm thresholds                   */
/* Inputs      : Regen Board data structure                                 */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
regenCheckTemperature( regenBoardEntry *regenBoard )
{
    microAlarm alarmStatus;             /* Temperature alarm status         */

                                        /* Extract temperature alarm status */
    alarmStatus = (microAlarm) regenBoard->alarmStatus.tempAlarm;

                                        /* Check if temperature has exceeded*/
                                        /* temperature WARN/ALARM thresholds*/
                                        /* and send alarm SRQ's if necessary*/
    if (IBC_AlarmThresholdCheck( regenBoard->filteredTemperature,
         regenBoard->tempWarning, regenBoard->tempAlarm,
         REGEN_TEMP_HYSTERESIS, &alarmStatus ) == TRUE)

         writeServiceRequest(3, REGEN_TEMP_ALARM_SRQ, alarmStatus,
             regenBoard->IBC_Board.baseAddress);

                                        /* Update temperature alarm status  */
    regenBoard->alarmStatus.tempAlarm = (unsigned) alarmStatus;
} /* regenCheckTemperature() */

/****************************************************************************/
/* Function    : regenCheckVoltage                                          */
/* Purpose     : Compare Bus Voltage to alarm thresholds                    */
/* Inputs      : Regen Board data structure                                 */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
regenCheckVoltage( regenBoardEntry *regenBoard )
{
    microAlarm alarmStatus;             /* Voltage alarm status             */

                                        /* *** Minimum Voltage Thresholds ***/
                                        /* Get minimum voltage alarm status */
    alarmStatus = (microAlarm) regenBoard->alarmStatus.voltageMinAlarm;

                                        /* Check if voltage is less than    */
                                        /* voltage WARN/ALARM thresholds    */
                                        /* and send alarm SRQ's if necessary*/
    if (IBC_BelowThresholdCheck( regenBoard->filteredBusVoltage,
           regenBoard->voltageMinWarning, regenBoard->voltageMinAlarm,
           REGEN_VOLTAGE_HYSTERESIS, &alarmStatus ) == TRUE)
    {
        writeServiceRequest(3, REGEN_MIN_VOLTAGE_ALARM_SRQ, alarmStatus,
           regenBoard->IBC_Board.baseAddress);
    } /* if */
                                        /* Update min voltage alarm status  */
    regenBoard->alarmStatus.voltageMinAlarm = (unsigned) alarmStatus;

                                        /* *** Maximum Voltage Thresholds ***/
                                        /* Get maximum voltage alarm status */
    alarmStatus = (microAlarm) regenBoard->alarmStatus.voltageMaxAlarm;

                                        /* Check if voltage has exceeded    */
                                        /* voltage WARN/ALARM thresholds    */
                                        /* and send alarm SRQ's if necessary*/
    if (IBC_AlarmThresholdCheck( regenBoard->filteredBusVoltage,
           regenBoard->voltageMaxWarning, regenBoard->voltageMaxAlarm,
           REGEN_VOLTAGE_HYSTERESIS, &alarmStatus ) == TRUE)

        writeServiceRequest(3, REGEN_MAX_VOLTAGE_ALARM_SRQ, alarmStatus,
           regenBoard->IBC_Board.baseAddress);

                                        /* Update max voltage alarm status  */
    regenBoard->alarmStatus.voltageMaxAlarm = (unsigned) alarmStatus;
} /* regenCheckVoltage() */

/****************************************************************************/
/* Function    : regenCheckHardware                                         */
/* Purpose     : Check for correct operation of regen controller by looking */
/*               at Bus Voltage and Duty Cycles                             */
/* Inputs      : Regen Board data structure                                 */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
regenCheckHardware( regenBoardEntry *regenBoard )
{
                                /* If Bus Voltage < hardware regen threshold*/
                                /* and duty cycle is too high then ERROR SRQ*/
    if ( (regenBoard->filteredBusVoltage <= REGEN_THRESHOLD) &&
       ( (regenBoard->filteredDutyCycle1 > REGEN_HIGH_DUTY_CYCLE) ||
         (regenBoard->filteredDutyCycle2 > REGEN_HIGH_DUTY_CYCLE) ) )
    {
        if ( !regenBoard->alarmStatus.openHeaterAlarm )
        {                       /* Set alarm status flag in regen structure */
            regenBoard->alarmStatus.openHeaterAlarm = (unsigned) TRUE;
                                /* Send OPEN HEATER On alarm SRQ to VME     */
            writeServiceRequest(2, REGEN_OPEN_HEATER_ALARM_ON,
                regenBoard->IBC_Board.baseAddress);

        } /* if */
    } /* if */

    else if (regenBoard->alarmStatus.openHeaterAlarm)
    {                           /* Clear alarm status flag in regen struct  */
        regenBoard->alarmStatus.openHeaterAlarm = (unsigned) FALSE;
                                /* Send OPEN HEATER Off alarm SRQ to VME    */
        writeServiceRequest(2, REGEN_OPEN_HEATER_ALARM_OFF,
            regenBoard->IBC_Board.baseAddress);
    } /* else if */

                                /* If Bus Voltage > hardware regen threshold*/
                                /* and duty cycle is too low then ERROR SRQ */
    if ( (regenBoard->filteredBusVoltage >= REGEN_HALF_POWER) &&
       ( (regenBoard->filteredDutyCycle1 < REGEN_LOW_DUTY_CYCLE) ||
         (regenBoard->filteredDutyCycle2 < REGEN_LOW_DUTY_CYCLE) ) )
    {
        if ( !regenBoard->alarmStatus.dutyCycleAlarm )
        {                       /* Set alarm status flag in regen structure */
            regenBoard->alarmStatus.dutyCycleAlarm = (unsigned) TRUE;
                                /* Send BAD DUTY CYCLE alarm On SRQ to VME  */
            writeServiceRequest(2, REGEN_BAD_DUTY_ALARM_ON,
                regenBoard->IBC_Board.baseAddress);
        } /* if */
    } /* if */

    else if (regenBoard->alarmStatus.dutyCycleAlarm)
    {                           /* Clear alarm status flag in regen struct  */
        regenBoard->alarmStatus.dutyCycleAlarm = (unsigned) FALSE;
                                /* Send BAD DUTY CYCLE alarm Off SRQ to VME */
        writeServiceRequest(2, REGEN_BAD_DUTY_ALARM_OFF,
             regenBoard->IBC_Board.baseAddress);
    } /* else if */

} /* regenCheckHardware() */

/****************************************************************************/
/* Function    : regenBoardFunctions                                        */
/* Purpose     : Regen Controller Board real-time loop functions            */
/* Inputs      : Regen Board data structure                                 */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
regenBoardFunctions( regenBoardEntry *regenBoard )
{
    Int16 dc1FiltOK, dc2FiltOK;

    if (IIRfilter(                      /* Read & Filter Regen Temperature  */
       REGEN_DEGREES(regenBoardMeasureAtoD(regenBoard, REGEN_TEMP_CHAN)),
       &regenBoard->filteredTemperature, &regenBoard->temperatureFilter ) == OK)
        regenCheckTemperature(regenBoard);/* Check temperature warn & alarm */

    if ( (dc1FiltOK = IIRfilter(        /* Read & Filter Duty Cycle 1       */
       REGEN_DUTY_CYCLE(regenBoardMeasureAtoD( regenBoard, REGEN_DUTY1_CHAN )),
       &regenBoard->filteredDutyCycle1, &regenBoard->dutyCycle1Filter )) == OK)
         regenBoard->filteredDutyCycle1 = max(regenBoard->filteredDutyCycle1,0);

    if ( (dc2FiltOK = IIRfilter(        /* Read & Filter Duty Cycle 2       */
       REGEN_DUTY_CYCLE(regenBoardMeasureAtoD( regenBoard, REGEN_DUTY2_CHAN )),
       &regenBoard->filteredDutyCycle2, &regenBoard->dutyCycle2Filter )) == OK)
         regenBoard->filteredDutyCycle2 = max(regenBoard->filteredDutyCycle2,0);
     regenBoardCalcMinMax(regenBoard);  /* Calculate min/max bus voltages   */
                                        /* using un-filtered voltages values*/

                                        /* Filter Bus Voltage               */
    if (IIRfilter( MILLIVOLTS_TO_VOLTS(regenBoard->busVoltage),
        &regenBoard->filteredBusVoltage, &regenBoard->voltageFilter) == OK)
    {
        regenBoard->filteredBusVoltage = max(regenBoard->filteredBusVoltage, 0);
        regenCheckVoltage(regenBoard);  /* Check Bus voltage alarms         */

        if ( (dc1FiltOK == OK) && (dc2FiltOK == OK) )
            regenCheckHardware(regenBoard); /* Check for correct HW function*/
    } /* if */
} /* regenBoardFunctions() */

