/* Moved the output of lat/long/etc to clockstats instead of syslog
   This is more in line with NMEA output and keeps extra stuff out of the syslog
   Added emate_last_code() function to format the output for the clockstats file.
   
   When the solution is invalid (always invalid at cold start) the syslog
   is updated with status reports until the position becomes valid.
   When the solution becomes valid the clockstats file is updated at poll times.
   The clockstats message is:

   ?Mate: sssss.sssss,yy.yyyyyyc,xxx.xxxxxxc,zzzz.z,nn,sol(xxxx):s,s,s

	?Mate		T|E+Mate Model
	ssssss.ssssss	seconds since midnight GPS (UTC) time
	yy.yyyyyyc	latitude(N|S) in decimal degrees
	xxx.xxxxxxc	longutude(E|W) in decimal degrees
	zzzz.z		elevation in metres
	nn			number of satellites
	sol(0000)		solution flags from GPS
	s,s,s		description of solution flags
	
   Everything is zero filled so the records are fixed length - except for the
   description of the solution flags. Easy to read into a database that way.

   Also added condition for Tripmate | Earthmate throughtout code. There are three
   basic differences: description, baudrate and magic string to start receiver.
   I left the device as /dev/emateX but this also could be changed to
   /dev/tmateX ? or the more generic /dev/gpsX

   Murray Marien (mm@DigitalMapping.sk.ca) feb.24.2002			*/

/* Changed magic word for TripMate.  Should handle both dynamically.
   Also needs to be put in Rockwell mode.
   jlw 01/02/04 (that's 2001-FEB-04 for you ISO people.) */

/* Observations are that the timestamp is on the previous second, and that
   there's even a little slop after that.  But, if I have to fudge all
   three of my reference clocks, to different values mind you, which
   one is correct?  (does position pinning affect timing?  11ms is how
   many miles? */

/*
  REFERENCE-CLOCK DRIVER for an DeLorme Earthmate. Uses
  just the serial port at 9600bps, no PPS no nothing...

  I use `emate' as an indentifier thruout the sourcecode.
  Heavily based upon refclock_nmea.c, replaces refclock_jupiter.c
  in the original ntp-sources (as long as I don't figure out how to do
  it the correct way :-) ).

  Christian Vogel <chris@obelix.hedonism.cx>

  $Id: refclock_jupiter.c,v 1.2 2000/12/26 14:05:30 chris Exp $

 */

/*

  The Tripmate defaults to NMEA, but it can be switched to Rockwell binary
  by sending the $PRWIIPRO,,RBIN cr/lf command.  The Tripmate does
  have a few annoying anomalies, like position pinning defaults to "on" and
  there is the idiotic 'ASTRAL' echo initialization sequence.

$PRWIIPRO,,RBIN

  ROCKWELL-BINARY-FORMAT uses sequence of unsigned shorts (16bit, 2 chars) in the
  order <low-byte><high-byte>  (so 0x1234 is   (uchar)0x34 (uchar)0x12 on the wire)

  A message looks like:

  0x81ff   (sync)
  <msgid>
  <msglen>
  <msgopt>
  <headersum>
 [                    }
  <msglen*data>       } Only when <msglen> greater than '0'
  <datasum>           }
 ]                    }

  DeLorme Earthmate has the annoying behaviour that it (upon startup and
  whenever it feels like) stops sending rockwell-messages and transmits
  the `MAGIC' string "EARTHA\r\n" every second. You have to send it the
  same string back so it speaks ROCKWELL again.

  The <msgopt> is a bitfield indicating a query for a specic message,
  an ack, a nak, ...

 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#if defined(REFCLOCK) && defined(CLOCK_JUPITER)

#include <stdio.h>
#include <ctype.h>
#include <sys/time.h>
#include <time.h>

#include "ntpd.h"
#include "ntp_io.h"
#include "ntp_refclock.h"
#include "ntp_unixtime.h"
#include "ntp_stdlib.h"
#include "ntp_calendar.h"

/****************************************************************************
 *
 * LOCAL #defines
 *
 ****************************************************************************/

