/****************************************************************************/
/* Copyright 1995 to 1996 MBARI                                             */
/****************************************************************************/
/* Summary  : Emergency Recovery Data Concetrator - Main C Source file      */
/* Filename : ers.c                                                         */
/* Author   : Andrew Pearce                                                 */
/* Project  : Tiburon ROV Emergency Recovery System                         */
/* Version  : 1.0                                                           */
/* Created  : 06/12/95                                                      */
/* Modified : 06/06/96                                                      */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/* $Header: /usr/tiburon/.cvsroot/micro/ibc/ers/ers.c,v 1.6 1997/05/07 15:39:27 pean Exp $
 * $Log: ers.c,v $
 * Revision 1.6  1997/05/07 15:39:27  pean
 * Cleaned up Include files and makefile for new directory structure.
 *
 * Revision 1.1.1.1  1997/05/02 18:51:50  pean
 * Initial check in of IBC microcontroller software
 *
 * Revision 1.6  96/08/15  11:50:59  11:50:59  pean (Andrew Pearce)
 * Fixed problem with writes to EEPROM corrupting op-code fetches
 *
 * Revision 1.5  96/08/07  15:30:01  15:30:01  pean (Andrew Pearce)
 * Fixed problem with GF scanning stopping when RESTART_SCAN command sent
 *
 * Revision 1.4  96/07/10  13:34:42  13:34:42  pean (Andrew Pearce)
 * Included fix for Quad Serial Board RS485 Mode transmitter enable problem
 *
 * Revision 1.3  96/06/11  13:44:43  13:44:43  pean (Andrew Pearce)
 * *** empty log message ***
 *
 * Revision 1.2  96/06/11  13:43:26  13:43:26  pean (Andrew Pearce)
 * Fixed VB Level Sensor Scaling
 *
 * Revision 1.1  96/06/10  13:25:45  13:25:45  pean (Andrew Pearce)
 * Initial revision: Prior to VB System Testing. No Acoustic Modem support.
*/
/****************************************************************************/

#pragma model(196)
#pragma nosignedchar

#include "C:/C96/INCLUDE/string.h"      /* string function library          */

#include "const.h"                      /* Misc. constants - TRUE/FALSE etc */
#include "types.h"                      /* MBARI style guide declarations   */
#include "microasm.h"                   /* Assembly language functions      */
#include "applic.h"                     /* Microcontroller applications     */
#include "malloc.h"                     /* Malloc support routines          */

#include "microdef.h"                   /* Microcontroller definitions      */
#include "microlib.h"                   /* Microcontroller Library decls.   */
#include "applay.h"                     /* Application layer funcitions     */
#include "serial.h"                     /* Serial layer functions           */
#include "proto.h"                      /* Serial Protocol layer functions  */
#include "microcmd.h"                   /* microcontroller commands         */

#include "timer.h"                      /* Timer library definitions        */
#include "ring.h"                       /* Ring buffer support routines     */
#include "syslib.h"                     /* Board Support library decls.     */

#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 "cpu196.h"                     /* IBC 80C196 CPU Board functions   */
#include "pic.h"                        /* Intel 82C59 Interrupt Controller */

#include "filter.h"                     /* IBC General IIR Filter Routines  */
#include "gf_board.h"                   /* Ground Fault Board Definitions   */
#include "low_pwr.h"                    /* Low Power Switch Board Defs      */
#include "atod.h"                       /* IBC A to D Board Definitions     */
#include "quad_serial.h"                /* Quad Serial Board Definitions    */
#include "scc2698.h"                    /* Signetics SCC2698 UART Definition*/
#include "falmouth.h"                   /* Falmouth Sensor Interface Defs   */

#include "ers.h"                        /* Application specific definitions */

/****************************************************************************/

const Char micro_id[] = "87C196KC Emergency Recovery Dcon";
const Char softwareRev[] = "$Revision: 1.6 $";
const Char serialNumber[] = "EREC 01";
const Char *microSerialNo;

MLocal Boolean microEnabled;            /* Enables serial command processing*/

MLocal Boolean canWaterAlarm;           /* Housing Water Alarm Status       */
MLocal Boolean jboxWaterAlarm;          /* J-Box Water Alarm Status         */
MLocal Nat16   waterAlarmThresh;        /* Water alarm threshold            */

                                        /* Array of IBC boards in backplane */
IBC_BoardEntry *(IBC_CardTable[IBC_MAX_CARDS]);
Int16   IBC_CardCount;                  /* IBC cards found in backplane     */

MLocal ersOperatingMode operatingMode;  /* ERS Operating Mode               */
MLocal ersDataReportMode dataReportMode;/* Data Reporting Mode              */
MLocal MBool vehicleInWater;            /* Vehicle In-Water Flag            */

MLocal alarmParam  hydPressure;         /* Hydraulic Pressure Alarm Status  */
MLocal filterParam hydPressureFilter;   /* Hydraulic Pressure Filter        */

MLocal alarmParam  batteryVoltage;      /* ERS Battery voltage              */
MLocal alarmParam  batteryLife;

MLocal Int16 vbLevelDeciMM;             /* VB System Level Deci Millimeters */
MLocal filterParam vbLevelFilter;       /* VB System Level filter           */

MLocal falmouthSensor pressureSensor;   /* Falmouth Pressure Sensor Data    */

MLocal struct
{
    Boolean initialized;                /* Comp level min & max values set  */
    Nat16 minValue;                     /* Minimum comp level value         */
    Nat16 maxValue;                     /* Maximum comp level value         */
    Nat16 rawValue;                     /* A/D Reading in counts            */
    alarmParam alarm;                   /* Alarm monitoring values          */
    filterParam filter;                 /* Compensator Level filter         */
} compLevel;

MLocal struct
{
    MBool   armed;
    Nat16   armCode;
    timerId onTimer;
} deploymentLatch;

#define ERS_DEBOUNCE 300                /* Approximately 1 msec             */
#define ONE_SECOND       SYSCLOCK_RATE  /* 1 second in ticker units         */
#define TEN_SECONDS 10 * SYSCLOCK_RATE  /* 10 seconds in ticker units       */
#define ONE_MINUTE  60 * SYSCLOCK_RATE  /* 1 minute in ticker units         */

typedef struct
{
    Nat16 magicNumber;
    Nat16 location;
    struct
    {
        Nat16 writes;
        Nat32 milliAmpSeconds;
    } currentEntry[EEPROM_ENTRIES];
} eepromData;

typedef enum
{
    BATTERY_STATE_IDLE,
    BATTERY_STATE_INIT,
    DUMMY_LOAD,
    READ_VOLTAGE
} batteryState;

MLocal struct
{
    Nat16        nextReadTick;
    Nat16        currentReadCount;
    Nat32        oneSecCurrentSumBits;
    Nat32        oneSecCurrentDrain;
    Nat16        secondCount;
    Nat32        oneMinCurrentSumBits;
    Nat32        oneMinCurrentDrain;

    Nat16        readTicks;
    batteryState readState;
    Nat16        updateFlags;
} batteryFSM;

MLocal struct
{
   quadSerialChan serialChan;
   MBool cmdRecvd;
   Nat16 cmdLen;
} rovnav;

                                        /* Forward declarations             */
MLocal Void readCompensatorLevel( IBC_BoardEntry * IBC_Cards[],
    Int16 ibcCardCount );

MLocal Void readHydraulicPressure( IBC_BoardEntry * IBC_Cards[],
    Int16 ibcCardCount );

MLocal Void readVbSystemLevel( IBC_BoardEntry * IBC_Cards[],
    Int16 ibcCardCount );


MLocal Void  ersReadSensors( Void );
MLocal Void  ersSendDataReport( Void );
MLocal Void  ersSendErrorMsg( Void );
MLocal Void  ersSingleDataReport( Void );
MLocal Void  ersSlowDataReport( Void );
MLocal Void  ersFastDataReport( Void );
MLocal Void  ersStopDataReporting( Void );
MLocal Int16 ersSwitchSensorsPower( MBool state );
MLocal Void  addIbcDataSwitchBoard( Void );

MLocal Void  ersOperatingModeChange( Void );
MLocal Void  readBatteryVoltage( Void );
MLocal Int16 measureBatteryVoltage( Void );
MLocal Nat16 readBatteryCurrentBits( Void );
MLocal MBool checkOperatingMode( Void );
MLocal Nat32 computeAvgBatteryCurrent( Void );
MLocal Void  checkBatteryLife( Void );

MLocal Void  writeCurrentToEeprom(Nat32 milliAmpSeconds);
MLocal Void  eepromDataProtectMode(MBool mode);

MLocal Void  vmeConsoleRouteSelect( consoleRouteSelect route );
MLocal Int16 deploymentLatchRelease( MBool state, Nat16 duration );

Int16 initRovnav( Byte* boardAddr, Nat16 chan,IBC_BoardEntry *IBC_CardTable[],
    Int16 cardCount );

Void checkRovnav( Void );

