xref: /plan9/sys/src/cmd/aux/gps/gpsfs.c (revision 3b86f2f88bade1f00206c7aa750b7add255f5724)
17c881178SDavid du Colombier #include <u.h>
27c881178SDavid du Colombier #include <libc.h>
37c881178SDavid du Colombier #include <fcall.h>
47c881178SDavid du Colombier #include <thread.h>
57c881178SDavid du Colombier #include <ctype.h>
67c881178SDavid du Colombier #include <9p.h>
77c881178SDavid du Colombier #include "dat.h"
87c881178SDavid du Colombier 
97c881178SDavid du Colombier enum
107c881178SDavid du Colombier {
117c881178SDavid du Colombier 	Numsize=	12,
127c881178SDavid du Colombier 	Vlnumsize=	22,
137c881178SDavid du Colombier 	Rawbuf=		0x10000,
147c881178SDavid du Colombier 	Rawmask=	Rawbuf-1,
157c881178SDavid du Colombier };
167c881178SDavid du Colombier 
177c881178SDavid du Colombier #define	nsecperchar	((int)(1000000000.0 * 10.0 / baud))
187c881178SDavid du Colombier 
197c881178SDavid du Colombier typedef struct Fix Fix;
207c881178SDavid du Colombier typedef struct Satellite Satellite;
217c881178SDavid du Colombier typedef struct GPSfile GPSfile;
227c881178SDavid du Colombier typedef struct Gpsmsg Gpsmsg;
237c881178SDavid du Colombier 
247c881178SDavid du Colombier struct Satellite {
257c881178SDavid du Colombier 	int		prn;
267c881178SDavid du Colombier 	int		elevation;
277c881178SDavid du Colombier 	int		azimuth;
287c881178SDavid du Colombier 	int		snr;
297c881178SDavid du Colombier };
307c881178SDavid du Colombier 
317c881178SDavid du Colombier struct Fix {
327c881178SDavid du Colombier 	int		messages;	/* bitmap of types seen */
337c881178SDavid du Colombier 	Place;
347c881178SDavid du Colombier 	/*
357c881178SDavid du Colombier 	 * The following are in Plan 9 time format:
367c881178SDavid du Colombier 	 * seconds or nanoseconds since the epoch.
377c881178SDavid du Colombier 	 */
387c881178SDavid du Colombier 	vlong		localtime;	/* nsec() value when first byte was read */
397c881178SDavid du Colombier 	vlong		gpstime;	/* nsec() value from GPS */
407c881178SDavid du Colombier 	long		time;		/* time() value from GPS */
417c881178SDavid du Colombier 
427c881178SDavid du Colombier 	double		zulu;
437c881178SDavid du Colombier 	int		date;
447c881178SDavid du Colombier 	char		valid;
457c881178SDavid du Colombier 	uchar		quality;
467c881178SDavid du Colombier 	ushort		satellites;
477c881178SDavid du Colombier 	double		pdop;
487c881178SDavid du Colombier 	double		hdop;
497c881178SDavid du Colombier 	double		vdop;
507c881178SDavid du Colombier 	double		altitude;
517c881178SDavid du Colombier 	double		sealevel;
527c881178SDavid du Colombier 	double		groundspeed;
537c881178SDavid du Colombier 	double		kmh;
547c881178SDavid du Colombier 	double		course;
557c881178SDavid du Colombier 	double		heading;
567c881178SDavid du Colombier 	double		magvar;
577c881178SDavid du Colombier 	Satellite	s[12];
587c881178SDavid du Colombier };
597c881178SDavid du Colombier 
607c881178SDavid du Colombier struct GPSfile {
617c881178SDavid du Colombier 	char	*name;
627c881178SDavid du Colombier 	char*	(*rread)(Req*);
637c881178SDavid du Colombier 	int	mode;
647c881178SDavid du Colombier 	vlong	offset;		/* for raw: rawout - read-offset */
657c881178SDavid du Colombier };
667c881178SDavid du Colombier 
677c881178SDavid du Colombier enum {
687c881178SDavid du Colombier 	ASTRAL,
697c881178SDavid du Colombier 	GPGGA,
707c881178SDavid du Colombier 	GPGLL,
717c881178SDavid du Colombier 	GPGSA,
727c881178SDavid du Colombier 	GPGSV,
737c881178SDavid du Colombier 	GPRMC,
747c881178SDavid du Colombier 	GPVTG,
757c881178SDavid du Colombier 	PRWIRID,
767c881178SDavid du Colombier 	PRWIZCH
777c881178SDavid du Colombier };
787c881178SDavid du Colombier 
797c881178SDavid du Colombier struct Gpsmsg {
807c881178SDavid du Colombier 	char *name;
817c881178SDavid du Colombier 	int tokens;
827c881178SDavid du Colombier 	ulong errors;
837c881178SDavid du Colombier };
847c881178SDavid du Colombier 
857c881178SDavid du Colombier char	raw[Rawbuf];
867c881178SDavid du Colombier vlong	rawin;
877c881178SDavid du Colombier vlong	rawout;
887c881178SDavid du Colombier 
895e6bdc2cSDavid du Colombier ulong	badlat, goodlat, suspectlat;
905e6bdc2cSDavid du Colombier ulong	badlon, goodlon, suspectlon;
915e6bdc2cSDavid du Colombier ulong	suspecttime, goodtime;
925e6bdc2cSDavid du Colombier 
937c881178SDavid du Colombier ulong histo[32];
947c881178SDavid du Colombier 
957c881178SDavid du Colombier char *serial = "/dev/eia0";
967c881178SDavid du Colombier 
977c881178SDavid du Colombier Gpsmsg gpsmsg[] = {
987c881178SDavid du Colombier [ASTRAL]	= { "ASTRAL",	 0,	0},
997c881178SDavid du Colombier [GPGGA]		= { "$GPGGA",	15,	0},
100*3b86f2f8SDavid du Colombier /* NMEA 2.3 permits optional 8th field, mode */
1017c881178SDavid du Colombier [GPGLL]		= { "$GPGLL",	 7,	0},
1027c881178SDavid du Colombier [GPGSA]		= { "$GPGSA",	18,	0},
1037c881178SDavid du Colombier [GPGSV]		= { "$GPGSV",	0,	0},
1047c881178SDavid du Colombier [GPRMC]		= { "$GPRMC",	0,	0},
1057c881178SDavid du Colombier [GPVTG]		= { "$GPVTG",	0,	0},
1067c881178SDavid du Colombier [PRWIRID]	= { "$PRWIRID",	0,	0},
1077c881178SDavid du Colombier [PRWIZCH]	= { "$PRWIZCH",	0,	0},
1087c881178SDavid du Colombier };
1097c881178SDavid du Colombier 
1107c881178SDavid du Colombier int ttyfd, ctlfd, debug;
1117c881178SDavid du Colombier int setrtc;
1127c881178SDavid du Colombier int baud = Baud;
1135e6bdc2cSDavid du Colombier char *baudstr = "b%dd1r1pns1l8i9";
1147c881178SDavid du Colombier ulong seconds;
1157c881178SDavid du Colombier ulong starttime;
1167c881178SDavid du Colombier ulong checksumerrors;
1177c881178SDavid du Colombier int gpsplayback;	/* If set, return times and positions with `invalid' marker set */
1187c881178SDavid du Colombier 
1197c881178SDavid du Colombier Place where = {-(74.0 + 23.9191/60.0), 40.0 + 41.1346/60.0};
1207c881178SDavid du Colombier 
1217c881178SDavid du Colombier Fix curfix;
1227c881178SDavid du Colombier Lock fixlock;
1237c881178SDavid du Colombier 
1247c881178SDavid du Colombier int	type(char*);
1257c881178SDavid du Colombier void	setline(void);
1267c881178SDavid du Colombier int	getonechar(vlong*);
1277c881178SDavid du Colombier void	getline(char*, int, vlong*);
1287c881178SDavid du Colombier void	putline(char*);
1295e6bdc2cSDavid du Colombier int	gettime(Fix*);
1307c881178SDavid du Colombier int	getzulu(char *, Fix*);
1317c881178SDavid du Colombier int	getalt(char*, char*, Fix*);
1327c881178SDavid du Colombier int	getsea(char*, char*, Fix*);
1337c881178SDavid du Colombier int	getlat(char*, char*, Fix*);
1347c881178SDavid du Colombier int	getlon(char*, char*, Fix*);
1357c881178SDavid du Colombier int	getgs(char*, Fix *);
1367c881178SDavid du Colombier int	getkmh(char*, Fix*);
1377c881178SDavid du Colombier int	getcrs(char*, Fix*);
1387c881178SDavid du Colombier int	gethdg(char*, Fix*);
1397c881178SDavid du Colombier int	getdate(char*, Fix*);
1407c881178SDavid du Colombier int	getmagvar(char*, char*, Fix*);
1417c881178SDavid du Colombier void	printfix(int, Fix*);
1427c881178SDavid du Colombier void	ropen(Req *r);
1437c881178SDavid du Colombier void	rread(Req *r);
1447c881178SDavid du Colombier void	rend(Srv *s);
1457c881178SDavid du Colombier void	gpsinit(void);
1467c881178SDavid du Colombier char*	readposn(Req*);
1477c881178SDavid du Colombier char*	readtime(Req*);
1487c881178SDavid du Colombier char*	readsats(Req*);
1497c881178SDavid du Colombier char*	readstats(Req*);
1507c881178SDavid du Colombier char*	readraw(Req*);
1517c881178SDavid du Colombier 
1527c881178SDavid du Colombier GPSfile files[] = {
1537c881178SDavid du Colombier 	{ "time",	readtime,	0444,	0 },
1547c881178SDavid du Colombier 	{ "position",	readposn,	0444,	0 },
1557c881178SDavid du Colombier 	{ "satellites",	readsats,	0444,	0 },
1567c881178SDavid du Colombier 	{ "stats",	readstats,	0444,	0 },
1577c881178SDavid du Colombier 	{ "raw",	readraw,	DMEXCL|0444, 0 },
1587c881178SDavid du Colombier };
1597c881178SDavid du Colombier 
1607c881178SDavid du Colombier Srv s = {
1617c881178SDavid du Colombier 	.open	= ropen,
1627c881178SDavid du Colombier 	.read	= rread,
1637c881178SDavid du Colombier 
1647c881178SDavid du Colombier 	.end = rend,
1657c881178SDavid du Colombier };
1667c881178SDavid du Colombier 
1677c881178SDavid du Colombier File *root;
1687c881178SDavid du Colombier File *gpsdir;
1697c881178SDavid du Colombier 
1707c881178SDavid du Colombier void
rend(Srv *)1717c881178SDavid du Colombier rend(Srv *)
1727c881178SDavid du Colombier {
1737c881178SDavid du Colombier 	sysfatal("gpsfs demised");
1747c881178SDavid du Colombier }
1757c881178SDavid du Colombier 
1767c881178SDavid du Colombier void
ropen(Req * r)1777c881178SDavid du Colombier ropen(Req *r)
1787c881178SDavid du Colombier {
1797c881178SDavid du Colombier 	respond(r, nil);
1807c881178SDavid du Colombier }
1817c881178SDavid du Colombier 
1827c881178SDavid du Colombier void
rread(Req * r)1837c881178SDavid du Colombier rread(Req *r)
1847c881178SDavid du Colombier {
1857c881178SDavid du Colombier 	GPSfile *f;
1867c881178SDavid du Colombier 
1877c881178SDavid du Colombier 	r->ofcall.count = 0;
1887c881178SDavid du Colombier 	f = r->fid->file->aux;
1897c881178SDavid du Colombier 	respond(r, f->rread(r));
1907c881178SDavid du Colombier }
1917c881178SDavid du Colombier 
1927c881178SDavid du Colombier void
fsinit(void)1937c881178SDavid du Colombier fsinit(void)
1947c881178SDavid du Colombier {
1957c881178SDavid du Colombier 	char* user;
1967c881178SDavid du Colombier 	int i;
1977c881178SDavid du Colombier 
1987c881178SDavid du Colombier 	user = getuser();
1997c881178SDavid du Colombier 	s.tree = alloctree(user, user, 0555, nil);
2007c881178SDavid du Colombier 	if(s.tree == nil)
2017c881178SDavid du Colombier 		sysfatal("fsinit: alloctree: %r");
2027c881178SDavid du Colombier 	root = s.tree->root;
2037c881178SDavid du Colombier 	if((gpsdir = createfile(root, "gps", user, DMDIR|0555, nil)) == nil)
2047c881178SDavid du Colombier 		sysfatal("fsinit: createfile: gps: %r");
2057c881178SDavid du Colombier 	for(i = 0; i < nelem(files); i++)
2067c881178SDavid du Colombier 		if(createfile(gpsdir, files[i].name, user, files[i].mode, files + i) == nil)
2077c881178SDavid du Colombier 			sysfatal("fsinit: createfile: %s: %r", files[i].name);
2087c881178SDavid du Colombier }
2097c881178SDavid du Colombier 
2107c881178SDavid du Colombier void
threadmain(int argc,char * argv[])2117c881178SDavid du Colombier threadmain(int argc, char*argv[])
2127c881178SDavid du Colombier {
2137c881178SDavid du Colombier 	char *srvname, *mntpt;
2147c881178SDavid du Colombier 
2157c881178SDavid du Colombier 	srvname = "gps";
2167c881178SDavid du Colombier 	mntpt = "/mnt";
2177c881178SDavid du Colombier 
2187c881178SDavid du Colombier 	ARGBEGIN {
2197c881178SDavid du Colombier 	default:
2207c881178SDavid du Colombier 		fprint(2, "usage: %s [-b baud] [-d device] [-l logfile] [-m mntpt] [-r] [-s postname]\n", argv0);
2217c881178SDavid du Colombier 		exits("usage");
2227c881178SDavid du Colombier 	case 'D':
2237c881178SDavid du Colombier 		debug++;
2247c881178SDavid du Colombier 		break;
2257c881178SDavid du Colombier 	case 'b':
2267c881178SDavid du Colombier 		baud = strtol(ARGF(), nil, 0);
2277c881178SDavid du Colombier 		break;
2287c881178SDavid du Colombier 	case 'd':
2297c881178SDavid du Colombier 		serial = ARGF();
2307c881178SDavid du Colombier 		break;
2317c881178SDavid du Colombier 	case 'r':
2327c881178SDavid du Colombier 		setrtc = 1;
2337c881178SDavid du Colombier 		break;
2347c881178SDavid du Colombier 	case 's':
2357c881178SDavid du Colombier 		srvname = ARGF();
2367c881178SDavid du Colombier 		break;
2377c881178SDavid du Colombier 	case 'm':
2387c881178SDavid du Colombier 		mntpt = ARGF();
2397c881178SDavid du Colombier 		break;
2407c881178SDavid du Colombier 	} ARGEND
2417c881178SDavid du Colombier 
2427c881178SDavid du Colombier 	fmtinstall('L', placeconv);
2437c881178SDavid du Colombier 
2447c881178SDavid du Colombier 	rfork(RFNOTEG);
2457c881178SDavid du Colombier 
2467c881178SDavid du Colombier 	fsinit();
2477c881178SDavid du Colombier 	gpsinit();
2487c881178SDavid du Colombier 	threadpostmountsrv(&s, srvname, mntpt, MBEFORE);
2497c881178SDavid du Colombier 	threadexits(nil);
2507c881178SDavid du Colombier }
2517c881178SDavid du Colombier 
2527c881178SDavid du Colombier static void
gpstrack(void *)2537c881178SDavid du Colombier gpstrack(void *)
2547c881178SDavid du Colombier {
2557c881178SDavid du Colombier 	Fix fix;
2567c881178SDavid du Colombier 	static char buf[256], *t[32];
2577c881178SDavid du Colombier 	int n, i, k, tp;
2587c881178SDavid du Colombier 	vlong localtime;
259843560e6SDavid du Colombier 	double d;
2607c881178SDavid du Colombier 
2617c881178SDavid du Colombier 	setline();
2627c881178SDavid du Colombier 	fix.messages = 0;
2635e6bdc2cSDavid du Colombier 	fix.lon = 181.0;
2645e6bdc2cSDavid du Colombier 	fix.lat = 91.0;
2657c881178SDavid du Colombier 	fix.zulu = 0;
2667c881178SDavid du Colombier 	fix.date = 0;
2677c881178SDavid du Colombier 	fix.valid = 0;
2687c881178SDavid du Colombier 	fix.quality = 0;
2697c881178SDavid du Colombier 	fix.satellites = 0;
2707c881178SDavid du Colombier 	fix.pdop = 0.0;
2717c881178SDavid du Colombier 	fix.hdop = 0.0;
2727c881178SDavid du Colombier 	fix.vdop = 0.0;
2737c881178SDavid du Colombier 	fix.altitude = 0.0;
2747c881178SDavid du Colombier 	fix.sealevel = 0.0;
2757c881178SDavid du Colombier 	fix.groundspeed = 0.0;
2767c881178SDavid du Colombier 	fix.kmh = 0.0;
2777c881178SDavid du Colombier 	fix.course = 0.0;
2787c881178SDavid du Colombier 	fix.heading = 0.0;
2797c881178SDavid du Colombier 	fix.magvar = 0.0;
2807c881178SDavid du Colombier 	for(;;){
2817c881178SDavid du Colombier 		getline(buf, sizeof buf, &localtime);
2827c881178SDavid du Colombier 		n = getfields(buf, t, nelem(t), 0,",\r\n");
2837c881178SDavid du Colombier 		if(n == 0)
2847c881178SDavid du Colombier 			continue;
2857c881178SDavid du Colombier 		tp = type(t[0]);
286*3b86f2f8SDavid du Colombier 		if(tp >= 0 && tp < nelem(gpsmsg) && gpsmsg[tp].tokens &&
287*3b86f2f8SDavid du Colombier 		    gpsmsg[tp].tokens > n){
2887c881178SDavid du Colombier 			gpsmsg[tp].errors++;
2897c881178SDavid du Colombier 			if(debug)
2907c881178SDavid du Colombier 				fprint(2, "%s: Expect %d tokens, got %d\n",
2917c881178SDavid du Colombier 					gpsmsg[tp].name, gpsmsg[tp].tokens, n);
2927c881178SDavid du Colombier 			continue;
2937c881178SDavid du Colombier 		}
2947c881178SDavid du Colombier 		switch(tp){
2957c881178SDavid du Colombier 		case ASTRAL:
2967c881178SDavid du Colombier 			putline("$IIGPQ,ASTRAL*73");
2977c881178SDavid du Colombier 			putline("$PRWIILOG,GGA,A,T,10,0");
2987c881178SDavid du Colombier 			putline("$PRWIILOG,RMC,A,T,10,0");
2997c881178SDavid du Colombier 			putline("$PRWIILOG,GSA,A,T,10,0");
3007c881178SDavid du Colombier 			putline("$PRWIILOG,GSV,V,,,");
3017c881178SDavid du Colombier 			fprint(2, "Reply: %s\n", "$IIGPQ,ASTRAL*73");
3027c881178SDavid du Colombier 			break;
3037c881178SDavid du Colombier 		case PRWIRID:
3047c881178SDavid du Colombier 		case PRWIZCH:
3057c881178SDavid du Colombier 			for(i = 0; i < n; i++) fprint(2, "%s,", t[i]);
3067c881178SDavid du Colombier 			fprint(2, "(%d tokens)\n", n);
3077c881178SDavid du Colombier 			break;
3087c881178SDavid du Colombier 		case GPGGA:
3095e6bdc2cSDavid du Colombier 			if(getlat(t[2], t[3], &fix))
3105e6bdc2cSDavid du Colombier 				break;
3115e6bdc2cSDavid du Colombier 			if(getlon(t[4], t[5], &fix))
3125e6bdc2cSDavid du Colombier 				break;
3137c881178SDavid du Colombier 			getzulu(t[1], &fix);
3145e6bdc2cSDavid du Colombier 			if(fix.date && gettime(&fix))
3155e6bdc2cSDavid du Colombier 				break;
3167c881178SDavid du Colombier 			if(isdigit(*t[7]))
3177c881178SDavid du Colombier 				fix.satellites = strtol(t[7], nil, 10);
318843560e6SDavid du Colombier 			if(isdigit(*t[8])){
319843560e6SDavid du Colombier 				d = strtod(t[8], nil);
320843560e6SDavid du Colombier 				if(!isNaN(d))
321843560e6SDavid du Colombier 					fix.hdop = d;
322843560e6SDavid du Colombier 			}
3237c881178SDavid du Colombier 			getalt(t[9], t[10], &fix);
3247c881178SDavid du Colombier 			getsea(t[11], t[12], &fix);
3255e6bdc2cSDavid du Colombier 			fix.localtime = localtime;
3265e6bdc2cSDavid du Colombier 			fix.quality = strtol(t[6], nil, 10);
3275e6bdc2cSDavid du Colombier 			fix.messages |= 1 << tp;
3287c881178SDavid du Colombier 			break;
3297c881178SDavid du Colombier 		case GPRMC:
3307c881178SDavid du Colombier 			fix.valid = *t[2];
3317c881178SDavid du Colombier 			getgs(t[7], &fix);
3327c881178SDavid du Colombier 			getcrs(t[8], &fix);
3337c881178SDavid du Colombier 			getdate(t[9], &fix);
3347c881178SDavid du Colombier 			getmagvar(t[10], t[11], &fix);
3357c881178SDavid du Colombier 			if((fix.messages & (1 << GPGGA)) == 0){
3365e6bdc2cSDavid du Colombier 				if(getlat(t[3], t[4], &fix))
3375e6bdc2cSDavid du Colombier 					break;
3385e6bdc2cSDavid du Colombier 				if(getlon(t[5], t[6], &fix))
3395e6bdc2cSDavid du Colombier 					break;
3407c881178SDavid du Colombier 				fix.localtime = localtime;
3417c881178SDavid du Colombier 				getzulu(t[1], &fix);
3427c881178SDavid du Colombier 				if(fix.date)
3437c881178SDavid du Colombier 					gettime(&fix);
3447c881178SDavid du Colombier 			}
3455e6bdc2cSDavid du Colombier 			fix.messages |= 1 << tp;
3467c881178SDavid du Colombier 			break;
3477c881178SDavid du Colombier 		case GPGSA:
348843560e6SDavid du Colombier 			if(*t[15]){
349843560e6SDavid du Colombier 				d = strtod(t[15], nil);
350843560e6SDavid du Colombier 				if(!isNaN(d))
351843560e6SDavid du Colombier 					fix.pdop = d;
352843560e6SDavid du Colombier 			}
353843560e6SDavid du Colombier 			if(*t[16]){
354843560e6SDavid du Colombier 				d = strtod(t[16], nil);
355843560e6SDavid du Colombier 				if(!isNaN(d))
356843560e6SDavid du Colombier 					fix.hdop = d;
357843560e6SDavid du Colombier 			}
358843560e6SDavid du Colombier 			if(*t[17]){
359843560e6SDavid du Colombier 				d = strtod(t[17], nil);
360843560e6SDavid du Colombier 				if(!isNaN(d))
361843560e6SDavid du Colombier 					fix.vdop = d;
362843560e6SDavid du Colombier 			}
3635e6bdc2cSDavid du Colombier 			fix.messages |= 1 << tp;
3647c881178SDavid du Colombier 			break;
3657c881178SDavid du Colombier 		case GPGLL:
3665e6bdc2cSDavid du Colombier 			if(getlat(t[1], t[2], &fix))
3675e6bdc2cSDavid du Colombier 				break;
3685e6bdc2cSDavid du Colombier 			if(getlon(t[3], t[4], &fix))
3695e6bdc2cSDavid du Colombier 				break;
3707c881178SDavid du Colombier 			getzulu(t[5], &fix);
3715e6bdc2cSDavid du Colombier 			fix.messages |= 1 << tp;
3727c881178SDavid du Colombier 			break;
3737c881178SDavid du Colombier 		case GPGSV:
3747c881178SDavid du Colombier 			if(n < 8){
3757c881178SDavid du Colombier 				gpsmsg[tp].errors++;
3767c881178SDavid du Colombier 				if(debug)
3777c881178SDavid du Colombier 					fprint(2, "%s: Expect at least 8 tokens, got %d\n",
3787c881178SDavid du Colombier 						gpsmsg[tp].name, n);
3797c881178SDavid du Colombier 				break;
3807c881178SDavid du Colombier 			}
3817c881178SDavid du Colombier 			i = 4*(strtol(t[2], nil, 10)-1);	/* starting entry in satellite table */
3827c881178SDavid du Colombier 			fix.satellites = strtol(t[3], nil, 10);
3837c881178SDavid du Colombier 			k = 4;
3847c881178SDavid du Colombier 			while(i < nelem(fix.s) && k + 3 < n){
3857c881178SDavid du Colombier 				fix.s[i].prn = strtol(t[k++], nil, 10);
3867c881178SDavid du Colombier 				fix.s[i].elevation = strtol(t[k++], nil, 10);
3877c881178SDavid du Colombier 				fix.s[i].azimuth = strtol(t[k++], nil, 10);
3887c881178SDavid du Colombier 				fix.s[i].snr = strtol(t[k++], nil, 10);
3897c881178SDavid du Colombier 				k += 4;
3907c881178SDavid du Colombier 				i++;
3917c881178SDavid du Colombier 			}
3925e6bdc2cSDavid du Colombier 			fix.messages |= 1 << tp;
3935e6bdc2cSDavid du Colombier 			break;
3947c881178SDavid du Colombier 		case GPVTG:
3957c881178SDavid du Colombier 			if(n < 8){
3967c881178SDavid du Colombier 				gpsmsg[tp].errors++;
3977c881178SDavid du Colombier 				if(debug)
3987c881178SDavid du Colombier 					fprint(2, "%s: Expect at least 8 tokens, got %d\n",
3997c881178SDavid du Colombier 						gpsmsg[tp].name, n);
4007c881178SDavid du Colombier 				break;
4017c881178SDavid du Colombier 			}
4027c881178SDavid du Colombier 			getcrs(t[2], &fix);
4037c881178SDavid du Colombier 			gethdg(t[4], &fix);
4047c881178SDavid du Colombier 			getgs(t[6], &fix);
4057c881178SDavid du Colombier 			if(n > 8)
4067c881178SDavid du Colombier 				getkmh(t[8], &fix);
4075e6bdc2cSDavid du Colombier 			fix.messages |= 1 << tp;
4087c881178SDavid du Colombier 			break;
4097c881178SDavid du Colombier 		default:
4107c881178SDavid du Colombier 			if(debug && fix.date)
4117c881178SDavid du Colombier 				fprint(2, "Don't know %s\n", t[0]);
4127c881178SDavid du Colombier 			break;
4137c881178SDavid du Colombier 		}
4147c881178SDavid du Colombier 		if(fix.valid){
4157c881178SDavid du Colombier 			seconds++;
4167c881178SDavid du Colombier 			lock(&fixlock);
4177c881178SDavid du Colombier 			memmove(&curfix, &fix, sizeof fix);
4187c881178SDavid du Colombier 			unlock(&fixlock);
4197c881178SDavid du Colombier 			if(debug)
4207c881178SDavid du Colombier 				printfix(2, &fix);
4217c881178SDavid du Colombier 			fix.valid = 0;
4227c881178SDavid du Colombier 			fix.messages = 0;
4237c881178SDavid du Colombier 			for(i = 0; i < nelem(fix.s); i++)
4247c881178SDavid du Colombier 				fix.s[i].prn = 0;
4257c881178SDavid du Colombier 			if(gpsplayback)
4267c881178SDavid du Colombier 				sleep(100);
4277c881178SDavid du Colombier 		}
4287c881178SDavid du Colombier 	}
4297c881178SDavid du Colombier }
4307c881178SDavid du Colombier 
4317c881178SDavid du Colombier void
gpsinit(void)4327c881178SDavid du Colombier gpsinit(void)
4337c881178SDavid du Colombier {
4347c881178SDavid du Colombier 	proccreate(gpstrack, nil, 4096);
4357c881178SDavid du Colombier }
4367c881178SDavid du Colombier 
4377c881178SDavid du Colombier void
printfix(int f,Fix * fix)4387c881178SDavid du Colombier printfix(int f, Fix *fix){
4397c881178SDavid du Colombier 	int i;
4407c881178SDavid du Colombier 
4417c881178SDavid du Colombier 	fprint(f, "%L, ", fix->Place);
4427c881178SDavid du Colombier 	fprint(f, "%g, ", fix->magvar);
4437c881178SDavid du Colombier 	fprint(f, "%gm - %gm = %gm, ", fix->altitude, fix->sealevel, fix->altitude - fix->sealevel);
4447c881178SDavid du Colombier 	fprint(f, "%06dZ(%g)-", (int)fix->zulu, fix->zulu);
4457c881178SDavid du Colombier 	fprint(f, "%06d\n", fix->date);
4467c881178SDavid du Colombier 	if(fix->lat >= 0)
4477c881178SDavid du Colombier 		fprint(f, "%11.8fN, ", fix->lat);
4487c881178SDavid du Colombier 	else
4497c881178SDavid du Colombier 		fprint(f, "%11.8fS, ", -fix->lat);
4507c881178SDavid du Colombier 	if(fix->lon >= 0)
4517c881178SDavid du Colombier 		fprint(f, "%12.8fE, ", fix->lon);
4527c881178SDavid du Colombier 	else
4537c881178SDavid du Colombier 		fprint(f, "%12.8fW, ", -fix->lon);
4547c881178SDavid du Colombier 	fprint(f, "%g@%g, ", fix->course, fix->groundspeed);
4557c881178SDavid du Colombier 	fprint(f, "(%c, %ds)\n", fix->valid, fix->satellites);
4567c881178SDavid du Colombier 	for(i = 0; i < nelem(fix->s); i++){
4577c881178SDavid du Colombier 		if(fix->s[i].prn == 0)
4587c881178SDavid du Colombier 			continue;
4597c881178SDavid du Colombier 		fprint(f, "[%d, %d°, %d°, %d]\n",
4607c881178SDavid du Colombier 			fix->s[i].prn, fix->s[i].elevation, fix->s[i].azimuth, fix->s[i].snr);
4617c881178SDavid du Colombier 	}
4627c881178SDavid du Colombier }
4637c881178SDavid du Colombier 
4647c881178SDavid du Colombier char*
readposn(Req * r)4657c881178SDavid du Colombier readposn(Req *r)
4667c881178SDavid du Colombier {
4677c881178SDavid du Colombier 	Fix f;
4687c881178SDavid du Colombier 	char buf[256];
4697c881178SDavid du Colombier 
4707c881178SDavid du Colombier 	lock(&fixlock);
4717c881178SDavid du Colombier 	memmove(&f, &curfix, sizeof f);
4727c881178SDavid du Colombier 	unlock(&fixlock);
4737c881178SDavid du Colombier 	snprint(buf, sizeof buf, "%x	%06dZ	%lud	%g	%g	%g	%g	%g	%g",
4747c881178SDavid du Colombier 		gpsplayback|f.quality, (int)f.zulu, f.time, f.lon, f.lat, f.altitude - f.sealevel,
4757c881178SDavid du Colombier 		f.course, f.groundspeed, f.magvar);
4767c881178SDavid du Colombier 	readstr(r, buf);
4777c881178SDavid du Colombier 	return nil;
4787c881178SDavid du Colombier }
4797c881178SDavid du Colombier 
4807c881178SDavid du Colombier char*
readtime(Req * r)4817c881178SDavid du Colombier readtime(Req *r)
4827c881178SDavid du Colombier {
4837c881178SDavid du Colombier 	Fix f;
4847c881178SDavid du Colombier 	char buf[Numsize+Vlnumsize+Vlnumsize+8];
4857c881178SDavid du Colombier 
4867c881178SDavid du Colombier 	lock(&fixlock);
4877c881178SDavid du Colombier 	memmove(&f, &curfix, sizeof f);
4887c881178SDavid du Colombier 	unlock(&fixlock);
4897c881178SDavid du Colombier 	seprint(buf, buf + sizeof buf, "%*.0lud %*.0llud %*.0llud %c",
4907c881178SDavid du Colombier 		Numsize-1, f.time,
4917c881178SDavid du Colombier 		Vlnumsize-1, f.gpstime,
4927c881178SDavid du Colombier 		Vlnumsize-1, f.localtime, f.valid + (gpsplayback?1:0));
4937c881178SDavid du Colombier 	readstr(r, buf);
4947c881178SDavid du Colombier 	return nil;
4957c881178SDavid du Colombier }
4967c881178SDavid du Colombier 
4977c881178SDavid du Colombier char*
readstats(Req * r)4987c881178SDavid du Colombier readstats(Req *r)
4997c881178SDavid du Colombier {
5007c881178SDavid du Colombier 	int i;
5017c881178SDavid du Colombier 	char buf[1024], *p;
5027c881178SDavid du Colombier 
5037c881178SDavid du Colombier 	p = buf;
5047c881178SDavid du Colombier 	p = seprint(p, buf + sizeof buf, "%lld bytes read, %ld samples processed in %ld seconds\n",
5057c881178SDavid du Colombier 		rawin, seconds, curfix.time - starttime);
5067c881178SDavid du Colombier 	p = seprint(p, buf + sizeof buf, "%lud checksum errors\n", checksumerrors);
5077c881178SDavid du Colombier 	p = seprint(p, buf + sizeof buf, "format errors:");
5087c881178SDavid du Colombier 	for(i = 0; i < nelem(gpsmsg); i++){
5097c881178SDavid du Colombier 		p = seprint(p, buf + sizeof buf, "[%s]: %ld, ",
5107c881178SDavid du Colombier 			gpsmsg[i].name, gpsmsg[i].errors);
5117c881178SDavid du Colombier 	}
5127c881178SDavid du Colombier 	p = seprint(p, buf + sizeof buf, "\nhistogram of # bytes received per buffer:\n");
5137c881178SDavid du Colombier 	for(i = 0; i < nelem(histo); i++){
5147c881178SDavid du Colombier 		p = seprint(p, buf + sizeof buf, "[%d]: %ld ",
5157c881178SDavid du Colombier 			i, histo[i]);
5167c881178SDavid du Colombier 	}
5175e6bdc2cSDavid du Colombier 	p = seprint(p, buf + sizeof buf, "\n");
5185e6bdc2cSDavid du Colombier 	p = seprint(p, buf + sizeof buf, "bad/good/suspect lat: %lud/%lud/%lud\n",
5195e6bdc2cSDavid du Colombier 		badlat, goodlat, suspectlat);
5205e6bdc2cSDavid du Colombier 	p = seprint(p, buf + sizeof buf, "bad/good/suspect lon: %lud/%lud/%lud\n",
5215e6bdc2cSDavid du Colombier 		badlon, goodlon, suspectlon);
5225e6bdc2cSDavid du Colombier 	p = seprint(p, buf + sizeof buf, "good/suspect time: %lud/%lud\n", goodtime, suspecttime);
5235e6bdc2cSDavid du Colombier 	USED(p);
5247c881178SDavid du Colombier 	readstr(r, buf);
5257c881178SDavid du Colombier 	return nil;
5267c881178SDavid du Colombier }
5277c881178SDavid du Colombier 
5287c881178SDavid du Colombier char*
readsats(Req * r)5297c881178SDavid du Colombier readsats(Req *r)
5307c881178SDavid du Colombier {
5317c881178SDavid du Colombier 	Fix f;
5327c881178SDavid du Colombier 	int i;
5337c881178SDavid du Colombier 	char buf[1024], *p;
5347c881178SDavid du Colombier 
5357c881178SDavid du Colombier 	lock(&fixlock);
5367c881178SDavid du Colombier 	memmove(&f, &curfix, sizeof f);
5377c881178SDavid du Colombier 	unlock(&fixlock);
5387c881178SDavid du Colombier 	p = seprint(buf, buf + sizeof buf, "%d	%d\n", gpsplayback|f.quality, f.satellites);
5397c881178SDavid du Colombier 	for(i = 0; i < nelem(f.s); i++){
5407c881178SDavid du Colombier 		if(f.s[i].prn == 0)
5417c881178SDavid du Colombier 			continue;
5427c881178SDavid du Colombier 		p = seprint(p, buf + sizeof buf, "%d	%d	%d	%d\n",
5437c881178SDavid du Colombier 			f.s[i].prn, f.s[i].elevation, f.s[i].azimuth, f.s[i].snr);
5447c881178SDavid du Colombier 	}
5457c881178SDavid du Colombier 	readstr(r, buf);
5467c881178SDavid du Colombier 	return nil;
5477c881178SDavid du Colombier }
5487c881178SDavid du Colombier 
5497c881178SDavid du Colombier char*
readraw(Req * r)5507c881178SDavid du Colombier readraw(Req *r)
5517c881178SDavid du Colombier {
5527c881178SDavid du Colombier 	int n;
5537c881178SDavid du Colombier 	GPSfile *f;
5547c881178SDavid du Colombier 
5557c881178SDavid du Colombier 	f = r->fid->file->aux;
5567c881178SDavid du Colombier 	if(rawin - rawout > Rawbuf){
5577c881178SDavid du Colombier 		rawout = rawin - Rawbuf;
5587c881178SDavid du Colombier 		f->offset = rawout - r->ifcall.offset;
5597c881178SDavid du Colombier 	}
5607c881178SDavid du Colombier 	n = Rawbuf - (rawout&Rawmask);
5617c881178SDavid du Colombier 	if(rawin - rawout < n)
5627c881178SDavid du Colombier 		n = rawin - rawout;
5637c881178SDavid du Colombier 	if(r->ifcall.count < n)
5647c881178SDavid du Colombier 		n = r->ifcall.count;
5657c881178SDavid du Colombier 	r->ofcall.count = n;
5667c881178SDavid du Colombier 	if(n > 0){
5677c881178SDavid du Colombier 		memmove(r->ofcall.data, raw + (rawout & Rawmask), n);
5687c881178SDavid du Colombier 		rawout += n;
5697c881178SDavid du Colombier 	}
5707c881178SDavid du Colombier 	return nil;
5717c881178SDavid du Colombier }
5727c881178SDavid du Colombier 
5737c881178SDavid du Colombier void
rtcset(long t)5747c881178SDavid du Colombier rtcset(long t)
5757c881178SDavid du Colombier {
5767c881178SDavid du Colombier 	static int fd;
5777c881178SDavid du Colombier 	long r;
5787c881178SDavid du Colombier 	int n;
5797c881178SDavid du Colombier 	char buf[32];
5807c881178SDavid du Colombier 
5817c881178SDavid du Colombier 	if(fd <= 0 && (fd = open("#r/rtc", ORDWR)) < 0){
5827c881178SDavid du Colombier 		fprint(2, "Can't open #r/rtc: %r\n");
5837c881178SDavid du Colombier 		return;
5847c881178SDavid du Colombier 	}
5857c881178SDavid du Colombier 	n = read(fd, buf, sizeof buf - 1);
5867c881178SDavid du Colombier 	if(n <= 0){
5877c881178SDavid du Colombier 		fprint(2, "Can't read #r/rtc: %r\n");
5887c881178SDavid du Colombier 		return;
5897c881178SDavid du Colombier 	}
5907c881178SDavid du Colombier 	buf[n] = '\0';
5917c881178SDavid du Colombier 	r = strtol(buf, nil, 0);
5927c881178SDavid du Colombier 	if(r <= 0){
5937c881178SDavid du Colombier 		fprint(2, "ridiculous #r/rtc: %ld\n", r);
5947c881178SDavid du Colombier 		return;
5957c881178SDavid du Colombier 	}
5967c881178SDavid du Colombier 	if(r - t > 1 || t - r > 0){
5977c881178SDavid du Colombier 		seek(fd, 0, 0);
5987c881178SDavid du Colombier 		fprint(fd, "%ld", t);
5997c881178SDavid du Colombier 		fprint(2, "correcting #r/rtc: %ld → %ld\n", r, t);
6007c881178SDavid du Colombier 	}
6017c881178SDavid du Colombier 	seek(fd, 0, 0);
6027c881178SDavid du Colombier }
6037c881178SDavid du Colombier 
6045e6bdc2cSDavid du Colombier int
gettime(Fix * f)6057c881178SDavid du Colombier gettime(Fix *f){
6067c881178SDavid du Colombier 	/* Convert zulu time and date to Plan9 time(2) */
6077c881178SDavid du Colombier 	Tm tm;
6087c881178SDavid du Colombier 	int zulu;
6097c881178SDavid du Colombier 	double d;
6105e6bdc2cSDavid du Colombier 	long t;
6115e6bdc2cSDavid du Colombier 	static int count;
6127c881178SDavid du Colombier 
6137c881178SDavid du Colombier 	zulu = f->zulu;
6147c881178SDavid du Colombier 	memset(&tm, 0, sizeof tm );
6157c881178SDavid du Colombier 	tm.sec = zulu % 100;
6167c881178SDavid du Colombier 	tm.min = (zulu/100) % 100;
6177c881178SDavid du Colombier 	tm.hour = zulu / 10000;
6187c881178SDavid du Colombier 	tm.year = f->date % 100 + 100;	/* This'll only work until 2099 */
6197c881178SDavid du Colombier 	tm.mon = ((f->date/100) % 100) - 1;
6207c881178SDavid du Colombier 	tm.mday = f->date / 10000;
6217c881178SDavid du Colombier 	strcpy(tm.zone, "GMT");
6225e6bdc2cSDavid du Colombier 	t = tm2sec(&tm);
6235e6bdc2cSDavid du Colombier 	if(f->time && count < 3 && (t - f->time > 10 || t - f->time <= 0)){
6245e6bdc2cSDavid du Colombier 		count++;
6255e6bdc2cSDavid du Colombier 		suspecttime++;
6265e6bdc2cSDavid du Colombier 		return -1;
6275e6bdc2cSDavid du Colombier 	}
6285e6bdc2cSDavid du Colombier 	goodtime++;
6295e6bdc2cSDavid du Colombier 	f->time = t;
6305e6bdc2cSDavid du Colombier 	count = 0;
6315e6bdc2cSDavid du Colombier 	if(starttime == 0) starttime = t;
6325e6bdc2cSDavid du Colombier 	f->gpstime = 1000000000LL * t + 1000000 * (int)modf(f->zulu, &d);
6337c881178SDavid du Colombier 	if(setrtc){
6345e6bdc2cSDavid du Colombier 		if(setrtc == 1 || (t % 300) == 0){
6355e6bdc2cSDavid du Colombier 			rtcset(t);
6367c881178SDavid du Colombier 			setrtc++;
6377c881178SDavid du Colombier 		}
6387c881178SDavid du Colombier 	}
6395e6bdc2cSDavid du Colombier 	return 0;
6407c881178SDavid du Colombier }
6417c881178SDavid du Colombier 
6427c881178SDavid du Colombier int
getzulu(char * s,Fix * f)6437c881178SDavid du Colombier getzulu(char *s, Fix *f){
644843560e6SDavid du Colombier 	double d;
645843560e6SDavid du Colombier 
6467c881178SDavid du Colombier 	if(*s == '\0') return 0;
6477c881178SDavid du Colombier 	if(isdigit(*s)){
648843560e6SDavid du Colombier 		d = strtod(s, nil);
649843560e6SDavid du Colombier 		if(!isNaN(d))
650843560e6SDavid du Colombier 			f->zulu = d;
6517c881178SDavid du Colombier 		return 1;
6527c881178SDavid du Colombier 	}
6537c881178SDavid du Colombier 	return 0;
6547c881178SDavid du Colombier }
6557c881178SDavid du Colombier 
6567c881178SDavid du Colombier int
getdate(char * s,Fix * f)6577c881178SDavid du Colombier getdate(char *s, Fix *f){
6587c881178SDavid du Colombier 	if(*s == 0) return 0;
6597c881178SDavid du Colombier 	if(isdigit(*s)){
6607c881178SDavid du Colombier 		f->date = strtol(s, nil, 10);
6617c881178SDavid du Colombier 		return 1;
6627c881178SDavid du Colombier 	}
6637c881178SDavid du Colombier 	return 0;
6647c881178SDavid du Colombier }
6657c881178SDavid du Colombier 
6667c881178SDavid du Colombier int
getgs(char * s,Fix * f)667843560e6SDavid du Colombier getgs(char *s, Fix *f){
668843560e6SDavid du Colombier 	double d;
6697c881178SDavid du Colombier 
670843560e6SDavid du Colombier 	if(*s == 0) return 0;
671843560e6SDavid du Colombier 	if(isdigit(*s)){
672843560e6SDavid du Colombier 		d = strtod(s, nil);
673843560e6SDavid du Colombier 		if(!isNaN(d))
674843560e6SDavid du Colombier 			f->groundspeed = d;
6757c881178SDavid du Colombier 		return 1;
6767c881178SDavid du Colombier 	}
6777c881178SDavid du Colombier 	return 0;
6787c881178SDavid du Colombier }
6797c881178SDavid du Colombier 
6807c881178SDavid du Colombier int
getkmh(char * s,Fix * f)681843560e6SDavid du Colombier getkmh(char *s, Fix *f){
682843560e6SDavid du Colombier 	double d;
6837c881178SDavid du Colombier 
684843560e6SDavid du Colombier 	if(*s == 0) return 0;
685843560e6SDavid du Colombier 	if(isdigit(*s)){
686843560e6SDavid du Colombier 		d = strtod(s, nil);
687843560e6SDavid du Colombier 		if(!isNaN(d))
688843560e6SDavid du Colombier 			f->kmh = d;
6897c881178SDavid du Colombier 		return 1;
6907c881178SDavid du Colombier 	}
6917c881178SDavid du Colombier 	return 0;
6927c881178SDavid du Colombier }
6937c881178SDavid du Colombier 
6947c881178SDavid du Colombier int
getcrs(char * s1,Fix * f)6957c881178SDavid du Colombier getcrs(char *s1, Fix *f){
696843560e6SDavid du Colombier 	double d;
6977c881178SDavid du Colombier 
6987c881178SDavid du Colombier 	if(*s1 == 0) return 0;
6997c881178SDavid du Colombier 	if(isdigit(*s1)){
700843560e6SDavid du Colombier 		d = strtod(s1, nil);
701843560e6SDavid du Colombier 		if(!isNaN(d))
702843560e6SDavid du Colombier 			f->course = d;
7037c881178SDavid du Colombier 		return 1;
7047c881178SDavid du Colombier 	}
7057c881178SDavid du Colombier 	return 0;
7067c881178SDavid du Colombier }
7077c881178SDavid du Colombier 
7087c881178SDavid du Colombier int
gethdg(char * s1,Fix * f)7097c881178SDavid du Colombier gethdg(char *s1, Fix *f){
710843560e6SDavid du Colombier 	double d;
7117c881178SDavid du Colombier 
7127c881178SDavid du Colombier 	if(*s1 == 0) return 0;
7137c881178SDavid du Colombier 	if(isdigit(*s1)){
714843560e6SDavid du Colombier 		d = strtod(s1, nil);
715843560e6SDavid du Colombier 		if(!isNaN(d))
716843560e6SDavid du Colombier 			f->heading = d;
7177c881178SDavid du Colombier 		return 1;
7187c881178SDavid du Colombier 	}
7197c881178SDavid du Colombier 	return 0;
7207c881178SDavid du Colombier }
7217c881178SDavid du Colombier 
7227c881178SDavid du Colombier int
getalt(char * s1,char * s2,Fix * f)7237c881178SDavid du Colombier getalt(char *s1, char *s2, Fix *f){
7247c881178SDavid du Colombier 	double alt;
7257c881178SDavid du Colombier 
7267c881178SDavid du Colombier 	if(*s1 == 0) return 0;
7277c881178SDavid du Colombier 	if(isdigit(*s1)){
7287c881178SDavid du Colombier 		alt = strtod(s1, nil);
729843560e6SDavid du Colombier 		if(*s2 == 'M' && !isNaN(alt)){
7307c881178SDavid du Colombier 			f->altitude = alt;
7317c881178SDavid du Colombier 			return 1;
7327c881178SDavid du Colombier 		}
7337c881178SDavid du Colombier 		return 0;
7347c881178SDavid du Colombier 	}
7357c881178SDavid du Colombier 	return 0;
7367c881178SDavid du Colombier }
7377c881178SDavid du Colombier 
7387c881178SDavid du Colombier int
getsea(char * s1,char * s2,Fix * f)7397c881178SDavid du Colombier getsea(char *s1, char *s2, Fix *f){
7407c881178SDavid du Colombier 	double alt;
7417c881178SDavid du Colombier 
7427c881178SDavid du Colombier 	if(*s1 == 0) return 0;
7437c881178SDavid du Colombier 	if(isdigit(*s1)){
7447c881178SDavid du Colombier 		alt = strtod(s1, nil);
7457c881178SDavid du Colombier 		if(*s2 == 'M'){
7467c881178SDavid du Colombier 			f->sealevel = alt;
7477c881178SDavid du Colombier 			return 1;
7487c881178SDavid du Colombier 		}
7497c881178SDavid du Colombier 		return 0;
7507c881178SDavid du Colombier 	}
7517c881178SDavid du Colombier 	return 0;
7527c881178SDavid du Colombier }
7537c881178SDavid du Colombier 
7547c881178SDavid du Colombier int
getlat(char * s1,char * s2,Fix * f)7557c881178SDavid du Colombier getlat(char *s1, char *s2, Fix *f){
7567c881178SDavid du Colombier 	double lat;
7575e6bdc2cSDavid du Colombier 	static count;
7587c881178SDavid du Colombier 
7595e6bdc2cSDavid du Colombier 	if(*s1 == 0 || !isdigit(*s1) || strlen(s1) <= 5){
7605e6bdc2cSDavid du Colombier 		badlat++;
7615e6bdc2cSDavid du Colombier 		return -1;
7625e6bdc2cSDavid du Colombier 	}
7637c881178SDavid du Colombier 	lat = strtod(s1+2, nil);
764843560e6SDavid du Colombier 	if(isNaN(lat)){
765843560e6SDavid du Colombier 		badlat++;
766843560e6SDavid du Colombier 		return -1;
767843560e6SDavid du Colombier 	}
7687c881178SDavid du Colombier 	lat /= 60.0;
7697c881178SDavid du Colombier 	lat += 10*(s1[0] - '0') + s1[1] - '0';
7705e6bdc2cSDavid du Colombier 	if(lat < 0 || lat > 90.0){
7715e6bdc2cSDavid du Colombier 		badlat++;
7725e6bdc2cSDavid du Colombier 		return -1;
7737c881178SDavid du Colombier 	}
7745e6bdc2cSDavid du Colombier 	switch(*s2){
7755e6bdc2cSDavid du Colombier 	default:
7765e6bdc2cSDavid du Colombier 		badlat++;
7775e6bdc2cSDavid du Colombier 		return -1;
7785e6bdc2cSDavid du Colombier 	case 'S':
7795e6bdc2cSDavid du Colombier 		lat = -lat;
7805e6bdc2cSDavid du Colombier 	case 'N':
7815e6bdc2cSDavid du Colombier 		break;
7825e6bdc2cSDavid du Colombier 	}
7835e6bdc2cSDavid du Colombier 	if(f->lat <= 90.0 && count < 3 && fabs(f->lat - lat) > 10.0){
7845e6bdc2cSDavid du Colombier 		count++;
7855e6bdc2cSDavid du Colombier 		suspectlat++;
7865e6bdc2cSDavid du Colombier 		return -1;
7875e6bdc2cSDavid du Colombier 	}
7887c881178SDavid du Colombier 	f->lat = lat;
7895e6bdc2cSDavid du Colombier 	count = 0;
7905e6bdc2cSDavid du Colombier 	goodlat++;
7917c881178SDavid du Colombier 	return 0;
7927c881178SDavid du Colombier }
7937c881178SDavid du Colombier 
7947c881178SDavid du Colombier int
getlon(char * s1,char * s2,Fix * f)7957c881178SDavid du Colombier getlon(char *s1, char *s2, Fix *f){
7967c881178SDavid du Colombier 	double lon;
7975e6bdc2cSDavid du Colombier 	static count;
7987c881178SDavid du Colombier 
7995e6bdc2cSDavid du Colombier 	if(*s1 == 0 || ! isdigit(*s1) || strlen(s1) <= 5){
8005e6bdc2cSDavid du Colombier 		badlon++;
8015e6bdc2cSDavid du Colombier 		return -1;
8025e6bdc2cSDavid du Colombier 	}
803843560e6SDavid du Colombier 	lon = strtod(s1+3, nil);
804843560e6SDavid du Colombier 	if(isNaN(lon)){
805843560e6SDavid du Colombier 		badlon++;
806843560e6SDavid du Colombier 		return -1;
807843560e6SDavid du Colombier 	}
808843560e6SDavid du Colombier 	lon /= 60.0;
809843560e6SDavid du Colombier 	lon += 100*(s1[0] - '0') + 10*(s1[1] - '0') + s1[2] - '0';
8105e6bdc2cSDavid du Colombier 	if(lon < 0 || lon > 180.0){
8115e6bdc2cSDavid du Colombier 		badlon++;
8125e6bdc2cSDavid du Colombier 		return -1;
8137c881178SDavid du Colombier 	}
8145e6bdc2cSDavid du Colombier 	switch(*s2){
8155e6bdc2cSDavid du Colombier 	default:
8165e6bdc2cSDavid du Colombier 		badlon++;
8175e6bdc2cSDavid du Colombier 		return -1;
8185e6bdc2cSDavid du Colombier 	case 'W':
8195e6bdc2cSDavid du Colombier 		lon = -lon;
8205e6bdc2cSDavid du Colombier 	case 'E':
8215e6bdc2cSDavid du Colombier 		break;
8225e6bdc2cSDavid du Colombier 	}
8235e6bdc2cSDavid du Colombier 	if(f->lon <= 180.0 && count < 3 && fabs(f->lon - lon) > 10.0){
8245e6bdc2cSDavid du Colombier 		count++;
8255e6bdc2cSDavid du Colombier 		suspectlon++;
8265e6bdc2cSDavid du Colombier 		return -1;
8275e6bdc2cSDavid du Colombier 	}
8287c881178SDavid du Colombier 	f->lon = lon;
8295e6bdc2cSDavid du Colombier 	goodlon++;
8305e6bdc2cSDavid du Colombier 	count = 0;
8317c881178SDavid du Colombier 	return 0;
8327c881178SDavid du Colombier }
8337c881178SDavid du Colombier 
8347c881178SDavid du Colombier int
getmagvar(char * s1,char * s2,Fix * f)8357c881178SDavid du Colombier getmagvar(char *s1, char *s2, Fix *f){
8367c881178SDavid du Colombier 	double magvar;
8377c881178SDavid du Colombier 
8387c881178SDavid du Colombier 	if(*s1 == 0) return 0;
8397c881178SDavid du Colombier 	if(isdigit(*s1) && strlen(s1) > 5){
840843560e6SDavid du Colombier 		magvar = strtod(s1+3, nil);
841843560e6SDavid du Colombier 		if(isNaN(magvar))
842843560e6SDavid du Colombier 			return 0;
843843560e6SDavid du Colombier 		magvar /= 60.0;
844843560e6SDavid du Colombier 		magvar += 100*(s1[0] - '0') + 10*(s1[1] - '0') + s1[2] - '0';
8457c881178SDavid du Colombier 		if(*s2 == 'W'){
8467c881178SDavid du Colombier 			f->magvar = -magvar;
8477c881178SDavid du Colombier 			return 1;
8487c881178SDavid du Colombier 		}
8497c881178SDavid du Colombier 		if(*s2 == 'E'){
8507c881178SDavid du Colombier 			f->magvar = magvar;
8517c881178SDavid du Colombier 			return 1;
8527c881178SDavid du Colombier 		}
8537c881178SDavid du Colombier 		return 0;
8547c881178SDavid du Colombier 	}
8557c881178SDavid du Colombier 	return 0;
8567c881178SDavid du Colombier }
8577c881178SDavid du Colombier 
8587c881178SDavid du Colombier void
putline(char * s)8597c881178SDavid du Colombier putline(char *s){
8607c881178SDavid du Colombier 	write(ttyfd, s, strlen(s));
8617c881178SDavid du Colombier 	write(ttyfd, "\r\n", 2);
8627c881178SDavid du Colombier }
8637c881178SDavid du Colombier 
8647c881178SDavid du Colombier int
type(char * s)8657c881178SDavid du Colombier type(char *s){
8667c881178SDavid du Colombier 	int i;
8677c881178SDavid du Colombier 
8687c881178SDavid du Colombier 	for(i = 0; i < nelem(gpsmsg); i++){
8697c881178SDavid du Colombier 		if(strcmp(s, gpsmsg[i].name) == 0) return i;
8707c881178SDavid du Colombier 	}
8717c881178SDavid du Colombier 	return -1;
8727c881178SDavid du Colombier }
8737c881178SDavid du Colombier 
8747c881178SDavid du Colombier void
setline(void)8757c881178SDavid du Colombier setline(void){
8767c881178SDavid du Colombier 	char *serialctl;
8777c881178SDavid du Colombier 
8787c881178SDavid du Colombier 	serialctl = smprint("%sctl", serial);
8797c881178SDavid du Colombier 	if((ttyfd = open(serial, ORDWR)) < 0)
8807c881178SDavid du Colombier 		sysfatal("%s: %r", serial);
8817c881178SDavid du Colombier 	if((ctlfd = open(serialctl, OWRITE)) >= 0){
8827c881178SDavid du Colombier 		if(fprint(ctlfd, baudstr, baud) < 0)
8837c881178SDavid du Colombier 			sysfatal("%s: %r", serialctl);
8847c881178SDavid du Colombier 	}else
8857c881178SDavid du Colombier 		gpsplayback = 0x8;
8867c881178SDavid du Colombier 	free(serialctl);
8877c881178SDavid du Colombier }
8887c881178SDavid du Colombier 
getonechar(vlong * t)8897c881178SDavid du Colombier int getonechar(vlong *t){
8907c881178SDavid du Colombier 	static char buf[32], *p;
8917c881178SDavid du Colombier 	static int n;
8927c881178SDavid du Colombier 
8937c881178SDavid du Colombier 	if(n == 0){
8947c881178SDavid du Colombier 		n = read(ttyfd, buf, sizeof(buf));
8957c881178SDavid du Colombier 		if(t) *t = nsec();
8967c881178SDavid du Colombier 		if(n < 0)
8977c881178SDavid du Colombier 			sysfatal("%s: %r", serial);
8987c881178SDavid du Colombier 		if(n == 0)
8997c881178SDavid du Colombier 			threadexits(nil);
9007c881178SDavid du Colombier 		/*
9017c881178SDavid du Colombier 		 * We received n characters, so the first must have been there
9027c881178SDavid du Colombier 		 * at least n/(10*baud) seconds (10 is 1 start
9037c881178SDavid du Colombier 		 * bit, one stop bit and 8 data bits per character)
9047c881178SDavid du Colombier 		 */
9057c881178SDavid du Colombier 		if(t) {
9067c881178SDavid du Colombier 			*t -= n * nsecperchar;
9077c881178SDavid du Colombier 			histo[n]++;
9087c881178SDavid du Colombier 		}
9097c881178SDavid du Colombier 		p = buf;
9107c881178SDavid du Colombier 	}
9117c881178SDavid du Colombier 	n--;
9127c881178SDavid du Colombier 	return *p++;
9137c881178SDavid du Colombier }
9147c881178SDavid du Colombier 
9157c881178SDavid du Colombier void
getline(char * s,int size,vlong * t)9167c881178SDavid du Colombier getline(char *s, int size, vlong *t){
9177c881178SDavid du Colombier 	uchar c;
9187c881178SDavid du Colombier 	char *p;
9197c881178SDavid du Colombier 	int n, cs;
9207c881178SDavid du Colombier 
9215e6bdc2cSDavid du Colombier tryagain:
9227c881178SDavid du Colombier 	for(;;){
9237c881178SDavid du Colombier 		p = s;
9247c881178SDavid du Colombier 		n = 0;
9257c881178SDavid du Colombier 		while((c = getonechar(t)) != '\n' && n < size){
9267c881178SDavid du Colombier 			t = nil;
9277c881178SDavid du Colombier 			if(c != '\r'){
9287c881178SDavid du Colombier 				*p++ = c;
9297c881178SDavid du Colombier 				n++;
9307c881178SDavid du Colombier 			}
9317c881178SDavid du Colombier 		}
9327c881178SDavid du Colombier 		if(n < size)
9337c881178SDavid du Colombier 			break;
9347c881178SDavid du Colombier 		while(getonechar(t) != '\n' && n < 4096)
9357c881178SDavid du Colombier 			n++;
9367c881178SDavid du Colombier 		if(n == 4096)
9377c881178SDavid du Colombier 			sysfatal("preposterous gps line, wrong baud rate?");
9387c881178SDavid du Colombier 		fprint(2, "ridiculous gps line: %d bytes\n", n);
9397c881178SDavid du Colombier 	}
9407c881178SDavid du Colombier 	*p = 0;
9417c881178SDavid du Colombier 	for(p = s; isdigit(*p); p++)
9427c881178SDavid du Colombier 		;
9437c881178SDavid du Colombier 	if(*p++ == '	')
9447c881178SDavid du Colombier 		memmove(s, p, strlen(p)+1);
9457c881178SDavid du Colombier 	if(s[0] == '$'){
9467c881178SDavid du Colombier 		if(n > 4 && s[n-3] == '*'){
9477c881178SDavid du Colombier 			s[n-3] = 0;
9487c881178SDavid du Colombier 			p = s+1;
9497c881178SDavid du Colombier 			cs = 0;
9507c881178SDavid du Colombier 			while(*p) cs ^= *p++;
9517c881178SDavid du Colombier 			n = strtol(&s[n-2], nil, 16);
9527c881178SDavid du Colombier 			if(n != cs){
9537c881178SDavid du Colombier 				if(debug)
9547c881178SDavid du Colombier 					fprint(2, "Checksum error %s, 0x%x, 0x%x\n",
9557c881178SDavid du Colombier 						s, n, cs);
9567c881178SDavid du Colombier 				checksumerrors++;
9575e6bdc2cSDavid du Colombier 				goto tryagain;
9587c881178SDavid du Colombier 			}
9597c881178SDavid du Colombier 		}
9607c881178SDavid du Colombier 	}
9617c881178SDavid du Colombier 	for(p = s; *p; rawin++)
9627c881178SDavid du Colombier 		raw[rawin & Rawmask] = *p++;
9637c881178SDavid du Colombier 	raw[rawin & Rawmask] = '\n';
9647c881178SDavid du Colombier 	rawin++;
9657c881178SDavid du Colombier }
966