#define EARTHMATE	0	 /* 0 for EarthMate, 1 for TripMate			*/
#define TRIPMATE	1

#define MMODEL EARTHMATE	/* compile for EarthMate						*/

#if MMODEL
#define DESCRIPTION "DeLorme Tripmate GPS Receiver"
#define MAGIC       "ASTRAL\r\n"           /* magic string for TripMate 	*/
#define MAGICLEN    8
#define SPEEDRS232	B4800
#else
#define DESCRIPTION "DeLorme Earthmate GPS Receiver"
#define MAGIC       "EARTHA\r\n"           /* magic string for EarthMate 	*/
#define MAGICLEN    8
#define SPEEDRS232	B9600
#endif

#define DEVICE      "/dev/emate%d"		   /* could be tmate for Tripmate 	*/
#define PRECISION   (-9)                   /* precision assumed (2ms) 	*/
#define REFID       "GPS\0"                /* reference Id 				*/
#define WBUFLEN     256                    /* decoder buffer length (words) */
#define PROTOCH     "$PRWIIPRO,,RBIN\r\n"  /* protocol change string		*/
#define PROTOLEN    17
#define SYNC_A      0xff
#define SYNC_B      0x81

#define HDR_LENGTH  5 /* length of header */
#define HDR_SYNC    0 /* position of fields in header */x
#define HDR_MSGID   1
#define HDR_MSGLEN  2
#define HDR_MSGOPT  3
#define HDR_HSUM    4

#define MSGID_GEOPOS 1000 /* geodetic position */
#define MSGLEN_GEOPOS 55

#define GEO_INVAL          9
#define GEO_INVAL_MASK  0x1f
#define INVAL_ALTITUDE  0x01
#define INVAL_NODGPS    0x02
#define INVAL_FEWSAT    0x04
#define INVAL_EXC_EHPE  0x08
#define INVAL_EXC_EVPE  0x10

#define GEO_SOLN          10
#define GEO_SOLN_MASK   0x07
#define SOLN_PROP       0x01
#define SOLN_ALTITUDE   0x02
#define SOLN_DGPS       0x04

#define GEO_NSAT          11

#define GEO_DAY           18
#define GEO_MONTH         19
#define GEO_YEAR          20
#define GEO_HOUR          21
#define GEO_MIN           22
#define GEO_SEC           23
#define GEO_NSEC          24

#define GEO_LAT           26
#define GEO_LONG          28
#define GEO_HEIGHT        30

#define LOG_COUNT        100 /* log gps info every LOG_COUNT polls */

#ifndef PI
#define PI 3.14159265358979323848
#endif

#define RADDEG(r) ((180*(double)(r))/PI)


/* Message options */
#define OPT_DISC    0x8000
#define OPT_CONN    0x4000
#define OPT_LOG     0x2000
#define OPT_QUERY   0x0800
#define OPT_REQUEST 0x0400
#define OPT_ACK     0x0200
#define OPT_NAK     0x0100


/****************************************************************************
 *
 * LOCAL declarations, typedefs and global vars...
 *
 ****************************************************************************/

#undef uchar
#undef ushort

typedef unsigned char uchar;
/* already defined in sys/types.h!!  typedef unsigned short ushort; */

/* current state of decoder: */
enum edstate { /* EnumDecoderSTATE */
     d_sync,
     d_sync_b,
     /*
       d_sync_b: Wait for 0x81 -> d_odd, honor 0xff or magic
       d_sync  : Wait for 0xff -> d_sync_b, honor magic
      */
     d_odd,    /* waiting for odd packet  (1st, 3rd, ...) */
     d_even    /* waiting for even packet (2nd, 4th, ...) */
};


/* result of feeding chars to decoder */
enum feedres {
     f_more,   /* just send more data... */
     f_sync,   /* that was the sync packet! Make note of time! */
     f_msg,    /* now I have a valid message to process */
     f_err,    /* that was -evil- */
     f_magic   /* send magic! */
};  /* decoder want more chars, has one msg, has err */

