/*
 *   THIS FILE IS UNDER RCS - DO NOT MODIFY UNLESS YOU HAVE
 *   CHECKED IT OUT USING THE COMMAND CHECKOUT.
 *
 *    $Id: k2misc.c,v 1.29 2005/07/27 19:28:49 friberg Exp $
 *
 *    Revision history:
 *     $Log: k2misc.c,v $
 *     Revision 1.29  2005/07/27 19:28:49  friberg
 *     2.40 changes for ForceBlockMode and comm stats
 *
 *     Revision 1.28  2005/03/26 00:17:46  kohler
 *     Version 2.38.  Added capability to get network code values from the K2
 *     headers.  The "Network" parameter in the config file is now optional.
 *     WMK 3/25/05
 *
 *     Revision 1.27  2004/06/04 16:36:04  lombard
 *     Fixed parts of extended status message.
 *
 *     Revision 1.26  2003/07/17 19:23:27  friberg
 *     Fixed a status message to include network and station name for the k2
 *     from which the error originated.
 *
 *     Revision 1.25  2003/06/06 01:24:33  lombard
 *     Changed to version 2.34: fix for byte alignment problem in extended status
 *     structure.
 *
 *     Revision 1.24  2003/05/29 13:33:40  friberg
 *     Added in calls to k2info and fixed GPS status message to include
 *     the station name. Changed all status messages to include
 *     network code in addition to station name
 *
 *     Revision 1.23  2003/05/15 00:41:47  lombard
 *     *** empty log message ***
 *
 *     Revision 1.22  2002/05/06 18:24:58  kohler
 *     Fixed typo in log statement in function k2mi_init_blkmde().
 *
 *     Revision 1.21  2002/04/22 16:35:55  lucky
 *     k2ew was being restarted by startstop because it would stop beating its
 *     heart when re-trying to connect. Made it so that we continue to beat our
 *     heart even when we go through the retry loop several times.
 *
 *     Revision 1.20  2002/01/30 14:28:31  friberg
 *     added robust comm recovery for time out case
 *
 *     Revision 1.19  2001/10/19 18:21:28  kohler
 *     k2mi_log_pktinfo() now sends msg to statmgr if a pcdrv error occurs.
 *
 *     Revision 1.18  2001/10/16 22:03:56  friberg
 *     Upgraded to version 2.25
 *
 *     Revision 1.17  2001/08/08 16:11:48  lucky
 *     version 2.23
 *
 *     Revision 1.15  2001/05/23 00:20:00  kohler
 *     Now, optionally writes entire K2 header to a binary file.
 *
 *     Revision 1.14  2001/05/08 00:14:38  kohler
 *     Minor logging changes.
 *
 *     Revision 1.13  2001/04/23 20:24:10  friberg
 *     Added station name remapping using the StationId config parameter.
 *
 *     Revision 1.12  2000/11/28 00:45:46  kohler
 *     Cosmetic changes to log file output.
 *
 *     Revision 1.11  2000/11/07 19:35:01  kohler
 *     Modified to complain if no GPS lock for xx hours.
 *
 *     Revision 1.10  2000/08/30 17:34:00  lombard
 *     See ChangeLog entry for 30 August 2000
 *
 *     Revision 1.9  2000/07/28 22:36:10  lombard
 *     Moved heartbeats to separate thread; added DontQuick command; removed
 *     redo_com() since it doesn't do any good; other minor bug fixes
 *
 *     Revision 1.8  2000/07/03 18:00:37  lombard
 *     Added code to limit age of waiting packets; stops circ buffer overflows
 *     Added and Deleted some config params.
 *     Added check of K2 station name against restart file station name.
 *     See ChangeLog for complete list.
 *
 *     Revision 1.7  2000/06/18 20:17:46  lombard
 *     transport calls for status and hearbteats was inproperly commented out.
 *
 *     Revision 1.6  2000/06/09 23:14:23  lombard
 *     Several bug fixes and improvements; See Changelog entry of 2000-06-09.
 *
 *     Revision 1.5  2000/05/17 15:12:42  lombard
 *     bug fix in ping_test
 *
 *     Revision 1.4  2000/05/16 23:39:16  lombard
 *     bug fixes, removed OutputThread Keepalive, added OnBattery alarm
 *     made alarms report only once per occurence
 *
 *     Revision 1.3  2000/05/12 19:02:19  lombard
 *     fixed g_stnid typo
 *
 *     Revision 1.2  2000/05/12 04:04:05  lombard
 *     Fixed conversion of disk space units
 *
 *     Revision 1.1  2000/05/04 23:48:20  lombard
 *     Initial revision
 *
 *
 *
 */
/*  k2misc.c:  Miscellaneous functions for K2:            */
/*      init_blkmde, ping_test, get_status, get_params    */
/*                                                        */
/*    1/1/99 -- [ET]  File started                        */
/*                                                        */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <earthworm.h>
#include <time.h>
#include "byteswap.h"
#include "glbvars.h"
#include "k2pktdef.h"        /* K2 packet definitions and types */
#include "k2ewerrs.h"        /* K2-to-Earthworm error codes */
#include "k2comif.h"         /* K2 COM port interface routines */
#include "k2pktio.h"         /* K2 packet send/receive routines */
#include "k2pktman.h"        /* K2 packet management functions */
#include "k2misc.h"          /* header file for this module */
#include "terminat.h"        /* for status/termination codes */
#include "k2info.h"          /* for info packet injection codes */

#define K2MI_INIT_TOUTMS  5000     /* packet timeout during init */
#define K2MI_PING_TOUTMS  1000     /* receive ping timeout in ms */
#define K2MI_PARM_TOUTMS  1000     /* receive params timeout in ms */
#define K2MI_TRAN_TOUTMS  500      /* transmission timeout */
#define K2MI_FLSH_SHORT   100      /* short flush interval in ms */
#define K2MI_FLSH_LONG    500      /* long flush interval in ms */
#define K2MI_MONMODE_MAX  6        /* number of times to try mon-mode cmd */
#define K2MI_BLKMODE_MAX  6        /* number of times to try block-mode cmd */
#define K2MI_PKT_SRCNUM   44       /* arbitrary packet source # for file */

/* buffer size for 'k2mi_init_blkmde()' fn: */
#define K2MI_IBFSIZ ((PACKET_MAX_SIZE-11)/5)

#define MAX_MSG_SIZE      256       /* Large enough for all faults in extended
                                       status message */
static char msg[MAX_MSG_SIZE];      /* Buffer for text messages */

static struct PACKET_HDR g_k2mi_hdrblk;          /* received header buffer */
static unsigned char g_k2mi_buff[PACKET_MAX_SIZE-9]; /* received data buffer */
static int rep_on_batt = 0, rep_low_batt = 0, rep_hwstat = 0;
static int rep_temp, rep_disk[2] = {0,0};