/****************************************************************************/
/* Function    : getWaterAlarmThreshCmd                                     */
/* Purpose     : Send water alarm threshold to serial port                  */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
getWaterAlarmThreshCmd( Void )
{
                                /* send water alarm threshold to serial port*/
    replyToCommand(2, CMD_OK, (Word) waterAlarmThresh );
} /* getWaterAlarmThreshCmd() */

/****************************************************************************/
/* Function    : setWaterAlarmThreshCmd                                     */
/* Purpose     : Process command to set water alarm threshold               */
/* Inputs      : Serial command buffer                                      */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
setWaterAlarmThreshCmd(Byte *commandBuf)
{
   Word    command;                /* Command from serial port             */

                                    /* Read threshold from command buf      */
    wordsFromBuf( commandBuf, 2, &command, (Word *) &waterAlarmThresh );
    replyToCommand(1, CMD_OK);      /* Send command OK to serial port       */
} /* setWaterAlarmThreshCmd() */

/****************************************************************************/
/* Function    : getWaterAlarmCmd                                           */
/* Purpose     : Process command to read water alarm status                 */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
getWaterAlarmCmd( Void )
{
    Word waterAlarmStatus = 0;

    if (canWaterAlarm)  waterAlarmStatus |= HOUSING_WATER_ALARM_BIT;
    if (jboxWaterAlarm) waterAlarmStatus |= JBOX_WATER_ALARM_BIT;

    replyToCommand(2, CMD_OK, (Word) waterAlarmStatus );
} /* getWaterAlarmCmd() */

/****************************************************************************/
/* Function    : getVehicleInWaterCmd                                       */
/* Purpose     : Send Vehicle In-Water status flag to serial port           */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
getVehicleInWaterCmd( Void )
{
                                /* send Vehicle In-Water Status Flag        */
    replyToCommand(2, CMD_OK, (Word) vehicleInWater );
} /* getVehicleInWaterCmd() */

/****************************************************************************/
/* Function    : getERSStateCmd                                             */
/* Purpose     : Send ERS State to serial port                              */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
getERSStateCmd( Void )
{
                                /* send ERS State Data to serial port       */
    replyToCommand(4, CMD_OK, (Word) operatingMode, vehicleInWater, 0);
} /* getERSStateCmd() */

/****************************************************************************/
/* Function    : getCompensatorLevelCmd                                     */
/* Purpose     : Send Compensator Level to serial port                      */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
getCompensatorLevelCmd( Void )
{
                                /* send compensator level to serial port    */
    replyToCommand(3, CMD_OK, compLevel.alarm.value, compLevel.rawValue );
} /* getCompensatorLevelCmd() */

/****************************************************************************/
/* Function    : setCompAlarmLevelsCmd                                      */
/* Purpose     : Process command to set Comp Alarm Thresholds               */
/* Inputs      : Serial command buffer                                      */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
setCompAlarmLevelsCmd( Byte *commandBuf )
{
    Word  command;              /* Serial command - discarded               */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 3, &command, &compLevel.alarm.warnThresh,
                 &compLevel.alarm.alarmThresh);

    replyToCommand(1, CMD_OK);  /* Report OK to serial port                 */
} /* setCompAlarmLevelsCmd() */

/****************************************************************************/
/* Function    : getCompLevelScalingCmd                                     */
/* Purpose     : Send Compensator Warning and Alarm Levels to serial port   */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
getCompLevelScalingCmd( Void )
{
                                /* send compensator scaling to serial port  */
    replyToCommand(3, CMD_OK, compLevel.minValue, compLevel.maxValue );
} /* getCompLevelScalingCmd() */

/****************************************************************************/
/* Function    : setCompLevelScalingCmd                                     */
/* Purpose     : Process command to set Comp Level Min and Max cal points   */
/* Inputs      : Serial command buffer                                      */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
setCompLevelScalingCmd( Byte *commandBuf )
{
    Word  command;              /* Serial command - discarded               */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 3, &command, &compLevel.minValue,
                 &compLevel.maxValue);

    replyToCommand(1, CMD_OK);  /* Report OK to serial port                 */
                                /* Enable comp level reading and alarms     */
    compLevel.initialized = TRUE;
} /* setCompLevelScalingCmd() */

/****************************************************************************/
/* Function    : getCompAlarmLevelsCmd                                      */
/* Purpose     : Send Compensator Warning and Alarm Levels to serial port   */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
getCompAlarmLevelsCmd( Void )
{
                                /* send compensator level to serial port    */
    replyToCommand(3, CMD_OK, compLevel.alarm.warnThresh,
         compLevel.alarm.alarmThresh );
} /* getCompAlarmLevelsCmd() */

/****************************************************************************/
/* Function    : getCompLevelAlarmCmd                                       */
/* Purpose     : Send Compensator Level Alarm status  to serial port        */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
getCompLevelAlarmCmd( Void )
{
                                /* send compensator level alarm             */
    replyToCommand(2, CMD_OK, compLevel.alarm.alarmStatus );
} /* getCompLevelAlarmCmd() */

/****************************************************************************/
/* Function    : setHydPressureAlarmLevelsCmd                               */
/* Purpose     : Process command to set Hydraulic Pressure Alarm Thresholds */
/* Inputs      : Serial command buffer                                      */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
setHydPressureAlarmLevelsCmd( Byte *commandBuf )
{
    Word  command;              /* Serial command - discarded               */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 3, &command, &hydPressure.warnThresh,
                 &hydPressure.alarmThresh);

    replyToCommand(1, CMD_OK);  /* Report OK to serial port                 */
} /* setHydPressureAlarmLevelsCmd() */

/****************************************************************************/
/* Function    : getHydPressureAlarmLevelsCmd                               */
/* Purpose     : Send Hydraulic Pressure Warn and Alarm Level to serial port*/
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
getHydPressureAlarmLevelsCmd( Void )
{
                                /* send hyd pressure alarm thresh           */
    replyToCommand(3, CMD_OK, hydPressure.warnThresh,
         hydPressure.alarmThresh );
} /* getHydPressureAlarmLevelsCmd() */

/****************************************************************************/
/* Function    : getHydPressureAlarmCmd                                     */
/* Purpose     : Send Hydraulic Pressure Alarm status to serial port        */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
getHydPressureAlarmCmd( Void )
{
                                /* send Hydraulic Pressure alarm            */
    replyToCommand(2, CMD_OK, hydPressure.alarmStatus );
} /* getHydPressureAlarmCmd() */


/****************************************************************************/
/* Function    : readBatteryVoltageCmd                                      */
/* Purpose     : Send Hydraulic Pressure Warn and Alarm Level to serial port*/
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
readBatteryVoltageCmd( Void )
{
                                /* start battery voltage reading            */
    batteryFSM.readTicks = read_sysclock();
    batteryFSM.readState = BATTERY_STATE_INIT;

    replyToCommand(1, CMD_OK);
} /* readBatteryVoltageCmd() */

/****************************************************************************/
/* Function    : setBatteryVoltageAlarmLevelsCmd                            */
/* Purpose     : Process command to set Battery Voltage Alarm Thresholds    */
/* Inputs      : Serial command buffer                                      */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
setBatteryVoltageAlarmLevelsCmd( Byte *commandBuf )
{
    Word  command;              /* Serial command - discarded               */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 3, &command, &batteryVoltage.warnThresh,
                 &batteryVoltage.alarmThresh);

    replyToCommand(1, CMD_OK);  /* Report OK to serial port                 */
} /* setBatteryVoltageAlarmLevelsCmd() */

/****************************************************************************/
/* Function    : getBatteryVoltageAlarmLevelsCmd                            */
/* Purpose     : Send Hydraulic Pressure Warn and Alarm Level to serial port*/
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
getBatteryVoltageAlarmLevelsCmd( Void )
{
                                /* send battery voltage alarm thresh        */
    replyToCommand(3, CMD_OK, batteryVoltage.warnThresh,
         batteryVoltage.alarmThresh );
} /* getBatteryVoltageAlarmLevelsCmd() */

/****************************************************************************/
/* Function    : setBatteryLifeAlarmLevelsCmd                               */
/* Purpose     : Process command to set Battery Life Alarm Thresholds       */
/* Inputs      : Serial command buffer                                      */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
setBatteryLifeAlarmLevelsCmd( Byte *commandBuf )
{
    Word  command;              /* Serial command - discarded               */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 3, &command, &batteryLife.warnThresh,
        &batteryLife.alarmThresh);

    replyToCommand(1, CMD_OK);  /* Report OK to serial port                 */
} /* setBatteryLifeAlarmLevelsCmd() */

/****************************************************************************/
/* Function    : getBatteryLifeAlarmLevelsCmd                               */
/* Purpose     : Send Hydraulic Pressure Warn and Alarm Level to serial port*/
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
getBatteryLifeAlarmLevelsCmd( Void )
{
                                /* send battery voltage alarm thresh        */
    replyToCommand(3, CMD_OK, batteryLife.warnThresh,
         batteryLife.alarmThresh );
} /* getBatteryLifeAlarmLevelsCmd() */