/* result of decoding the solution of the gps receiver in the 1000 - message */
enum solres {
     s_valid,   /* valid solution */
     s_invalid, /* no valid solution */
     s_error    /* parse error */
};

struct emate {
     l_fp synctime; /* timestamp when sync came in */
     ushort pollid;   /* we are polling for that msgid right now */
     int didreset;    /* have we already sent cold start? */
     struct dstate { /* decoder state */
	  enum edstate state;
	  ushort wbuf[WBUFLEN];   /* the ushorts meet here */
	  int    bufp;
	  /*
	    in d_sync/d_syncb: next `magic' char to look for
	    in d_even/d_odd  : char goes to wbuf[bufp]
	   */
	  ushort msgid;   /* convenient to have them handy! */
	  ushort msglen;
	  ushort msgopt;
     } decoder;
     struct gpsstate { /* earthmate protocol state */
	  long latitude;   /* position information in raw-format */
	  long longitude;
	  long height;
	  ushort invalid;  /* bits for invalid solution */
	  ushort solution; /* bits for valid solution */
	  ushort nsat;     /* number of satellites used in solution*/
	  int changed;     /* information has changed since last report */
     } gps;
};

ushort emate_cksum(ushort *buf,int len);
unsigned char * emate_optstring(ushort opt);
static int emate_start(int unit,struct peer *peer);
static void emate_shutdown(int unit, struct peer *peer);
enum feedres emate_feed_char(uchar c, struct dstate *decoder);
static void emate_send(int fd,ushort msgid,ushort msglen, ushort msgflag, ushort *data);
int emate_leapyear(int year);
int emate_day_of_year(int year,int month,int day);
static enum solres emate_decode_1000(struct refclockproc *pp,struct dstate *d,struct gpsstate *g);
static void emate_disconnect(int fd,ushort msgid);
static void emate_recv(struct recvbuf *rbufp);
static void emate_poll(int unit, struct peer *peer);
static void emate_log_gps(struct gpsstate *g);
static void emate_last_code(struct gpsstate *g, struct refclockproc *pp);
static void emate_send_reset(int fd);


/* transfer vector */
struct refclock refclock_jupiter = {
     emate_start,
     emate_shutdown,
     emate_poll,
     noentry,
     noentry,
     noentry,
     NOFLAGS
};

/***************************************************************************
 *
 * emate_cksum : Build checksum. Is used in header and data-area.
 *
 ***************************************************************************/
ushort emate_cksum(ushort *buf,int len){
     ushort s=0;
     int i;
     for(i=0;i<len;i++)
	  s=(s+buf[i])&0xffff;
     s=(1+~s)&0xffff;
     return s;
};

/***************************************************************************
 *
 * emate_optstring : Convert option-word into string for easy viewing
 *
 ***************************************************************************/
unsigned char * emate_optstring(ushort opt) {
     static char returnme[]="dclqran";
     returnme[0]=(opt & OPT_DISC    )?'D':'-';
     returnme[1]=(opt & OPT_CONN    )?'C':'-';
     returnme[2]=(opt & OPT_LOG     )?'L':'-';
     returnme[3]=(opt & OPT_QUERY   )?'Q':'-';
     returnme[4]=(opt & OPT_REQUEST )?'R':'-';
     returnme[5]=(opt & OPT_ACK     )?'A':'-';
     returnme[6]=(opt & OPT_NAK     )?'N':'-';
     return returnme;
}

/***************************************************************************
 *
 * emate_start: Init the emate clock... return 1 on success, 0 on failure
 *
 ***************************************************************************/
