1187712Sraj/* 2187712Sraj * refclock_fg - clock driver for the Forum Graphic GPS datating station 3187712Sraj */ 4187712Sraj 5187712Sraj#ifdef HAVE_CONFIG_H 6187712Sraj# include <config.h> 7187712Sraj#endif 8187712Sraj 9187712Sraj#if defined(REFCLOCK) && defined(CLOCK_FG) 10187712Sraj 11187712Sraj#include "ntpd.h" 12187712Sraj#include "ntp_io.h" 13187712Sraj#include "ntp_refclock.h" 14187712Sraj#include "ntp_calendar.h" 15187712Sraj#include "ntp_stdlib.h" 16187712Sraj 17187712Sraj/* 18187712Sraj * This driver supports the Forum Graphic GPS dating station. 19187712Sraj * More information about FG GPS is available on http://www.forumgraphic.com 20187712Sraj * Contact das@amt.ru for any question about this driver. 21187712Sraj */ 22187712Sraj 23187712Sraj/* 24187712Sraj * Interface definitions 25187712Sraj */ 26187712Sraj#define DEVICE "/dev/fgclock%d" 27187712Sraj#define PRECISION (-10) /* precision assumed (about 1 ms) */ 28187712Sraj#define REFID "GPS" 29187712Sraj#define DESCRIPTION "Forum Graphic GPS dating station" 30187712Sraj#define LENFG 26 /* timecode length */ 31187712Sraj#define SPEED232 B9600 /* uart speed (9600 baud) */ 32187712Sraj 33187712Sraj/* 34187712Sraj * Function prototypes 35187712Sraj */ 36187712Srajstatic int fg_init (int); 37187712Srajstatic int fg_start (int, struct peer *); 38187712Srajstatic void fg_shutdown (int, struct peer *); 39187712Srajstatic void fg_poll (int, struct peer *); 40187712Srajstatic void fg_receive (struct recvbuf *); 41187712Sraj 42187712Sraj/* 43187712Sraj * Forum Graphic unit control structure 44187712Sraj */ 45187712Sraj 46187712Srajstruct fgunit { 47187712Sraj int pollnum; /* Use peer.poll instead? */ 48187712Sraj int status; /* Hug to check status information on GPS */ 49187712Sraj int y2kwarn; /* Y2K bug */ 50187712Sraj}; 51187712Sraj 52187712Sraj/* 53187712Sraj * Queries definition 54187712Sraj */ 55187712Srajstatic char fginit[] = { 0x10, 0x48, 0x10, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56187712Sraj0, 0, 0, 0, 0, 0, 0, 0, 0 }; 57187712Srajstatic char fgdate[] = { 0x10, 0x44, 0x10, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58187712Sraj0, 0, 0, 0, 0, 0, 0, 0, 0 }; 59187712Sraj 60187712Sraj/* 61187712Sraj * Transfer vector 62187712Sraj */ 63187712Srajstruct refclock refclock_fg = { 64187712Sraj fg_start, /* start up driver */ 65187712Sraj fg_shutdown, /* shut down driver */ 66187712Sraj fg_poll, /* transmit poll message */ 67187712Sraj noentry, /* not used */ 68187712Sraj noentry, /* initialize driver (not used) */ 69187712Sraj noentry, /* not used */ 70187712Sraj NOFLAGS /* not used */ 71187712Sraj}; 72187712Sraj 73187712Sraj/* 74187712Sraj * fg_init - Initialization of FG GPS. 75187712Sraj */ 76187712Sraj 77187712Srajstatic int 78187712Srajfg_init( 79187712Sraj int fd 80187712Sraj ) 81187712Sraj{ 82187712Sraj if (write(fd, fginit, LENFG) != LENFG) 83187712Sraj return 0; 84187712Sraj 85187712Sraj return 1; 86187712Sraj} 87187712Sraj 88187712Sraj/* 89187712Sraj * fg_start - open the device and initialize data for processing 90187712Sraj */ 91187712Srajstatic int 92187712Srajfg_start( 93187712Sraj int unit, 94187712Sraj struct peer *peer 95187712Sraj ) 96187712Sraj{ 97187712Sraj struct refclockproc *pp; 98187712Sraj struct fgunit *up; 99187712Sraj int fd; 100187712Sraj char device[20]; 101187712Sraj 102187712Sraj 103187712Sraj /* 104187712Sraj * Open device file for reading. 105187712Sraj */ 106187712Sraj snprintf(device, sizeof(device), DEVICE, unit); 107187712Sraj 108187712Sraj DPRINTF(1, ("starting FG with device %s\n",device)); 109187712Sraj 110187712Sraj fd = refclock_open(device, SPEED232, LDISC_CLK); 111187712Sraj if (fd <= 0) 112187712Sraj return (0); 113187712Sraj 114187712Sraj /* 115187712Sraj * Allocate and initialize unit structure 116187712Sraj */ 117187712Sraj 118187712Sraj up = emalloc(sizeof(struct fgunit)); 119187712Sraj memset(up, 0, sizeof(struct fgunit)); 120187712Sraj pp = peer->procptr; 121187712Sraj pp->unitptr = up; 122187712Sraj pp->io.clock_recv = fg_receive; 123187712Sraj pp->io.srcclock = peer; 124187712Sraj pp->io.datalen = 0; 125187712Sraj pp->io.fd = fd; 126187712Sraj if (!io_addclock(&pp->io)) { 127187712Sraj close(fd); 128187712Sraj pp->io.fd = -1; 129187712Sraj return 0; 130187712Sraj } 131187712Sraj 132187712Sraj 133187712Sraj /* 134187712Sraj * Initialize miscellaneous variables 135187712Sraj */ 136187712Sraj peer->precision = PRECISION; 137187712Sraj pp->clockdesc = DESCRIPTION; 138187712Sraj memcpy(&pp->refid, REFID, 3); 139187712Sraj up->pollnum = 0; 140187712Sraj 141187712Sraj /* 142187712Sraj * Setup dating station to use GPS receiver. 143187712Sraj * GPS receiver should work before this operation. 144187712Sraj */ 145276876Sloos if(!fg_init(pp->io.fd)) 146187712Sraj refclock_report(peer, CEVNT_FAULT); 147187712Sraj 148187712Sraj return (1); 149187712Sraj} 150187712Sraj 151187712Sraj 152187712Sraj/* 153187712Sraj * fg_shutdown - shut down the clock 154276876Sloos */ 155187712Srajstatic void 156187712Srajfg_shutdown( 157187712Sraj int unit, 158187712Sraj struct peer *peer 159187712Sraj ) 160187712Sraj{ 161187712Sraj struct refclockproc *pp; 162187712Sraj struct fgunit *up; 163187712Sraj 164187712Sraj pp = peer->procptr; 165187712Sraj up = pp->unitptr; 166187712Sraj if (pp->io.fd != -1) 167187712Sraj io_closeclock(&pp->io); 168187712Sraj if (up != NULL) 169187712Sraj free(up); 170187712Sraj} 171187712Sraj 172187712Sraj 173187712Sraj/* 174187712Sraj * fg_poll - called by the transmit procedure 175187712Sraj */ 176187712Srajstatic void 177187712Srajfg_poll( 178187712Sraj int unit, 179187712Sraj struct peer *peer 180187712Sraj ) 181187712Sraj{ 182187712Sraj struct refclockproc *pp; 183187712Sraj 184187712Sraj pp = peer->procptr; 185187712Sraj 186187712Sraj /* 187187712Sraj * Time to poll the clock. The FG clock responds to a 188187712Sraj * "<DLE>D<DLE><CR>" by returning a timecode in the format specified 189187712Sraj * above. If nothing is heard from the clock for two polls, 190187712Sraj * declare a timeout and keep going. 191187712Sraj */ 192187712Sraj 193187712Sraj if (write(pp->io.fd, fgdate, LENFG) != LENFG) 194187712Sraj refclock_report(peer, CEVNT_FAULT); 195187712Sraj else 196187712Sraj pp->polls++; 197187712Sraj 198187712Sraj /* 199187712Sraj if (pp->coderecv == pp->codeproc) { 200187712Sraj refclock_report(peer, CEVNT_TIMEOUT); 201187712Sraj return; 202187712Sraj } 203187712Sraj */ 204187712Sraj 205187712Sraj record_clock_stats(&peer->srcadr, pp->a_lastcode); 206187712Sraj 207187712Sraj return; 208187712Sraj 209187712Sraj} 210187712Sraj 211187712Sraj/* 212187712Sraj * fg_receive - receive data from the serial interface 213187712Sraj */ 214187712Srajstatic void 215187712Srajfg_receive( 216187712Sraj struct recvbuf *rbufp 217187712Sraj ) 218187712Sraj{ 219187712Sraj struct refclockproc *pp; 220187712Sraj struct fgunit *up; 221187712Sraj struct peer *peer; 222187712Sraj char *bpt; 223187712Sraj 224187712Sraj /* 225187712Sraj * Initialize pointers and read the timecode and timestamp 226187712Sraj * We can't use gtlin function because we need bynary data in buf */ 227187712Sraj 228187712Sraj peer = rbufp->recv_peer; 229187712Sraj pp = peer->procptr; 230187712Sraj up = pp->unitptr; 231187712Sraj 232187712Sraj /* 233187712Sraj * Below hug to implement receiving of status information 234187712Sraj */ 235187712Sraj if(!up->pollnum) { 236187712Sraj up->pollnum++; 237187712Sraj return; 238187712Sraj } 239187712Sraj 240187712Sraj 241187712Sraj if (rbufp->recv_length < (LENFG - 2)) { 242187712Sraj refclock_report(peer, CEVNT_BADREPLY); 243187712Sraj return; /* The reply is invalid discard it. */ 244187712Sraj } 245187712Sraj 246187712Sraj /* Below I trying to find a correct reply in buffer. 247187712Sraj * Sometime GPS reply located in the beginnig of buffer, 248187712Sraj * sometime you can find it with some offset. 249187712Sraj */ 250187712Sraj 251187712Sraj bpt = (char *)rbufp->recv_space.X_recv_buffer; 252187712Sraj while (*bpt != '\x10') 253187712Sraj bpt++; 254187712Sraj 255187712Sraj#define BP2(x) ( bpt[x] & 15 ) 256187712Sraj#define BP1(x) (( bpt[x] & 240 ) >> 4) 257187712Sraj 258187712Sraj pp->year = BP1(2) * 10 + BP2(2); 259187712Sraj 260187712Sraj if (pp->year == 94) { 261187712Sraj refclock_report(peer, CEVNT_BADREPLY); 262187712Sraj if (!fg_init(pp->io.fd)) 263187712Sraj refclock_report(peer, CEVNT_FAULT); 264187712Sraj return; 265187712Sraj /* GPS is just powered up. The date is invalid - 266187712Sraj discarding it. Initilize GPS one more time */ 267187712Sraj /* Sorry - this driver will broken in 2094 ;) */ 268187712Sraj } 269187712Sraj 270187712Sraj if (pp->year < 99) 271187712Sraj pp->year += 100; 272187712Sraj 273187712Sraj pp->year += 1900; 274187712Sraj pp->day = 100 * BP2(3) + 10 * BP1(4) + BP2(4); 275187712Sraj 276187712Sraj/* 277187712Sraj After Jan, 10 2000 Forum Graphic GPS receiver had a very strange 278187712Sraj benahour. It doubles day number for an hours in replys after 10:10:10 UTC 279187712Sraj and doubles min every hour at HH:10:ss for a minute. 280187712Sraj Hope it is a problem of my unit only and not a Y2K problem of FG GPS. 281187712Sraj Below small code to avoid such situation. 282187712Sraj*/ 283187712Sraj if (up->y2kwarn > 10) 284187712Sraj pp->hour = BP1(6)*10 + BP2(6); 285187712Sraj else 286187712Sraj pp->hour = BP1(5)*10 + BP2(5); 287187712Sraj 288187712Sraj if ((up->y2kwarn > 10) && (pp->hour == 10)) { 289187712Sraj pp->minute = BP1(7)*10 + BP2(7); 290187712Sraj pp->second = BP1(8)*10 + BP2(8); 291187712Sraj pp->nsec = (BP1(9)*10 + BP2(9)) * 1000000; 292187712Sraj pp->nsec += BP1(10) * 1000; 293187712Sraj } else { 294187712Sraj pp->hour = BP1(5)*10 + BP2(5); 295187712Sraj pp->minute = BP1(6)*10 + BP2(6); 296187712Sraj pp->second = BP1(7)*10 + BP2(7); 297187712Sraj pp->nsec = (BP1(8)*10 + BP2(8)) * 1000000; 298187712Sraj pp->nsec += BP1(9) * 1000; 299187712Sraj } 300187712Sraj 301187712Sraj if ((pp->hour == 10) && (pp->minute == 10)) { 302187712Sraj up->y2kwarn++; 303187712Sraj } 304187712Sraj 305187712Sraj snprintf(pp->a_lastcode, sizeof(pp->a_lastcode), 306187712Sraj "%d %d %d %d %d", pp->year, pp->day, pp->hour, 307187712Sraj pp->minute, pp->second); 308187712Sraj pp->lencode = strlen(pp->a_lastcode); 309187712Sraj /*get_systime(&pp->lastrec);*/ 310187712Sraj 311187712Sraj#ifdef DEBUG 312187712Sraj if (debug) 313187712Sraj printf("fg: time is %04d/%03d %02d:%02d:%02d UTC\n", 314187712Sraj pp->year, pp->day, pp->hour, pp->minute, pp->second); 315187712Sraj#endif 316187712Sraj pp->disp = (10e-6); 317187712Sraj pp->lastrec = rbufp->recv_time; /* Is it better than get_systime()? */ 318187712Sraj /* pp->leap = LEAP_NOWARNING; */ 319187712Sraj 320187712Sraj /* 321187712Sraj * Process the new sample in the median filter and determine the 322187712Sraj * timecode timestamp. 323187712Sraj */ 324187712Sraj 325187712Sraj if (!refclock_process(pp)) 326187712Sraj refclock_report(peer, CEVNT_BADTIME); 327187712Sraj pp->lastref = pp->lastrec; 328187712Sraj refclock_receive(peer); 329187712Sraj return; 330187712Sraj} 331187712Sraj 332187712Sraj 333187712Sraj#else 334187712Srajint refclock_fg_bs; 335187712Sraj#endif /* REFCLOCK */ 336187712Sraj