1 /* 2 * refclock_fg - clock driver for the Forum Graphic GPS datating station 3 */ 4 5 #ifdef HAVE_CONFIG_H 6 # include <config.h> 7 #endif 8 9 #if defined(REFCLOCK) && defined(CLOCK_FG) 10 11 #include <time.h> 12 13 #include "ntpd.h" 14 #include "ntp_io.h" 15 #include "ntp_refclock.h" 16 #include "ntp_calendar.h" 17 #include "ntp_stdlib.h" 18 19 /* 20 * This driver supports the Forum Graphic GPS dating station. 21 * More information about FG GPS is available on http://www.forumgraphic.com 22 * Contact das@amt.ru for any question about this driver. 23 */ 24 25 /* 26 * Interface definitions 27 */ 28 #define DEVICE "/dev/fgclock%d" 29 #define PRECISION (-10) /* precision assumed (about 1 ms) */ 30 #define REFID "GPS" 31 #define DESCRIPTION "Forum Graphic GPS dating station" 32 #define LENFG 26 /* timecode length */ 33 #define SPEED232 B9600 /* uart speed (9600 baud) */ 34 35 /* 36 * Function prototypes 37 */ 38 static int fg_init P((int)); 39 static int fg_start P((int, struct peer *)); 40 static void fg_shutdown P((int, struct peer *)); 41 static void fg_poll P((int, struct peer *)); 42 static void fg_receive P((struct recvbuf *)); 43 44 /* 45 * Forum Graphic unit control structure 46 */ 47 48 struct fgunit { 49 int pollnum; /* Use peer.poll instead? */ 50 int status; /* Hug to check status information on GPS */ 51 int y2kwarn; /* Y2K bug */ 52 }; 53 54 /* 55 * Queries definition 56 */ 57 static char fginit[] = { 0x10, 0x48, 0x10, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 59 static char fgdate[] = { 0x10, 0x44, 0x10, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 61 62 /* 63 * Transfer vector 64 */ 65 struct refclock refclock_fg = { 66 fg_start, /* start up driver */ 67 fg_shutdown, /* shut down driver */ 68 fg_poll, /* transmit poll message */ 69 noentry, /* not used */ 70 noentry, /* initialize driver (not used) */ 71 noentry, /* not used */ 72 NOFLAGS /* not used */ 73 }; 74 75 /* 76 * fg_init - Initialization of FG GPS. 77 */ 78 79 static int 80 fg_init( 81 int fd 82 ) 83 { 84 if (write(fd, fginit, LENFG) != LENFG) 85 return 0; 86 87 return (1); 88 89 } 90 91 /* 92 * fg_start - open the device and initialize data for processing 93 */ 94 static int 95 fg_start( 96 int unit, 97 struct peer *peer 98 ) 99 { 100 struct refclockproc *pp; 101 struct fgunit *up; 102 int fd; 103 char device[20]; 104 105 106 /* 107 * Open device file for reading. 108 */ 109 (void)sprintf(device, DEVICE, unit); 110 111 #ifdef DEBUG 112 if (debug) 113 printf ("starting FG with device %s\n",device); 114 #endif 115 if (!(fd = refclock_open(device, SPEED232, LDISC_CLK))) 116 return (0); 117 118 /* 119 * Allocate and initialize unit structure 120 */ 121 122 if (!(up = (struct fgunit *) 123 emalloc(sizeof(struct fgunit)))) { 124 (void) close(fd); 125 return (0); 126 } 127 memset((char *)up, 0, sizeof(struct fgunit)); 128 pp = peer->procptr; 129 pp->unitptr = (caddr_t)up; 130 pp->io.clock_recv = fg_receive; 131 pp->io.srcclock = (caddr_t)peer; 132 pp->io.datalen = 0; 133 pp->io.fd = fd; 134 if (!io_addclock(&pp->io)) { 135 (void) close(fd); 136 return (0); 137 } 138 139 140 /* 141 * Initialize miscellaneous variables 142 */ 143 peer->precision = PRECISION; 144 pp->clockdesc = DESCRIPTION; 145 memcpy((char *)&pp->refid, REFID, 3); 146 up->pollnum = 0; 147 148 /* 149 * Setup dating station to use GPS receiver. 150 * GPS receiver should work before this operation. 151 */ 152 if(!fg_init(pp->io.fd)) 153 refclock_report(peer, CEVNT_FAULT); 154 155 return (1); 156 } 157 158 159 /* 160 * fg_shutdown - shut down the clock 161 */ 162 static void 163 fg_shutdown( 164 int unit, 165 struct peer *peer 166 ) 167 { 168 struct refclockproc *pp; 169 struct fgunit *up; 170 171 pp = peer->procptr; 172 up = (struct fgunit *)pp->unitptr; 173 io_closeclock(&pp->io); 174 free(up); 175 } 176 177 178 /* 179 * fg_poll - called by the transmit procedure 180 */ 181 static void 182 fg_poll( 183 int unit, 184 struct peer *peer 185 ) 186 { 187 struct refclockproc *pp; 188 189 pp = peer->procptr; 190 191 /* 192 * Time to poll the clock. The FG clock responds to a 193 * "<DLE>D<DLE><CR>" by returning a timecode in the format specified 194 * above. If nothing is heard from the clock for two polls, 195 * declare a timeout and keep going. 196 */ 197 198 if (write(pp->io.fd, fgdate, LENFG) != LENFG) 199 refclock_report(peer, CEVNT_FAULT); 200 else 201 pp->polls++; 202 203 if (peer->burst > 0) 204 return; 205 /* 206 if (pp->coderecv == pp->codeproc) { 207 refclock_report(peer, CEVNT_TIMEOUT); 208 return; 209 } 210 */ 211 peer->burst = NSTAGE; 212 213 record_clock_stats(&peer->srcadr, pp->a_lastcode); 214 215 216 return; 217 218 } 219 220 /* 221 * fg_receive - receive data from the serial interface 222 */ 223 static void 224 fg_receive( 225 struct recvbuf *rbufp 226 ) 227 { 228 struct refclockproc *pp; 229 struct fgunit *up; 230 struct peer *peer; 231 char *bpt; 232 233 /* 234 * Initialize pointers and read the timecode and timestamp 235 * We can't use gtlin function because we need bynary data in buf */ 236 237 peer = (struct peer *)rbufp->recv_srcclock; 238 pp = peer->procptr; 239 up = (struct fgunit *)pp->unitptr; 240 241 /* 242 * Below hug to implement receiving of status information 243 */ 244 if(!up->pollnum) 245 { 246 up->pollnum++; 247 return; 248 } 249 250 251 if (rbufp->recv_length < (LENFG-2)) 252 { 253 refclock_report(peer, CEVNT_BADREPLY); 254 return; /* The reply is invalid discard it. */ 255 } 256 257 /* Below I trying to find a correct reply in buffer. 258 * Sometime GPS reply located in the beginnig of buffer, 259 * sometime you can find it with some offset. 260 */ 261 262 bpt = (char *)rbufp->recv_space.X_recv_buffer; 263 while(*bpt != '') 264 bpt++; 265 266 #define BP2(x) ( bpt[x] & 15 ) 267 #define BP1(x) (( bpt[x] & 240 ) >> 4) 268 269 pp->year = BP1(2)*10 + BP2(2); 270 271 if(pp->year == 94) 272 { 273 refclock_report(peer, CEVNT_BADREPLY); 274 if(!fg_init(pp->io.fd)) 275 refclock_report(peer, CEVNT_FAULT); 276 return; 277 /* GPS is just powered up. The date is invalid - 278 discarding it. Initilize GPS one more time */ 279 /* Sorry - this driver will broken in 2094 ;) */ 280 } 281 282 if (pp->year < 99) 283 pp->year += 100; 284 285 pp->year += 1900; 286 pp->day = 100 * BP2(3) + 10 * BP1(4) + BP2(4); 287 288 /* 289 After Jan, 10 2000 Forum Graphic GPS receiver had a very strange 290 benahour. It doubles day number for an hours in replys after 10:10:10 UTC 291 and doubles min every hour at HH:10:ss for a minute. 292 Hope it is a problem of my unit only and not a Y2K problem of FG GPS. 293 Below small code to avoid such situation. 294 */ 295 if(up->y2kwarn > 10) 296 pp->hour = BP1(6)*10 + BP2(6); 297 else 298 pp->hour = BP1(5)*10 + BP2(5); 299 300 if((up->y2kwarn > 10) && (pp->hour == 10)) 301 { 302 pp->minute = BP1(7)*10 + BP2(7); 303 pp->second = BP1(8)*10 + BP2(8); 304 pp->msec = BP1(9)*10 + BP2(9); 305 pp->usec = BP1(10); 306 } else { 307 pp->hour = BP1(5)*10 + BP2(5); 308 pp->minute = BP1(6)*10 + BP2(6); 309 pp->second = BP1(7)*10 + BP2(7); 310 pp->msec = BP1(8)*10 + BP2(8); 311 pp->usec = BP1(9); 312 } 313 314 if((pp->hour == 10) && (pp->minute == 10)) 315 { 316 up->y2kwarn++; 317 } 318 319 sprintf(pp->a_lastcode, "%d %d %d %d %d", pp->year, pp->day, pp->hour, pp->minute, pp->second); 320 pp->lencode = strlen(pp->a_lastcode); 321 /*get_systime(&pp->lastrec);*/ 322 323 #ifdef DEBUG 324 if (debug) 325 printf ("fg: time is %04d/%03d %02d:%02d:%02d UTC\n", 326 pp->year, pp->day, pp->hour, pp->minute, pp->second); 327 #endif 328 329 if (peer->stratum <= 1) 330 peer->refid = pp->refid; 331 pp->disp = (10e-6); 332 pp->lastrec = rbufp->recv_time; /* Is it better then get_systime()? */ 333 /* pp->leap = LEAP_NOWARNING; */ 334 335 /* 336 * Process the new sample in the median filter and determine the 337 * timecode timestamp. 338 */ 339 340 if (!refclock_process(pp)) 341 refclock_report(peer, CEVNT_BADTIME); 342 343 refclock_receive(peer); 344 return; 345 } 346 347 348 #else 349 int refclock_fg_bs; 350 #endif /* REFCLOCK */ 351