static int emate_start(int unit,struct peer *peer){
     struct emate *up;
     struct refclockproc *pp;
     int fd;
     char device[128];

     (void)snprintf(device,sizeof(device),DEVICE,unit);

     if(!(fd=refclock_open(device,SPEEDRS232,LDISC_RAW))){
	  return 0;
     }
     if (debug > 0) { printf("%s device opened.\n",MMODEL?"TripMate":"EarthMate"); }

     if(!(up=emalloc(sizeof(struct emate)))){
	  return 0;
     }

     memset(up,'\0',sizeof(struct emate));

     pp = peer->procptr;
     pp->io.clock_recv = emate_recv;
     pp->io.srcclock = (caddr_t)peer;
     pp->io.datalen = 1; /* we want single bytes! */
     pp->io.fd=fd;
     pp->unitptr=(caddr_t)up;

     if(!io_addclock(&pp->io)){
	  close(fd);
	  free(up);
	  return 0;
     }

     peer->precision = PRECISION;
     pp->clockdesc = DESCRIPTION;
     memcpy(&pp->refid,REFID,4);

     /* we do not init our receiver here, because we do not know
	what state it is in... Maybe he wants EARTHA... maybe not... */

     return 1;

}/* emate_start */

/***************************************************************************
 *
 * emate_shutdown
 *
 ***************************************************************************/
static void emate_shutdown(int unit, struct peer *peer){
     io_closeclock(&peer->procptr->io);
     free(peer->procptr->unitptr);
}

/***************************************************************************
 *
 * emate_feed_char -> decode the message char-by-char. State is kept in
 * struct dstate * decoder. Returns a feedres-enum as defined above.
 *
 ***************************************************************************/
enum feedres emate_feed_char(uchar c, struct dstate *decoder){
     uchar magic[MAGICLEN]=MAGIC; /* magic to search for */

     switch(decoder->state){
     case d_sync_b:
	  if(c==SYNC_B){
	       decoder->state=d_odd;
	       decoder->wbuf[0]=0x81ff;
	       decoder->bufp=1;

	       if (debug > 2) { printf("found RBIN sync\n"); }

	       return f_more;       /* hooray we found our 2nd sync byte */
	  }
	  /* fallthru! */
     case d_sync:
	  if(c==SYNC_A){
	       decoder->state=d_sync_b;
	       decoder->bufp=0;
	       return f_sync;       /* note time of sync-byte 0xff */
	  }

	  /* we found no sync-byte (0xff/0x81. Reset decoder) */
	  decoder->state=d_sync;

	  /* look for magic */
	  if(c==magic[decoder->bufp]){
	       decoder->bufp++;
	       if(decoder->bufp == MAGICLEN){
		    decoder->bufp=0;
		    return f_magic; /* we found the magic string */
	       } else
		    return f_more;  /* gimme more data */
	  } else {
	       decoder->bufp=0;
	       return f_more;       /* gimme more data */
	  }
	  break;

     case d_odd:
	  decoder->wbuf[decoder->bufp]=c; /* low byte */
	  decoder->state=d_even;
	  return f_more;
	  break;

     case d_even:

	  decoder->wbuf[decoder->bufp++]+=(0x100*c); /* high byte */
	  decoder->state=d_odd;

	  if(decoder->bufp == HDR_LENGTH ){ /* header is here */
	       ushort s;
	       s=emate_cksum(decoder->wbuf,HDR_LENGTH-1);
	       if(s!=decoder->wbuf[HDR_HSUM]){
		    decoder->state=d_sync;
		    decoder->bufp=0;
		    return f_err; /* error: Checksum bad. */
	       }
	       decoder->msgid =decoder->wbuf[HDR_MSGID];
	       decoder->msglen=decoder->wbuf[HDR_MSGLEN];
	       decoder->msgopt=decoder->wbuf[HDR_MSGOPT];
	       /* special: empty data-section */
	       if(decoder->msglen == 0){
		    decoder->state = d_sync;
		    decoder->bufp=0;
		    return f_msg; /* this message has no data */
	       }
	       /* check length of message to come! */
	       if(decoder->msglen > WBUFLEN-6){ /* will not fit! */
		    decoder->state = d_sync;
		    decoder->bufp=0;
		    return f_err;
	       }
	       return f_more; /* normal header, data follows */
	  }
	  /* a message with data has a total of <header(HDR_LENGTH)>+<data>+<checksum(1)>
	     of data. So we look for decoder->bufp to be len+HDR_LENGTH+1 */
	  if(decoder->bufp > 3 && (decoder->bufp == (decoder->msglen+HDR_LENGTH+1))){
	       ushort s;
	       s=emate_cksum(&(decoder->wbuf[5]),decoder->msglen);
	       if(s != decoder->wbuf[decoder->msglen+HDR_LENGTH]){
		    decoder->state = d_sync;
		    decoder->bufp = 0;
		    return f_err;
	       }
	       decoder->state = d_sync;
	       decoder->bufp=0;
	       return f_msg;
	  }
     }
     return f_more;
}

