Expand PMF_FN_* macros.
[netbsd-mini2440.git] / dist / ntp / ntpd / refclock_fg.c
blob29d5eb103d5ae385139902b47d69235d717d5c4c
1 /* $NetBSD: refclock_fg.c,v 1.3 2003/12/04 16:23:37 drochner Exp $ */
3 /*
4 * refclock_fg - clock driver for the Forum Graphic GPS datating station
5 */
7 #ifdef HAVE_CONFIG_H
8 # include <config.h>
9 #endif
11 #if defined(REFCLOCK) && defined(CLOCK_FG)
13 #include "ntpd.h"
14 #include "ntp_io.h"
15 #include "ntp_refclock.h"
16 #include "ntp_calendar.h"
17 #include "ntp_stdlib.h"
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.
26 * Interface definitions
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) */
36 * Function prototypes
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 *));
44 /*
45 * Forum Graphic unit control structure
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 */
54 /*
55 * Queries definition
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 };
63 * Transfer vector
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 */
76 * fg_init - Initialization of FG GPS.
79 static int
80 fg_init(
81 int fd
84 if (write(fd, fginit, LENFG) != LENFG)
85 return 0;
87 return (1);
92 * fg_start - open the device and initialize data for processing
94 static int
95 fg_start(
96 int unit,
97 struct peer *peer
100 struct refclockproc *pp;
101 struct fgunit *up;
102 int fd;
103 char device[20];
107 * Open device file for reading.
109 (void)sprintf(device, DEVICE, unit);
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);
119 * Allocate and initialize unit structure
122 if (!(up = (struct fgunit *)
123 emalloc(sizeof(struct fgunit)))) {
124 (void) close(fd);
125 return (0);
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);
141 * Initialize miscellaneous variables
143 peer->precision = PRECISION;
144 pp->clockdesc = DESCRIPTION;
145 memcpy((char *)&pp->refid, REFID, 3);
146 up->pollnum = 0;
149 * Setup dating station to use GPS receiver.
150 * GPS receiver should work before this operation.
152 if(!fg_init(pp->io.fd))
153 refclock_report(peer, CEVNT_FAULT);
155 return (1);
160 * fg_shutdown - shut down the clock
162 static void
163 fg_shutdown(
164 int unit,
165 struct peer *peer
168 struct refclockproc *pp;
169 struct fgunit *up;
171 pp = peer->procptr;
172 up = (struct fgunit *)pp->unitptr;
173 io_closeclock(&pp->io);
174 free(up);
179 * fg_poll - called by the transmit procedure
181 static void
182 fg_poll(
183 int unit,
184 struct peer *peer
187 struct refclockproc *pp;
189 pp = peer->procptr;
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.
198 if (write(pp->io.fd, fgdate, LENFG) != LENFG)
199 refclock_report(peer, CEVNT_FAULT);
200 else
201 pp->polls++;
203 if (peer->burst > 0)
204 return;
206 if (pp->coderecv == pp->codeproc) {
207 refclock_report(peer, CEVNT_TIMEOUT);
208 return;
211 peer->burst = NSTAGE;
213 record_clock_stats(&peer->srcadr, pp->a_lastcode);
216 return;
221 * fg_receive - receive data from the serial interface
223 static void
224 fg_receive(
225 struct recvbuf *rbufp
228 struct refclockproc *pp;
229 struct fgunit *up;
230 struct peer *peer;
231 char *bpt;
234 * Initialize pointers and read the timecode and timestamp
235 * We can't use gtlin function because we need bynary data in buf */
237 peer = (struct peer *)rbufp->recv_srcclock;
238 pp = peer->procptr;
239 up = (struct fgunit *)pp->unitptr;
242 * Below hug to implement receiving of status information
244 if(!up->pollnum)
246 up->pollnum++;
247 return;
251 if (rbufp->recv_length < (LENFG-2))
253 refclock_report(peer, CEVNT_BADREPLY);
254 return; /* The reply is invalid discard it. */
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.
262 bpt = (char *)rbufp->recv_space.X_recv_buffer;
263 while(*bpt != '\x10')
264 bpt++;
266 #define BP2(x) ( bpt[x] & 15 )
267 #define BP1(x) (( bpt[x] & 240 ) >> 4)
269 pp->year = BP1(2)*10 + BP2(2);
271 if(pp->year == 94)
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 ;) */
282 if (pp->year < 99)
283 pp->year += 100;
285 pp->year += 1900;
286 pp->day = 100 * BP2(3) + 10 * BP1(4) + BP2(4);
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.
295 if(up->y2kwarn > 10)
296 pp->hour = BP1(6)*10 + BP2(6);
297 else
298 pp->hour = BP1(5)*10 + BP2(5);
300 if((up->y2kwarn > 10) && (pp->hour == 10))
302 pp->minute = BP1(7)*10 + BP2(7);
303 pp->second = BP1(8)*10 + BP2(8);
304 pp->nsec = (BP1(9)*10 + BP2(9)) * 1000000;
305 pp->nsec += BP1(10) * 1000;
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->nsec = (BP1(8)*10 + BP2(8)) * 1000000;
311 pp->nsec += BP1(9) * 1000;
314 if((pp->hour == 10) && (pp->minute == 10))
316 up->y2kwarn++;
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);*/
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 pp->disp = (10e-6);
329 pp->lastrec = rbufp->recv_time; /* Is it better than get_systime()? */
330 /* pp->leap = LEAP_NOWARNING; */
333 * Process the new sample in the median filter and determine the
334 * timecode timestamp.
337 if (!refclock_process(pp))
338 refclock_report(peer, CEVNT_BADTIME);
339 pp->lastref = pp->lastrec;
340 refclock_receive(peer);
341 return;
345 #else
346 int refclock_fg_bs;
347 #endif /* REFCLOCK */