1 /* $NetBSD: refclock_fg.c,v 1.6 2024/08/18 20:47:18 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
fg_init(int fd)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
fg_start(int unit,struct peer * peer)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(&peer->srcadr, 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
fg_shutdown(int unit,struct peer * peer)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
fg_poll(int unit,struct peer * peer)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
fg_receive(struct recvbuf * rbufp)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 beginning of buffer,
250 * sometime you can find it with some offset.
251 */
252
253 bpt = (char *)rbufp->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 NONEMPTY_TRANSLATION_UNIT
337 #endif /* REFCLOCK */
338