/****************************************************************************/
/* Function    : getBatteryAlarmsCmd                                        */
/* Purpose     : Send Battery Capacity & Voltage Alarm status to serial port*/
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
getBatteryAlarmsCmd( Void )
{
                                /* send battery alarm status to serial port */
    replyToCommand(3, CMD_OK, batteryLife.alarmStatus,
         batteryVoltage.alarmStatus );
} /* getBatteryAlarmsCmd() */

/****************************************************************************/
/* Function    : getErsTelemetryCmd                                         */
/* Purpose     : Send Ers Telemetry Values to serial port                   */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
getErsTelemetryCmd( Void )
{                               /* send telemetry to serial port            */
    replyToCommand(9, CMD_OK, hydPressure.value, vbLevelDeciMM,
         batteryFSM.updateFlags, batteryLife.value,  batteryVoltage.value,
         MICROAMPS_TO_MILLIAMPS(batteryFSM.oneSecCurrentDrain),
         pressureSensor.integer, pressureSensor.fraction);

    batteryFSM.updateFlags = 0;
} /* getErsTelemeteryCmd() */

/****************************************************************************/
/* Function    : deploymentLatchArmCmd                                      */
/* Purpose     : Process command to arm deployment latch                    */
/* Inputs      : Serial command buffer                                      */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
deploymentLatchArmCmd( Byte *commandBuf )
{
    Word  command;              /* Serial command - discarded               */
    Word  armCode;              /* Security code word                       */
    Word  state;                /* armed state                              */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 3, &command, &armCode, &state);

    if (armCode != deploymentLatch.armCode)
        replyToCommand(1, CMD_ERROR);
    else
    {
        deploymentLatch.armed = (MBool) state;
        replyToCommand(1, CMD_OK);
    } /* else */

    replyToCommand(1, CMD_OK);
} /* deploymentLatchArmCmd() */

/****************************************************************************/
/* Function    : deploymentLatchReleaseCmd                                  */
/* Purpose     : Process command to operate deployment latch                */
/* Inputs      : Serial command buffer                                      */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
deploymentLatchReleaseCmd( Byte *commandBuf )
{
    Word  command;              /* Serial command - discarded               */
    Word  armCode;              /* Security code word                       */
    MBool state;                /* Latch Power On/Off                       */
    Nat16 duration;             /* Latch Power On Time                      */

                                /* Extract command and arguments from buffer*/
    wordsFromBuf(commandBuf, 4, &command, &armCode, &state, &duration);

    if (armCode == deploymentLatch.armCode)
        replyToCommand(1, (deploymentLatchRelease(state, duration)
                == OK) ? CMD_OK : CMD_ERROR);
    else
        replyToCommand(1, CMD_ERROR);
} /* deploymentLatchReleaseCmd() */

/****************************************************************************/
/* Function    : processApplicCommands                                      */
/* Purpose     : Decode application specific serial port commands           */
/* Inputs      : Command buffer and command buffer length                   */
/* Outputs     : Return TRUE if command recognized and processed, else FALSE*/
/****************************************************************************/
    MLocal Int16
processApplicCommands( Char *commandBuf, Int16 commandLen )
{
                                /* try to recognize the command and call    */
                                /* the appropriate handler                  */
    switch (wordFromBuf(commandBuf, 0))
    {
        case EREC_DCON_APP | GET_WATER_ALARM:
            getWaterAlarmCmd(); /* Send water alarm status                  */
            break;

        case EREC_DCON_APP | GET_WATER_ALARM_THRESH:
            getWaterAlarmThreshCmd();
            break;

        case EREC_DCON_APP | SET_WATER_ALARM_THRESH:
            setWaterAlarmThreshCmd(commandBuf);
            break;

        case EREC_DCON_APP | GET_ERS_STATE:
            getERSStateCmd();
            break;

        case EREC_DCON_APP | GET_VEHICLE_IN_WATER:
            getVehicleInWaterCmd();
            break;

        case EREC_DCON_APP | GET_COMP_LEVEL:
            getCompensatorLevelCmd();
            break;

        case EREC_DCON_APP | GET_COMP_LEVEL_ALARM:
            getCompLevelAlarmCmd();
            break;

        case EREC_DCON_APP | GET_COMP_ALARM_LEVELS:
            getCompAlarmLevelsCmd();
            break;

        case EREC_DCON_APP | SET_COMP_ALARM_LEVELS:
            setCompAlarmLevelsCmd(commandBuf);
            break;

        case EREC_DCON_APP | GET_COMP_LEVEL_SCALING:
            getCompLevelScalingCmd();
            break;

        case EREC_DCON_APP | SET_COMP_LEVEL_SCALING:
            setCompLevelScalingCmd(commandBuf);
            break;

        case EREC_DCON_APP | GET_HYD_PRESS_ALARM_LVLS:
            getHydPressureAlarmLevelsCmd();
            break;

        case EREC_DCON_APP | SET_HYD_PRESS_ALARM_LVLS:
            setHydPressureAlarmLevelsCmd(commandBuf);
            break;

        case EREC_DCON_APP | GET_HYD_PRESS_ALARM:
            getHydPressureAlarmCmd();
            break;

        case EREC_DCON_APP | READ_BATTERY_VOLTAGE:
            readBatteryVoltageCmd();
            break;

        case EREC_DCON_APP | GET_BAT_VOLT_ALARM_LEVELS:
            getBatteryVoltageAlarmLevelsCmd();
            break;

        case EREC_DCON_APP | SET_BAT_VOLT_ALARM_LEVELS:
            setBatteryVoltageAlarmLevelsCmd(commandBuf);
            break;

        case EREC_DCON_APP | GET_BAT_LIFE_ALARM_LEVELS:
            getBatteryLifeAlarmLevelsCmd();
            break;

        case EREC_DCON_APP | SET_BAT_LIFE_ALARM_LEVELS:
            setBatteryLifeAlarmLevelsCmd(commandBuf);
            break;

        case EREC_DCON_APP | GET_BATTERY_ALARMS:
            getBatteryAlarmsCmd();
            break;

        case EREC_DCON_APP | GET_TELEMETRY:
            getErsTelemetryCmd();
            break;

        case EREC_DCON_APP | ARM_DEPLOYMENT_LATCH:
            deploymentLatchArmCmd(commandBuf);
            break;

        case EREC_DCON_APP | RELEASE_DEPLOYMENT_LATCH:
            deploymentLatchReleaseCmd(commandBuf);
            break;

        default:
            return (FALSE);     /* Command not recognized return FALSE      */

    } /* switch */

    return (TRUE);              /* Command recognized & processed           */
} /* processApplicCommands() */

/****************************************************************************/
/* Function    : executeCommand                                             */
/* Purpose     : Check commands received from serial port and call handler  */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
executeCommand( Void )
{
    Byte*   commandBuf;             /* Pointer to the command packet data.  */
    Int16   commandLen;             /* Length of the command packet data    */

                                    /* read a command from serial port      */
    while (readRecvdPacket(&commandBuf, &commandLen) == OK)
    {
        if ( wordFromBuf(commandBuf, 0) == (MICRO_APP | CMD_ENABLE) )
        {
            microEnabled = TRUE;
            replyToCommand(1, CMD_OK );
        } /* if */

        else if (microEnabled)
        {                           /* try to recognize the command and call*/
                                    /* the appropriate handler              */
            if ( processApplicCommands( commandBuf, commandLen ) == FALSE )

                if ( processIBCCommands ( IBC_CardTable, IBC_CardCount,
                                commandBuf, commandLen ) == FALSE )

                    processMicroCommands ( commandBuf, commandLen );
        } /* if */

        freeRecvdPacket(commandBuf);/* Free up the memory allocated for the */
                                    /* packet by readSerialPacket()         */
    } /* while */
} /* executeCommand() */

/****************************************************************************/
/* Function    : processSRQPacket                                           */
/* Purpose     : proceses Service Request Packets                           */
/* Inputs      : received packet buffer                                     */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
processSRQPacket(Byte *command_buf)
{
} /* processSRQPacket() */