/**************************************************************************
 * k2mi_init_blkmde:  verifies communications with K2 unit. If K2 is      *
 *       already in block mode, then return. Otherwise, try to establish  *
 *       communications with the K2                                       *
 *       Sockets are never retried on timeouts                            *
 *         *pseqnum - address of sequence number for sent command packet  *
 *                    which will be incremented                           *
 *      return value:  K2R_POSITIVE if K2 is already sending stream data  *
 *                     K2R_NO_ERROR K2 has been switched to block mode    *
 *                     K2R_ERROR if an error occurred                     *
 *                     K2R_TIMEOUT if a timeout occured while             *
 *                         communicating with K2                          *
 **************************************************************************/

int k2mi_init_blkmde(unsigned char *pseqnum)
{
  int blkmde_count = K2MI_BLKMODE_MAX;
  int monmde_count = K2MI_MONMODE_MAX;
  int idx, dcnt, slen, rc1, rc2;
  static const char *monmde_str_arr[]={          /* monitor-mode strings */
                                 "\\\\\\\\\r", "\r","\\\\\\\r","\\","\r\\\\\\\\\r"};
  static const char blkmde_str[]="BLOCK\r";      /* block-mode command string */

  idx = 0;                  /* initialize monitor-mode string array index */


  do     /* loop while attempting to activate and confirm K2 block mode */
  {
  	/* Tell heartbeat thread we're happy */
	  g_mt_working = 1;


    if (gcfg_debug > 3)
      logit("e", "flushing...");
    if (k2c_flush_recv(K2MI_FLSH_SHORT) != K2R_NO_ERROR)
      return K2R_ERROR;
    if (gcfg_debug > 3)
      logit("e", "done\n");

    /* check if data is coming in and if it's valid packets */
    if ( (rc1 = k2c_rcvflg_tout(K2MI_FLSH_LONG)) == K2R_ERROR)
    {
      return rc1;
    }
    else if (rc1 == K2R_NO_ERROR )
    {         /* Data ready to be read */
      if ( (rc2 = k2pm_recv_wstmpkt(&g_k2mi_hdrblk, g_k2mi_buff,
                                    gcfg_commtimeout_itvl, 0)) == K2R_POSITIVE)
      {
        if (gcfg_debug > 3)
          logit("e", "k2mi_init_blkmde: stream packet ret on try %d\n",
                blkmde_count - K2MI_BLKMODE_MAX);
        return K2R_POSITIVE;   /* Found K2 already streaming in block mode */

      }
      else if (rc2 == K2R_ERROR)
        return rc2;
    }

    /* no valid packets coming in */
    /* check if K2-ping can be sent and received */
    if ( (rc1 = k2mi_ping_test(1, pseqnum)) == K2R_NO_ERROR)
      return rc1;                   /* ping returned OK, comms are up */
    else if (rc1 == K2R_ERROR)
      return rc1;                   /* comm error; give up */

    monmde_count = K2MI_MONMODE_MAX;
    do
    {    /* loop while attempting to activate and confirm K2 monitor mode */

  	  /* Tell heartbeat thread we're happy */
	  g_mt_working = 1;

      /* send a monitor-mode string */
      if ( (slen = strlen(monmde_str_arr[idx])) == 1)
      {
        /* if '\' (or '\r') then delay a bit before */
        if ( (rc1 = k2c_flush_recv(K2MI_FLSH_LONG)) == K2R_ERROR)
          return rc1;
      }

      if (gcfg_debug > 2)
        logit("e", "sending MM %d\n", idx);

      if ( (rc1 = k2c_tran_buff((const unsigned char *)(monmde_str_arr[idx]),
                                slen, gcfg_commtimeout_itvl, 0)) != slen)
        return rc1;     /* if error transmitting then return */

      /* increment index into monitor-mode string array */
      if(++idx >= (sizeof(monmde_str_arr)/sizeof(monmde_str_arr[0])))
        idx = 0;             /* if past end-of-array then wrap around to beg */

      /* wait a bit for (possible) data to appear */
      /*  (wait a bit longer if '\' (or '\r') command) */
      if ( (rc1 = k2c_rcvflg_tout( ((slen==1)?K2MI_FLSH_LONG:K2MI_FLSH_SHORT)))
           == K2R_ERROR)
        return rc1;

      /* save any data returned from K2 */
      if ( (dcnt = k2c_recv_buff(g_k2mi_buff, K2MI_IBFSIZ,
                                 gcfg_commtimeout_itvl, 0)) > 0)
      {       /* data was received */
        /* Tell heartbeat thread we're happy */
        g_mt_working = 1;

        g_k2mi_buff[dcnt] = '\0';           /* NULL terminate buffer */
        if (gcfg_debug > 2)
          logit("e", "received:  %s\n", g_k2mi_buff);

        /* check if returned data contains monitor-mode prompt ('*') */
        if ( strchr((char *)g_k2mi_buff, (int)'*') != NULL)
          break;             /* if monitor-mode prompt ('*') then exit loop */
      }
      else if (dcnt == K2R_ERROR)
        return dcnt;
    }
    while(--monmde_count);         /* loop if count not expired */
    /* Now we should be in monitor mode, unless monmde_count expired */

    /* send block-mode command string */
    slen = strlen(blkmde_str);
    if (gcfg_debug > 2)
      logit("e", "%s\n",blkmde_str);

    if ( (rc1 = k2c_tran_buff((unsigned char *)blkmde_str, slen,
                                          gcfg_commtimeout_itvl,0)) != slen)
    {    /* error transmitting; return code */
      return (rc1 < 0) ? rc1 : K2R_ERROR;   /* only use code if negative */
                                            /* 1/29/2002 -- [ET] */
    }
    if ( (rc1 = k2c_flush_recv(K2MI_FLSH_SHORT)) == K2R_ERROR)
      return rc1;
  }
  while (--blkmde_count);           /* loop if count not expired */
  logit("et", "k2mi_init_blkmde: failed to put k2 in block mode\n");
  return K2R_ERROR;
}

/**************************************************************************
 * k2mi_force_blkmde:  forces communications with K2 unit into BLOCK mode.*
 *       Sockets are never retried on timeouts                            *
 *      return value:  K2R_POSITIVE has been switched to block mode       *
 *                     K2R_ERROR if an error occurred                     *
 *                     K2R_TIMEOUT if a timeout occured while             *
 *                         communicating with K2                          *
 **************************************************************************/
