xref: /netbsd-src/external/bsd/ntp/dist/ntpd/refclock_fg.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: refclock_fg.c,v 1.4 2016/01/08 21:35:39 christos Exp $	*/
2 
3 /*
4  * refclock_fg - clock driver for the Forum Graphic GPS datating station
5  */
6 
7 #ifdef HAVE_CONFIG_H
8 # include <config.h>
9 #endif
10 
11 #if defined(REFCLOCK) && defined(CLOCK_FG)
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 	(int);
39 static	int 	fg_start 	(int, struct peer *);
40 static	void	fg_shutdown	(int, struct peer *);
41 static	void	fg_poll		(int, struct peer *);
42 static	void	fg_receive	(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  * fg_start - open the device and initialize data for processing
92  */
93 static int
94 fg_start(
95 	int unit,
96 	struct peer *peer
97 	)
98 {
99 	struct refclockproc *pp;
100 	struct fgunit *up;
101 	int fd;
102 	char device[20];
103 
104 
105 	/*
106 	 * Open device file for reading.
107 	 */
108 	snprintf(device, sizeof(device), DEVICE, unit);
109 
110 	DPRINTF(1, ("starting FG with device %s\n",device));
111 
112 	fd = refclock_open(device, SPEED232, LDISC_CLK);
113 	if (fd <= 0)
114 		return (0);
115 
116 	/*
117 	 * Allocate and initialize unit structure
118 	 */
119 
120 	up = emalloc(sizeof(struct fgunit));
121 	memset(up, 0, sizeof(struct fgunit));
122 	pp = peer->procptr;
123 	pp->unitptr = up;
124 	pp->io.clock_recv = fg_receive;
125 	pp->io.srcclock = peer;
126 	pp->io.datalen = 0;
127 	pp->io.fd = fd;
128  	if (!io_addclock(&pp->io)) {
129 		close(fd);
130 		pp->io.fd = -1;
131 		return 0;
132 	}
133 
134 
135 	/*
136 	 * Initialize miscellaneous variables
137 	 */
138 	peer->precision = PRECISION;
139 	pp->clockdesc = DESCRIPTION;
140 	memcpy(&pp->refid, REFID, 3);
141 	up->pollnum = 0;
142 
143 	/*
144 	 * Setup dating station to use GPS receiver.
145 	 * GPS receiver should work before this operation.
146 	 */
147 	if(!fg_init(pp->io.fd))
148 		refclock_report(peer, CEVNT_FAULT);
149 
150 	return (1);
151 }
152 
153 
154 /*
155  * fg_shutdown - shut down the clock
156  */
157 static void
158 fg_shutdown(
159 	int unit,
160 	struct peer *peer
161 	)
162 {
163 	struct refclockproc *pp;
164 	struct fgunit *up;
165 
166 	pp = peer->procptr;
167 	up = pp->unitptr;
168 	if (pp->io.fd != -1)
169 		io_closeclock(&pp->io);
170 	if (up != NULL)
171 		free(up);
172 }
173 
174 
175 /*
176  * fg_poll - called by the transmit procedure
177  */
178 static void
179 fg_poll(
180 	int unit,
181 	struct peer *peer
182 	)
183 {
184 	struct refclockproc *pp;
185 
186 	pp = peer->procptr;
187 
188 	/*
189 	 * Time to poll the clock. The FG clock responds to a
190 	 * "<DLE>D<DLE><CR>" by returning a timecode in the format specified
191 	 * above. If nothing is heard from the clock for two polls,
192 	 * declare a timeout and keep going.
193 	 */
194 
195 	if (write(pp->io.fd, fgdate, LENFG) != LENFG)
196 		refclock_report(peer, CEVNT_FAULT);
197 	else
198 		pp->polls++;
199 
200 	/*
201 	if (pp->coderecv == pp->codeproc) {
202 		refclock_report(peer, CEVNT_TIMEOUT);
203 		return;
204 	}
205 	*/
206 
207 	record_clock_stats(&peer->srcadr, pp->a_lastcode);
208 
209 	return;
210 
211 }
212 
213 /*
214  * fg_receive - receive data from the serial interface
215  */
216 static void
217 fg_receive(
218 	struct recvbuf *rbufp
219 	)
220 {
221 	struct refclockproc *pp;
222 	struct fgunit *up;
223 	struct peer *peer;
224 	char *bpt;
225 
226 	/*
227 	 * Initialize pointers and read the timecode and timestamp
228 	 * We can't use gtlin function because we need bynary data in buf */
229 
230 	peer = rbufp->recv_peer;
231 	pp = peer->procptr;
232 	up = pp->unitptr;
233 
234 	/*
235 	 * Below hug to implement receiving of status information
236 	 */
237 	if(!up->pollnum) {
238 		up->pollnum++;
239 		return;
240 	}
241 
242 
243 	if (rbufp->recv_length < (LENFG - 2)) {
244 		refclock_report(peer, CEVNT_BADREPLY);
245 		return; /* The reply is invalid discard it. */
246 	}
247 
248 	/* Below I trying to find a correct reply in buffer.
249 	 * Sometime GPS reply located in the beginnig of buffer,
250 	 * sometime you can find it with some offset.
251 	 */
252 
253 	bpt = (char *)rbufp->recv_space.X_recv_buffer;
254 	while (*bpt != '\x10')
255 		bpt++;
256 
257 #define BP2(x) ( bpt[x] & 15 )
258 #define BP1(x) (( bpt[x] & 240 ) >> 4)
259 
260 	pp->year = BP1(2) * 10 + BP2(2);
261 
262 	if (pp->year == 94) {
263 		refclock_report(peer, CEVNT_BADREPLY);
264 		if (!fg_init(pp->io.fd))
265 			refclock_report(peer, CEVNT_FAULT);
266 		return;
267 		 /* GPS is just powered up. The date is invalid -
268 		 discarding it. Initilize GPS one more time */
269 		/* Sorry - this driver will broken in 2094 ;) */
270 	}
271 
272 	if (pp->year < 99)
273 		pp->year += 100;
274 
275 	pp->year +=  1900;
276 	pp->day = 100 * BP2(3) + 10 * BP1(4) + BP2(4);
277 
278 /*
279    After Jan, 10 2000 Forum Graphic GPS receiver had a very strange
280    benahour. It doubles day number for an hours in replys after 10:10:10 UTC
281    and doubles min every hour at HH:10:ss for a minute.
282    Hope it is a problem of my unit only and not a Y2K problem of FG GPS.
283    Below small code to avoid such situation.
284 */
285 	if (up->y2kwarn > 10)
286 		pp->hour = BP1(6)*10 + BP2(6);
287 	else
288 		pp->hour = BP1(5)*10 + BP2(5);
289 
290 	if ((up->y2kwarn > 10) && (pp->hour == 10)) {
291 		pp->minute = BP1(7)*10 + BP2(7);
292 		pp->second = BP1(8)*10 + BP2(8);
293 		pp->nsec = (BP1(9)*10 + BP2(9)) * 1000000;
294 		pp->nsec += BP1(10) * 1000;
295 	} else {
296 		pp->hour = BP1(5)*10 + BP2(5);
297 		pp->minute = BP1(6)*10 + BP2(6);
298 		pp->second = BP1(7)*10 + BP2(7);
299 		pp->nsec = (BP1(8)*10 + BP2(8)) * 1000000;
300 		pp->nsec += BP1(9) * 1000;
301 	}
302 
303 	if ((pp->hour == 10) && (pp->minute == 10)) {
304 		up->y2kwarn++;
305 	}
306 
307 	snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
308 		 "%d %d %d %d %d", pp->year, pp->day, pp->hour,
309 		 pp->minute, pp->second);
310 	pp->lencode = strlen(pp->a_lastcode);
311 	/*get_systime(&pp->lastrec);*/
312 
313 #ifdef DEBUG
314 	if (debug)
315 		printf("fg: time is %04d/%03d %02d:%02d:%02d UTC\n",
316 		       pp->year, pp->day, pp->hour, pp->minute, pp->second);
317 #endif
318 	pp->disp =  (10e-6);
319 	pp->lastrec = rbufp->recv_time; /* Is it better than get_systime()? */
320 	/* pp->leap = LEAP_NOWARNING; */
321 
322 	/*
323 	 * Process the new sample in the median filter and determine the
324 	 * timecode timestamp.
325 	 */
326 
327 	if (!refclock_process(pp))
328 		refclock_report(peer, CEVNT_BADTIME);
329 	pp->lastref = pp->lastrec;
330 	refclock_receive(peer);
331 	return;
332 }
333 
334 
335 #else
336 int refclock_fg_bs;
337 #endif /* REFCLOCK */
338