/****************************************************************************/
/* Function    : initializeAlarms                                           */
/* Purpose     : Initializes various alarm status and thresholds            */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
initializeAlarms( Void )
{
    waterAlarmThresh = WATER_ALARM_THRESHOLD;
    canWaterAlarm    = FALSE;   /* Clear water alarm status flags           */
    jboxWaterAlarm   = FALSE;

    compLevel.alarm.value = 0;
    compLevel.alarm.warnThresh  = COMP_LEVEL_WARN_THRESHOLD;
    compLevel.alarm.alarmThresh = COMP_LEVEL_ALARM_THRESHOLD;
    compLevel.alarm.alarmStatus = MICRO_ALARM_OK;
    compLevel.minValue = 0;
    compLevel.maxValue = 0;
    compLevel.initialized = FALSE;
                                /* Initialize Compensation Level Filter     */
    initIIRFilter(&compLevel.filter, COMP_LEVEL_FILT_MAX);

    hydPressure.value = 0;
    hydPressure.warnThresh  = HYD_PRES_WARN_THRESHOLD;
    hydPressure.alarmThresh = HYD_PRES_ALARM_THRESHOLD;
    hydPressure.alarmStatus = MICRO_ALARM_OK;

                                /* Initialize Hydraulic Pressure Reading    */
    initIIRFilter(&hydPressureFilter, HYD_PRES_MAX);

                               /* Initialize Battery Usage Alarm            */
    batteryLife.value           = 0;
    batteryLife.warnThresh      = BATTERY_LIFE_WARN_THRESH;
    batteryLife.alarmThresh     = BATTERY_LIFE_ALARM_THRESH;
    batteryLife.alarmStatus     = MICRO_ALARM_OK;

                               /* Initialize Battery Voltage Reading        */
    batteryVoltage.value        = 0;
    batteryVoltage.warnThresh   = BATTERY_VOLT_WARN_THRESH;
    batteryVoltage.alarmThresh  = BATTERY_VOLT_ALARM_THRESH;
    batteryVoltage.alarmStatus  = MICRO_ALARM_OK;
} /* initializeAlarms() */

/****************************************************************************/
/* Function    : initializeErs                                              */
/* Purpose     : initialize ERS hardware and software                       */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
initializeErs( Void )
{
    initializeAlarms();                /* Application specific alarms       */

    vbLevelDeciMM = 0;
#if 0
    initIIRFilter(&vbLevelFilter, VB_LEVEL_MAX);
#endif

    operatingMode  = ERS_POWER_OFF;    /* Set operating mode                */
    dataReportMode = STOP_DATA_REPORT; /* Set Data Report Mode Off          */

                                       /* Set In-Water Sensor Flag          */
    vehicleInWater = readVehicleInWaterSensor();

                                       /* Initialize Battery State Machine  */
    batteryFSM.nextReadTick = read_sysclock() + ONE_SECOND;
    batteryFSM.currentReadCount        = 0;
    batteryFSM.oneSecCurrentSumBits    = 0;
    batteryFSM.oneSecCurrentDrain      = 0;
    batteryFSM.secondCount             = 0;
    batteryFSM.oneMinCurrentSumBits    = 0;
    batteryFSM.oneMinCurrentDrain      = 0;

    batteryFSM.readTicks   = 0;
    batteryFSM.readState   = BATTERY_STATE_INIT;
    batteryFSM.updateFlags = 0;

    batteryVoltage.value   =  0;

    deploymentLatch.armed      = FALSE;
    deploymentLatch.armCode    = DEPLOYMENT_LATCH_ARM_CODE;
    deploymentLatch.onTimer    = timerCreate();
} /* initializeErs() */

/****************************************************************************/
/* Function    : checkWaterAlarms                                           */
/* Purpose     : Monitor water alarm lines and check against alarm threshold*/
/* Inputs      : None                                                       */
/* Outputs     : None, but sends SRQ packet to serial port                  */
/****************************************************************************/
    MLocal Void
checkWaterAlarms( Void )
{
    if (IBC_ThresholdCheck(readAnalogChannel(HOUSING_WATER_ALARM_CHAN) + 0x200,
         waterAlarmThresh, WATER_ALARM_HYSTERESIS, &canWaterAlarm ) == TRUE)

        writeServiceRequest(1, (canWaterAlarm ? CAN_WATER_ALARM_ON_SRQ
                                              : CAN_WATER_ALARM_OFF_SRQ));

    if (IBC_ThresholdCheck( readAnalogChannel( JBOX_WATER_ALARM_CHAN ) + 0x200,
         waterAlarmThresh, WATER_ALARM_HYSTERESIS, &jboxWaterAlarm ) == TRUE)

        writeServiceRequest(1, (jboxWaterAlarm ? JBOX_WATER_ALARM_ON_SRQ
                                               : JBOX_WATER_ALARM_OFF_SRQ));
} /* checkWaterAlarms() */

/****************************************************************************/
/* Function    : mainLoop                                                   */
/* Purpose     : C main program called from assembler startup routine       */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
mainLoop( Void )
{
    initIoport1();              /* Initialize Input/Output Port 1           */
    initIoport2();              /* Initialize Input/Output Port 2           */

    initMalloc();               /* Initialize Memory allocator              */
    initBoardStatus();          /* Initialize board status word             */

    microEnabled = FALSE;       /* Disabled processing of serial commands   */
    microSerialNo = serialNumber;

                                /* Route VME Console to Telemetry System    */
    vmeConsoleRouteSelect(CONSOLE_TO_TELEM);

                                /* Initialize installed IBC Cards           */
    IBC_CardCount = IBC_initializeCards( &IBC_CardTable, IBC_MAX_CARDS );

    addIbcDataSwitchBoard();    /* Add Data Switch Board Entry              */
    initializeErs();            /* Initialize ERS system functions          */

    initSerialProtocol(NULL);   /* Initialize serial protocol               */

                                /* Initialize Falmouth Pressure Sensor I/O  */
    initFalmouthSensor( &pressureSensor, (Byte *) QUAD_SERIAL_0_ADDR,
        PRESSURE_SENSOR_SERIAL_CHAN, IBC_CardTable, IBC_CardCount );

    initRovnav( (Byte *) QUAD_SERIAL_0_ADDR, ACOUSTIC_MODEM_SERIAL_CHAN,
        IBC_CardTable, IBC_CardCount );

    measureBatteryVoltage();    /* Initial battery voltage measurement      */
    checkBatteryLife();         /* Check battery life used so far           */

                                /* Send Micro Reset SRQ                     */
    writeServiceRequest(1, MICRO_RESET_SRQ);

    FOREVER
    {                           /* Repeat forever                           */
        executeCommand();       /* Process any commands from the VME CPU    */

        checkWaterAlarms();     /* Check water alarms                       */

        checkRovnav();          /* Check for received Acoustic Modem Command*/

                                /* Check battery current and operating mode */
        if (checkOperatingMode() == TRUE)
            ersOperatingModeChange();

        if (operatingMode == OPERATING_MODE)
            ersReadSensors();

        readBatteryVoltage();   /* Read battery voltage (if requested)      */

                                /* Execute any IBC card specific functions  */
        IBC_CardFunctions( &IBC_CardTable, IBC_CardCount );

        if (operatingMode == EMERGENCY_MODE)
            idle();             /* Put processor to sleep until next tick   */

    } /* FOREVER */
} /* mainLoop() */

/****************************************************************************/
/* Function    : readCompensatorLevel                                       */
/* Purpose     : Read and filter Compensator Level Pot                      */
/* Inputs      : IBC card table, number of IBC cards in rack                */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
readCompensatorLevel( IBC_BoardEntry * IBC_Cards[], Int16 ibcCardCount )
{
    Int16 card;                         /* Card index into Card Table       */
    Nat16 rawLevel;                     /* Raw Compensator Level reading    */
    Nat16 level;                        /* Level in percent of full scale   */

                                        /* Check Presence of Low Power Sw   */
    if ( (card = ibcBoardTableIndex(IBC_Cards, ibcCardCount,
        (Byte *) LOW_PWR_SWITCH_12V_0_ADDR, LOW_POWER_SWITCH) ) == ERROR)
        return;
                                        /* Check that vicor is turned on    */
    if (!LPS_BoardSwitchState( (LPS_BoardEntry *) IBC_Cards[card],
           COMP_LEVEL_SENSOR_PWR_CHAN ))
    {
        compLevel.alarm.value = 0;      /* Set comp level to zero           */
        return;
    } /* if */

    if ( (card = ibcBoardTableIndex(IBC_Cards, ibcCardCount,
        (Byte *) QUAD_A_TO_D_0_ADDR, QUAD_A_TO_D ) ) == ERROR)
    return;
                                        /* Read raw compensator level       */
    compLevel.rawValue = rawLevel = AtoD_RawAtoDValue(
         (AtoD_BoardEntry *) IBC_Cards[card], COMPENSATION_LEVEL_CHAN );

    if (compLevel.initialized == FALSE) /* Wait for min/max via serial port */
        return;
                                        /* Convert to a percentage value    */
    level = (Nat16) (( ((Nat32) (rawLevel - compLevel.minValue)) * 100) /
                      (compLevel.maxValue - compLevel.minValue));

    if (level > 100) level = 100;
    level = 100 - level;
                                        /* Filter compensator level sensor  */
    if (IIRFilter( level, &compLevel.alarm.value, &compLevel.filter) == OK)
    {
         compLevel.alarm.value = min(compLevel.alarm.value,
                  COMP_LEVEL_SENSOR_MAX);
                                        /* Check if compLevel is below the  */
                                        /* current WARNING/ALARM thresholds */
                                        /* & send alarm SRQ's if necessary  */
        if (IBC_BelowThresholdCheck( compLevel.alarm.value,
            compLevel.alarm.warnThresh,  compLevel.alarm.alarmThresh,
            COMP_LEVEL_HYSTERESIS, &compLevel.alarm.alarmStatus ) == TRUE)

            writeServiceRequest(2, COMP_LEVEL_ALARM_SRQ,
                 compLevel.alarm.alarmStatus);
     } /* if */
} /* readCompensatorLevel() */