int k2mi_force_blkmde()
{
  int blkmde_count = K2MI_BLKMODE_MAX;
  int monmde_count = K2MI_MONMODE_MAX;
  int idx, dcnt, slen, rc1, rc2;
  static const char *monmde_str_arr[]={          /* monitor-mode strings */
                                 "\\\\\\\\\r", "\r","\\\\\\\r","\\","\r\\\\\\\\\r"};
  static const char blkmde_str[]="BLOCK\r";      /* block-mode command string */

  idx = 0;                  /* initialize monitor-mode string array index */


  do     /* loop while attempting to activate and confirm K2 block mode */
  {
  	/* Tell heartbeat thread we're happy */
	  g_mt_working = 1;


    if (gcfg_debug > 2)
      logit("e", "flushing...");
    if (k2c_flush_recv(K2MI_FLSH_SHORT) != K2R_NO_ERROR)
      return K2R_ERROR;
    if (gcfg_debug > 2)
      logit("e", "done\n");

    monmde_count = K2MI_MONMODE_MAX;
    do
    {    /* loop while attempting to activate and confirm K2 monitor mode */

  	  /* Tell heartbeat thread we're happy */
	  g_mt_working = 1;

      /* send a monitor-mode string */
      if ( (slen = strlen(monmde_str_arr[idx])) == 1)
      {
        /* if '\' (or '\r') then delay a bit before */
        if ( (rc1 = k2c_flush_recv(K2MI_FLSH_LONG)) == K2R_ERROR)
          return rc1;
      }

      if (gcfg_debug > 2)
        logit("e", "sending MM %d\n", idx);

      if ( (rc1 = k2c_tran_buff((const unsigned char *)(monmde_str_arr[idx]),
                                slen, gcfg_commtimeout_itvl, 0)) != slen)
        return rc1;     /* if error transmitting then return */

      /* increment index into monitor-mode string array */
      if(++idx >= (sizeof(monmde_str_arr)/sizeof(monmde_str_arr[0])))
        idx = 0;             /* if past end-of-array then wrap around to beg */

      /* wait a bit for (possible) data to appear */
      /*  (wait a bit longer if '\' (or '\r') command) */
      if ( (rc1 = k2c_rcvflg_tout( ((slen==1)?K2MI_FLSH_LONG:K2MI_FLSH_SHORT)))
           == K2R_ERROR)
        return rc1;

      /* save any data returned from K2 */
      if ( (dcnt = k2c_recv_buff(g_k2mi_buff, K2MI_IBFSIZ,
                                 gcfg_commtimeout_itvl, 0)) > 0)
      {       /* data was received */
        /* Tell heartbeat thread we're happy */
        g_mt_working = 1;

        g_k2mi_buff[dcnt] = '\0';           /* NULL terminate buffer */
        if (gcfg_debug > 2)
          logit("e", "received:  %s\n", g_k2mi_buff);

        /* check if returned data contains monitor-mode prompt ('*') */
        if ( strchr((char *)g_k2mi_buff, (int)'*') != NULL)
          break;             /* if monitor-mode prompt ('*') then exit loop */
      }
      else if (dcnt == K2R_ERROR)
        return dcnt;
    }
    while(--monmde_count);         /* loop if count not expired */
    /* Now we should be in monitor mode, unless monmde_count expired */

    /* send block-mode command string */
    slen = strlen(blkmde_str);
    if (gcfg_debug > 2)
      logit("e", "%s\n",blkmde_str);

    if ( (rc1 = k2c_tran_buff((unsigned char *)blkmde_str, slen,
                                          gcfg_commtimeout_itvl,0)) != slen)
    {    /* error transmitting; return code */
      return (rc1 < 0) ? rc1 : K2R_ERROR;   /* only use code if negative */
                                            /* 1/29/2002 -- [ET] */
    }
    if ( (rc1 = k2c_flush_recv(K2MI_FLSH_SHORT)) == K2R_ERROR)
      return rc1;
    if (rc1 == 0) return K2R_POSITIVE;
  }
  while (--blkmde_count);           /* loop if count not expired */
  logit("et", "k2mi_init_blkmde: failed to put k2 in block mode\n");
  return K2R_ERROR;
}


/**************************************************************************
 * k2mi_ping_test:  executes ping test on K2                              *
 *        Sockets are never retried on timeout                            *
 *         count    - number of pings to send and confirm                 *
 *         *pseqnum - address of sequence number for sent command packet  *
 *                    which will be incremented                           *
 *      return value:  returns K2R_NO_ERROR if `count' pings returned;    *
 *                     returns K2R_ERROR on error or K2R_TIMEOUT          *
 **************************************************************************/

int k2mi_ping_test(int count, unsigned char *pseqnum)
{
  int rc, slen;
  static const char ping_test_data[]=
                               "K2 ping test data \\\\\xC0\xC0\\1234567890";

  if (gcfg_debug > 2)
    logit("e", "ping test\n");

  /* check receive and flush any waiting data */
  if (gcfg_debug > 3)
    logit("e", "flushing...");
  if ( (rc = k2c_flush_recv(0)) != K2R_NO_ERROR)
    return rc;
  if (gcfg_debug > 3)
    logit("e", "done\n");

  while(1)    /* loop for each ping send/receive test */
  {           /* send ping packet */
    slen = strlen(ping_test_data);       /* get string length */
    if (gcfg_debug > 3)
      logit("e", "sending ping\n");
    if ( (rc = k2p_send_packet((unsigned char)PKC_PING, *pseqnum,
                               K2MI_PKT_SRCNUM, slen,
                               (unsigned char *)ping_test_data, 0))
         != K2R_NO_ERROR)
      break;                 /* if error then exit loop */

    /* wait for expected response packet (ignore SDS auto-packets) */
    if ( (rc = k2pm_recv_waitpkt(PKR_PING, *pseqnum, K2MI_PKT_SRCNUM,
                                 &g_k2mi_hdrblk, g_k2mi_buff, PKR_STRDATA,
                                 gcfg_commtimeout_itvl, 0)) != K2R_POSITIVE)
    {
      if (rc == K2R_NO_ERROR)  /* something returned, but not a packet */
        rc = K2R_TIMEOUT;      /* call it a timeout */
      break;
    }
    if (rc == K2R_POSITIVE)  /* packet returned */
      rc = K2R_NO_ERROR;     /* switch return value to current convention */

    /* check packet data */
    if(g_k2mi_hdrblk.dataLength != slen ||
       strncmp((char *)g_k2mi_buff, ping_test_data, (size_t)slen) != 0)
    {    /* packet data mismatch */
      rc = K2R_ERROR;            /* setup error code */
      logit("et", "k2mi_ping_test: ping data mismatch\n");
      break;                           /* exit loop */
    }
    if(--count <= 0)              /* decrement count */
      break;                      /* if done then exit loop */
    ++(*pseqnum);                 /* increment sequence number */
  }
  ++(*pseqnum);         /* increment sequence number */
  if (gcfg_debug > 2)
    logit("e", "k2mi_ping_test returning %d\n", rc);

  return rc;            /* return OK code */
}

/**************************************************************************
 * k2mi_get_params:  fetches general K2 parameters (note that none of     *
 *      the numeric multi-byte entries are byte-swapped by this function) *
 *      Never does communication retries.                                 *
 *         pk2hdr  - address of header block to receive parameter data    *
 *         *pseqnum - address of sequence number for sent command packet  *
 *                    which will be incremented                           *
 *      return value:  returns K2R_POSITIVE on return of param packet     *
 *                     K2R_NO_ERROR if no fatal errors occured but param  *
 *                       packet didn't come back;                         *
 *                     K2R_ERROR on fatal errors; K2R_TIMEOUT on timeout  *
 **************************************************************************/