/***************************************************************************
 *
 * emate_send -> Write message to earthmate attached to filedescriptor fd
 * magid/len/options as given, data points to message-data (only used if len>0)
 *
 ***************************************************************************/
static void emate_send(int fd,ushort msgid,ushort msglen, ushort msgflag, ushort *data){
     ushort wbuf[WBUFLEN];
     uchar cbuf[2*WBUFLEN];
     int i;

     wbuf[0]=0x81ff;
     wbuf[1]=msgid;
     wbuf[2]=msglen;
     wbuf[3]=msgflag;
     wbuf[4]=emate_cksum(wbuf,4);

     if (debug > 1) { printf("Sending some RBIN to %s.\n",MMODEL?"TripMate":"EarthMate");}
     if(msglen){
	  for(i=0;i<msglen;i++)
	       wbuf[5+i]=data[i];
	  wbuf[5+msglen]=emate_cksum(data,msglen);
	  msglen++; /* we have a checksum */
     }

     /* copy shorts to char-buffer */
     for(i=0;i<5+msglen;i++){
	  cbuf[2*i    ]=wbuf[i] & 0xff; /* low byte */
	  cbuf[2*i + 1]=0xff & (wbuf[i] >> 8); /* high byte */
     };

     write(fd,cbuf,2*(5+msglen)); /* write packet */
}

/***************************************************************************
 *
 * emate_leapyear(year)     Is year a leapyear (feb has 29 days)?
 *   returns 0:
 *
 ***************************************************************************/

int emate_leapyear(int year){
     if(year % 4)
	  return 0; /* year does not divide by 4 -> no leapyear */
     /* year divides by 4, so it's a leapyear.... only if it does divide by 100... */
     if(year % 100)
	  return 1;
     /* so year divides by 100. Therefore it's no leapyear... only if it divides by 400..*/
     if(year % 400)
	  return 0;
     return 1;
}


/***************************************************************************
 *
 * emate_day_of_year(y,m,d): Return day_of_year for date y:m:d.
 *   y: full year, including thousands and houndreds
 *   m: month, 1-based. (Jan=1, Feb=2, .. Dec=12)
 *   d: Day of month
 *   returns day of year
 *
 ***************************************************************************/
int emate_day_of_year(int year,int month,int day){
     int i, daytab[]={31,28,31,30,31,30,31,31,30,31,30,31}; /* no leapyear! */
     for(i=0;i<(month-1);i++)
	  day += (i==1 && emate_leapyear(year))?29:daytab[i];
     return day;
};

/***************************************************************************
 *
 * emate_decode_1000
 *  this one does the real work and converts the earthmate-data
 *  found in (dstate*)d to something useful in (refclockproc*)pp
 *  Maybe we add something like logging our position later?
 *
 ***************************************************************************/
static enum solres emate_decode_1000(struct refclockproc *pp,struct dstate *d,struct gpsstate *g){

     if(d->msgid  != MSGID_GEOPOS){
	  return s_error;
     }
     if (debug > 1) { printf("Got a GEOPOS message.\n");}
     if((d->msglen+6) != MSGLEN_GEOPOS){
	  return s_error;
     }