/****************************************************************************/
/* Function    : readHydraulicPressure                                      */
/* Purpose     : Read and filter Hydraulic Pressure Sensor                  */
/* Inputs      : IBC card table, number of IBC cards in rack                */
/* Outputs     : updates hydraulic pressure in PSI                          */
/****************************************************************************/
    MLocal Void
readHydraulicPressure( IBC_BoardEntry * IBC_Cards[], Int16 ibcCardCount )
{
    Int16 card;                         /* Card index into Card Table       */
    Nat16 pressure;                     /* Hydraulic Pressure in PSI        */

                                        /* Check Presence of Low Power Sw   */
    if ( (card = ibcBoardTableIndex(IBC_Cards, ibcCardCount,
        (Byte *) LOW_PWR_SWITCH_24V_0_ADDR, LOW_POWER_SWITCH) ) == ERROR)
        return;
                                        /* Check that vicor is turned on    */
    if (!LPS_BoardSwitchState( (LPS_BoardEntry *) IBC_Cards[card], LPS_VICOR))
    {
        hydPressure.value = 0;          /* Set pressure to zero             */
        return;
    } /* if */

    if ( (card = ibcBoardTableIndex(IBC_Cards, ibcCardCount,
        (Byte *) QUAD_A_TO_D_0_ADDR, QUAD_A_TO_D ) ) == ERROR)
    return;
                                        /* Read hydraulic pressure          */
    pressure = HYD_PRES_PSI(AtoD_RawAtoDValue(
        (AtoD_BoardEntry *) IBC_Cards[card], HYDRAULIC_PRESSURE_CHAN ));

                                        /* Filter hydraulic pressure        */
    if (IIRFilter( pressure, &hydPressure.value, &hydPressureFilter) == OK)
    {
                                        /* Check if pressure is below the   */
                                        /* current WARNING/ALARM thresholds */
                                        /* & send alarm SRQ's if necessary  */
        if (IBC_BelowThresholdCheck( hydPressure.value,
            hydPressure.warnThresh,  hydPressure.alarmThresh,
            HYD_PRES_HYSTERESIS, &hydPressure.alarmStatus ) == TRUE)

            writeServiceRequest(2, HYDRAULIC_PRES_ALARM_SRQ,
                 hydPressure.alarmStatus);
    } /* if */
} /* readHydraulicPressure() */

/****************************************************************************/
/* Function    : readVbSystemLevel                                          */
/* Purpose     : Read and filter VB System Tank Level                       */
/* Inputs      : IBC card table, number of IBC cards in rack                */
/* Outputs     : updates hydraulic pressure in PSI                          */
/****************************************************************************/
    MLocal Void
readVbSystemLevel( IBC_BoardEntry * IBC_Cards[], Int16 ibcCardCount )
{
#if 0
    Int16 card;                         /* Card index into Card Table       */
    Int16 vbLevel;                      /* VB Tank Level in bits            */


                                        /* Check Presence of Low Power Sw   */
    if ( (card = ibcBoardTableIndex(IBC_Cards, ibcCardCount,
        (Byte *) LOW_PWR_SWITCH_12V_0_ADDR, LOW_POWER_SWITCH) ) == ERROR)
        return;
                                        /* Check that switch is turned on   */
    if (!LPS_BoardSwitchState( (LPS_BoardEntry *) IBC_Cards[card],
        VB_LEVEL_SENSOR_PWR_CHAN ))
    {
        vbLevelDeciMM = 0;              /* Set VB level to zero             */
        return;
    } /* if */

    if ( (card = ibcBoardTableIndex(IBC_Cards, ibcCardCount,
        (Byte *) QUAD_A_TO_D_0_ADDR, QUAD_A_TO_D ) ) == ERROR)
    return;
                                        /* Read VB System Tank Level        */
    vbLevel = ATOD_BIPOLAR(AtoD_RawAtoDValue(
        (AtoD_BoardEntry *) IBC_Cards[card], VB_LEVEL_SENSOR_CHAN ));

                                        /* Filter VB System Level           */
    IIRFilter( VB_LEVEL_DECI_MM(vbLevel), &vbLevelDeciMM, &vbLevelFilter );
#endif
} /* readVbSystemLevel() */

/****************************************************************************/
/* Function    : readVehicleInWaterSensor                                   */
/* Purpose     : Read and debounce vehicle in-water sensor                  */
/* Inputs      : None                                                       */
/* Outputs     : Returns TRUE or FALSE                                      */
/****************************************************************************/
     MLocal MBool
readVehicleInWaterSensor( Void )
{
    Int16 delay;                        /* For short delay to debounce      */
    Int16 reads;                        /* Used to debounce in-water sensor */
    Int16 inWaterCount;                 /* Counts in-water TRUE status      */

    inWaterCount = 0;
    for (reads = 0; reads < NUM_IN_WATER_READS; reads++)
    {
        if (ioport0 & (0x01 << VEHICLE_IN_WATER_CHAN))
            inWaterCount++;

        for (delay = 0; delay < ERS_DEBOUNCE; delay++);
    } /* if */

    return (inWaterCount < (NUM_IN_WATER_READS - 1));
} /* readVehicleInWaterSensor() */

/****************************************************************************/
/* Function    : checkVehicleInWaterSensor                                  */
/* Purpose     : Check Vehicle In-Water Sensor & notify VME if state changed*/
/* Inputs      : None                                                       */
/* Outputs     : Modifies global vehicleInWater                             */
/****************************************************************************/
    MLocal Void
checkVehicleInWaterSensor( Void )
{
    Nat16 lastInWaterState;
                                    /* Read In-Water Sensor state           */
    lastInWaterState = vehicleInWater;
    vehicleInWater   = readVehicleInWaterSensor();

                                    /* Check if latch state changed         */
    if (vehicleInWater != lastInWaterState)
    {
                                    /* Notify VME of in-water status changed*/
        writeServiceRequest(2, VEHICLE_IN_WATER_SRQ, vehicleInWater);
    } /* if */
} /* checkVehicleInWaterSensor() */

/****************************************************************************/
/* Function    : readBatteryCurrentBits                                     */
/* Purpose     : Read battery current                                       */
/* Inputs      : IBC card table, number of IBC cards in rack                */
/* Outputs     : returns battery current in bits                            */
/****************************************************************************/
    MLocal Nat16
readBatteryCurrentBits( Void )
{
    Int16 card;                         /* Card index into Card Table       */

    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
        (Byte *) QUAD_A_TO_D_0_ADDR, QUAD_A_TO_D ) ) == ERROR)
    return(ERROR);

                                        /* Read battery current             */
    return (AtoD_RawAtoDValue(
       (AtoD_BoardEntry *) IBC_CardTable[card], EREC_BATTERY_CHAN ));
} /* readBatteryCurrentBits() */

/****************************************************************************/
/* Function    : checkOperatingMode                                         */
/* Purpose     : Set operating mode if ERS is on battery or vehicle power   */
/* Inputs      : None                                                       */
/* Outputs     : Returns TRUE/FALSE, global variables are changed           */
/****************************************************************************/
    MLocal MBool
checkOperatingMode( Void )
{
    Nat32 avgCurrent;                 /* Average current (microamps)        */
    ersOperatingMode lastOperatingMode = operatingMode;

                                      /* Measure average battery current    */
    avgCurrent = computeAvgBatteryCurrent();

                                      /* Decide if we're running off battery*/
    if (avgCurrent >= (Nat32) BATT_THRESHOLD_CURRENT)
                                      /* ****** RUNNING ON BATTERY ******** */
                                      /* Set operating Mode                 */
        operatingMode = EMERGENCY_MODE;
    else                              /* **** Running on External Power *****/
        operatingMode = OPERATING_MODE;

                                      /* Mode change so report to VME       */
    if (lastOperatingMode != operatingMode)
    {
        writeServiceRequest(2, ERS_MODE_SRQ, operatingMode);
                                  /* Schedule voltage readings every minute */
                                  /* in emergency mode, or once in operating*/
                                  /* mode                                   */
        batteryFSM.readTicks = read_sysclock();
        batteryFSM.readState = BATTERY_STATE_INIT;
    } /* if */

    if (operatingMode == EMERGENCY_MODE)
        if ( (batteryFSM.readState == BATTERY_STATE_IDLE) &&
            ((read_sysclock() - batteryFSM.readTicks) > ONE_MINUTE) )
             batteryFSM.readState = BATTERY_STATE_INIT;

                                       /* Return TRUE if mode changed        */
    return (lastOperatingMode != operatingMode ? TRUE : FALSE);
} /* checkOperatingMode() */

    MLocal Void
