1 #include <u.h>
2 #include <libc.h>
3 #include <fcall.h>
4 #include <thread.h>
5 #include <ctype.h>
6 #include <9p.h>
7 #include "dat.h"
8
9 enum
10 {
11 Numsize= 12,
12 Vlnumsize= 22,
13 Rawbuf= 0x10000,
14 Rawmask= Rawbuf-1,
15 };
16
17 #define nsecperchar ((int)(1000000000.0 * 10.0 / baud))
18
19 typedef struct Fix Fix;
20 typedef struct Satellite Satellite;
21 typedef struct GPSfile GPSfile;
22 typedef struct Gpsmsg Gpsmsg;
23
24 struct Satellite {
25 int prn;
26 int elevation;
27 int azimuth;
28 int snr;
29 };
30
31 struct Fix {
32 int messages; /* bitmap of types seen */
33 Place;
34 /*
35 * The following are in Plan 9 time format:
36 * seconds or nanoseconds since the epoch.
37 */
38 vlong localtime; /* nsec() value when first byte was read */
39 vlong gpstime; /* nsec() value from GPS */
40 long time; /* time() value from GPS */
41
42 double zulu;
43 int date;
44 char valid;
45 uchar quality;
46 ushort satellites;
47 double pdop;
48 double hdop;
49 double vdop;
50 double altitude;
51 double sealevel;
52 double groundspeed;
53 double kmh;
54 double course;
55 double heading;
56 double magvar;
57 Satellite s[12];
58 };
59
60 struct GPSfile {
61 char *name;
62 char* (*rread)(Req*);
63 int mode;
64 vlong offset; /* for raw: rawout - read-offset */
65 };
66
67 enum {
68 ASTRAL,
69 GPGGA,
70 GPGLL,
71 GPGSA,
72 GPGSV,
73 GPRMC,
74 GPVTG,
75 PRWIRID,
76 PRWIZCH
77 };
78
79 struct Gpsmsg {
80 char *name;
81 int tokens;
82 ulong errors;
83 };
84
85 char raw[Rawbuf];
86 vlong rawin;
87 vlong rawout;
88
89 ulong badlat, goodlat, suspectlat;
90 ulong badlon, goodlon, suspectlon;
91 ulong suspecttime, goodtime;
92
93 ulong histo[32];
94
95 char *serial = "/dev/eia0";
96
97 Gpsmsg gpsmsg[] = {
98 [ASTRAL] = { "ASTRAL", 0, 0},
99 [GPGGA] = { "$GPGGA", 15, 0},
100 /* NMEA 2.3 permits optional 8th field, mode */
101 [GPGLL] = { "$GPGLL", 7, 0},
102 [GPGSA] = { "$GPGSA", 18, 0},
103 [GPGSV] = { "$GPGSV", 0, 0},
104 [GPRMC] = { "$GPRMC", 0, 0},
105 [GPVTG] = { "$GPVTG", 0, 0},
106 [PRWIRID] = { "$PRWIRID", 0, 0},
107 [PRWIZCH] = { "$PRWIZCH", 0, 0},
108 };
109
110 int ttyfd, ctlfd, debug;
111 int setrtc;
112 int baud = Baud;
113 char *baudstr = "b%dd1r1pns1l8i9";
114 ulong seconds;
115 ulong starttime;
116 ulong checksumerrors;
117 int gpsplayback; /* If set, return times and positions with `invalid' marker set */
118
119 Place where = {-(74.0 + 23.9191/60.0), 40.0 + 41.1346/60.0};
120
121 Fix curfix;
122 Lock fixlock;
123
124 int type(char*);
125 void setline(void);
126 int getonechar(vlong*);
127 void getline(char*, int, vlong*);
128 void putline(char*);
129 int gettime(Fix*);
130 int getzulu(char *, Fix*);
131 int getalt(char*, char*, Fix*);
132 int getsea(char*, char*, Fix*);
133 int getlat(char*, char*, Fix*);
134 int getlon(char*, char*, Fix*);
135 int getgs(char*, Fix *);
136 int getkmh(char*, Fix*);
137 int getcrs(char*, Fix*);
138 int gethdg(char*, Fix*);
139 int getdate(char*, Fix*);
140 int getmagvar(char*, char*, Fix*);
141 void printfix(int, Fix*);
142 void ropen(Req *r);
143 void rread(Req *r);
144 void rend(Srv *s);
145 void gpsinit(void);
146 char* readposn(Req*);
147 char* readtime(Req*);
148 char* readsats(Req*);
149 char* readstats(Req*);
150 char* readraw(Req*);
151
152 GPSfile files[] = {
153 { "time", readtime, 0444, 0 },
154 { "position", readposn, 0444, 0 },
155 { "satellites", readsats, 0444, 0 },
156 { "stats", readstats, 0444, 0 },
157 { "raw", readraw, DMEXCL|0444, 0 },
158 };
159
160 Srv s = {
161 .open = ropen,
162 .read = rread,
163
164 .end = rend,
165 };
166
167 File *root;
168 File *gpsdir;
169
170 void
rend(Srv *)171 rend(Srv *)
172 {
173 sysfatal("gpsfs demised");
174 }
175
176 void
ropen(Req * r)177 ropen(Req *r)
178 {
179 respond(r, nil);
180 }
181
182 void
rread(Req * r)183 rread(Req *r)
184 {
185 GPSfile *f;
186
187 r->ofcall.count = 0;
188 f = r->fid->file->aux;
189 respond(r, f->rread(r));
190 }
191
192 void
fsinit(void)193 fsinit(void)
194 {
195 char* user;
196 int i;
197
198 user = getuser();
199 s.tree = alloctree(user, user, 0555, nil);
200 if(s.tree == nil)
201 sysfatal("fsinit: alloctree: %r");
202 root = s.tree->root;
203 if((gpsdir = createfile(root, "gps", user, DMDIR|0555, nil)) == nil)
204 sysfatal("fsinit: createfile: gps: %r");
205 for(i = 0; i < nelem(files); i++)
206 if(createfile(gpsdir, files[i].name, user, files[i].mode, files + i) == nil)
207 sysfatal("fsinit: createfile: %s: %r", files[i].name);
208 }
209
210 void
threadmain(int argc,char * argv[])211 threadmain(int argc, char*argv[])
212 {
213 char *srvname, *mntpt;
214
215 srvname = "gps";
216 mntpt = "/mnt";
217
218 ARGBEGIN {
219 default:
220 fprint(2, "usage: %s [-b baud] [-d device] [-l logfile] [-m mntpt] [-r] [-s postname]\n", argv0);
221 exits("usage");
222 case 'D':
223 debug++;
224 break;
225 case 'b':
226 baud = strtol(ARGF(), nil, 0);
227 break;
228 case 'd':
229 serial = ARGF();
230 break;
231 case 'r':
232 setrtc = 1;
233 break;
234 case 's':
235 srvname = ARGF();
236 break;
237 case 'm':
238 mntpt = ARGF();
239 break;
240 } ARGEND
241
242 fmtinstall('L', placeconv);
243
244 rfork(RFNOTEG);
245
246 fsinit();
247 gpsinit();
248 threadpostmountsrv(&s, srvname, mntpt, MBEFORE);
249 threadexits(nil);
250 }
251
252 static void
gpstrack(void *)253 gpstrack(void *)
254 {
255 Fix fix;
256 static char buf[256], *t[32];
257 int n, i, k, tp;
258 vlong localtime;
259 double d;
260
261 setline();
262 fix.messages = 0;
263 fix.lon = 181.0;
264 fix.lat = 91.0;
265 fix.zulu = 0;
266 fix.date = 0;
267 fix.valid = 0;
268 fix.quality = 0;
269 fix.satellites = 0;
270 fix.pdop = 0.0;
271 fix.hdop = 0.0;
272 fix.vdop = 0.0;
273 fix.altitude = 0.0;
274 fix.sealevel = 0.0;
275 fix.groundspeed = 0.0;
276 fix.kmh = 0.0;
277 fix.course = 0.0;
278 fix.heading = 0.0;
279 fix.magvar = 0.0;
280 for(;;){
281 getline(buf, sizeof buf, &localtime);
282 n = getfields(buf, t, nelem(t), 0,",\r\n");
283 if(n == 0)
284 continue;
285 tp = type(t[0]);
286 if(tp >= 0 && tp < nelem(gpsmsg) && gpsmsg[tp].tokens &&
287 gpsmsg[tp].tokens > n){
288 gpsmsg[tp].errors++;
289 if(debug)
290 fprint(2, "%s: Expect %d tokens, got %d\n",
291 gpsmsg[tp].name, gpsmsg[tp].tokens, n);
292 continue;
293 }
294 switch(tp){
295 case ASTRAL:
296 putline("$IIGPQ,ASTRAL*73");
297 putline("$PRWIILOG,GGA,A,T,10,0");
298 putline("$PRWIILOG,RMC,A,T,10,0");
299 putline("$PRWIILOG,GSA,A,T,10,0");
300 putline("$PRWIILOG,GSV,V,,,");
301 fprint(2, "Reply: %s\n", "$IIGPQ,ASTRAL*73");
302 break;
303 case PRWIRID:
304 case PRWIZCH:
305 for(i = 0; i < n; i++) fprint(2, "%s,", t[i]);
306 fprint(2, "(%d tokens)\n", n);
307 break;
308 case GPGGA:
309 if(getlat(t[2], t[3], &fix))
310 break;
311 if(getlon(t[4], t[5], &fix))
312 break;
313 getzulu(t[1], &fix);
314 if(fix.date && gettime(&fix))
315 break;
316 if(isdigit(*t[7]))
317 fix.satellites = strtol(t[7], nil, 10);
318 if(isdigit(*t[8])){
319 d = strtod(t[8], nil);
320 if(!isNaN(d))
321 fix.hdop = d;
322 }
323 getalt(t[9], t[10], &fix);
324 getsea(t[11], t[12], &fix);
325 fix.localtime = localtime;
326 fix.quality = strtol(t[6], nil, 10);
327 fix.messages |= 1 << tp;
328 break;
329 case GPRMC:
330 fix.valid = *t[2];
331 getgs(t[7], &fix);
332 getcrs(t[8], &fix);
333 getdate(t[9], &fix);
334 getmagvar(t[10], t[11], &fix);
335 if((fix.messages & (1 << GPGGA)) == 0){
336 if(getlat(t[3], t[4], &fix))
337 break;
338 if(getlon(t[5], t[6], &fix))
339 break;
340 fix.localtime = localtime;
341 getzulu(t[1], &fix);
342 if(fix.date)
343 gettime(&fix);
344 }
345 fix.messages |= 1 << tp;
346 break;
347 case GPGSA:
348 if(*t[15]){
349 d = strtod(t[15], nil);
350 if(!isNaN(d))
351 fix.pdop = d;
352 }
353 if(*t[16]){
354 d = strtod(t[16], nil);
355 if(!isNaN(d))
356 fix.hdop = d;
357 }
358 if(*t[17]){
359 d = strtod(t[17], nil);
360 if(!isNaN(d))
361 fix.vdop = d;
362 }
363 fix.messages |= 1 << tp;
364 break;
365 case GPGLL:
366 if(getlat(t[1], t[2], &fix))
367 break;
368 if(getlon(t[3], t[4], &fix))
369 break;
370 getzulu(t[5], &fix);
371 fix.messages |= 1 << tp;
372 break;
373 case GPGSV:
374 if(n < 8){
375 gpsmsg[tp].errors++;
376 if(debug)
377 fprint(2, "%s: Expect at least 8 tokens, got %d\n",
378 gpsmsg[tp].name, n);
379 break;
380 }
381 i = 4*(strtol(t[2], nil, 10)-1); /* starting entry in satellite table */
382 fix.satellites = strtol(t[3], nil, 10);
383 k = 4;
384 while(i < nelem(fix.s) && k + 3 < n){
385 fix.s[i].prn = strtol(t[k++], nil, 10);
386 fix.s[i].elevation = strtol(t[k++], nil, 10);
387 fix.s[i].azimuth = strtol(t[k++], nil, 10);
388 fix.s[i].snr = strtol(t[k++], nil, 10);
389 k += 4;
390 i++;
391 }
392 fix.messages |= 1 << tp;
393 break;
394 case GPVTG:
395 if(n < 8){
396 gpsmsg[tp].errors++;
397 if(debug)
398 fprint(2, "%s: Expect at least 8 tokens, got %d\n",
399 gpsmsg[tp].name, n);
400 break;
401 }
402 getcrs(t[2], &fix);
403 gethdg(t[4], &fix);
404 getgs(t[6], &fix);
405 if(n > 8)
406 getkmh(t[8], &fix);
407 fix.messages |= 1 << tp;
408 break;
409 default:
410 if(debug && fix.date)
411 fprint(2, "Don't know %s\n", t[0]);
412 break;
413 }
414 if(fix.valid){
415 seconds++;
416 lock(&fixlock);
417 memmove(&curfix, &fix, sizeof fix);
418 unlock(&fixlock);
419 if(debug)
420 printfix(2, &fix);
421 fix.valid = 0;
422 fix.messages = 0;
423 for(i = 0; i < nelem(fix.s); i++)
424 fix.s[i].prn = 0;
425 if(gpsplayback)
426 sleep(100);
427 }
428 }
429 }
430
431 void
gpsinit(void)432 gpsinit(void)
433 {
434 proccreate(gpstrack, nil, 4096);
435 }
436
437 void
printfix(int f,Fix * fix)438 printfix(int f, Fix *fix){
439 int i;
440
441 fprint(f, "%L, ", fix->Place);
442 fprint(f, "%g, ", fix->magvar);
443 fprint(f, "%gm - %gm = %gm, ", fix->altitude, fix->sealevel, fix->altitude - fix->sealevel);
444 fprint(f, "%06dZ(%g)-", (int)fix->zulu, fix->zulu);
445 fprint(f, "%06d\n", fix->date);
446 if(fix->lat >= 0)
447 fprint(f, "%11.8fN, ", fix->lat);
448 else
449 fprint(f, "%11.8fS, ", -fix->lat);
450 if(fix->lon >= 0)
451 fprint(f, "%12.8fE, ", fix->lon);
452 else
453 fprint(f, "%12.8fW, ", -fix->lon);
454 fprint(f, "%g@%g, ", fix->course, fix->groundspeed);
455 fprint(f, "(%c, %ds)\n", fix->valid, fix->satellites);
456 for(i = 0; i < nelem(fix->s); i++){
457 if(fix->s[i].prn == 0)
458 continue;
459 fprint(f, "[%d, %d°, %d°, %d]\n",
460 fix->s[i].prn, fix->s[i].elevation, fix->s[i].azimuth, fix->s[i].snr);
461 }
462 }
463
464 char*
readposn(Req * r)465 readposn(Req *r)
466 {
467 Fix f;
468 char buf[256];
469
470 lock(&fixlock);
471 memmove(&f, &curfix, sizeof f);
472 unlock(&fixlock);
473 snprint(buf, sizeof buf, "%x %06dZ %lud %g %g %g %g %g %g",
474 gpsplayback|f.quality, (int)f.zulu, f.time, f.lon, f.lat, f.altitude - f.sealevel,
475 f.course, f.groundspeed, f.magvar);
476 readstr(r, buf);
477 return nil;
478 }
479
480 char*
readtime(Req * r)481 readtime(Req *r)
482 {
483 Fix f;
484 char buf[Numsize+Vlnumsize+Vlnumsize+8];
485
486 lock(&fixlock);
487 memmove(&f, &curfix, sizeof f);
488 unlock(&fixlock);
489 seprint(buf, buf + sizeof buf, "%*.0lud %*.0llud %*.0llud %c",
490 Numsize-1, f.time,
491 Vlnumsize-1, f.gpstime,
492 Vlnumsize-1, f.localtime, f.valid + (gpsplayback?1:0));
493 readstr(r, buf);
494 return nil;
495 }
496
497 char*
readstats(Req * r)498 readstats(Req *r)
499 {
500 int i;
501 char buf[1024], *p;
502
503 p = buf;
504 p = seprint(p, buf + sizeof buf, "%lld bytes read, %ld samples processed in %ld seconds\n",
505 rawin, seconds, curfix.time - starttime);
506 p = seprint(p, buf + sizeof buf, "%lud checksum errors\n", checksumerrors);
507 p = seprint(p, buf + sizeof buf, "format errors:");
508 for(i = 0; i < nelem(gpsmsg); i++){
509 p = seprint(p, buf + sizeof buf, "[%s]: %ld, ",
510 gpsmsg[i].name, gpsmsg[i].errors);
511 }
512 p = seprint(p, buf + sizeof buf, "\nhistogram of # bytes received per buffer:\n");
513 for(i = 0; i < nelem(histo); i++){
514 p = seprint(p, buf + sizeof buf, "[%d]: %ld ",
515 i, histo[i]);
516 }
517 p = seprint(p, buf + sizeof buf, "\n");
518 p = seprint(p, buf + sizeof buf, "bad/good/suspect lat: %lud/%lud/%lud\n",
519 badlat, goodlat, suspectlat);
520 p = seprint(p, buf + sizeof buf, "bad/good/suspect lon: %lud/%lud/%lud\n",
521 badlon, goodlon, suspectlon);
522 p = seprint(p, buf + sizeof buf, "good/suspect time: %lud/%lud\n", goodtime, suspecttime);
523 USED(p);
524 readstr(r, buf);
525 return nil;
526 }
527
528 char*
readsats(Req * r)529 readsats(Req *r)
530 {
531 Fix f;
532 int i;
533 char buf[1024], *p;
534
535 lock(&fixlock);
536 memmove(&f, &curfix, sizeof f);
537 unlock(&fixlock);
538 p = seprint(buf, buf + sizeof buf, "%d %d\n", gpsplayback|f.quality, f.satellites);
539 for(i = 0; i < nelem(f.s); i++){
540 if(f.s[i].prn == 0)
541 continue;
542 p = seprint(p, buf + sizeof buf, "%d %d %d %d\n",
543 f.s[i].prn, f.s[i].elevation, f.s[i].azimuth, f.s[i].snr);
544 }
545 readstr(r, buf);
546 return nil;
547 }
548
549 char*
readraw(Req * r)550 readraw(Req *r)
551 {
552 int n;
553 GPSfile *f;
554
555 f = r->fid->file->aux;
556 if(rawin - rawout > Rawbuf){
557 rawout = rawin - Rawbuf;
558 f->offset = rawout - r->ifcall.offset;
559 }
560 n = Rawbuf - (rawout&Rawmask);
561 if(rawin - rawout < n)
562 n = rawin - rawout;
563 if(r->ifcall.count < n)
564 n = r->ifcall.count;
565 r->ofcall.count = n;
566 if(n > 0){
567 memmove(r->ofcall.data, raw + (rawout & Rawmask), n);
568 rawout += n;
569 }
570 return nil;
571 }
572
573 void
rtcset(long t)574 rtcset(long t)
575 {
576 static int fd;
577 long r;
578 int n;
579 char buf[32];
580
581 if(fd <= 0 && (fd = open("#r/rtc", ORDWR)) < 0){
582 fprint(2, "Can't open #r/rtc: %r\n");
583 return;
584 }
585 n = read(fd, buf, sizeof buf - 1);
586 if(n <= 0){
587 fprint(2, "Can't read #r/rtc: %r\n");
588 return;
589 }
590 buf[n] = '\0';
591 r = strtol(buf, nil, 0);
592 if(r <= 0){
593 fprint(2, "ridiculous #r/rtc: %ld\n", r);
594 return;
595 }
596 if(r - t > 1 || t - r > 0){
597 seek(fd, 0, 0);
598 fprint(fd, "%ld", t);
599 fprint(2, "correcting #r/rtc: %ld → %ld\n", r, t);
600 }
601 seek(fd, 0, 0);
602 }
603
604 int
gettime(Fix * f)605 gettime(Fix *f){
606 /* Convert zulu time and date to Plan9 time(2) */
607 Tm tm;
608 int zulu;
609 double d;
610 long t;
611 static int count;
612
613 zulu = f->zulu;
614 memset(&tm, 0, sizeof tm );
615 tm.sec = zulu % 100;
616 tm.min = (zulu/100) % 100;
617 tm.hour = zulu / 10000;
618 tm.year = f->date % 100 + 100; /* This'll only work until 2099 */
619 tm.mon = ((f->date/100) % 100) - 1;
620 tm.mday = f->date / 10000;
621 strcpy(tm.zone, "GMT");
622 t = tm2sec(&tm);
623 if(f->time && count < 3 && (t - f->time > 10 || t - f->time <= 0)){
624 count++;
625 suspecttime++;
626 return -1;
627 }
628 goodtime++;
629 f->time = t;
630 count = 0;
631 if(starttime == 0) starttime = t;
632 f->gpstime = 1000000000LL * t + 1000000 * (int)modf(f->zulu, &d);
633 if(setrtc){
634 if(setrtc == 1 || (t % 300) == 0){
635 rtcset(t);
636 setrtc++;
637 }
638 }
639 return 0;
640 }
641
642 int
getzulu(char * s,Fix * f)643 getzulu(char *s, Fix *f){
644 double d;
645
646 if(*s == '\0') return 0;
647 if(isdigit(*s)){
648 d = strtod(s, nil);
649 if(!isNaN(d))
650 f->zulu = d;
651 return 1;
652 }
653 return 0;
654 }
655
656 int
getdate(char * s,Fix * f)657 getdate(char *s, Fix *f){
658 if(*s == 0) return 0;
659 if(isdigit(*s)){
660 f->date = strtol(s, nil, 10);
661 return 1;
662 }
663 return 0;
664 }
665
666 int
getgs(char * s,Fix * f)667 getgs(char *s, Fix *f){
668 double d;
669
670 if(*s == 0) return 0;
671 if(isdigit(*s)){
672 d = strtod(s, nil);
673 if(!isNaN(d))
674 f->groundspeed = d;
675 return 1;
676 }
677 return 0;
678 }
679
680 int
getkmh(char * s,Fix * f)681 getkmh(char *s, Fix *f){
682 double d;
683
684 if(*s == 0) return 0;
685 if(isdigit(*s)){
686 d = strtod(s, nil);
687 if(!isNaN(d))
688 f->kmh = d;
689 return 1;
690 }
691 return 0;
692 }
693
694 int
getcrs(char * s1,Fix * f)695 getcrs(char *s1, Fix *f){
696 double d;
697
698 if(*s1 == 0) return 0;
699 if(isdigit(*s1)){
700 d = strtod(s1, nil);
701 if(!isNaN(d))
702 f->course = d;
703 return 1;
704 }
705 return 0;
706 }
707
708 int
gethdg(char * s1,Fix * f)709 gethdg(char *s1, Fix *f){
710 double d;
711
712 if(*s1 == 0) return 0;
713 if(isdigit(*s1)){
714 d = strtod(s1, nil);
715 if(!isNaN(d))
716 f->heading = d;
717 return 1;
718 }
719 return 0;
720 }
721
722 int
getalt(char * s1,char * s2,Fix * f)723 getalt(char *s1, char *s2, Fix *f){
724 double alt;
725
726 if(*s1 == 0) return 0;
727 if(isdigit(*s1)){
728 alt = strtod(s1, nil);
729 if(*s2 == 'M' && !isNaN(alt)){
730 f->altitude = alt;
731 return 1;
732 }
733 return 0;
734 }
735 return 0;
736 }
737
738 int
getsea(char * s1,char * s2,Fix * f)739 getsea(char *s1, char *s2, Fix *f){
740 double alt;
741
742 if(*s1 == 0) return 0;
743 if(isdigit(*s1)){
744 alt = strtod(s1, nil);
745 if(*s2 == 'M'){
746 f->sealevel = alt;
747 return 1;
748 }
749 return 0;
750 }
751 return 0;
752 }
753
754 int
getlat(char * s1,char * s2,Fix * f)755 getlat(char *s1, char *s2, Fix *f){
756 double lat;
757 static count;
758
759 if(*s1 == 0 || !isdigit(*s1) || strlen(s1) <= 5){
760 badlat++;
761 return -1;
762 }
763 lat = strtod(s1+2, nil);
764 if(isNaN(lat)){
765 badlat++;
766 return -1;
767 }
768 lat /= 60.0;
769 lat += 10*(s1[0] - '0') + s1[1] - '0';
770 if(lat < 0 || lat > 90.0){
771 badlat++;
772 return -1;
773 }
774 switch(*s2){
775 default:
776 badlat++;
777 return -1;
778 case 'S':
779 lat = -lat;
780 case 'N':
781 break;
782 }
783 if(f->lat <= 90.0 && count < 3 && fabs(f->lat - lat) > 10.0){
784 count++;
785 suspectlat++;
786 return -1;
787 }
788 f->lat = lat;
789 count = 0;
790 goodlat++;
791 return 0;
792 }
793
794 int
getlon(char * s1,char * s2,Fix * f)795 getlon(char *s1, char *s2, Fix *f){
796 double lon;
797 static count;
798
799 if(*s1 == 0 || ! isdigit(*s1) || strlen(s1) <= 5){
800 badlon++;
801 return -1;
802 }
803 lon = strtod(s1+3, nil);
804 if(isNaN(lon)){
805 badlon++;
806 return -1;
807 }
808 lon /= 60.0;
809 lon += 100*(s1[0] - '0') + 10*(s1[1] - '0') + s1[2] - '0';
810 if(lon < 0 || lon > 180.0){
811 badlon++;
812 return -1;
813 }
814 switch(*s2){
815 default:
816 badlon++;
817 return -1;
818 case 'W':
819 lon = -lon;
820 case 'E':
821 break;
822 }
823 if(f->lon <= 180.0 && count < 3 && fabs(f->lon - lon) > 10.0){
824 count++;
825 suspectlon++;
826 return -1;
827 }
828 f->lon = lon;
829 goodlon++;
830 count = 0;
831 return 0;
832 }
833
834 int
getmagvar(char * s1,char * s2,Fix * f)835 getmagvar(char *s1, char *s2, Fix *f){
836 double magvar;
837
838 if(*s1 == 0) return 0;
839 if(isdigit(*s1) && strlen(s1) > 5){
840 magvar = strtod(s1+3, nil);
841 if(isNaN(magvar))
842 return 0;
843 magvar /= 60.0;
844 magvar += 100*(s1[0] - '0') + 10*(s1[1] - '0') + s1[2] - '0';
845 if(*s2 == 'W'){
846 f->magvar = -magvar;
847 return 1;
848 }
849 if(*s2 == 'E'){
850 f->magvar = magvar;
851 return 1;
852 }
853 return 0;
854 }
855 return 0;
856 }
857
858 void
putline(char * s)859 putline(char *s){
860 write(ttyfd, s, strlen(s));
861 write(ttyfd, "\r\n", 2);
862 }
863
864 int
type(char * s)865 type(char *s){
866 int i;
867
868 for(i = 0; i < nelem(gpsmsg); i++){
869 if(strcmp(s, gpsmsg[i].name) == 0) return i;
870 }
871 return -1;
872 }
873
874 void
setline(void)875 setline(void){
876 char *serialctl;
877
878 serialctl = smprint("%sctl", serial);
879 if((ttyfd = open(serial, ORDWR)) < 0)
880 sysfatal("%s: %r", serial);
881 if((ctlfd = open(serialctl, OWRITE)) >= 0){
882 if(fprint(ctlfd, baudstr, baud) < 0)
883 sysfatal("%s: %r", serialctl);
884 }else
885 gpsplayback = 0x8;
886 free(serialctl);
887 }
888
getonechar(vlong * t)889 int getonechar(vlong *t){
890 static char buf[32], *p;
891 static int n;
892
893 if(n == 0){
894 n = read(ttyfd, buf, sizeof(buf));
895 if(t) *t = nsec();
896 if(n < 0)
897 sysfatal("%s: %r", serial);
898 if(n == 0)
899 threadexits(nil);
900 /*
901 * We received n characters, so the first must have been there
902 * at least n/(10*baud) seconds (10 is 1 start
903 * bit, one stop bit and 8 data bits per character)
904 */
905 if(t) {
906 *t -= n * nsecperchar;
907 histo[n]++;
908 }
909 p = buf;
910 }
911 n--;
912 return *p++;
913 }
914
915 void
getline(char * s,int size,vlong * t)916 getline(char *s, int size, vlong *t){
917 uchar c;
918 char *p;
919 int n, cs;
920
921 tryagain:
922 for(;;){
923 p = s;
924 n = 0;
925 while((c = getonechar(t)) != '\n' && n < size){
926 t = nil;
927 if(c != '\r'){
928 *p++ = c;
929 n++;
930 }
931 }
932 if(n < size)
933 break;
934 while(getonechar(t) != '\n' && n < 4096)
935 n++;
936 if(n == 4096)
937 sysfatal("preposterous gps line, wrong baud rate?");
938 fprint(2, "ridiculous gps line: %d bytes\n", n);
939 }
940 *p = 0;
941 for(p = s; isdigit(*p); p++)
942 ;
943 if(*p++ == ' ')
944 memmove(s, p, strlen(p)+1);
945 if(s[0] == '$'){
946 if(n > 4 && s[n-3] == '*'){
947 s[n-3] = 0;
948 p = s+1;
949 cs = 0;
950 while(*p) cs ^= *p++;
951 n = strtol(&s[n-2], nil, 16);
952 if(n != cs){
953 if(debug)
954 fprint(2, "Checksum error %s, 0x%x, 0x%x\n",
955 s, n, cs);
956 checksumerrors++;
957 goto tryagain;
958 }
959 }
960 }
961 for(p = s; *p; rawin++)
962 raw[rawin & Rawmask] = *p++;
963 raw[rawin & Rawmask] = '\n';
964 rawin++;
965 }
966