int k2mi_get_params(K2_HEADER *pk2hdr, unsigned char *pseqnum)
{
  int rc;
  size_t to_copy;

#ifdef DEBUG
  if(pk2hdr == NULL)              /* if NULL pointer then */
  {
    logit("et", "k2mi_get_params: NULL paramter pk2hdr\n");
    return K2R_ERROR;
  }
#endif

  /* check receive and flush any waiting data */
  if ( (rc = k2c_flush_recv(0)) != K2R_NO_ERROR)
    return rc;

  /* send get-K2-parameters command packet */
  if ( (rc = k2p_send_packet((unsigned char)PKC_GETPARMS, *pseqnum,
                             K2MI_PKT_SRCNUM, (unsigned short)0,
                             NULL, 0)) == K2R_NO_ERROR)
  {      /* send OK; wait for expected response packet (ignore message pkts) */
    if ( (rc = k2pm_recv_waitpkt((unsigned char)PKR_PARMS, *pseqnum,
                                 K2MI_PKT_SRCNUM, &g_k2mi_hdrblk,
                                 g_k2mi_buff, (unsigned char)PKC_MSG,
                                 gcfg_commtimeout_itvl, 0)) == K2R_POSITIVE)
    {         /* 'parameters' packet received OK */
      if (g_k2mi_hdrblk.dataLength == (unsigned short)K2_HEAD_SIZE)
      {       /* packet size OK */
        memset(pk2hdr, 0, sizeof(*pk2hdr));
        /* copy data into K2 parameters header block to be returned */
        /*  (copy smaller of # of bytes received or struct size) */
        to_copy = (sizeof(*pk2hdr) < K2_HEAD_SIZE) ?
          sizeof(*pk2hdr) : K2_HEAD_SIZE;
        memcpy(pk2hdr, g_k2mi_buff, to_copy);
      }
      else    /* unexpected packet size */
      {
        logit("et", "k2mi_get_params: wrong sized packet returned from K2\n");
        return K2R_NO_ERROR;
      }
    }
  }
  ++(*pseqnum);         /* increment sequence number */
  return rc;            /* return code */
}


/**************************************************************************
 * k2mi_get_status:  fetches K2 status block (note that none of the       *
 *      numeric multi-byte entries are byte-swapped by this function)     *
 *         pk2stat  - address of STATUS_INFO block to receive status data *
 *         *pseqnum - address of sequence number for sent command packet  *
 *                    which will be incremented                           *
 *      return value:  returns K2R_POSITIVE on return of status packet;   *
 *                     K2R_NO_ERROR if no fatal errors occured but status *
 *                       packet didn't return;                            *
 *                     K2R_ERROR on fatal errors; K2R_TIMEOUT on timeout  *
 **************************************************************************/

int k2mi_get_status(struct STATUS_INFO *pk2stat, unsigned char *pseqnum)
{
  int rc;
  size_t to_copy;

#ifdef DEBUG
  if (pk2stat == NULL)             /* if NULL pointer then */
  {
    logit("et", "k2mi_get_status: NULL paramter pk2stat\n");
    return K2R_ERROR;
  }
#endif

  /* check receive and flush any waiting data */
  if ( (rc = k2c_flush_recv(0)) != K2R_NO_ERROR)
    return rc;

  /* send get-K2-status command packet */
  if ( (rc = k2p_send_packet((unsigned char)PKC_GETSTATUS, *pseqnum,
                             K2MI_PKT_SRCNUM, (unsigned short)0,
                             NULL, 0)) == K2R_NO_ERROR)
  {      /* send OK; wait for expected response packet (ignore none) */
    if ( (rc = k2pm_recv_waitpkt((unsigned char)PKR_STATUS, *pseqnum,
                                 K2MI_PKT_SRCNUM, &g_k2mi_hdrblk,
                                 g_k2mi_buff, (unsigned char)K2PM_IGNR_NONE,
                                 gcfg_commtimeout_itvl, 0)) == K2R_POSITIVE)
    {         /* 'parameters' packet received OK */
      if (g_k2mi_hdrblk.dataLength == (unsigned short)STATUS_SIZE)
      {       /* packet size OK */
        memset(pk2stat, 0, sizeof(*pk2stat));
        /* copy data into K2 parameters status block to be returned */
        /*  (copy smaller of # of bytes received or struct size) */
        to_copy = (sizeof(*pk2stat) < STATUS_SIZE) ?
          sizeof(*pk2stat) : STATUS_SIZE;
        memcpy(pk2stat, g_k2mi_buff, to_copy);
      }
      else    /* unexpected packet size */
      {
        logit("et", "k2mi_get_status: wrong sized packet returned from K2\n");
        return K2R_NO_ERROR;
      }
    }
  }
  ++(*pseqnum);         /* increment sequence number */
  return rc;            /* return code */
}

/**************************************************************************
 * k2mi_get_extstatus:  fetches K2 extended status block (note that none  *
 *      of the numeric multi-byte entries are byte-swapped by this        *
 *      function)                                                         *
 *         pk2stat  - address of EXT_STATUS_INFO block to receive         *
 *         *pseqnum - address of sequence number for sent command packet  *
 *                    which will be incremented                           *
 *      return value:  returns K2R_POSITIVE on return of status packet;   *
 *                     K2R_NO_ERROR if no fatal errors occured but status *
 *                       packet didn't return;                            *
 *                     K2R_ERROR on fatal errors; K2R_TIMEOUT on timeout  *
 **************************************************************************/

