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