     /* update gps state (if pointer specified != NULL...) */
     if(g!=NULL){

	  if( (g->invalid  != (d->wbuf[GEO_INVAL] & GEO_INVAL_MASK ))||
	      (g->solution != (d->wbuf[GEO_SOLN]  & GEO_SOLN_MASK  ))||
	       g->nsat     !=  d->wbuf[GEO_NSAT]                    ){

	       g->changed = 1;
	  }

	  g->invalid   = (d->wbuf[GEO_INVAL] & GEO_INVAL_MASK );
	  g->solution  = (d->wbuf[GEO_SOLN]  & GEO_SOLN_MASK  );
	  g->nsat      =  d->wbuf[GEO_NSAT];
	  g->longitude = (d->wbuf[GEO_LONG  ]+d->wbuf[GEO_LONG+1  ]*0x10000);
	  g->latitude  = (d->wbuf[GEO_LAT   ]+d->wbuf[GEO_LAT+1   ]*0x10000);
	  g->height    = (d->wbuf[GEO_HEIGHT]+d->wbuf[GEO_HEIGHT+1]*0x10000);

     }

     if(d->wbuf[GEO_INVAL] & GEO_INVAL_MASK){
	  return s_invalid;
     }


     /* update refclockp if specified */
     if(pp!=NULL){
	  pp->day = emate_day_of_year(
	       d->wbuf[GEO_YEAR],d->wbuf[GEO_MONTH],d->wbuf[GEO_DAY]);
	  pp->year   = d->wbuf[GEO_YEAR];
	  pp->hour   = d->wbuf[GEO_HOUR];
	  pp->minute = d->wbuf[GEO_MIN];
	  pp->second = d->wbuf[GEO_SEC];
	  pp->usec   = (d->wbuf[GEO_NSEC]+(d->wbuf[GEO_NSEC+1]*0x10000))/1000;
     }

     return s_valid;
}

/***************************************************************************
 *
 * emate_disconnect -> tell the emate-receiver that we are not interested
 * in receiving messages of type *msgid (send empty message with that
 * msgid which has the DISCONNECT-bit set in its options)
 *
 ***************************************************************************/
static void emate_disconnect(int fd,ushort msgid){
	  emate_send(fd,msgid,0,OPT_DISC,NULL);
}


/***************************************************************************
 *
 * emate_recv receive data from port. We *really* want only single bytes
 * to appear here, so set datalen to 1 in emate_start
 *
 ***************************************************************************/