int k2mi_get_extstatus(struct EXT2_STATUS_INFO *pk2stat, unsigned char *pseqnum,
		       int *pext_size)
{
  int rc;
  size_t to_copy;

#ifdef DEBUG
  if (pk2stat == NULL)             /* if NULL pointer then */
  {
    logit("et", "k2mi_get_extstatus: NULL paramter pk2stat\n");
    return K2R_ERROR;
  }
#endif

  /* check receive and flush any waiting data */
  if ( (rc = k2c_flush_recv(0)) != K2R_NO_ERROR)
    return rc;

  /* send get-K2-status command packet */
  if ( (rc = k2p_send_packet((unsigned char)PKC_EXTSTATUS, *pseqnum,
                             K2MI_PKT_SRCNUM, (unsigned short)0,
                             NULL,0)) == K2R_NO_ERROR)
  {      /* send OK; wait for expected response packet (ignore none) */
    if ( (rc = k2pm_recv_waitpkt((unsigned char)PKR_EXTSTATUS, *pseqnum,
                                 K2MI_PKT_SRCNUM, &g_k2mi_hdrblk,
                                 g_k2mi_buff, (unsigned char)K2PM_IGNR_NONE,
                                 gcfg_commtimeout_itvl, 0)) == K2R_POSITIVE)
    {         /* 'parameters' packet received OK */
      if (g_k2mi_hdrblk.dataLength == (unsigned short)EXT_SIZE)
      {       /* packet size OK */
        memset(pk2stat, 0, sizeof(*pk2stat));
        /* copy data into K2 parameters status block to be returned */
        /*  (copy smaller of # of bytes received or struct size) */
        to_copy = (sizeof(*pk2stat) < EXT_SIZE) ? sizeof(*pk2stat) : EXT_SIZE;
        memcpy(pk2stat, g_k2mi_buff, to_copy);
	*pext_size = EXT_SIZE;
      }
      else if (g_k2mi_hdrblk.dataLength == (unsigned short)EXT2_SIZE)
      {
	  memset(pk2stat, 0, sizeof(*pk2stat));
	  /* copy data into K2 parameters status block to be returned */
	  /*  (copy smaller of # of bytes received or struct size) */
	  to_copy = (sizeof(*pk2stat) < EXT2_SIZE) ? sizeof(*pk2stat) : EXT2_SIZE;
	  memcpy(pk2stat, g_k2mi_buff, to_copy);
	  *pext_size = EXT2_SIZE;
      }
    else    /* unexpected packet size */
      {
        logit("et","k2mi_get_extstatus: wrong sized packet returned from"
              " K2 (was %d, should be %d or %d)\n",
	      (int)(g_k2mi_hdrblk.dataLength),EXT_SIZE, EXT2_SIZE);
	*pext_size = 0;
        return K2R_NO_ERROR;
      }
    }
  }
  ++(*pseqnum);         /* increment sequence number */
  return rc;            /* return code */
}

/**************************************************************************
 * k2mi_req_status:  requests K2 status block                             *
 *         *pseqnum - address of sequence number for sent command packet  *
 *                    which will be incremented                           *
 *      return value:  returns K2R_NO_ERROR if request is transmitted     *
 *                     K2R_ERROR on fatal errors; K2R_TIMEOUT on timeout  *
 **************************************************************************/

int k2mi_req_status(unsigned char *pseqnum)
{
  int rc;

  /* send get-K2-status command packet */
  rc = k2p_send_packet((unsigned char)PKC_GETSTATUS, *pseqnum,
                       K2MI_PKT_SRCNUM, (unsigned short)0,
                       NULL,1);
  ++(*pseqnum);         /* increment sequence number */
  return rc;            /* return code */
}

/**************************************************************************
 * k2mi_req_extstatus:  requests K2 extended status block                 *
 *         *pseqnum - address of sequence number for sent command packet  *
 *                    which will be incremented                           *
 *      return value:  returns K2R_NO_ERROR if request is transmitted     *
 *                     K2R_ERROR on fatal errors; K2R_TIMEOUT on timeout  *
 **************************************************************************/

int k2mi_req_extstatus(unsigned char *pseqnum)
{
  int rc;

  /* send get-K2-status command packet */
  rc = k2p_send_packet((unsigned char)PKC_EXTSTATUS, *pseqnum,
                       K2MI_PKT_SRCNUM, (unsigned short)0,
                       NULL,1);
  ++(*pseqnum);         /* increment sequence number */
  return rc;            /* return code */
}


/**************************************************************************
 * k2mi_req_params:  requests K2 paramter block                            *
 *         *pseqnum - address of sequence number for sent command packet  *
 *                    which will be incremented                           *
 *      return value:  returns K2R_NO_ERROR if request is transmitted     *
 *                     K2R_ERROR on fatal errors; K2R_TIMEOUT on timeout  *
 **************************************************************************/

int k2mi_req_params(unsigned char *pseqnum)
{
  int rc;

  /* send get-K2-params command packet */
  rc = k2p_send_packet((unsigned char)PKC_GETPARMS, *pseqnum,
                       K2MI_PKT_SRCNUM, (unsigned short)0,
                       NULL,1);
  ++(*pseqnum);         /* increment sequence number */
  return rc;            /* return code */
}


/**************************************************************************
 * k2mi_log_pktinfo:  logs information about given packet                 *
 *         phdrblk  - address of header block for packet                  *
 *         databuff - address of data buffer for packet                   *
 **************************************************************************/

void k2mi_log_pktinfo(struct PACKET_HDR *phdrblk, unsigned char *databuff)
{
  int slen, index;
  unsigned long alarm, chanmask = 0x00000001;

  switch(phdrblk->typeCode)
  {      /* process packet based on type-code */
  case PKC_MSG:            /* system message packet from K2 */
    /* Ignore these messages */
    if (strncmp((char *)databuff, "CAUTION: Acquisition is ON", 26) == 0)
       break;

    /* Log the message string (if not too long) */
    logit("et","K2 message received: %s\n",
          (strlen((char *)databuff) < PACKET_MAX_SIZE-11) ?
          (char *)databuff: "<unterminated-string>");

    /* If flash memory error is detected, send msg to statmgr */
    /* Added in version 2.26 by W Kohler, added station name to message in 2.35  */
    if ( strncmp((char *)databuff, "pcdrv_raw_read, error", 21) == 0 ) 
    {
       sprintf(msg, "K2 <%s>: pcdrv_raw_read error (bad flash memory)",
                g_stnid);
       k2mi_status_hb(g_error_ltype, K2STAT_BAD_DISK, msg);
    }
    if ( strncmp((char *)databuff, "pcdrv_raw_write, error", 22) == 0 )
    {
       sprintf(msg, "K2 <%s>: pcdrv_raw_write error (bad flash memory)",
                g_stnid);
       k2mi_status_hb(g_error_ltype, K2STAT_BAD_DISK, msg);
    }
    break;
  case PKC_SETALARM:       /* alarm message from K2 */
    /* display alarm as 32-bit int (if length OK) */
    if(phdrblk->dataLength == (unsigned short)4)
    {
      sprintf(msg, "K2 alarm received for channels: ");
      slen = strlen(msg);
      alarm = BYTESWAP_UINT32(((struct K2_ALARM*)databuff)->channelBitMap);
      for (index = 0; index < ABS_MAX_CHANNELS; index++)
      {
        if (alarm & chanmask)
        {
          sprintf(&msg[slen], "%d ", index + 1);
          slen = strlen(msg);
        }
        chanmask = chanmask << 1L;
      }
      logit("et","%s\n", msg);
    }
    else
      logit("et","K2 alarm: <unexpected data-length>\n");
    break;
  case PKR_STRDATA:        /* serial data stream auto packet */
  case PKR_STRRQDATA:      /* serial data stream requested packet */
    if ( gcfg_debug > 0 )
       logit("et","Unexpected SDS (%s) packet, "
             "seq-num=%hu, src-num=%hu, data-len=%hu\n",
             ((phdrblk->typeCode == (unsigned char)PKR_STRDATA) ?
             "PKR_STRDATA" : "PKR_STRRQDATA"),
             (unsigned short)(phdrblk->seqNo), phdrblk->source,
             phdrblk->dataLength);
    break;
  case PKR_STATUS:
    if (phdrblk->dataLength == (unsigned short) STATUS_SIZE)
      k2mi_report_status((struct STATUS_INFO *)databuff);
    else
      logit("et", "k2ew: abnormal length status packet received\n");
    break;
 case PKR_EXTSTATUS:
    if (phdrblk->dataLength == (unsigned short)EXT_SIZE)
      k2mi_report_extstatus((struct EXT2_STATUS_INFO *)databuff, EXT_SIZE);
    else if (phdrblk->dataLength == (unsigned short)EXT2_SIZE)
      k2mi_report_extstatus((struct EXT2_STATUS_INFO *)databuff, EXT2_SIZE);
    else
    {
      logit("et", "k2ew: abnormal length extended status packet"
            " received (%d)\n",(int)(phdrblk->dataLength));
      if (phdrblk->dataLength == (unsigned short)(EXT_SIZE*2))
      {  /* extended packet is new double-length size */
        g_extstatus_avail = 0;    /* clear flag to stop future requests */
        if(gcfg_debug > 0)
        {     /* debug enabled; log message */
          logit("et",
                   "Extended status packets will no longer be requested\n");
        }
      }
    }
    break;
  case PKR_PARMS:
    if (phdrblk->dataLength == (unsigned short)K2_HEAD_SIZE)
      k2mi_report_params((K2_HEADER *)databuff);
    else
      logit("et", "k2ew: abnormal length params packet received\n");
    break;
  case PKR_STRNAK:
    logit("et", "K2 STR_NAK rcvd: %s\n",
          k2pm_strnak_errcd(phdrblk, databuff, NULL));
    break;
  default:       /* unknown packet type-code, display message */
    logit("et","Unexpected packet received, type-code=%02hXH, "
          "seq-num=%hu, src-num=%hu, data-len=%hu\n",
          (unsigned short)(phdrblk->typeCode),
          (unsigned short)(phdrblk->seqNo),
          phdrblk->source, phdrblk->dataLength);
  }
}