ersOperatingModeChange( Void )
{
    Int16 card;                       /* Card index into Card Table         */
    LPS_BoardEntry *lpsBoard;

                 /* **** Devices connected to 12V LPS Board **** */
                                      /* Check Presence of Low Power Sw     */
    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
        (Byte *) LOW_PWR_SWITCH_12V_0_ADDR, LOW_POWER_SWITCH) ) != ERROR)
    {
        lpsBoard = (LPS_BoardEntry *) IBC_CardTable[card];
        if (operatingMode == OPERATING_MODE)
        {                             /* Switch Vicor Output On             */
            LPS_BoardVicorCtrl( lpsBoard, ON );
        } /* if */
        else
        {                             /* Switch Off ERS Sensor Suite        */
            LPS_BoardSwitch( lpsBoard, PRESSURE_SENSOR_PWR_CHAN,   OFF );
            LPS_BoardSwitch( lpsBoard, VB_LEVEL_SENSOR_PWR_CHAN,   OFF );
            LPS_BoardSwitch( lpsBoard, COMP_LEVEL_SENSOR_PWR_CHAN, OFF );
            LPS_BoardSwitch( lpsBoard, TOOLSLED_SENSOR_PWR_CHAN,   OFF );
                                      /* Switch Vicor Output Off            */
            LPS_BoardVicorCtrl( lpsBoard, OFF);
        } /* if */
    } /* if */

                 /* **** Devices connected to 24V LPS 0 Board **** */

    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
        (Byte *) LOW_PWR_SWITCH_24V_0_ADDR, LOW_POWER_SWITCH) ) != ERROR)
    {
        lpsBoard = (LPS_BoardEntry *) IBC_CardTable[card];

        if (operatingMode == OPERATING_MODE)
        {                             /* Switch Vicor Output On             */
            LPS_BoardVicorCtrl( lpsBoard, ON);
                                      /* Switch On ERS Sensor Suite         */
        } /* if */
        else
        {                             /* Switch Off Devices/Solenoids       */
            LPS_BoardSwitch( lpsBoard, VB_SELECT_SOLENOID_CHAN,  OFF );
            LPS_BoardSwitch( lpsBoard, HYDRAULIC_SOLENOID_CHAN,  OFF );
            LPS_BoardSwitch( lpsBoard, ARM_HYD_SELECT_CHAN,      OFF );
            LPS_BoardSwitch( lpsBoard, TOOLSLED_HYD_SELECT_CHAN, OFF );
                                      /* Switch Vicor Output Off            */
            LPS_BoardVicorCtrl( lpsBoard, OFF );
        } /* else */
    } /* if */

                 /* **** Devices connected to 24V LPS 1 Board **** */

    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
        (Byte *) LOW_PWR_SWITCH_24V_1_ADDR, LOW_POWER_SWITCH) ) != ERROR)
    {
        lpsBoard = (LPS_BoardEntry *) IBC_CardTable[card];

        if (operatingMode == OPERATING_MODE)
        {                             /* Switch Vicor Output On             */
            LPS_BoardVicorCtrl( lpsBoard, ON );
        } /* if */
        else
        {
            LPS_BoardSwitch( lpsBoard, VB_FILL_SOLENOID_CHAN, OFF );
        } /* else */
    } /* if */
} /* ersOperatingModeChange () */

    MLocal Nat32
computeAvgBatteryCurrent( Void )
{
    Nat16 currentBits;                /* Raw current in bits                */
    Nat32 avgCurrentBits;             /* Average current (bits)             */
    Nat32 avgCurrent;                 /* Average current (microamps)        */

    avgCurrent = batteryFSM.oneSecCurrentDrain;

                                      /* Stop summing, if overflow          */
    if ((batteryFSM.oneSecCurrentSumBits < 0xffff0000) &&
        (batteryFSM.currentReadCount < 0xffff))
    {
                                      /* Read battery current in bits       */
        if ((currentBits = readBatteryCurrentBits()) == ERROR)
            return(0);
                                      /* Add battery current in bits        */
        batteryFSM.oneSecCurrentSumBits += currentBits;
        batteryFSM.currentReadCount++;

                                      /* One seconds has elapsed            */
        if (read_sysclock() >= batteryFSM.nextReadTick)
        {
            avgCurrentBits =          /* Compute average current over 1 sec */
                batteryFSM.oneSecCurrentSumBits / batteryFSM.currentReadCount;

            avgCurrent = BATTERY_MICROAMPS(avgCurrentBits);
            batteryFSM.oneSecCurrentDrain = avgCurrent;

            batteryFSM.updateFlags |= BATTERY_CURRENT_UPDATE;

                                      /* Add one sec avg to one minute sum  */
            batteryFSM.oneMinCurrentSumBits += avgCurrentBits;

            batteryFSM.currentReadCount = 0; /* Clear one second average    */
            batteryFSM.oneSecCurrentSumBits = 0;
            batteryFSM.nextReadTick = read_sysclock() + ONE_SECOND;

                                      /* If battery report time has elapsed */
            if (++batteryFSM.secondCount >= (BATTERY_REPORT_TIME - 1))
            {                         /* Compute cumulative current over a  */
                                      /* 1 minute period in milliamp-seconds*/
                batteryFSM.oneMinCurrentDrain =
                    BATTERY_MILLIAMPS(batteryFSM.oneMinCurrentSumBits);

                batteryFSM.secondCount = 0;  /* Clear one minute average    */
                batteryFSM.oneMinCurrentSumBits = 0;

                if (batteryFSM.oneMinCurrentDrain >=
                     MICROAMPS_TO_MILLIAMPS(BATT_THRESHOLD_CURRENT))
                {
                    writeCurrentToEeprom(batteryFSM.oneMinCurrentDrain);
                    checkBatteryLife(); /* Check battery life used so far   */
                } /* if */

            } /* if (Battery report time elapsed) */
        } /* if (One second elapsed) */
    } /* if */

    return (avgCurrent);
} /* computeAvgBatteryCurrent() */

/****************************************************************************/
/* Function    : measureBatteryVoltage                                      */
/* Purpose     : Wait for battery voltage mreeasurement update              */
/* Inputs      : IBC card table, number of IBC cards in rack                */
/* Outputs     : updates battery voltage in millivolts                      */
/****************************************************************************/
    MLocal Int16
measureBatteryVoltage( Void )
{
    do
    {
                               /* Execute any IBC card specific functions  */
        IBC_CardFunctions( &IBC_CardTable, IBC_CardCount );
        readBatteryVoltage();
    } while ((batteryFSM.readState != BATTERY_STATE_IDLE)
          && (batteryFSM.readState != BATTERY_STATE_INIT));
} /* measureBatteryVoltage() */

/****************************************************************************/
/* Function    : readBatteryVoltage                                         */
/* Purpose     : Read battery voltage                                       */
/* Inputs      : IBC card table, number of IBC cards in rack                */
/* Outputs     : updates battery voltage in millivolts                      */
/****************************************************************************/
    MLocal Void
readBatteryVoltage( Void )
{
    static Nat16 noLoadCurrent;
    static Nat16 loadCurrent;

    switch (batteryFSM.readState)
    {
        case BATTERY_STATE_IDLE:         /* Disable battery dummy load      */
            clrIoport1Bit(BATTERY_LOAD_ENABLE);
            break;

        case BATTERY_STATE_INIT:        /* Disable battery dummy load       */
            clrIoport1Bit(BATTERY_LOAD_ENABLE);
            batteryFSM.readTicks = read_sysclock();
            batteryFSM.readState = DUMMY_LOAD;
            break;

        case DUMMY_LOAD:               /* Read current without dummy load  */
            if ( (read_sysclock() - batteryFSM.readTicks) > ONE_SECOND )
            {
                noLoadCurrent = readBatteryCurrentBits();

                                       /* Enable battery dummy load resistor*/
                setIoport1Bit(BATTERY_LOAD_ENABLE);
                batteryFSM.readTicks = read_sysclock();
                batteryFSM.readState = READ_VOLTAGE;
            } /* if */
            break;

        case READ_VOLTAGE:              /* Calculate battery voltage        */
            if ( (read_sysclock() - batteryFSM.readTicks) > ONE_SECOND )
            {
                loadCurrent = readBatteryCurrentBits();

                                        /* Disable battery dummy load       */
                clrIoport1Bit(BATTERY_LOAD_ENABLE);
                batteryFSM.readTicks = read_sysclock();
                batteryFSM.readState = BATTERY_STATE_IDLE;

                batteryVoltage.value = BATTERY_MILLIVOLTS(
                     BATTERY_MICROAMPS(loadCurrent - noLoadCurrent));

                batteryFSM.updateFlags |= BATTERY_VOLTAGE_UPDATE;

                                        /* Check if voltage is below the    */
                                        /* current WARNING/ALARM thresholds */
                                        /* & send alarm SRQ's if necessary  */
                if (IBC_BelowThresholdCheck( batteryVoltage.value,
                    batteryVoltage.warnThresh, batteryVoltage.alarmThresh,
                    BATTERY_HYSTERESIS, &batteryVoltage.alarmStatus ) == TRUE)

                    writeServiceRequest(2, BATTERY_VOLTAGE_ALARM_SRQ,
                        batteryVoltage.alarmStatus);
            } /* if */
            break;

    } /* switch */
} /* readBatteryVoltage() */