static void emate_recv(struct recvbuf *rbufp){
     struct peer *peer;
     struct refclockproc *pp;
     struct emate *up;
     int i;

     peer = (struct peer *)rbufp->recv_srcclock;
     pp   = peer->procptr;
     up   = (struct emate *)pp->unitptr;

     if (debug > 2) {
	 printf("%s says",MMODEL?"TripMate":"EarthMate");
	 for(i=0;i<rbufp->recv_length;i++)
	      printf("%02x ", rbufp->recv_buffer[i]);
	 printf("\n");
     }
     for(i=0;i<rbufp->recv_length;i++)
	  switch(emate_feed_char(rbufp->recv_buffer[i],&(up->decoder))){
	  case f_more:
	       break;
	  case f_sync:
	       /* note time for sync */
	       up->synctime = rbufp->recv_time;
	       break;
	  case f_magic:
	       /* we send the magic words... */
	       msyslog(LOG_INFO,"%s: Received the magic string. I'll try to answer.",MMODEL?"TripMate":"EarthMate");
	       if (debug > 0) { printf("%s sent magic.\n",MMODEL?"TripMate":"EarthMate");}
	       write(pp->io.fd,MAGIC,MAGICLEN);
	       if (debug > 0) { printf("We sent magic.\n");}
	       sleep(1);
	       if (debug > 0) { printf("Delayed.\n");}
	       /* should we delay some? Perhaps. */
	       write(pp->io.fd,PROTOCH,PROTOLEN); /* Change to Rockwell proto */
	       if (debug > 0) { printf("Changed to RBIN.\n");}
	       sleep(1);
	       if (debug > 0) { printf("Delayed again.\n");}
	       break;
	  case f_err:
	       msyslog(LOG_INFO,"%s: Received junk.\n",MMODEL?"TripMate":"EarthMate");
	       if (debug > 0) { printf("%s sent junk.\n",MMODEL?"TripMate":"EarthMate");}
	       refclock_report(peer,CEVNT_BADREPLY);
	       break;
	  case f_msg:
	       if(!up->didreset){
		    emate_send_reset(pp->io.fd);
		    up->didreset++;
		    break;
	       }
	       if(up->decoder.msgopt & (OPT_ACK | OPT_NAK)){
		    msyslog(LOG_INFO,"%s: Got ACK/NAK-msg: id=%d len=%d opt=%s",MMODEL?"TripMate":"EarthMate"
		    	    ,up->decoder.msgid,up->decoder.msglen, emate_optstring(up->decoder.msgopt));
		    break; /* ignore ack and nak packets */
	       }
	       if(up->decoder.msgid != up->pollid){
		    msyslog(LOG_INFO,"%s: Unexpected msg %d received. I'll try to disable...",
			    MMODEL?"TripMate":"EarthMate",up->decoder.msgid);
		    emate_disconnect(pp->io.fd,up->decoder.msgid);
		    break;
	       }
	       if(up->pollid){
		    up->pollid=0;
		    switch(emate_decode_1000(pp,&(up->decoder),&(up->gps))){

		    case s_valid:
			 if (debug > 1) { printf("Valid decode.\n"); }
			 pp->lastrec = up->synctime;

			 if(!refclock_process(pp)){
			      refclock_report(peer,CEVNT_BADTIME);
			 } else {
			      refclock_receive(peer);
	/* make up a lastcode for clockstats							*/
			      emate_last_code(&up->gps, pp);
			      record_clock_stats(&peer->srcadr,pp->a_lastcode);
			 }

	/*	only report if invalid - see below
			 if(up->gps.changed)
			      emate_log_gps(&up->gps);
	*/			      
			 break;

		    case s_invalid:
			 if (debug > 1) { printf("Invalid decode.\n"); }
			 refclock_report(peer,CEVNT_BADTIME);
	/* while invalid, if status changes report it to the syslog			*/
			 if(up->gps.changed)
			      emate_log_gps(&up->gps);
			 break;

		    case s_error:
			 if (debug > 1) { printf("Other decode problem.\n"); }
			 refclock_report(peer,CEVNT_BADREPLY);
			 break;
		    }
	       }
	       break;
	  }
}

/***************************************************************************
 *
 * emate_poll -> Send packet to earthmate-receiver to make him send the
 * 1000/geodetic_position-message (set msgid to 1000 and set OPT_QUERY flag)
 *
 ***************************************************************************/
static void emate_poll(int unit, struct peer *peer){
     struct refclockproc *pp;
     struct emate *up;

     if (debug > 0) { printf("Polling %s.\n",MMODEL?"TripMate":"EarthMate");}
     /* send poll packet for 1000 msg */
     pp   = peer->procptr;
     up   = (struct emate *)pp->unitptr;
     emate_send(pp->io.fd,MSGID_GEOPOS,0,OPT_QUERY,NULL);
     up->pollid=MSGID_GEOPOS;
}

/***************************************************************************
 *
 * emate_log_gps -> write gps-info to syslog. This allows me to track changes
 * in number of satellites etc... in my logfile...
 *
 ***************************************************************************/