/*****************************************************************
 * k2mi_report_status: Report information form K2 status packet  *
 *         pk2stat - pointer to K2 status structure              *
 *****************************************************************/

void k2mi_report_status( struct STATUS_INFO *pk2stat)
{
  time_t timevar;
  unsigned short wtemp1, wtemp2;
  char tempchar, disk[2] = {'A', 'B'};
  int tempbat, index;
  double batv, diskfree;
  static char msg2[MAX_MSG_SIZE];      /* another buffer for text messages */

  if (gcfg_inject_info==1)
	k2info_send(K2INFO_TYPE_STATUS, (char *) pk2stat);
  /* System time and clock source */
  timevar = (time_t)(BYTESWAP_UINT32(pk2stat->systemTime) + K2_TIME_CONV);
  sprintf(msg, "K2 Time = %s", asctime(gmtime(&timevar)));
  msg[strlen(msg)-1] = '\0';         /* delete the newline left by ctime() */

  switch (pk2stat->clockSource)
  {
  case 0:
    strcat(msg, " set from RTC");
    break;
  case 1:
    strcat(msg, " set from Keyboard");
    break;
  case 2:
    strcat(msg, " set from External Pulse");
    break;
  case 3:
    strcat(msg, " set from Internal GPS");
    break;
  default:
    strcat(msg, " set from (?)");
  }
  logit("et","%s\n", msg);

  /* Battery voltage and hardware status */
  tempbat = (int) pk2stat->batteryStatus;   /* char, no need to swap */
  batv = tempbat/10.0;
  if (tempbat < 0)
  {
    batv = -batv;
  }
  if (tempbat > 0) /* K2 says battery voltage is zero when it is charging */
  {
    logit("et", "K2 HW Status = %s Battery Status: %0.1f V\n",
          (pk2stat->hardwareStatus == (unsigned char)SF_OK)?"OK":"FAULT", batv);
    if (gcfg_on_batt && rep_on_batt == 0)
    {
    sprintf(msg, "K2 <%s>: lost external power; battery voltage: %0.1f V", 
		g_stnid, batv);
    k2mi_status_hb(g_error_ltype, K2STAT_ON_BATT, msg);
    rep_on_batt = 1;
    }
  }
  else
  {
    logit("et", "K2 HW Status = %s Battery Status: charging\n",
          (pk2stat->hardwareStatus == (unsigned char)SF_OK)?"OK":"FAULT");
    if (rep_on_batt == 1 && gcfg_on_batt)
    {
    sprintf(msg, "K2 <%s>: external power restored", g_stnid);
    k2mi_status_hb(g_error_ltype, K2STAT_OFF_BATT, msg);
    rep_on_batt = 0;
    rep_low_batt = 0;
    }
  }

  if (gcfg_battery_alarm > 0 && rep_low_batt == 0 && tempbat > 0
      && batv < 0.1 * gcfg_battery_alarm)
  {
    sprintf(msg, "K2 <%s>: low battery voltage: %0.1f V", 
		g_stnid, batv);
    k2mi_status_hb(g_error_ltype, K2STAT_LOW_BATT, msg);
    rep_low_batt = 1;
  }
  /* We assume battery voltage goes up only when external power is restored */

  if (gcfg_hwstat_alarm && rep_hwstat == 0
      && pk2stat->hardwareStatus == (unsigned char) SF_FAULT)
  {
    sprintf(msg, "K2 <%s> hardware fault", g_stnid);
    k2mi_status_hb(g_error_ltype, K2STAT_HW_FAULT, msg);
    rep_hwstat = 1;
  }
  else if (gcfg_hwstat_alarm && rep_hwstat == 1)
  {
    sprintf(msg, "K2 <%s> hardware fault cleared", g_stnid);
    k2mi_status_hb(g_error_ltype, K2STAT_HWFLT_CLR, msg);
    rep_hwstat = 0;
  }

  /* Event and recording error counts */
  logit("et", "Events: %hu Recording Errors: %hu\n",
        BYTESWAP_UINT16(pk2stat->events),
        BYTESWAP_UINT16(pk2stat->recordingErrors));

  /* Trigger status */
  wtemp1 = BYTESWAP_UINT16(pk2stat->triggerStatus);
  sprintf(msg, "Acquisition: ");
  if (wtemp1 & 0x0001)
  {
    strcat(msg, "on (");
    if (((wtemp1 & 0x0002) == 0) &&
        ((wtemp1 & 0x0004) == 0) &&
        ((wtemp1 & 0x0010) == 0))
      strcat(msg, "not ");
    strcat(msg, "triggered);");
  }
  else
    strcat(msg, "off;");

  strcat(msg, "  Alarm: ");
  if ((wtemp1 & 0x0008) == 0)
    strcat(msg, "not ");
  strcat(msg, "triggered.");
  logit("et", "%s\n", msg);

  /* Disk drive status */
  strcpy(msg, "Drive:");
  for (index = 0; index < 2; index++)
  {
    sprintf(&msg[strlen(msg)], " %c: ", 'A' + index);
    wtemp1 = BYTESWAP_UINT16(pk2stat->driveStatus[index]);
    if (wtemp1 & SF_NOT_READY)
    {
      strcat(msg, "not ready");
      if ( gcfg_disk_alarm[index] > 0.0 && rep_disk[index] == 0)
      {
        sprintf(msg2, "K2 <%s>: disk %c failure", 
		g_stnid, disk[index]);
        k2mi_status_hb(g_error_ltype, K2STAT_BAD_DISK, msg2);
        rep_disk[index] = 1;
      }
    }
    else
    {
      if ((wtemp1 & SF_GB) == SF_GB)
      {
        tempchar = 'G';
        diskfree = 1073741824.0;   /* `giga' bytes */
      }
      else
      {
        if ((wtemp1 & SF_MB) == SF_MB)
        {
          tempchar = 'M';
          diskfree = 1048576.0;  /* `mega' bytes */
        }
        else
        {
          if ((wtemp1 & SF_KB) == SF_KB)
          {
            tempchar = 'K';
            diskfree = 1024.0;  /* `Kilo' bytes */
          }
        }
      }
      wtemp2 = wtemp1 & SF_FREE;
      diskfree *= (double)wtemp2;
      sprintf(&msg[strlen(msg)], "%d %cB FREE", wtemp2, tempchar);

      if (gcfg_disk_alarm[index] > 0.0 && diskfree < gcfg_disk_alarm[index]
          && rep_disk[index] == 0)
      {
        sprintf(msg2, "K2 <%s>: low free space on disk A: %d %cB",
                g_stnid, wtemp2, tempchar);
        k2mi_status_hb(g_error_ltype, K2STAT_LOW_DISK, msg2);
        rep_disk[index] = 1;
      }
      else if (gcfg_disk_alarm[index] > 0.0 && rep_disk[index] == 1
               && diskfree >= gcfg_disk_alarm[index] )
      {   /* Disk is no longer full */
        sprintf(msg2, "K2 <%s>: disk space OK; A: %d %cB",
                g_stnid, wtemp2, tempchar);
        k2mi_status_hb(g_error_ltype, K2STAT_OK_DISK, msg2);
        rep_disk[index] = 0;
      }
    }
  }
  logit("et", "%s\n", msg);
}

