/* 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 $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 (so 0x1234 is (uchar)0x34 (uchar)0x12 on the wire) A message looks like: 0x81ff (sync) [ } } Only when greater than '0' } ] } 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 is a bitfield indicating a query for a specic message, an ack, a nak, ... */ #ifdef HAVE_CONFIG_H # include #endif #if defined(REFCLOCK) && defined(CLOCK_JUPITER) #include #include #include #include #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 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 ++ 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> 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;irecv_length;i++) printf("%02x ", rbufp->recv_buffer[i]); printf("\n"); } for(i=0;irecv_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) */