17 #define nsecperchar ((int)(1000000000.0 * 10.0 / baud))
19 typedef struct Fix Fix;
20 typedef struct Satellite Satellite;
21 typedef struct GPSfile GPSfile;
22 typedef struct Gpsmsg Gpsmsg;
32 int messages; /* bitmap of types seen */
35 * The following are in Plan 9 time format:
36 * seconds or nanoseconds since the epoch.
38 vlong localtime; /* nsec() value when first byte was read */
39 vlong gpstime; /* nsec() value from GPS */
40 long time; /* time() value from GPS */
64 vlong offset; /* for raw: rawout - read-offset */
89 ulong badlat, goodlat, suspectlat;
90 ulong badlon, goodlon, suspectlon;
91 ulong suspecttime, goodtime;
95 char *serial = "/dev/eia0";
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},
110 int ttyfd, ctlfd, debug;
113 char *baudstr = "b%dd1r1pns1l8i9";
116 ulong checksumerrors;
117 int gpsplayback; /* If set, return times and positions with `invalid' marker set */
119 Place where = {-(74.0 + 23.9191/60.0), 40.0 + 41.1346/60.0};
126 int getonechar(vlong*);
127 void getline(char*, int, vlong*);
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*);
146 char* readposn(Req*);
147 char* readtime(Req*);
148 char* readsats(Req*);
149 char* readstats(Req*);
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 },
173 sysfatal("gpsfs demised");
188 f = r->fid->file->aux;
189 respond(r, f->rread(r));
199 s.tree = alloctree(user, user, 0555, nil);
201 sysfatal("fsinit: alloctree: %r");
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);
211 threadmain(int argc, char*argv[])
213 char *srvname, *mntpt;
220 fprint(2, "usage: %s [-b baud] [-d device] [-l logfile] [-m mntpt] [-r] [-s postname]\n", argv0);
226 baud = strtol(ARGF(), nil, 0);
242 fmtinstall('L', placeconv);
248 threadpostmountsrv(&s, srvname, mntpt, MBEFORE);
256 static char buf[256], *t[32];
275 fix.groundspeed = 0.0;
281 getline(buf, sizeof buf, &localtime);
282 n = getfields(buf, t, nelem(t), 0,",\r\n");
286 if(tp >= 0 && tp < nelem(gpsmsg) && gpsmsg[tp].tokens &&
287 gpsmsg[tp].tokens > n){
290 fprint(2, "%s: Expect %d tokens, got %d\n",
291 gpsmsg[tp].name, gpsmsg[tp].tokens, n);
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");
305 for(i = 0; i < n; i++) fprint(2, "%s,", t[i]);
306 fprint(2, "(%d tokens)\n", n);
309 if(getlat(t[2], t[3], &fix))
311 if(getlon(t[4], t[5], &fix))
314 if(fix.date && gettime(&fix))
317 fix.satellites = strtol(t[7], nil, 10);
319 d = strtod(t[8], nil);
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;
334 getmagvar(t[10], t[11], &fix);
335 if((fix.messages & (1 << GPGGA)) == 0){
336 if(getlat(t[3], t[4], &fix))
338 if(getlon(t[5], t[6], &fix))
340 fix.localtime = localtime;
345 fix.messages |= 1 << tp;
349 d = strtod(t[15], nil);
354 d = strtod(t[16], nil);
359 d = strtod(t[17], nil);
363 fix.messages |= 1 << tp;
366 if(getlat(t[1], t[2], &fix))
368 if(getlon(t[3], t[4], &fix))
371 fix.messages |= 1 << tp;
377 fprint(2, "%s: Expect at least 8 tokens, got %d\n",
381 i = 4*(strtol(t[2], nil, 10)-1); /* starting entry in satellite table */
382 fix.satellites = strtol(t[3], nil, 10);
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);
392 fix.messages |= 1 << tp;
398 fprint(2, "%s: Expect at least 8 tokens, got %d\n",
407 fix.messages |= 1 << tp;
410 if(debug && fix.date)
411 fprint(2, "Don't know %s\n", t[0]);
417 memmove(&curfix, &fix, sizeof fix);
423 for(i = 0; i < nelem(fix.s); i++)
434 proccreate(gpstrack, nil, 4096);
438 printfix(int f, Fix *fix){
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);
447 fprint(f, "%11.8fN, ", fix->lat);
449 fprint(f, "%11.8fS, ", -fix->lat);
451 fprint(f, "%12.8fE, ", fix->lon);
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)
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);
471 memmove(&f, &curfix, sizeof f);
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);
484 char buf[Numsize+Vlnumsize+Vlnumsize+8];
487 memmove(&f, &curfix, sizeof f);
489 seprint(buf, buf + sizeof buf, "%*.0lud %*.0llud %*.0llud %c",
491 Vlnumsize-1, f.gpstime,
492 Vlnumsize-1, f.localtime, f.valid + (gpsplayback?1:0));
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);
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 ",
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);
536 memmove(&f, &curfix, sizeof f);
538 p = seprint(buf, buf + sizeof buf, "%d %d\n", gpsplayback|f.quality, f.satellites);
539 for(i = 0; i < nelem(f.s); i++){
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);
555 f = r->fid->file->aux;
556 if(rawin - rawout > Rawbuf){
557 rawout = rawin - Rawbuf;
558 f->offset = rawout - r->ifcall.offset;
560 n = Rawbuf - (rawout&Rawmask);
561 if(rawin - rawout < n)
563 if(r->ifcall.count < n)
567 memmove(r->ofcall.data, raw + (rawout & Rawmask), n);
581 if(fd <= 0 && (fd = open("#r/rtc", ORDWR)) < 0){
582 fprint(2, "Can't open #r/rtc: %r\n");
585 n = read(fd, buf, sizeof buf - 1);
587 fprint(2, "Can't read #r/rtc: %r\n");
591 r = strtol(buf, nil, 0);
593 fprint(2, "ridiculous #r/rtc: %ld\n", r);
596 if(r - t > 1 || t - r > 0){
598 fprint(fd, "%ld", t);
599 fprint(2, "correcting #r/rtc: %ld → %ld\n", r, t);
606 /* Convert zulu time and date to Plan9 time(2) */
614 memset(&tm, 0, sizeof tm );
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");
623 if(f->time && count < 3 && (t - f->time > 10 || t - f->time <= 0)){
631 if(starttime == 0) starttime = t;
632 f->gpstime = 1000000000LL * t + 1000000 * (int)modf(f->zulu, &d);
634 if(setrtc == 1 || (t % 300) == 0){
643 getzulu(char *s, Fix *f){
646 if(*s == '\0') return 0;
657 getdate(char *s, Fix *f){
658 if(*s == 0) return 0;
660 f->date = strtol(s, nil, 10);
667 getgs(char *s, Fix *f){
670 if(*s == 0) return 0;
681 getkmh(char *s, Fix *f){
684 if(*s == 0) return 0;
695 getcrs(char *s1, Fix *f){
698 if(*s1 == 0) return 0;
709 gethdg(char *s1, Fix *f){
712 if(*s1 == 0) return 0;
723 getalt(char *s1, char *s2, Fix *f){
726 if(*s1 == 0) return 0;
728 alt = strtod(s1, nil);
729 if(*s2 == 'M' && !isNaN(alt)){
739 getsea(char *s1, char *s2, Fix *f){
742 if(*s1 == 0) return 0;
744 alt = strtod(s1, nil);
755 getlat(char *s1, char *s2, Fix *f){
759 if(*s1 == 0 || !isdigit(*s1) || strlen(s1) <= 5){
763 lat = strtod(s1+2, nil);
769 lat += 10*(s1[0] - '0') + s1[1] - '0';
770 if(lat < 0 || lat > 90.0){
783 if(f->lat <= 90.0 && count < 3 && fabs(f->lat - lat) > 10.0){
795 getlon(char *s1, char *s2, Fix *f){
799 if(*s1 == 0 || ! isdigit(*s1) || strlen(s1) <= 5){
803 lon = strtod(s1+3, nil);
809 lon += 100*(s1[0] - '0') + 10*(s1[1] - '0') + s1[2] - '0';
810 if(lon < 0 || lon > 180.0){
823 if(f->lon <= 180.0 && count < 3 && fabs(f->lon - lon) > 10.0){
835 getmagvar(char *s1, char *s2, Fix *f){
838 if(*s1 == 0) return 0;
839 if(isdigit(*s1) && strlen(s1) > 5){
840 magvar = strtod(s1+3, nil);
844 magvar += 100*(s1[0] - '0') + 10*(s1[1] - '0') + s1[2] - '0';
860 write(ttyfd, s, strlen(s));
861 write(ttyfd, "\r\n", 2);
868 for(i = 0; i < nelem(gpsmsg); i++){
869 if(strcmp(s, gpsmsg[i].name) == 0) return i;
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);
889 int getonechar(vlong *t){
890 static char buf[32], *p;
894 n = read(ttyfd, buf, sizeof(buf));
897 sysfatal("%s: %r", serial);
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)
906 *t -= n * nsecperchar;
916 getline(char *s, int size, vlong *t){
925 while((c = getonechar(t)) != '\n' && n < size){
934 while(getonechar(t) != '\n' && n < 4096)
937 sysfatal("preposterous gps line, wrong baud rate?");
938 fprint(2, "ridiculous gps line: %d bytes\n", n);
941 for(p = s; isdigit(*p); p++)
944 memmove(s, p, strlen(p)+1);
946 if(n > 4 && s[n-3] == '*'){
950 while(*p) cs ^= *p++;
951 n = strtol(&s[n-2], nil, 16);
954 fprint(2, "Checksum error %s, 0x%x, 0x%x\n",
961 for(p = s; *p; rawin++)
962 raw[rawin & Rawmask] = *p++;
963 raw[rawin & Rawmask] = '\n';