/*****************************************************************
 * k2mi_report_extstatus: Report information form K2 extended    *
 *             status packet                                     *
 *   It seems the K2 doesn't actually support this message, so   *
 *   don't expect this function to be used: PNL 8/22/00          *
 *   Since the data reported here is not included in the params  *
 *   structure, we keep this code around, just in case KMI gets  *
 *   ambitious and implements what they advertise; hah!          *
 *         pk2stat - pointer to K2 extended status structure     *
 *****************************************************************/

void k2mi_report_extstatus( struct EXT2_STATUS_INFO *pk2stat, int ext_size)
{
  unsigned short us_dummy;
  unsigned long ul_dummy;
  unsigned short fault;
  time_t timevar;
  char msg[20];
  
  /* The extended status structure is not byte-aligned for Sparc or Intel *
   * hardware. So we have to copy out the bytes into dummy variables      *
   * before we can manipluate them. Thanks, kinemetrics!                  */

  /* Last restart time */
  memcpy(&ul_dummy, ((char *)pk2stat) + 2, sizeof(long));
  timevar = (time_t)(BYTESWAP_UINT32(ul_dummy) + K2_TIME_CONV);
  switch (pk2stat->clockSource)
  {
  case 0:
    strcpy(msg, "RTC");
    break;
  case 1:
    strcpy(msg, "Keyboard");
    break;
  case 2:
    strcpy(msg, "External Pulse");
    break;
  case 3:
    strcpy(msg, "Internal GPS");
    break;
  default:
    strcpy(msg, "(?)");
  }
  logit("e", "K2 Extended Status Message: last restart: Time = %s set from %s\n",
        asctime(gmtime(&timevar)), msg);

  /* Fault status */
  memcpy(&us_dummy, ((char *)pk2stat) + 6, sizeof(short));
  fault = BYTESWAP_UINT16(us_dummy);
  strcpy(msg, "K2 fault status: ");
  if (fault != (unsigned short)0)
  {
    if (fault & FAULT_SYSTEM)
      strcat(msg, "bad parameters; ");
    if (fault & FAULT_FLASH)
      strcat(msg, "flash error; ");
    if (fault & FAULT_RAM)
      sprintf(&msg[strlen(msg)], "ram error at %lX; ",
              BYTESWAP_UINT32(pk2stat->lastRAMError));
    if (fault & FAULT_PCMCIA)
      strcat(msg, "bad/missing PCMCIA; ");
    if (fault & FAULT_DSP)
      strcat(msg, "failed to load DSP; ");
    if (fault & FAULT_PARMBLK)
      strcat(msg, "param block CRC error; ");
    if (fault & FAULT_FLASH_MAINTENANCE)
      strcat(msg, "flash maintenance required;");

    /* I assume that a fault here will also be reported by the HW fault bit
       in the status message, so we don't bother sending and earthworm status
       message here */
  }
  else
    strcat(msg, "OK");

  logit("e", "%s\n", msg);

  /* inject it if requested to */
  if (gcfg_inject_info==1 && ext_size==EXT_SIZE)
	k2info_send(K2INFO_TYPE_ESTATUS, (char *) pk2stat);
  else if (gcfg_inject_info==1 && ext_size==EXT2_SIZE)
	k2info_send(K2INFO_TYPE_E2STATUS, (char *) pk2stat);

  if (ext_size == EXT_SIZE) return;
  
  /* Report additional information in the "extended" extended status packet */

  return;
}


/*****************************************************************
 * k2mi_write_header: Write binary K2 header to disk file        *
 *         pk2hdr - pointer to K2 header structure               *
 *****************************************************************/

static void k2mi_write_header( K2_HEADER *k2hdr )
{
   FILE *fp = fopen( gcfg_header_filename, "wb" );
   if ( fp == NULL )
   {
      logit( "e", "Can't open new K2 header file: <%s>\n", gcfg_header_filename );
      return;
   }
   if ( fwrite( k2hdr, sizeof(K2_HEADER), 1, fp ) < 1 )
   {
      logit( "e", "Error writing K2 header file: <%s>\n", gcfg_header_filename );
      fclose( fp );
      return;
   }
   fclose( fp );           /* All ok */
   if (gcfg_debug > 0)
      logit( "et", "K2 header file written: <%s>\n", gcfg_header_filename );
   return;
}


/*****************************************************************
 * k2mi_report_params: Report some information from K2 param     *
 *             packet: temperature and GPS                       *
 *         pk2hdr - pointer to K2 param structure                *
 *****************************************************************/