/****************************************************************************/
/* Function    : eepromWriteCompleted                                       */
/* Purpose     : Wait for EEPROM write cycle to complete                    */
/* Inputs      : Last EEPROM address written and value written              */
/* Outputs     : Returns OK if write was successful, else ERROR             */
/****************************************************************************/
    MLocal Void
eepromWriteCompleted( volatile void* eepromAddr )
{
    Byte  memVal;

    do
    {
        memVal = *(Byte *) eepromAddr;
    } while (memVal != *(Byte *) eepromAddr);
} /* eepromWriteCompleted() */

    MLocal Void
eepromDataProtectMode(MBool mode)
{
    if (mode == TRUE)
    {                              /* Enable EEPROM Software Write Protect  */
                                   /* See Atmel AT28HC256 Data Sheet        */
        *(EEPROM_REG_ADDR(0x5555)) = 0xaa;
        *(EEPROM_REG_ADDR(0x2aaa)) = 0x55;
        *(EEPROM_REG_ADDR(0x5555)) = 0xa0;
    }
    else
    {                              /* Disable EEPROM Software Write Protect */
                                   /* See Atmel AT28HC256 Data Sheet        */
        *(EEPROM_REG_ADDR(0x5555)) = 0xaa;
        *(EEPROM_REG_ADDR(0x2aaa)) = 0x55;
        *(EEPROM_REG_ADDR(0x5555)) = 0x80;

        *(EEPROM_REG_ADDR(0x5555)) = 0xaa;
        *(EEPROM_REG_ADDR(0x2aaa)) = 0x55;
        *(EEPROM_REG_ADDR(0x5555)) = 0x20;
    } /* if */

    eepromWriteCompleted( (void *) EEPROM_BASE_ADDR);
} /* eepromDataProtectMode() */

   MLocal Void
writeCurrentToEeprom(Nat32 milliAmpSeconds)
{
    Nat16 writes;
    Nat16 location;

    volatile eepromData *eeprom = (eepromData *)
        (((char*) EEPROM_BASE_ADDR) + (EEPROM_SIZE - sizeof(eepromData)));

    if (eeprom->magicNumber != EEPROM_MAGIC)
    {                               /* EEPROM is corrupt so initialize      */
        intDisable;
        eepromDataProtectMode(FALSE); /* Disable EEPROM Data Protect Mode   */

        for (location = 0; location < EEPROM_ENTRIES; location++)
        {
            eeprom->currentEntry[location].writes = 1;
            eeprom->currentEntry[location].milliAmpSeconds = 0;
        } /* if */

        eeprom->location = 0;;
        eeprom->magicNumber = EEPROM_MAGIC;
        eepromWriteCompleted(eeprom);

        eepromDataProtectMode(TRUE);/* Enable EEPROM Data Protect Mode      */
        intEnable;
    } /* if */

                                    /* Copy location counter from EEPROM    */
    if ((location = eeprom->location) > EEPROM_ENTRIES)
        return;

    milliAmpSeconds += eeprom->currentEntry[location].milliAmpSeconds;

    while (location <= EEPROM_ENTRIES)
    {
        writes = eeprom->currentEntry[location].writes;
                                    /* Num of writes exceeded use next entry*/
        if (writes <= EEPROM_MAX_WRITES)
        {
            intDisable;
            eepromDataProtectMode(FALSE);/* Disable EEPROM Data Protect Mode*/

                                   /* Store new location index value        */
            if (location != eeprom->location)
                eeprom->location = location;

            eeprom->currentEntry[location].milliAmpSeconds = milliAmpSeconds;
            eeprom->currentEntry[location].writes = writes + 1;
            eepromWriteCompleted(eeprom);

            eepromDataProtectMode(TRUE); /* Enable EEPROM Data Protect Mode */
            intEnable;
            return;                /* EEPROM Values update so exit          */
        } /* if */

        location++;
    } /* while */
} /* writeCurrentToEeprom() */

    MLocal Void
checkBatteryLife( Void )
{
    Nat16 location;
    Nat16 spentLifeInDeciAmpHours;

    volatile eepromData *eeprom = (eepromData *)
        (((char*) EEPROM_BASE_ADDR) + (EEPROM_SIZE - sizeof(eepromData)));

    if (eeprom->magicNumber != EEPROM_MAGIC)
        return;

    if ((location = eeprom->location) > EEPROM_ENTRIES)
        return;

    batteryLife.value =
         MA_SEC_TO_DECI_AH(eeprom->currentEntry[location].milliAmpSeconds);

    batteryFSM.updateFlags |= BATTERY_LIFE_UPDATE;

    if (IBC_AlarmThresholdCheck( batteryLife.value,
         batteryLife.warnThresh, batteryLife.alarmThresh,
         BATTERY_LIFE_HYSTERESIS, &batteryLife.alarmStatus ) == TRUE)

        writeServiceRequest(2, BATTERY_LIFE_SRQ, batteryLife.alarmStatus);
} /* checkBatteryLife() */

/****************************************************************************/
/* Function    : vmeConsoleRouteSelect                                      */
/* Purpose     : Selects route for VME computer console serial port         */
/* Inputs      : route select (CONSOLE_TO_IBC or CONSOLE_TO_TELEM)          */
/* Outputs     : None                                                       */
/****************************************************************************/
    MLocal Void
vmeConsoleRouteSelect( consoleRouteSelect route )
{
    if (route == CONSOLE_TO_IBC)   /* Set Port Bit High to select IBC      */
        writeIoport1(VME_CONSOLE_PORT_CONTROL, OR_PORT);

    if (route == CONSOLE_TO_TELEM) /* Set Port Bit Low to select Telemetry */
        writeIoport1(~VME_CONSOLE_PORT_CONTROL, AND_PORT);
} /* vmeConsoleRouteSelect() */

   MLocal Void
stopDeploymentLatch( Void )
{
    Int16 card;                       /* Card index into Card Table         */

                                      /* Check Presence of Low Power Sw     */
    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
        (Byte *) LOW_PWR_SWITCH_24V_1_ADDR, LOW_POWER_SWITCH) ) == ERROR)
        return;

    LPS_BoardSwitch( (LPS_BoardEntry *) IBC_CardTable[card],
        DEPLOY_LATCH_RELEASE_CHAN, OFF );

    writeServiceRequest(2, DEPLOYMENT_LATCH_RUN_SRQ, OFF);
} /* stopDeploymentLatch() */

    MLocal Int16
deploymentLatchRelease( MBool state, Nat16 duration )
{
    Int16 card;                       /* Card index into Card Table         */
    Int16 status;
    LPS_BoardEntry *lpsBoard;

                                      /* Check Presence of Low Power Sw     */
    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
        (Byte *) LOW_PWR_SWITCH_24V_1_ADDR, LOW_POWER_SWITCH) ) == ERROR)
        return(ERROR);

    lpsBoard = (LPS_BoardEntry *) IBC_CardTable[card];

    if (deploymentLatch.onTimer != (timerId) NULL)
        timerCancel(deploymentLatch.onTimer);

    if (state == OFF)
        status = LPS_BoardSwitch( lpsBoard, DEPLOY_LATCH_RELEASE_CHAN, OFF );
    else
    {
        if (!deploymentLatch.armed)
            return (ERROR);

        status = LPS_BoardSwitch( lpsBoard, DEPLOY_LATCH_RELEASE_CHAN, ON );
        if ((duration > 0) && (deploymentLatch.onTimer != (timerId) NULL))
                                       /* Start Timer for latch off period */
            timerStart(deploymentLatch.onTimer, duration,
                stopDeploymentLatch, 0);
    } /* else */

   if (status == OK)
       writeServiceRequest(2, DEPLOYMENT_LATCH_RUN_SRQ, state);

    return(status);
} /* deploymentLatchRelease() */

    MLocal Void
vbSystemFillCmd( Void )
{
    Int16 card;                       /* Card index into Card Table         */
    LPS_BoardEntry *lpsBoard;

                                      /* Check Presence of Low Power Sw     */
    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
        (Byte *) LOW_PWR_SWITCH_24V_1_ADDR, LOW_POWER_SWITCH) ) == ERROR)
        return;

    lpsBoard = (LPS_BoardEntry *) IBC_CardTable[card];

    LPS_BoardVicorCtrl(lpsBoard, ON);
    LPS_BoardSwitch( lpsBoard, VB_FILL_SOLENOID_CHAN, ON );

    taskDelay(SYSCLOCK_RATE);

    LPS_BoardSwitch( lpsBoard, VB_FILL_SOLENOID_CHAN, OFF );
    LPS_BoardVicorCtrl(lpsBoard, OFF);
} /* vbSystemFillCmd() */

    MLocal Void