static void emate_log_gps(struct gpsstate *g){
/*     double d_lon,d_lat,d_height;	*/

     /* reset flag */
     g->changed=0;

/*	d_lat = RADDEG(g->latitude  * 1e-8);
     d_lon = RADDEG(g->longitude * 1e-8);
     d_height = g->height * 1e-2;
*/
     msyslog(LOG_INFO,"%s: gps invalid(%04x): %s,%s,%s,%s,%s "
	     "solution(%04x): %s,%s,%s nsat:%d",
		MMODEL?"TripMate":"EarthMate",
	     g->invalid,
	     g->invalid & INVAL_ALTITUDE ? "altitude":"-",
	     g->invalid & INVAL_NODGPS   ? "nodgps":"-",
	     g->invalid & INVAL_FEWSAT   ? "not enough satellites":"-",
	     g->invalid & INVAL_EXC_EHPE ? "exceed ehpe":"-",
	     g->invalid & INVAL_EXC_EVPE ? "exceed evpe":"-",
	     g->solution,
	     (g->solution & SOLN_PROP     ? "propagated":"-"),
	     (g->solution & SOLN_ALTITUDE ? "altitude used":"-"),
	     (g->solution & SOLN_DGPS     ? "dgps used":"-"),
	     g->nsat);

/*	this is invalid data so don't bother reporting it - see below

     msyslog(LOG_INFO,"EMATE: lat:%12.8f%c lon:%12.8f%c height: %9.2fm\n",
	     d_lat,(d_lat>0?'N':'S'),
	     d_lon,(d_lon>0?'E':'W'),
	     d_height);
*/
}

/***************************************************************************
 *
 * emate_last_code() -> write GPS info to pp->a_lastcode. This is output to 
 * clockstats file if it's turned on
 *
 ***************************************************************************/

/*   The clockstats message is:
   
   ?Mate: sssss.sssss,yy.yyyyyyc,xxx.xxxxxxc,zzzz.z,nn,sol(xxxx):s,s,s

	?Mate		T|E+Mate Model
	ssssss.ssssss	seconds since midnight
	yy.yyyyyyc	latitude(N|S) in decimal degrees
	xxx.xxxxxxc	longutude(E|W) in decimal degrees
	zzzz.z		elevation in metres
	nn			number of satellites
	sol(0000)		solution flags from GPS
	s,s,s		description of solution flags
	
   Everything is zero filled so the records are fixed length - except for the
   description of the solution flags. Easy to read into a database that way.

   The size of a_lastcode is BMAX or 128 characters.
   The maximum possible size of the message with all the solution descriptions
   is 98 chars so I didn't put any size checks in code. Be aware that there is a
   limit if you change this.

*/

static void emate_last_code(struct gpsstate *g, struct refclockproc *pp){
     double d_lon,d_lat,d_height;

     d_lat = RADDEG(g->latitude  * 1e-8);
     d_lon = RADDEG(g->longitude * 1e-8);
     d_height = g->height * 1e-2;

    	sprintf(pp->a_lastcode,"%s: %05d.%06ld,%09.6f%c,%010.6f%c,%06.1f"
    		",%02d,sol(%04x):%s,%s,%s",
		MMODEL?"TMate":"EMate",
    		pp->hour*3600+pp->minute*60+pp->second,pp->usec,
     	fabs(d_lat),(d_lat>0?'N':'S'),
     	fabs(d_lon),(d_lon>0?'E':'W'),
     	d_height,
	    	g->nsat,
     	g->solution,
     	(g->solution & SOLN_PROP     ? "propagated":""),
     	(g->solution & SOLN_ALTITUDE ? "altitude used":""),
     	(g->solution & SOLN_DGPS     ? "dgps used":"")
	);
}

/***************************************************************************
 *
 * emate_send_reset(fd) -> make emate do a full reset
 *
 ***************************************************************************/
static void emate_send_reset(int fd){
     ushort data[]={
	  0,        /* sequence no. */
	  0x8007    /* bit0: invalidate ram, bit1: invalidate eeprom,
		       bit2: invalidate rtc, bit15: cold start */
     };
     msyslog(LOG_INFO,"%s: Sending COLD START command.",MMODEL?"TripMate":"EarthMate");
     emate_send(fd,1303,2,OPT_ACK|OPT_NAK|OPT_REQUEST,data);
}

/* nb: this driver doesn't use PPS				*/
#else /* not (REFCLOCK && CLOCK_JUPITER && PPS) 	*/
int refclock_jupiter_bs;
#endif /* not (REFCLOCK && CLOCK_JUPITER && PPS) 	*/
