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
fg_init(int fd)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
fg_start(int unit,struct peer * peer)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(&peer->srcadr, 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
fg_shutdown(int unit,struct peer * peer)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
fg_poll(int unit,struct peer * peer)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
fg_receive(struct recvbuf * rbufp)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 beginning of buffer,
248 * sometime you can find it with some offset.
249 */
250
251 bpt = (char *)rbufp->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 NONEMPTY_TRANSLATION_UNIT
335 #endif /* REFCLOCK */
336