void k2mi_report_params( K2_HEADER *pk2hdr)
{
  short batt_voltx10;
  double batv;
  short temp;

/* Write K2 header to a disk file
   ******************************/
   if (gcfg_header_filename[0] != '\0')
      k2mi_write_header( pk2hdr );

   if (gcfg_inject_info == 1)
	k2info_send(K2INFO_TYPE_HEADER, (char *) pk2hdr);

/* System temperature
   ******************/
  temp = (short)BYTESWAP_UINT16(pk2hdr->roParms.misc.temperature);
  logit("et", "Temperature: %d.%1d deg C\n", temp / 10, temp % 10);

  if ((int)temp < gcfg_templo_alarm && rep_temp == 0)
  {
    sprintf(msg, "K2 <%s>: low temperature: %d.%1d C", 
		g_stnid, temp / 10, temp % 10);
    k2mi_status_hb(g_error_ltype, K2STAT_LOW_TEMP, msg);
    rep_temp = 1;
  }
  else if ((int)temp > gcfg_temphi_alarm && rep_temp == 0)
  {
    sprintf(msg, "K2 <%s>: high temperature: %d.%1d C", 
		g_stnid, temp / 10, temp % 10);
    k2mi_status_hb(g_error_ltype, K2STAT_HIGH_TEMP, msg);
    rep_temp = 1;
  }
  else if (rep_temp == 1)
  {
    sprintf(msg, "K2 <%s>: temperature OK: %d.%1d C", 
		g_stnid, temp / 10, temp % 10);
    k2mi_status_hb(g_error_ltype, K2STAT_OK_TEMP, msg);
    rep_temp = 0;
  }

/* Log the last GPS lock time and drift. If the GPS hasn't locked
   for gcfg_gpslock_alarm hours, send one message to statmgr.
   If and when the GPS locks up again, send another msg to statmgr.
   ***************************************************************/
   {
      const int year2000 = 946080000;   /* Jan 1, 2000 in seconds */

      time_t now      = time(0);        /* PC clock time */
      time_t timevar1 = (time_t)(BYTESWAP_UINT32(pk2hdr->roParms.timing.gpsLastLockTime[0])
                         + K2_TIME_CONV);
      char *timestr1  = asctime(gmtime(&timevar1));

      timestr1[strlen(timestr1)-1] = '\0';
      temp = (short)BYTESWAP_UINT32(pk2hdr->roParms.timing.gpsLastDrift[0]);
      logit("et", "Last GPS lock: %s  Drift: %d msec\n", timestr1, temp );

      if ( (gcfg_gpslock_alarm >= 0.0) && (now > year2000) && (timevar1 > year2000) )
      {
         static int prev_gpslock  = 1;            /* Assume GPS was locked at startup */
         double timeSinceLastLock = (now - timevar1) / 3600.;

         if ( timeSinceLastLock >= gcfg_gpslock_alarm )
         {
            logit( "et", "WARNING. GPS hasn't locked for %.1lf hours.\n", timeSinceLastLock );
            if ( prev_gpslock == 1 )
            {
               sprintf(msg, "K2 <%s>: No GPS lock for %.1lf hours.", 
			g_stnid, timeSinceLastLock );
               k2mi_status_hb(g_error_ltype, K2STAT_GPSLOCK, msg);
            }
            prev_gpslock = 0;
         }
         else
         {
            if ( prev_gpslock == 0 ) 
	    {
               sprintf(msg, "K2 <%s>: GPS lock acquired.", g_stnid);
               k2mi_status_hb(g_error_ltype, K2STAT_GPSLOCK, msg);
	    }
            prev_gpslock = 1;
         }
      }
   }

  /* Battery voltage is reported as negative when supplied from external *
   * source. That's what we need here; internal battery voltage is       *
   * reported in status block, above */
  batt_voltx10 = (signed short)BYTESWAP_UINT16(pk2hdr->roParms.misc.batteryVoltage);

  if (batt_voltx10 < 0)
  {
    batt_voltx10 = -batt_voltx10;
    batv = batt_voltx10 / 10.0;
    logit("et", "External battery voltage: %0.1f V\n", batv);
  }

  /* If k2ew used a restart file to read the station name
   * (g_validrestart_flag == 1), we must verify that name against
   * the K2's idea of station name. If they don't match, someone
   * must have used a non-unique restart-file name in the config file.
   * If k2ew continues, then the trace data will be labeled with the
   * wrong station name: Bad doodoo! */
  if (g_validrestart_flag == 1)
  {      /* restart file was used */
    if(strncmp(g_k2_stnid,pk2hdr->rwParms.misc.stnID,sizeof(g_k2_stnid)-1) != 0
                && strncmp(g_stnid,gcfg_stnid_remap,sizeof(g_stnid)-1) != 0)
    {         /* original station name and remapped name do not match */
      logit("et","k2ew: MAJOR ERROR: K2 station name <%s> does not match "
                 "station name <%s>\n"
                 "\tfrom restart file (\"%s\"); Terminating!\n",
              pk2hdr->rwParms.misc.stnID,g_k2_stnid, gcfg_restart_filename);
      g_terminate_flg = 1;        /* set program terminate flag */

    }
    else      /* name matches OK */
      g_validrestart_flag = 2;    /* so we don't do this test again */
  }
}


/*************************************************************
 * k2mi_status_hb: sends heartbeat or status messages to     *
 *         earthworm transport ring.                         *
 *       type: the message type: heartbeat or status         *
 *       code: the error code for status messages            *
 *       message: message text, if any                       *
 *************************************************************/

void k2mi_status_hb(unsigned char type, short code, char* message )
{
  char          outMsg[MAX_MSG_SIZE];  /* The outgoing message.        */
  time_t        msgTime;        /* Time of the message.                 */

  /*  Get the time of the message                                       */
  time( &msgTime );

  /*  Build & process the message based on the type                     */
  if ( g_heartbeat_logo.type == type )
  {
    sprintf( outMsg, "%ld %ld\n\0", msgTime, g_pidval );

    /*Write the message to the output region                            */
    if ( tport_putmsg( &g_tport_region, &g_heartbeat_logo,
                       (long) strlen( outMsg ), outMsg ) != PUT_OK )
    {
      /*     Log an error message                                       */
      logit( "et", "k2ew: Failed to send a heartbeat message (%d).\n",
             code );
    }
  }
  else
  {
    if ( message )
    {
      sprintf( outMsg, "%ld %hd %s\n\0", msgTime, code, message );
      logit("et","Error:%d (%s)\n", code, message );
    }
    else
    {
      sprintf( outMsg, "%ld %hd\n\0", msgTime, code );
      logit("et","Error:%d (No description)\n", code );
    }

    /*Write the message to the output region                         */
    if ( tport_putmsg( &g_tport_region, &g_error_logo,
                       (long) strlen( outMsg ), outMsg ) != PUT_OK )
    {
      /*     Log an error message                                    */
      logit( "et", "k2ew: Failed to send an error message (%d).\n",
             code );
    }
  }
}