ersSendDataReport( Void )
{
    char buffer[32];
    Nat16 location;
    Nat16 batteryLifeRemaining;

    volatile eepromData *eeprom = (eepromData *)
        (((char*) EEPROM_BASE_ADDR) + (EEPROM_SIZE - sizeof(eepromData)));

    if (eeprom->magicNumber == EEPROM_MAGIC)
        batteryLifeRemaining = ((Nat16)
            (eeprom->currentEntry[location].milliAmpSeconds * 100L)
            / BATTERY_CAPACITY);
    else
        batteryLifeRemaining = 999;

#if 0
    sprintf(buffer, "%4d %4d %3d %3d %3d\n",
            pressureSensor.integer, pressureSensor.fraction,
            batteryLifeRemaining, batteryVoltage.value * 100,
            compLevel.alarm.value);
#endif
} /* ersSendDataReport() */

    MLocal Void
ersSendErrorMsg( Void )
{
} /* ersSendErrorMsg() */

   MLocal Void
ersSingleDataReport( Void )
{
    if (ersSwitchSensorsPower( ON ) == ERROR)
    {
        ersSendErrorMsg();
        return;
    } /* if */

    dataReportMode = SINGLE_DATA_REPORT;
    taskDelay (TEN_SECONDS);          /* Wait for sensors to power up       */
    ersReadSensors();                 /* Read ERS Sensor Suite              */
    ersSendDataReport();              /* Send Data to Modem                 */

    if (operatingMode != OPERATING_MODE)
        ersSwitchSensorsPower( OFF );

    dataReportMode = STOP_DATA_REPORT;
} /* ersSingleDataReport() */

   MLocal Void
ersSlowDataReport( Void )
{
    Nat16 delay;

    if (ersSwitchSensorsPower( ON ) == ERROR)
    {
        ersSendErrorMsg();
        return;
    } /* if */

    dataReportMode = SLOW_DATA_REPORT;
    taskDelay (TEN_SECONDS);          /* Wait for sensors to power up        */

    while(dataReportMode == SLOW_DATA_REPORT)
    {
        ersReadSensors();             /* Read ERS Sensor Suite               */
        ersSendDataReport();          /* Send Data to Modem                  */

        for (delay = 0;
            (delay < ONE_MINUTE/ TEN_SECONDS) &&
            (dataReportMode == SLOW_DATA_REPORT);
             delay++)

            taskDelay (TEN_SECONDS);  /* Wait 1 Minute between updates       */
    } /* while */

    if ( (dataReportMode == STOP_DATA_REPORT) &&
        (operatingMode != OPERATING_MODE) )
        ersSwitchSensorsPower( OFF );
} /* ersSlowDataReport() */


   MLocal Void
ersFastDataReport( Void )
{
    if (ersSwitchSensorsPower( ON ) == ERROR)
    {
        ersSendErrorMsg();
        return;
    } /* if */

    dataReportMode = FAST_DATA_REPORT;

    while(dataReportMode == FAST_DATA_REPORT)
    {
        taskDelay (TEN_SECONDS);      /* Wait for sensors to power up       */
        ersReadSensors();             /* Read ERS Sensor Suite              */
        ersSendDataReport();          /* Send Data to Modem                 */
    } /* while */

    if ( (dataReportMode == STOP_DATA_REPORT) &&
        (operatingMode != OPERATING_MODE) )
        ersSwitchSensorsPower( OFF );
} /* ersFastDataReport() */

    MLocal Void
ersStopDataReporting( Void )
{
    dataReportMode = STOP_DATA_REPORT;
} /* ersStopDataReporting() */

    MLocal Int16
ersSwitchSensorsPower( MBool state )
{
    Int16 card;                       /* Card index into Card Table         */
    LPS_BoardEntry *lpsBoard;

                                      /* Check Presence of Low Power Sw     */
    if ( (card = ibcBoardTableIndex(IBC_CardTable, IBC_CardCount,
        (Byte *) LOW_PWR_SWITCH_12V_0_ADDR, LOW_POWER_SWITCH) ) == ERROR)
        return(ERROR);

    lpsBoard = (LPS_BoardEntry *) IBC_CardTable[card];

    if (state == ON)                  /* Switch Vicor Output On             */
        LPS_BoardVicorCtrl(lpsBoard, ON);
                                      /* Switch On ERS Sensor Suite         */
    LPS_BoardSwitch( lpsBoard, PRESSURE_SENSOR_PWR_CHAN,   state );
    LPS_BoardSwitch( lpsBoard, COMP_LEVEL_SENSOR_PWR_CHAN, state );
    LPS_BoardSwitch( lpsBoard, TOOLSLED_SENSOR_PWR_CHAN,   state );

    if (state == OFF)                 /* Switch Vicor Output Off            */
        LPS_BoardVicorCtrl(lpsBoard, OFF);
    return (OK);
} /* ersSwitchSensorsPower() */

    MLocal Void
ersReadSensors( Void )
{
                                      /* Read Falmouth Pressure Sensor      */
    readFalmouthSensorValue( &pressureSensor );
                                      /* Read Compensation Level Sensor     */
    readCompensatorLevel( &IBC_CardTable, IBC_CardCount );

    if (operatingMode == OPERATING_MODE)
    {
#if 0
                                /* Read VB System Tank Level Sensor         */
        readVbSystemLevel( &IBC_CardTable, IBC_CardCount );

#endif
                                /* Read Hydraulic Pressure Sensor           */
        readHydraulicPressure( &IBC_CardTable, IBC_CardCount );
    } /* if */

    checkVehicleInWaterSensor();      /* Check Vehicle In-Water Sensor      */
} /* ersReadSensors() */

    MLocal Void
addIbcDataSwitchBoard( Void )
{
    IBC_BoardEntry *dataSwitchBoard;

    Byte      *baseAddress;             /* Board base address               */
    IBC_BoardType boardType;            /* Board type                       */
    Byte      intrLevel;                /* Board interrupt level            */

                                      /* Allocate space for board entry     */
    dataSwitchBoard = (IBC_BoardEntry *) malloc( sizeof (IBC_BoardEntry) );

    dataSwitchBoard->baseAddress = (Byte *) DATA_SWITCH_BOARD_0_ADDR;
    dataSwitchBoard->boardType = DATA_SWITCH;
    dataSwitchBoard->intrLevel = IBC_INT_LVL_OFF;

                                /* Add Entry for Data Switch Board          */
    IBC_CardTable[IBC_CardCount++] = dataSwitchBoard;
} /* addIbcDataSwitchBoard() */

    Int16
initRovnav( Byte* boardAddr, Nat16 chan,IBC_BoardEntry *IBC_CardTable[],
    Int16 cardCount )
{
    Int16 serialCard;           /* Serial Card index into IBC Card Table    */

    rovnav.cmdRecvd = FALSE;
    rovnav.cmdLen = 0;
    rovnav.serialChan.boardEntry = (serialBoardEntry*) NULL;

                                /* Find card index of serial board in table */
    if ( (serialCard = ibcBoardTableIndex(IBC_CardTable, cardCount,
         boardAddr, QUAD_SERIAL) ) == ERROR)
        return (ERROR);

    rovnav.serialChan.boardEntry =
       (serialBoardEntry*) IBC_CardTable[serialCard];

    rovnav.serialChan.channel = chan;

    serialBoardOpenChan(rovnav.serialChan.boardEntry, chan);
                               /* Set for 9600, 8 data, 1 stop, no parity */
    sccSetLineFormat(rovnav.serialChan.boardEntry, chan,
             9600, 8, 1, 2, HANDSHAKE_OFF, RS485_MODE);

    return (OK);
} /* initRovnav() */

    Void
checkRovnav( Void )
{
  Byte   buffer[ROVNAV_CMD_LEN + 2];

  if (serialBoardRxBytes(rovnav.serialChan.boardEntry,
      rovnav.serialChan.channel) >= ROVNAV_CMD_LEN)
  {
      while(serialBoardRxBytes(rovnav.serialChan.boardEntry,
                      rovnav.serialChan.channel))
      {
          if (serialBoardRead(rovnav.serialChan.boardEntry,
                      rovnav.serialChan.channel, buffer, 1) > 0)
          {
              if (buffer[0] == '$')
              {
                  buffer[1] = buffer[2] = buffer[3] = 0;
                  if (serialBoardRead(rovnav.serialChan.boardEntry,
                      rovnav.serialChan.channel,
                      buffer + 1, ROVNAV_CMD_LEN - 1) > 0)
                  {
#if 0
                          itoa(reply, pressureSensor.integer,
                           4, 16, '0', UNSIGNED);
#endif
                      serialBoardWrite(rovnav.serialChan.boardEntry,
                         rovnav.serialChan.channel, "1234", 4);

                      writeServiceRequest(3, ROVNAV_MSG_SRQ,
                        bytesToWord(buffer[0], buffer[1]),
                        bytesToWord(buffer[2], buffer[3]));
                  } /* if */
              } /* if */
          } /* if */
      } /* while */
  } /* if */
} /* checkRovnav() */



